├── pytrollercli
├── README.md
├── pytrollercli
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ └── create.py
│ ├── verb
│ │ ├── __init__.py
│ │ └── create.py
│ ├── command
│ │ ├── __init__.py
│ │ └── pytroller.py
│ └── resource
│ │ ├── __init__.py
│ │ ├── package
│ │ ├── controller_plugin.xml.em
│ │ ├── package.xml.em
│ │ └── CMakeLists.txt.em
│ │ ├── test
│ │ ├── test_params.yaml.em
│ │ ├── test_load_pytroller.cpp.em
│ │ ├── test_pytroller.hpp.em
│ │ └── test_pytroller.cpp.em
│ │ ├── src
│ │ ├── pytroller_parameters.yaml.em
│ │ ├── pytroller_logic.pyx.em
│ │ └── pytroller.cpp.em
│ │ ├── script
│ │ └── pytroller_logic_impl.py.em
│ │ └── include
│ │ ├── visibility_control.h.em
│ │ └── pytroller.hpp.em
├── resource
│ └── pytrollercli
├── CHANGELOG.rst
├── package.xml
└── setup.py
├── pytroller_tools
├── pytroller_tools
│ ├── __init__.py
│ └── generate_pxd.py
├── resource
│ └── pytroller_tools
├── setup.cfg
├── CHANGELOG.rst
├── package.xml
├── test
│ ├── test_pep257.py
│ ├── test_flake8.py
│ └── test_copyright.py
├── setup.py
└── LICENSE
├── .gitignore
├── pytroller
├── CMakeLists.txt
├── CHANGELOG.rst
└── package.xml
├── .github
├── dependabot.yml
└── workflows
│ ├── ci-format.yml
│ └── ci.yml
├── CONTRIBUTING.md
├── README.md
├── .pre-commit-config.yaml
└── LICENSE
/pytrollercli/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytrollercli/resource/pytrollercli:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytroller_tools/pytroller_tools/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytroller_tools/resource/pytroller_tools:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/api/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/verb/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/command/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/
2 | build/
3 | log/
4 | install/
5 | __pycache__/
6 |
--------------------------------------------------------------------------------
/pytroller_tools/setup.cfg:
--------------------------------------------------------------------------------
1 | [develop]
2 | script_dir=$base/lib/pytroller_tools
3 | [install]
4 | install_scripts=$base/lib/pytroller_tools
5 |
--------------------------------------------------------------------------------
/pytroller/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(pytroller NONE)
3 | find_package(ament_cmake REQUIRED)
4 | ament_package()
5 |
--------------------------------------------------------------------------------
/pytroller/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 | Changelog for package pytroller
3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 |
5 | 0.0.1 (2023-09-20)
6 | -------------------
7 |
--------------------------------------------------------------------------------
/pytrollercli/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 | Changelog for package pytrollercli
3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 |
5 | 0.0.1 (2023-09-20)
6 | -------------------
7 |
--------------------------------------------------------------------------------
/pytroller_tools/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 | Changelog for package pytroller_tools
3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 |
5 | 0.0.1 (2023-09-20)
6 | -------------------
7 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/package/controller_plugin.xml.em:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Python controller for ros2_control
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/test/test_params.yaml.em:
--------------------------------------------------------------------------------
1 | load_@(pytroller_name):
2 | ros__parameters:
3 | interface_full_names:
4 | - "joint1/position"
5 | - "joint2/position"
6 | command_topic_name: "~/commands"
7 | command_topic_type: "std_msgs/msg/Float64MultiArray"
8 |
9 | test_@(pytroller_name):
10 | ros__parameters:
11 | interface_full_names:
12 | - "joint1/position"
13 | - "joint2/position"
14 | command_topic_name: "~/commands"
15 | command_topic_type: "std_msgs/msg/Float64MultiArray"
16 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "github-actions"
9 | # Workflow files stored in the
10 | # default location of `.github/workflows`
11 | directory: "/"
12 | schedule:
13 | interval: "weekly"
14 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/src/pytroller_parameters.yaml.em:
--------------------------------------------------------------------------------
1 | @(pytroller_name):
2 | interface_full_names: {
3 | type: string_array,
4 | default_value: [],
5 | description: "Name (WARNING, full names, e.g., 'joint_1/effort') of the interface(s) to command",
6 | }
7 | command_topic_name: {
8 | type: string,
9 | default_value: "",
10 | description: "Optional. Name of the subscribed command topic"
11 | }
12 | command_topic_type: {
13 | type: string,
14 | default_value: "",
15 | description: "Optional. Type of the subscribed command topic"
16 | }
17 |
--------------------------------------------------------------------------------
/.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: Format
5 |
6 | on:
7 | workflow_dispatch:
8 | pull_request:
9 |
10 | jobs:
11 | pre-commit:
12 | name: Format
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: actions/setup-python@v5.0.0
17 | with:
18 | python-version: '3.10'
19 | - name: Install system hooks
20 | run: 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 |
--------------------------------------------------------------------------------
/pytroller/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | pytroller
5 | 0.1.0
6 | Meta-package aggregating the pytroller packages and documentation
7 | Maciej Bednarczyk
8 | Apache License 2.0
9 |
10 | ament_cmake
11 | pytrollercli
12 | pytroller_tools
13 |
14 |
15 | ament_cmake
16 |
17 |
18 |
--------------------------------------------------------------------------------
/pytroller_tools/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | pytroller_tools
5 | 0.1.0
6 | TODO: Package description
7 | mcbed
8 | Apache-2.0
9 |
10 | ament_copyright
11 | ament_flake8
12 | ament_pep257
13 | python3-pytest
14 |
15 |
16 | ament_python
17 |
18 |
19 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/script/pytroller_logic_impl.py.em:
--------------------------------------------------------------------------------
1 | # Copyright 2023 ICube-Robotics
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 | def pytroller_logic_impl(period, states, commands, msg, params):
16 |
17 | return commands
18 |
--------------------------------------------------------------------------------
/pytroller_tools/test/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 |
--------------------------------------------------------------------------------
/pytrollercli/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | pytrollercli
5 | 0.1.0
6 |
7 | The Pytroller command line tools for ROS2 Control.
8 |
9 | Maciej Bednarczyk
10 | Apache License 2.0
11 |
12 | rcl_interfaces
13 | rclpy
14 | ros2cli
15 | rosidl_runtime_py
16 |
17 | ament_copyright
18 | ament_flake8
19 | ament_pep257
20 | ament_xmllint
21 |
22 |
23 | ament_python
24 |
25 |
26 |
--------------------------------------------------------------------------------
/pytroller_tools/test/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, \
24 | 'Found %d code style errors / warnings:\n' % len(errors) + \
25 | '\n'.join(errors)
26 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/package/package.xml.em:
--------------------------------------------------------------------------------
1 |
2 |
3 | @pytroller_name
4 | 0.0.1
5 | Python controller for ros2_control.
6 | @maintainer_name
7 |
8 | Apache License 2.0
9 |
10 | ament_cmake
11 |
12 | controller_interface
13 | generate_parameter_library
14 | hardware_interface
15 | pluginlib
16 | rclcpp
17 | rclcpp_lifecycle
18 | realtime_tools
19 | cython3
20 |
21 | pytroller_tools
22 |
23 | ament_cmake_gmock
24 | controller_manager
25 | ros2_control_test_assets
26 |
27 |
28 | ament_cmake
29 |
30 |
31 |
--------------------------------------------------------------------------------
/pytroller_tools/test/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 | # Remove the `skip` decorator once the source file(s) have a copyright header
20 | @pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
21 | @pytest.mark.copyright
22 | @pytest.mark.linter
23 | def test_copyright():
24 | rc = main(argv=['.', 'test'])
25 | assert rc == 0, 'Found errors'
26 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/verb/create.py:
--------------------------------------------------------------------------------
1 | # Copyright 2023 ICube-Robotics
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 os
16 |
17 | from ros2cli.verb import VerbExtension
18 |
19 | from pytrollercli.api.create import create_pytroller
20 |
21 |
22 | class CreateVerb(VerbExtension):
23 | """Create a new pytroller for ros2_control."""
24 |
25 | def add_arguments(self, parser, cli_name):
26 | parser.add_argument(
27 | 'pytroller_name',
28 | default='pytroller',
29 | help='The pytroller name')
30 | parser.add_argument(
31 | '--destination-directory',
32 | default=os.curdir,
33 | help='Directory where to create the package directory')
34 |
35 | def main(self, *, args):
36 | create_pytroller(
37 | args.pytroller_name,
38 | args.destination_directory,
39 | )
40 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/command/pytroller.py:
--------------------------------------------------------------------------------
1 | # Copyright 2023 ICube-Robotics
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 |
16 | from ros2cli.command import add_subparsers_on_demand
17 | from ros2cli.command import CommandExtension
18 |
19 |
20 | class PytrollerCommand(CommandExtension):
21 | """Various control related sub-commands."""
22 |
23 | def add_arguments(self, parser, cli_name):
24 | self._subparser = parser
25 | # get verb extensions and let them add their arguments
26 | add_subparsers_on_demand(parser, cli_name, "_verb", "pytrollercli.verb", required=False)
27 |
28 | def main(self, *, parser, args):
29 | if not hasattr(args, "_verb"):
30 | # in case no verb was passed
31 | self._subparser.print_help()
32 | return 0
33 |
34 | extension = getattr(args, "_verb")
35 |
36 | # call the verb's main method
37 | return extension.main(args=args)
38 |
--------------------------------------------------------------------------------
/pytroller_tools/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # Copyright 2023 ICube-Robotics
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 | from setuptools import find_packages, setup
18 |
19 | package_name = 'pytroller_tools'
20 |
21 | setup(
22 | name=package_name,
23 | version='0.0.1',
24 | packages=find_packages(exclude=['test']),
25 | data_files=[
26 | ('share/ament_index/resource_index/packages',
27 | ['resource/' + package_name]),
28 | ('share/' + package_name, ['package.xml']),
29 | ],
30 | install_requires=['setuptools'],
31 | zip_safe=True,
32 | maintainer='Maciej Bednarczyk',
33 | maintainer_email='mcbed.robotics@gmail.com',
34 | url="https://github.com/ICube-Robotics/pytroller",
35 | description='Tools for Pytrollers: Python controllers for ros2_control',
36 | license='Apache-2.0',
37 | tests_require=['pytest'],
38 | entry_points={
39 | 'console_scripts': [
40 | "generate_pxd = pytroller_tools.generate_pxd:main",
41 | ],
42 | },
43 | )
44 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 | jobs:
9 | CI:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Prepare
13 | run: |
14 | mkdir -p ${{github.workspace}}/src
15 | - uses: actions/checkout@v4
16 | with:
17 | path: src/pytroller
18 |
19 | - name: Build
20 | uses: addnab/docker-run-action@v3
21 | with:
22 | image: ros:humble
23 | options: -v ${{github.workspace}}/:/ros/
24 | run: |
25 | cd /ros
26 | apt update && apt upgrade
27 | sudo apt install python3-pip -y
28 | pip3 install cython
29 | . /opt/ros/humble/setup.sh
30 | rosdep install --ignore-src --from-paths . -y -r
31 | colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
32 | . install/setup.sh
33 | ros2 pytroller create my_pytroller
34 | rosdep install --ignore-src --from-paths . -y -r
35 | colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
36 |
37 | - name: Test
38 | uses: addnab/docker-run-action@v3
39 | with:
40 | image: ros:humble
41 | options: -v ${{github.workspace}}/:/ros/
42 | run: |
43 | cd /ros
44 | apt update && apt upgrade
45 | sudo apt install python3-pip -y
46 | pip3 install cython
47 | . /opt/ros/humble/setup.sh
48 | rosdep install --ignore-src --from-paths . -y -r
49 | colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
50 | . install/setup.sh
51 | ros2 pytroller create my_pytroller
52 | rosdep install --ignore-src --from-paths . -y -r
53 | colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
54 | colcon test
55 | colcon test-result
56 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/test/test_load_pytroller.cpp.em:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ICube-Robotics
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/executors/single_threaded_executor.hpp"
21 | #include "ros2_control_test_assets/descriptions.hpp"
22 |
23 | TEST(TestLoad@(pytroller_class), load_plugin)
24 | {
25 | pluginlib::ClassLoader plugin_loader{
26 | "controller_interface", "controller_interface::ControllerInterface"};
27 | ASSERT_NO_THROW(
28 | plugin_loader.createSharedInstance("@(pytroller_name)/@(pytroller_class)")
29 | );
30 | }
31 |
32 | TEST(TestLoad@(pytroller_class), load_controller)
33 | {
34 | std::shared_ptr executor =
35 | std::make_shared();
36 |
37 | controller_manager::ControllerManager cm(
38 | std::make_unique(
39 | ros2_control_test_assets::minimal_robot_urdf),
40 | executor, "test_controller_manager");
41 |
42 | ASSERT_NE(
43 | cm.load_controller(
44 | "load_@(pytroller_name)",
45 | "@(pytroller_name)/@(pytroller_class)"
46 | ),
47 | nullptr
48 | );
49 | }
50 |
51 | int main(int argc, char ** argv)
52 | {
53 | ::testing::InitGoogleTest(&argc, argv);
54 | rclcpp::init(argc, argv);
55 | int result = RUN_ALL_TESTS();
56 | rclcpp::shutdown();
57 | return result;
58 | }
59 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/src/pytroller_logic.pyx.em:
--------------------------------------------------------------------------------
1 | # Copyright 2023 ICube-Robotics
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 | # distutils: language = c++
16 |
17 | from libcpp.unordered_map cimport unordered_map
18 | from libcpp.vector cimport vector
19 | from libcpp.string cimport string
20 |
21 | ##########################################################################################################################################################
22 |
23 | include "../script/@(pytroller_name)_logic_impl.py"
24 |
25 | include "@(pytroller_name)_parameters.pxd"
26 |
27 | ##########################################################################################################################################################
28 |
29 | import importlib
30 | from rclpy.serialization import deserialize_message
31 |
32 | cdef public int @(pytroller_name)_logic(
33 | double period,
34 | unordered_map[string, double] states,
35 | unordered_map[string, double] & commands,
36 | vector[int] & msg,
37 | Params param
38 | ):
39 | try:
40 | command_message = None
41 | # Read command msg if applicable (i.e., there is one...)
42 | if (len(msg) > 0):
43 | mt = param.command_topic_type.decode("utf-8").split('/')
44 | messagetype = getattr(importlib.import_module('.'.join(mt[:2])), mt[-1])
45 | command_message = deserialize_message(bytes(msg), type(messagetype()))
46 | # Either way, call python logic
47 | (&commands)[0] = pytroller_logic_impl(period, states, commands, command_message, param)
48 | except Exception as error:
49 | print("An exception occurred:", error)
50 | return -1
51 | return 0
52 |
--------------------------------------------------------------------------------
/pytrollercli/setup.py:
--------------------------------------------------------------------------------
1 | # Copyright 2023 ICube-Robotics
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 find_packages
16 | from setuptools import setup
17 |
18 | package_name = "pytrollercli"
19 |
20 | setup(
21 | name=package_name,
22 | version="0.0.1",
23 | packages=find_packages(exclude=["test"]),
24 | data_files=[
25 | ("share/" + package_name, ["package.xml"]),
26 | ("share/ament_index/resource_index/packages", ["resource/" + package_name]),
27 | ],
28 | install_requires=["ros2cli"],
29 | zip_safe=True,
30 | author="Maciej Bednarczyk",
31 | author_email="mcbed.robotics@gmail.com",
32 | maintainer="Maciej Bednarczyk",
33 | maintainer_email="mcbed.robotics@gmail.com",
34 | url="https://github.com/mcbed/pytroller",
35 | keywords=[],
36 | classifiers=[
37 | "Environment :: Console",
38 | "Intended Audience :: Developers",
39 | "License :: OSI Approved :: Apache Software License",
40 | "Programming Language :: Python",
41 | ],
42 | description="Pytroller command interface.",
43 | long_description="""\
44 | Pytroller command interface.""",
45 | license="Apache License, Version 2.0",
46 | tests_require=["pytest"],
47 | entry_points={
48 | "ros2cli.command": [
49 | "pytroller = pytrollercli.command.pytroller:PytrollerCommand",
50 | ],
51 | "pytrollercli.verb": [
52 | "create = pytrollercli.verb.create:CreateVerb",
53 |
54 | ],
55 | },
56 | package_data={
57 | 'pytrollercli': [
58 | 'resource/**/*',
59 | ],
60 | },
61 | )
62 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/include/visibility_control.h.em:
--------------------------------------------------------------------------------
1 | // Copyright 2020 PAL Robotics S.L.
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 | /* This header must be included by all rclcpp headers which declare symbols
16 | * which are defined in the rclcpp library. When not building the rclcpp
17 | * library, i.e. when using the headers in other package's code, the contents
18 | * of this header change the visibility of certain symbols which the rclcpp
19 | * library cannot have, but the consuming code must have inorder to link.
20 | */
21 |
22 | #ifndef @(pytroller_name.upper())__VISIBILITY_CONTROL_H_
23 | #define @(pytroller_name.upper())__VISIBILITY_CONTROL_H_
24 |
25 | // This logic was borrowed (then namespaced) from the examples on the gcc wiki:
26 | // https://gcc.gnu.org/wiki/Visibility
27 |
28 | #if defined _WIN32 || defined __CYGWIN__
29 | #ifdef __GNUC__
30 | #define @(pytroller_name.upper())_EXPORT __attribute__((dllexport))
31 | #define @(pytroller_name.upper())_IMPORT __attribute__((dllimport))
32 | #else
33 | #define @(pytroller_name.upper())_EXPORT __declspec(dllexport)
34 | #define @(pytroller_name.upper())_IMPORT __declspec(dllimport)
35 | #endif
36 | #ifdef @(pytroller_name.upper())_BUILDING_DLL
37 | #define @(pytroller_name.upper())_PUBLIC @(pytroller_name.upper())_EXPORT
38 | #else
39 | #define @(pytroller_name.upper())_PUBLIC @(pytroller_name.upper())_IMPORT
40 | #endif
41 | #define @(pytroller_name.upper())_PUBLIC_TYPE @(pytroller_name.upper())_PUBLIC
42 | #define @(pytroller_name.upper())_LOCAL
43 | #else
44 | #define @(pytroller_name.upper())_EXPORT __attribute__((visibility("default")))
45 | #define @(pytroller_name.upper())_IMPORT
46 | #if __GNUC__ >= 4
47 | #define @(pytroller_name.upper())_PUBLIC __attribute__((visibility("default")))
48 | #define @(pytroller_name.upper())_LOCAL __attribute__((visibility("hidden")))
49 | #else
50 | #define @(pytroller_name.upper())_PUBLIC
51 | #define @(pytroller_name.upper())_LOCAL
52 | #endif
53 | #define @(pytroller_name.upper())_PUBLIC_TYPE
54 | #endif
55 |
56 | #endif // @(pytroller_name.upper())__VISIBILITY_CONTROL_H_
57 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 | Thank you for your interest in contributing to `pytroller`. Whether it's a bug report, new feature, correction, or additional documentation, we greatly value feedback and contributions from the community.
3 |
4 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution.
5 |
6 |
7 | ## Reporting Bugs/Feature Requests
8 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
9 |
10 | When filing an issue, please check [existing open][issues], or [recently closed][closed-issues], issues to make sure somebody else hasn't already reported the issue.
11 |
12 | Please try to include as much information as you can. Details like these are incredibly useful:
13 |
14 | * A reproducible test case or series of steps
15 | * The version of our code being used
16 | * Any modifications you've made relevant to the bug
17 | * Anything unusual about your environment or deployment
18 |
19 |
20 | ## Contributing via Pull Requests
21 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
22 |
23 | 1. You are working against the latest source on the *main* branch.
24 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
25 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
26 |
27 | To send us a pull request, please:
28 |
29 | 1. Fork the repository.
30 | 2. Modify the source; please focus on the specific change you are contributing.
31 | If you also reformat all the code, it will be hard for us to focus on your change.
32 | 3. Ensure local tests pass.
33 | 4. Commit to your fork using clear commit messages.
34 | 5. Send a pull request, answering any default questions in the pull request interface.
35 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
36 |
37 | GitHub provides additional documentation on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
38 |
39 |
40 | ## Licensing
41 | Any contribution that you make to this repository will be under the Apache 2 License, as dictated by that [license](http://www.apache.org/licenses/LICENSE-2.0.html):
42 |
43 | ~~~
44 | 5. Submission of Contributions. Unless You explicitly state otherwise,
45 | any Contribution intentionally submitted for inclusion in the Work
46 | by You to the Licensor shall be under the terms and conditions of
47 | this License, without any additional terms or conditions.
48 | Notwithstanding the above, nothing herein shall supersede or modify
49 | the terms of any separate license agreement you may have executed
50 | with Licensor regarding such Contributions.
51 | ~~~
52 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/test/test_pytroller.hpp.em:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ICube-Robotics
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 | // author: Maciej Bednarczyk
16 |
17 | #ifndef TEST_@(pytroller_name.upper())_HPP_
18 | #define TEST_@(pytroller_name.upper())_HPP_
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include "gmock/gmock.h"
26 |
27 | #include "hardware_interface/handle.hpp"
28 | #include "hardware_interface/types/hardware_interface_type_values.hpp"
29 | #include "@(pytroller_name)/@(pytroller_name).hpp"
30 |
31 | using hardware_interface::CommandInterface;
32 | using hardware_interface::StateInterface;
33 |
34 | // subclassing and friending so we can access member variables
35 | class Friend@(pytroller_class) : public @(pytroller_name)::@(pytroller_class)
36 | {
37 | FRIEND_TEST(Test@(pytroller_class), CommandSuccessTest);
38 | FRIEND_TEST(Test@(pytroller_class), WrongCommandCheckTest);
39 | FRIEND_TEST(Test@(pytroller_class), CommandSuccessTestWithConstraints);
40 | };
41 |
42 | class Test@(pytroller_class) : public ::testing::Test
43 | {
44 | public:
45 | static void SetUpTestCase();
46 | static void TearDownTestCase();
47 |
48 | void SetUp();
49 | void TearDown();
50 |
51 | void SetUpController();
52 | void SetUpHandles();
53 |
54 | protected:
55 | std::unique_ptr controller_;
56 |
57 | const std::vector joint_names_ = {"joint1", "joint2"};
58 | // dummy joint state values used for tests
59 | std::vector joint_commands_ = {
60 | std::numeric_limits::quiet_NaN(),
61 | std::numeric_limits::quiet_NaN()
62 | };
63 | std::vector joint_states_position_ = {0.0, 0.0};
64 | std::vector joint_states_velocity_ = {0.0, 0.0};
65 |
66 | CommandInterface joint1_ci_{joint_names_[0], hardware_interface::HW_IF_POSITION, &joint_commands_[0]};
67 | CommandInterface joint2_ci_{joint_names_[1], hardware_interface::HW_IF_POSITION, &joint_commands_[1]};
68 | StateInterface joint1_sip_{joint_names_[0], hardware_interface::HW_IF_POSITION, &joint_states_position_[0]};
69 | StateInterface joint2_sip_{joint_names_[1], hardware_interface::HW_IF_POSITION, &joint_states_position_[1]};
70 | StateInterface joint1_siv_{joint_names_[0], hardware_interface::HW_IF_VELOCITY, &joint_states_velocity_[0]};
71 | StateInterface joint2_siv_{joint_names_[1], hardware_interface::HW_IF_VELOCITY, &joint_states_velocity_[1]};
72 | };
73 |
74 | #endif // TEST_MPI_CONTROLLER_HPP_
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pytroller
2 | [](https://opensource.org/licenses/Apache-2.0)
3 | [](https://github.com/ICube-Robotics/pytroller/actions/workflows/ci.yml)
4 |
5 | Python controller generator for `ros2_control`.
6 |
7 | ## Usage
8 |
9 | Generate the controller package using :
10 | ```shell
11 | $ ros2 pytroller create my_pytroller --destination-directory controllers
12 | ```
13 |
14 | The raw Python controller logic script `my_pytroller_logic_impl.py` to be implemented is located the `script` directory inside the newly created controller package.
15 |
16 | This Python script containing the definition of the `pytroller_logic_impl` function that takes as input
17 | - `state` : type `dict` {`joint/interface`, value} : current joint states on all available interfaces
18 | - `commands` : type `dict` {`joint/interface`, value} : commands to be assigned to joint command interfaces
19 | - `msg` : command message from subscriber
20 | - `params` : type `dict` {`parameter`, value} : node parameters
21 |
22 | For example :
23 |
24 | ```python
25 | # my_pytroller/script/my_pytroller_logic_impl.py
26 |
27 | from math import cos, sin
28 |
29 | def pytroller_logic_impl(period, states, commands, msg, params):
30 |
31 | commands['joint1/effort'.encode('ascii')] = msg.data[0]
32 | commands['joint2/effort'.encode('ascii')] = msg.data[1]
33 |
34 | return commands
35 | ```
36 |
37 | Install all dependencies and build the controller using
38 | ```shell
39 | $ rosdep install --ignore-src --from-paths . -y -r
40 | $ colcon build
41 | ```
42 |
43 | ## Parameters
44 | Pytrollers use parameters generated by the [generate_parameter_library](https://github.com/PickNikRobotics/generate_parameter_library) package and are defined in the `my_pytroller_parameters.yaml` file :
45 |
46 | ```yaml
47 | my_pytroller:
48 | interface_full_names: {
49 | type: string_array,
50 | default_value: [],
51 | description: "Full names of the interface(s) to command",
52 | }
53 | command_topic_name: {
54 | type: string,
55 | default_value: "" # "~/commands",
56 | description: "Name of the subscribed command topic"
57 | }
58 | command_topic_type: {
59 | type: string,
60 | default_value: "" # "std_msgs/msg/Float64MultiArray",
61 | description: "Type of the subscribed command topic"
62 | }
63 | ```
64 | Additional parameters can be added to this configuration file. To do so, refer to the [generate_parameter_library](https://github.com/PickNikRobotics/generate_parameter_library) package documentation.
65 |
66 | The values of the defined parameters are set in the controller configuration file used for ros2-control.
67 |
68 | ## Contacts ##
69 | 
70 |
71 | [ICube Laboratory](https://icube.unistra.fr), [University of Strasbourg](https://www.unistra.fr/), France
72 |
73 | __Maciej Bednarczyk:__ [mcbed.robotics@gmail.com](mailto:mcbed.robotics@gmail.com), @github: [mcbed](https://github.com/mcbed)
74 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/include/pytroller.hpp.em:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ICube-Robotics
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 | // author: Maciej Bednarczyk
16 |
17 | #ifndef @(pytroller_name.upper())__@(pytroller_name.upper())_HPP_
18 | #define @(pytroller_name.upper())__@(pytroller_name.upper())_HPP_
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include "controller_interface/controller_interface.hpp"
26 | #include "@(pytroller_name)/visibility_control.h"
27 | #include "rclcpp/serialized_message.hpp"
28 | #include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
29 | #include "rclcpp_lifecycle/state.hpp"
30 | #include "realtime_tools/realtime_buffer.h"
31 | #include "@(pytroller_name)_parameters.hpp"
32 |
33 | namespace @(pytroller_name)
34 | {
35 | /**
36 | * \brief Python controller for a set of joints and interfaces.
37 | *
38 | * Subscribes to:
39 | * - \b commands : The commands to apply.
40 | */
41 | class @(pytroller_class) : public controller_interface::ControllerInterface
42 | {
43 | public:
44 | @(pytroller_name.upper())_PUBLIC
45 | @(pytroller_class)();
46 |
47 | @(pytroller_name.upper())_PUBLIC
48 | ~@(pytroller_class)() = default;
49 |
50 | @(pytroller_name.upper())_PUBLIC
51 | controller_interface::InterfaceConfiguration command_interface_configuration() const override;
52 |
53 | @(pytroller_name.upper())_PUBLIC
54 | controller_interface::InterfaceConfiguration state_interface_configuration() const override;
55 |
56 | @(pytroller_name.upper())_PUBLIC
57 | controller_interface::CallbackReturn on_init() override;
58 |
59 | @(pytroller_name.upper())_PUBLIC
60 | controller_interface::CallbackReturn on_configure(
61 | const rclcpp_lifecycle::State & previous_state) override;
62 |
63 | @(pytroller_name.upper())_PUBLIC
64 | controller_interface::CallbackReturn on_activate(
65 | const rclcpp_lifecycle::State & previous_state) override;
66 |
67 | @(pytroller_name.upper())_PUBLIC
68 | controller_interface::CallbackReturn on_deactivate(
69 | const rclcpp_lifecycle::State & previous_state) override;
70 |
71 | @(pytroller_name.upper())_PUBLIC
72 | controller_interface::return_type update(
73 | const rclcpp::Time & time, const rclcpp::Duration & period) override;
74 |
75 | protected:
76 | void declare_parameters();
77 | controller_interface::CallbackReturn read_parameters();
78 |
79 | std::shared_ptr param_listener_;
80 | Params params_;
81 |
82 | std::vector command_interface_types_;
83 | std::unordered_map states_;
84 | std::unordered_map commands_;
85 |
86 | realtime_tools::RealtimeBuffer> rt_command_ptr_;
87 | rclcpp::GenericSubscription::SharedPtr command_subscriber_;
88 | };
89 |
90 | } // namespace @(pytroller_name)
91 |
92 | #endif // @(pytroller_name.upper())__FORWARD_CONTROLLERS_BASE_HPP_
93 |
--------------------------------------------------------------------------------
/pytroller_tools/pytroller_tools/generate_pxd.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # Copyright 2023 ICube-Robotics
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 | import argparse
18 | import sys
19 |
20 |
21 | def extract_params_struct(content):
22 | struct_name = "struct Params {"
23 | struct2_name = "struct StackParams {"
24 | struct_start = content.find(struct_name)
25 | struct_end = content.find(struct2_name)
26 | return_struct = ''
27 | if struct_start != -1:
28 | struct_content = content[struct_start:struct_end]
29 | for line in struct_content.splitlines()[1:-4]:
30 | match line.strip().split(' = ')[0].split(' ')[0]:
31 | case 'bool':
32 | return_struct += 'bool'
33 | case 'double':
34 | return_struct += 'float'
35 | case 'int64_t':
36 | return_struct += 'int'
37 | case 'std::string':
38 | return_struct += 'string'
39 | case 'std::vector':
40 | return_struct += 'vector[bool]'
41 | case 'std::vector':
42 | return_struct += 'vector[float]'
43 | case 'std::vector':
44 | return_struct += 'vector[int64_t]'
45 | case 'std::vector':
46 | return_struct += 'vector[string]'
47 |
48 | return_struct += ' ' + line.strip().split(' = ')[0].split(' ')[1] + '\n'
49 | return return_struct
50 |
51 | return None
52 |
53 |
54 | def read_cpp_file(file_path):
55 | with open(file_path) as file:
56 | return file.read()
57 |
58 |
59 | def run(output_pxd_file, input_header_file):
60 | cpp_content = read_cpp_file(input_header_file)
61 |
62 | params_struct_content = extract_params_struct(cpp_content)
63 | namespace_start = cpp_content.find("namespace")
64 | namespace_end = cpp_content.find("{", namespace_start)
65 | namespace = cpp_content[namespace_start+10:namespace_end-1]
66 |
67 | if params_struct_content:
68 | pxd_file = '# distutils: language = c++\n\n'
69 | pxd_file += 'from libcpp.vector cimport vector\n'
70 | pxd_file += 'from libcpp.string cimport string\n\n'
71 | pxd_file += 'cdef extern from "' + input_header_file + '" namespace "' + namespace + '":\n'
72 | pxd_file += '\tcdef struct Params:\n'
73 | for line in params_struct_content.splitlines():
74 | pxd_file += '\t\t' + line + '\n'
75 |
76 | with open(output_pxd_file, "w") as text_file:
77 | text_file.write(pxd_file)
78 |
79 | else:
80 | print('Params struct not found in the file.')
81 |
82 |
83 | def parse_args():
84 | parser = argparse.ArgumentParser()
85 | parser.add_argument("output_pxd_file")
86 | parser.add_argument("input_cpp_header_file")
87 | return parser.parse_args()
88 |
89 |
90 | def main():
91 | args = parse_args()
92 | output_file = args.output_pxd_file
93 | input_file = args.input_cpp_header_file
94 |
95 | run(output_file, input_file)
96 |
97 |
98 | if __name__ == "__main__":
99 | sys.exit(main())
100 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/test/test_pytroller.cpp.em:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ICube-Robotics
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 | // author: Maciej Bednarczyk
16 |
17 | #include
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include "hardware_interface/loaned_command_interface.hpp"
26 | #include "hardware_interface/loaned_state_interface.hpp"
27 | #include "hardware_interface/types/hardware_interface_return_values.hpp"
28 | #include "lifecycle_msgs/msg/state.hpp"
29 | #include "rclcpp/utilities.hpp"
30 | #include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
31 | #include "test_@(pytroller_name).hpp"
32 |
33 | using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
34 | using hardware_interface::LoanedCommandInterface;
35 | using hardware_interface::LoanedStateInterface;
36 |
37 | namespace
38 | {
39 | rclcpp::WaitResultKind wait_for(rclcpp::SubscriptionBase::SharedPtr subscription)
40 | {
41 | rclcpp::WaitSet wait_set;
42 | wait_set.add_subscription(subscription);
43 | const auto timeout = std::chrono::seconds(10);
44 | return wait_set.wait(timeout).kind();
45 | }
46 | } // namespace
47 |
48 | void Test@(pytroller_class)::SetUpTestCase() { rclcpp::init(0, nullptr); }
49 |
50 | void Test@(pytroller_class)::TearDownTestCase() { rclcpp::shutdown(); }
51 |
52 | void Test@(pytroller_class)::SetUp()
53 | {
54 | controller_ = std::make_unique();
55 | }
56 |
57 | void Test@(pytroller_class)::TearDown() { controller_.reset(nullptr); }
58 |
59 | void Test@(pytroller_class)::SetUpController()
60 | {
61 | const auto result = controller_->init("test_@(pytroller_name)");
62 | ASSERT_EQ(result, controller_interface::return_type::OK);
63 |
64 | std::vector command_ifs;
65 | command_ifs.emplace_back(joint1_ci_);
66 | command_ifs.emplace_back(joint2_ci_);
67 |
68 | std::vector state_ifs;
69 | state_ifs.emplace_back(joint1_sip_);
70 | state_ifs.emplace_back(joint1_siv_);
71 | state_ifs.emplace_back(joint2_sip_);
72 | state_ifs.emplace_back(joint2_siv_);
73 |
74 | controller_->assign_interfaces(std::move(command_ifs), std::move(state_ifs));
75 | }
76 |
77 | TEST_F(Test@(pytroller_class), ConfigureAndActivateParamsSuccess)
78 | {
79 | SetUpController();
80 |
81 | // configure successful
82 | ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
83 | ASSERT_EQ(controller_->on_activate(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
84 | }
85 |
86 | TEST_F(Test@(pytroller_class), NoCommandCheckTest)
87 | {
88 | SetUpController();
89 |
90 | ASSERT_EQ(controller_->on_configure(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
91 | ASSERT_EQ(controller_->on_activate(rclcpp_lifecycle::State()), CallbackReturn::SUCCESS);
92 |
93 | // update successful, no command received yet
94 | ASSERT_EQ(
95 | controller_->update(rclcpp::Time(0), rclcpp::Duration::from_seconds(0.005)),
96 | controller_interface::return_type::OK);
97 |
98 | // // check joint commands are still the default ones
99 | // ASSERT_EQ(joint1_ci_.get_value(), std::numeric_limits::quiet_NaN());
100 | // ASSERT_EQ(joint2_ci_.get_value(), std::numeric_limits::quiet_NaN());
101 | }
102 |
103 | int main(int argc, char ** argv)
104 | {
105 | ::testing::InitGoogleTest(&argc, argv);
106 | rclcpp::init(argc, argv);
107 | int result = RUN_ALL_TESTS();
108 | rclcpp::shutdown();
109 | return result;
110 | }
111 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # To use:
2 | # pip install pre-commit
3 | # pre-commit run -a
4 | #
5 | # (to run on all files)
6 | # pre-commit run --all-files
7 | #
8 | # Or:
9 | #
10 | # pre-commit install # (runs every time you commit in git)
11 | #
12 | # To update this file:
13 | #
14 | # pre-commit autoupdate
15 | #
16 | # See https://github.com/pre-commit/pre-commit
17 |
18 | # exclude:
19 | repos:
20 | # Standard hooks
21 | - repo: https://github.com/pre-commit/pre-commit-hooks
22 | rev: v4.4.0
23 | hooks:
24 | - id: check-added-large-files
25 | - id: check-ast
26 | - id: check-case-conflict
27 | - id: check-docstring-first
28 | - id: check-merge-conflict
29 | - id: check-symlinks
30 | - id: check-xml
31 | - id: check-yaml
32 | - id: debug-statements
33 | - id: end-of-file-fixer
34 | - id: mixed-line-ending
35 | - id: trailing-whitespace
36 | exclude_types: [rst]
37 | - id: fix-byte-order-marker
38 |
39 | # Python hooks
40 | - repo: https://github.com/asottile/pyupgrade
41 | rev: v3.3.1
42 | hooks:
43 | - id: pyupgrade
44 | args: [--py36-plus]
45 |
46 | # PyDocStyle
47 | - repo: https://github.com/PyCQA/pydocstyle
48 | rev: 6.3.0
49 | hooks:
50 | - id: pydocstyle
51 | args: ["--ignore=D100,D101,D102,D103,D104,D105,D106,D107,D203,D212,D404"]
52 |
53 | - repo: https://github.com/pycqa/flake8
54 | rev: 6.0.0
55 | hooks:
56 | - id: flake8
57 | args: ["--extend-ignore=E501"]
58 |
59 | # Uncrustify
60 | - repo: local
61 | hooks:
62 | - id: ament_uncrustify
63 | name: ament_uncrustify
64 | description: Uncrustify.
65 | stages: [commit]
66 | entry: ament_uncrustify
67 | language: system
68 | files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$
69 | args: ["--reformat"]
70 |
71 | # CPP hooks
72 | - repo: local
73 | hooks:
74 | - id: clang-format
75 | name: clang-format
76 | description: Format files with ClangFormat.
77 | entry: clang-format-14
78 | language: system
79 | files: \.(c|cc|cxx|cpp|frag|glsl|h|hpp|hxx|ih|ispc|ipp|java|js|m|proto|vert)$
80 | args: ['-fallback-style=none', '-i']
81 |
82 | # - repo: local
83 | # hooks:
84 | # - id: ament_cppcheck
85 | # name: ament_cppcheck
86 | # description: Static code analysis of C/C++ files.
87 | # stages: [commit]
88 | # entry: ament_cppcheck
89 | # language: system
90 | # files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$
91 |
92 | # Maybe use https://github.com/cpplint/cpplint instead
93 | - repo: local
94 | hooks:
95 | - id: ament_cpplint
96 | name: ament_cpplint
97 | description: Static code analysis of C/C++ files.
98 | stages: [commit]
99 | entry: ament_cpplint
100 | language: system
101 | files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$
102 | args: ["--linelength=100", "--filter=-whitespace/newline"]
103 |
104 | # Cmake hooks
105 | - repo: local
106 | hooks:
107 | - id: ament_lint_cmake
108 | name: ament_lint_cmake
109 | description: Check format of CMakeLists.txt files.
110 | stages: [commit]
111 | entry: ament_lint_cmake
112 | language: system
113 | files: CMakeLists\.txt$
114 |
115 | # Copyright
116 | - repo: local
117 | hooks:
118 | - id: ament_copyright
119 | name: ament_copyright
120 | description: Check if copyright notice is available in all files.
121 | stages: [commit]
122 | entry: ament_copyright
123 | language: system
124 |
125 | # Docs - RestructuredText hooks
126 | - repo: https://github.com/PyCQA/doc8
127 | rev: v1.1.1
128 | hooks:
129 | - id: doc8
130 | args: ['--max-line-length=100', '--ignore=D001']
131 | exclude: CHANGELOG\.rst$
132 |
133 | - repo: https://github.com/pre-commit/pygrep-hooks
134 | rev: v1.10.0
135 | hooks:
136 | - id: rst-backticks
137 | exclude: CHANGELOG\.rst$
138 | - id: rst-directive-colons
139 | - id: rst-inline-touching-normal
140 |
141 | # Spellcheck in comments and docs
142 | # skipping of *.svg files is not working...
143 | - repo: https://github.com/codespell-project/codespell
144 | rev: v2.2.2
145 | hooks:
146 | - id: codespell
147 | args: ['--write-changes']
148 | exclude: CHANGELOG\.rst|\.(svg|pyc)$
149 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/package/CMakeLists.txt.em:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.16)
2 | project(@(pytroller_name) LANGUAGES CXX)
3 |
4 | if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
5 | add_compile_options(-Wall -Wextra -Wpedantic -Wconversion)
6 | endif()
7 |
8 | set(THIS_PACKAGE_INCLUDE_DEPENDS
9 | controller_interface
10 | generate_parameter_library
11 | hardware_interface
12 | pluginlib
13 | rclcpp
14 | rclcpp_lifecycle
15 | realtime_tools
16 | )
17 |
18 | find_package(ament_cmake REQUIRED)
19 | foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
20 | find_package(${Dependency} REQUIRED)
21 | endforeach()
22 |
23 | find_package(PythonLibs REQUIRED)
24 | include_directories(${PYTHON_INCLUDE_DIRS})
25 |
26 | # Set the parameter header file name
27 | set(PARAM_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/@(pytroller_name)_parameters/include)
28 | set(PARAM_HEADER_FILE ${PARAM_INCLUDE_DIR}/@(pytroller_name)_parameters.hpp)
29 |
30 | # Make logic build directory
31 | set(LOGIC_DIR ${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}/@(pytroller_name)_logic)
32 | set(LOGIC_INCLUDE_DIR ${LOGIC_DIR}/include/@(pytroller_name))
33 | file(MAKE_DIRECTORY ${LOGIC_DIR})
34 | file(MAKE_DIRECTORY ${LOGIC_INCLUDE_DIR})
35 |
36 | file (REMOVE ${LOGIC_DIR}/@(pytroller_name)_logic.cpp)
37 | file (REMOVE ${LOGIC_DIR}/@(pytroller_name)_logic.h)
38 | file (REMOVE ${LOGIC_INCLUDE_DIR}/@(pytroller_name)_logic.h)
39 |
40 | add_custom_command(
41 | OUTPUT ${LOGIC_DIR}/@(pytroller_name)_logic.cpp ${LOGIC_DIR}/@(pytroller_name)_logic.h
42 | COMMAND cython3 -3 --cplus ${CMAKE_CURRENT_SOURCE_DIR}/src/@(pytroller_name)_logic.pyx
43 | -o ${LOGIC_DIR}/@(pytroller_name)_logic.cpp -I ${PARAM_INCLUDE_DIR}
44 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src
45 | DEPENDS
46 | ${CMAKE_CURRENT_SOURCE_DIR}/src/@(pytroller_name)_logic.pyx
47 | ${CMAKE_CURRENT_SOURCE_DIR}/script/@(pytroller_name)_logic_impl.py
48 | )
49 |
50 | # Copy the header file into the include directory
51 | add_custom_command(
52 | OUTPUT ${LOGIC_INCLUDE_DIR}/@(pytroller_name)_logic.h
53 | COMMAND ${CMAKE_COMMAND} -E copy
54 | ${LOGIC_DIR}/@(pytroller_name)_logic.h ${LOGIC_INCLUDE_DIR}/@(pytroller_name)_logic.h
55 | DEPENDS ${LOGIC_DIR}/@(pytroller_name)_logic.h
56 | )
57 |
58 | generate_parameter_library(
59 | @(pytroller_name)_parameters
60 | src/@(pytroller_name)_parameters.yaml
61 | )
62 |
63 | file (REMOVE ${PARAM_INCLUDE_DIR}/@(pytroller_name)_parameters.pxd)
64 |
65 | # Generate the pxd for the library
66 | add_custom_command(
67 | OUTPUT ${PARAM_INCLUDE_DIR}/@(pytroller_name)_parameters.pxd
68 | COMMAND ros2 run pytroller_tools generate_pxd ${PARAM_INCLUDE_DIR}/@(pytroller_name)_parameters.pxd ${PARAM_HEADER_FILE}
69 | DEPENDS ${PARAM_HEADER_FILE}
70 | )
71 |
72 | add_library(@(pytroller_name) SHARED
73 | src/@(pytroller_name).cpp
74 | ${LOGIC_DIR}/@(pytroller_name)_logic.cpp
75 | ${LOGIC_INCLUDE_DIR}/@(pytroller_name)_logic.h
76 | ${PARAM_INCLUDE_DIR}/@(pytroller_name)_parameters.pxd
77 | )
78 | target_compile_features(@(pytroller_name) PUBLIC cxx_std_17)
79 | target_include_directories(@(pytroller_name) PUBLIC
80 | $
81 | $
82 | )
83 | target_include_directories(@(pytroller_name) PUBLIC
84 | $
85 | $
86 | )
87 | target_link_libraries(@(pytroller_name) PUBLIC
88 | @(pytroller_name)_parameters
89 | ${PYTHON_LIBRARIES}
90 | )
91 | ament_target_dependencies(@(pytroller_name) PUBLIC ${THIS_PACKAGE_INCLUDE_DEPENDS})
92 | # Causes the visibility macros to use dllexport rather than dllimport,
93 | # which is appropriate when building the dll but not consuming it.
94 | target_compile_definitions(@(pytroller_name) PRIVATE "PYTROLLER_BUILDING_DLL")
95 | pluginlib_export_plugin_description_file(controller_interface controller_plugin.xml)
96 |
97 |
98 | if(BUILD_TESTING)
99 | find_package(ament_lint_auto REQUIRED)
100 | find_package(ament_cmake_gmock REQUIRED)
101 | find_package(controller_manager REQUIRED)
102 | find_package(hardware_interface REQUIRED)
103 | find_package(ros2_control_test_assets REQUIRED)
104 |
105 | ament_lint_auto_find_test_dependencies()
106 |
107 | # Load test
108 | add_rostest_with_parameters_gmock(
109 | test_load_@(pytroller_name)
110 | test/test_load_@(pytroller_name).cpp
111 | ${CMAKE_CURRENT_SOURCE_DIR}/test/test_params.yaml
112 | )
113 | target_link_libraries(test_load_@(pytroller_name)
114 | @(pytroller_name)
115 | )
116 | ament_target_dependencies(test_load_@(pytroller_name)
117 | controller_manager
118 | hardware_interface
119 | ros2_control_test_assets
120 | )
121 |
122 | # Controller test
123 | add_rostest_with_parameters_gmock(
124 | test_@(pytroller_name)
125 | test/test_@(pytroller_name).cpp
126 | ${CMAKE_CURRENT_SOURCE_DIR}/test/test_params.yaml
127 | )
128 |
129 | target_link_libraries(test_@(pytroller_name)
130 | @(pytroller_name)
131 | )
132 |
133 | ament_target_dependencies(test_load_@(pytroller_name)
134 | controller_manager
135 | hardware_interface
136 | )
137 |
138 | endif()
139 |
140 | install(
141 | DIRECTORY ${LOGIC_DIR}/include
142 | DESTINATION include/@(pytroller_name)
143 | )
144 | install(
145 | DIRECTORY include/
146 | DESTINATION include/@(pytroller_name)
147 | )
148 | install(
149 | TARGETS
150 | @(pytroller_name)
151 | @(pytroller_name)_parameters
152 | EXPORT export_@(pytroller_name)
153 | RUNTIME DESTINATION bin
154 | ARCHIVE DESTINATION lib
155 | LIBRARY DESTINATION lib
156 | INCLUDES DESTINATION include
157 | )
158 |
159 | ament_export_targets(export_@(pytroller_name) HAS_LIBRARY_TARGET)
160 | ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS})
161 | ament_package()
162 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/api/create.py:
--------------------------------------------------------------------------------
1 | # Copyright 2023 ICube-Robotics
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 io import StringIO
16 | import os
17 | import sys
18 |
19 | import em
20 | import importlib.resources as importlib_resources
21 |
22 |
23 | def _expand_template(template_file, data, output_file):
24 | output = StringIO()
25 | interpreter = em.Interpreter(
26 | output=output,
27 | options={
28 | em.BUFFERED_OPT: True,
29 | em.RAW_OPT: True,
30 | },
31 | globals=data,
32 | )
33 | with open(template_file) as h:
34 | try:
35 | interpreter.file(h)
36 | content = output.getvalue()
37 | except Exception as e:
38 | if os.path.exists(output_file):
39 | os.remove(output_file)
40 | print("Exception when expanding '%s' into '%s': %s" %
41 | (template_file, output_file, e), file=sys.stderr)
42 | raise
43 | finally:
44 | interpreter.shutdown()
45 |
46 | if os.path.exists(output_file):
47 | with open(output_file) as h:
48 | if h.read() == content:
49 | return
50 | else:
51 | os.makedirs(os.path.dirname(output_file), exist_ok=True)
52 |
53 | with open(output_file, 'w') as h:
54 | h.write(content)
55 |
56 |
57 | def _create_folder(folder_name, base_directory, exist_ok=True):
58 | folder_path = os.path.join(base_directory, folder_name)
59 |
60 | print('creating folder', folder_path)
61 | os.makedirs(folder_path, exist_ok=exist_ok)
62 |
63 | return folder_path
64 |
65 |
66 | def _create_template_file(
67 | template_subdir, template_file_name, output_directory, output_file_name, template_config
68 | ):
69 | full_package = 'pytrollercli.resource.' + template_subdir
70 | with importlib_resources.path(full_package, template_file_name) as path:
71 | template_path = str(path)
72 | if not os.path.exists(template_path):
73 | raise FileNotFoundError('template not found:', template_path)
74 |
75 | output_file_path = os.path.join(output_directory, output_file_name)
76 |
77 | print('creating', output_file_path)
78 | _expand_template(template_path, template_config, output_file_path)
79 |
80 |
81 | def create_package_environment(pytroller_name, destination_directory):
82 | package_directory = _create_folder(pytroller_name, destination_directory)
83 |
84 | package_xml_config = {
85 | 'pytroller_name': pytroller_name,
86 | 'maintainer_name': 'maintainer',
87 | 'maintainer_email': 'maintainer@email.com'
88 | }
89 | _create_template_file(
90 | 'package',
91 | 'package.xml.em',
92 | package_directory,
93 | 'package.xml',
94 | package_xml_config)
95 |
96 | source_directory = None
97 | include_directory = None
98 | print('creating source and include folder')
99 | source_directory = _create_folder('src', package_directory)
100 | source_directory = _create_folder('script', package_directory)
101 | source_directory = _create_folder('test', package_directory)
102 | include_directory = _create_folder(pytroller_name, package_directory + os.sep + 'include')
103 |
104 | return package_directory, source_directory, include_directory
105 |
106 |
107 | def populate_ament_cmake(package_name, package_directory):
108 | class_name = package_name.replace('_', ' ').title()
109 | class_name = ''.join(x for x in class_name if not x.isspace())
110 | cmakelists_config = {
111 | 'pytroller_name': package_name,
112 | }
113 | _create_template_file(
114 | 'package',
115 | 'CMakeLists.txt.em',
116 | package_directory,
117 | 'CMakeLists.txt',
118 | cmakelists_config)
119 |
120 | plugin_config = {
121 | 'pytroller_name': package_name,
122 | 'pytroller_class': class_name,
123 | }
124 | _create_template_file(
125 | 'package',
126 | 'controller_plugin.xml.em',
127 | package_directory,
128 | 'controller_plugin.xml',
129 | plugin_config)
130 |
131 |
132 | def populate_logic(package_name, source_directory):
133 | cmakelists_config = {
134 | 'pytroller_name': package_name,
135 | }
136 | _create_template_file(
137 | 'src',
138 | 'pytroller_logic.pyx.em',
139 | source_directory,
140 | package_name + '_logic.pyx',
141 | cmakelists_config)
142 |
143 |
144 | def populate_logic_impl(package_name, script_directory):
145 | # os.popen('cp ' + logic_script + ' ' + source_directory + os.sep + package_name +'_logic_impl.py')
146 | impl_config = {
147 | 'pytroller_name': package_name,
148 | }
149 | _create_template_file(
150 | 'script',
151 | 'pytroller_logic_impl.py.em',
152 | script_directory,
153 | package_name + '_logic_impl.py',
154 | impl_config)
155 |
156 |
157 | def populate_cpp_library(package_name, source_directory, include_directory):
158 | class_name = package_name.replace('_', ' ').title()
159 | class_name = ''.join(x for x in class_name if not x.isspace())
160 | cpp_header_config = {
161 | 'pytroller_name': package_name,
162 | 'pytroller_class': class_name,
163 | }
164 | _create_template_file(
165 | 'include',
166 | 'pytroller.hpp.em',
167 | include_directory,
168 | package_name + '.hpp',
169 | cpp_header_config)
170 |
171 | cpp_library_config = {
172 | 'pytroller_name': package_name,
173 | 'pytroller_class': class_name
174 | }
175 | _create_template_file(
176 | 'src',
177 | 'pytroller.cpp.em',
178 | source_directory,
179 | package_name + '.cpp',
180 | cpp_library_config)
181 |
182 | visibility_config = {
183 | 'pytroller_name': package_name,
184 | }
185 | _create_template_file(
186 | 'include',
187 | 'visibility_control.h.em',
188 | include_directory,
189 | 'visibility_control.h',
190 | visibility_config)
191 |
192 | parameters_config = {
193 | 'pytroller_name': package_name,
194 | }
195 | _create_template_file(
196 | 'src',
197 | 'pytroller_parameters.yaml.em',
198 | source_directory,
199 | package_name + '_parameters.yaml',
200 | parameters_config)
201 |
202 |
203 | def populate_test(package_name, source_directory):
204 | class_name = package_name.replace('_', ' ').title()
205 | class_name = ''.join(x for x in class_name if not x.isspace())
206 | load_test_config = {
207 | 'pytroller_name': package_name,
208 | 'pytroller_class': class_name
209 | }
210 | _create_template_file(
211 | 'test',
212 | 'test_load_pytroller.cpp.em',
213 | source_directory,
214 | 'test_load_' + package_name + '.cpp',
215 | load_test_config)
216 |
217 | test_param_config = {
218 | 'pytroller_name': package_name,
219 | }
220 | _create_template_file(
221 | 'test',
222 | 'test_params.yaml.em',
223 | source_directory,
224 | 'test_params.yaml',
225 | test_param_config)
226 |
227 | test_header_config = {
228 | 'pytroller_name': package_name,
229 | 'pytroller_class': class_name
230 | }
231 | _create_template_file(
232 | 'test',
233 | 'test_pytroller.hpp.em',
234 | source_directory,
235 | 'test_' + package_name + '.hpp',
236 | test_header_config)
237 |
238 | test_lib_config = {
239 | 'pytroller_name': package_name,
240 | 'pytroller_class': class_name
241 | }
242 | _create_template_file(
243 | 'test',
244 | 'test_pytroller.cpp.em',
245 | source_directory,
246 | 'test_' + package_name + '.cpp',
247 | test_lib_config)
248 |
249 |
250 | def create_pytroller(pytroller_name, destination_directory):
251 | create_package_environment(pytroller_name, destination_directory)
252 | populate_ament_cmake(pytroller_name, destination_directory+'/'+pytroller_name)
253 | populate_cpp_library(pytroller_name,
254 | destination_directory+'/'+pytroller_name+"/src",
255 | destination_directory+'/'+pytroller_name+"/include/"+pytroller_name)
256 | populate_logic(pytroller_name,
257 | destination_directory+'/'+pytroller_name+"/src")
258 | populate_logic_impl(pytroller_name,
259 | destination_directory+'/'+pytroller_name+"/script")
260 | populate_test(pytroller_name,
261 | destination_directory+'/'+pytroller_name+"/test")
262 |
--------------------------------------------------------------------------------
/pytrollercli/pytrollercli/resource/src/pytroller.cpp.em:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ICube-Robotics
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 | // author: Maciej Bednarczyk
16 |
17 | #include
18 |
19 | #include "@(pytroller_name)/@(pytroller_name).hpp"
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | #include "controller_interface/helpers.hpp"
29 | #include "hardware_interface/loaned_command_interface.hpp"
30 | #include "rclcpp/logging.hpp"
31 | #include "rclcpp/qos.hpp"
32 |
33 | #include
34 | #include "@(pytroller_name)/@(pytroller_name)_logic.h"
35 |
36 | namespace @(pytroller_name)
37 | {
38 | @(pytroller_class)::@(pytroller_class)()
39 | : controller_interface::ControllerInterface(),
40 | rt_command_ptr_(nullptr),
41 | command_subscriber_(nullptr)
42 | {
43 | // Workaround to fix undeclared symbols in cpython
44 | // https://stackoverflow.com/questions/49784583/numpy-import-fails-on-multiarray-extension-library-when-called-from-embedded-pyt
45 | // TODO : remove ASAP
46 | dlopen("libpython3.10.so", RTLD_LAZY | RTLD_GLOBAL);
47 | }
48 |
49 | controller_interface::CallbackReturn @(pytroller_class)::on_init()
50 | {
51 | try
52 | {
53 | declare_parameters();
54 | }
55 | catch (const std::exception & e)
56 | {
57 | fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what());
58 | return controller_interface::CallbackReturn::ERROR;
59 | }
60 |
61 | if (PyImport_AppendInittab("@(pytroller_name)_logic", PyInit_@(pytroller_name)_logic) == -1) {
62 | fprintf(stderr, "Error: could not extend in-built modules table\n");
63 | return controller_interface::CallbackReturn::ERROR;
64 | }
65 |
66 | Py_Initialize();
67 | PyImport_ImportModule("@(pytroller_name)_logic");
68 |
69 | return controller_interface::CallbackReturn::SUCCESS;
70 | }
71 |
72 | controller_interface::CallbackReturn @(pytroller_class)::on_configure(
73 | const rclcpp_lifecycle::State & /*previous_state*/)
74 | {
75 | auto ret = this->read_parameters();
76 | if (ret != controller_interface::CallbackReturn::SUCCESS)
77 | {
78 | return ret;
79 | }
80 |
81 | for (auto index = 0ul; index < command_interfaces_.size(); ++index)
82 | {
83 | commands_.insert({command_interfaces_[index].get_name(), command_interfaces_[index].get_value()});
84 | }
85 |
86 | for (auto index = 0ul; index < state_interfaces_.size(); ++index)
87 | {
88 | states_.insert({state_interfaces_[index].get_name(), state_interfaces_[index].get_value()});
89 | }
90 |
91 | if (params_.command_topic_name.empty() != params_.command_topic_type.empty()) {
92 | RCLCPP_FATAL(
93 | get_node()->get_logger(),
94 | "The parameters 'command_topic_name' and 'command_topic_type' should BOTH be empty OR set!");
95 | return controller_interface::CallbackReturn::ERROR;
96 | }
97 | if (!params_.command_topic_name.empty()) {
98 | command_subscriber_ = get_node()->create_generic_subscription(
99 | params_.command_topic_name, params_.command_topic_type, rclcpp::SystemDefaultsQoS(),
100 | [this](std::shared_ptr msg) { rt_command_ptr_.writeFromNonRT(msg); });
101 | }
102 | else {
103 | command_subscriber_ = nullptr;
104 | }
105 |
106 | RCLCPP_INFO(get_node()->get_logger(), "configure successful");
107 | return controller_interface::CallbackReturn::SUCCESS;
108 | }
109 |
110 | controller_interface::InterfaceConfiguration
111 | @(pytroller_class)::command_interface_configuration() const
112 | {
113 | controller_interface::InterfaceConfiguration command_interfaces_config;
114 | command_interfaces_config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
115 | command_interfaces_config.names = command_interface_types_;
116 |
117 | return command_interfaces_config;
118 | }
119 |
120 | controller_interface::InterfaceConfiguration @(pytroller_class)::state_interface_configuration()
121 | const
122 | {
123 | return controller_interface::InterfaceConfiguration{
124 | controller_interface::interface_configuration_type::ALL};
125 | }
126 |
127 | controller_interface::CallbackReturn @(pytroller_class)::on_activate(
128 | const rclcpp_lifecycle::State & /*previous_state*/)
129 | {
130 | std::vector>
131 | ordered_interfaces;
132 | if (
133 | !controller_interface::get_ordered_interfaces(
134 | command_interfaces_, command_interface_types_, std::string(""), ordered_interfaces) ||
135 | command_interface_types_.size() != ordered_interfaces.size())
136 | {
137 | RCLCPP_ERROR(
138 | get_node()->get_logger(), "Expected %zu command interfaces, got %zu",
139 | command_interface_types_.size(), ordered_interfaces.size());
140 | return controller_interface::CallbackReturn::ERROR;
141 | }
142 |
143 | // reset command buffer if a command came through callback when controller was inactive
144 | rt_command_ptr_ = realtime_tools::RealtimeBuffer>(nullptr);
145 |
146 | RCLCPP_INFO(get_node()->get_logger(), "activate successful");
147 | return controller_interface::CallbackReturn::SUCCESS;
148 | }
149 |
150 | controller_interface::CallbackReturn @(pytroller_class)::on_deactivate(
151 | const rclcpp_lifecycle::State & /*previous_state*/)
152 | {
153 | // reset command buffer
154 | rt_command_ptr_ = realtime_tools::RealtimeBuffer>(nullptr);
155 | return controller_interface::CallbackReturn::SUCCESS;
156 | }
157 |
158 | controller_interface::return_type @(pytroller_class)::update(
159 | const rclcpp::Time & /*time*/, const rclcpp::Duration & period)
160 | {
161 | // update parameters if changed
162 | if (param_listener_->is_old(params_)) {
163 | params_ = param_listener_->get_params();
164 | }
165 |
166 | // Read command msg if needed
167 | std::vector message;
168 | if (command_subscriber_) {
169 | auto msg = rt_command_ptr_.readFromRT();
170 | if (!msg || !(*msg)) {
171 | // no command received yet
172 | return controller_interface::return_type::OK;
173 | }
174 | // fill message buffer to be passed to python
175 | message = std::vector(
176 | (*msg)->get_rcl_serialized_message().buffer,
177 | (*msg)->get_rcl_serialized_message().buffer + (*msg)->size()
178 | );
179 | }
180 |
181 | // fill commands and states maps to be passed to python
182 | for (auto index = 0ul; index < command_interfaces_.size(); ++index) {
183 | commands_[command_interfaces_[index].get_name()] = std::numeric_limits::quiet_NaN();
184 | }
185 | for (auto index = 0ul; index < state_interfaces_.size(); ++index) {
186 | states_[state_interfaces_[index].get_name()] = state_interfaces_[index].get_value();
187 | }
188 |
189 | // run cython function
190 | if (@(pytroller_name)_logic(period.seconds(), states_, commands_, message, params_)) {
191 | RCLCPP_ERROR_THROTTLE(
192 | get_node()->get_logger(), *(get_node()->get_clock()), 1000,
193 | "@(pytroller_name) logic failed.");
194 | return controller_interface::return_type::ERROR;
195 | }
196 |
197 | // retrieve commands from python and fill command interfaces
198 | for (auto index = 0ul; index < command_interfaces_.size(); ++index) {
199 | command_interfaces_[index].set_value(commands_[command_interfaces_[index].get_name()]);
200 | }
201 |
202 | return controller_interface::return_type::OK;
203 | }
204 |
205 | void @(pytroller_class)::declare_parameters()
206 | {
207 | param_listener_ = std::make_shared(get_node());
208 | }
209 |
210 | controller_interface::CallbackReturn @(pytroller_class)::read_parameters()
211 | {
212 | if (!param_listener_)
213 | {
214 | RCLCPP_ERROR(get_node()->get_logger(), "Error encountered during init");
215 | return controller_interface::CallbackReturn::ERROR;
216 | }
217 | params_ = param_listener_->get_params();
218 |
219 | if (params_.interface_full_names.empty())
220 | {
221 | RCLCPP_ERROR(get_node()->get_logger(), "'interface_full_names' parameter was empty");
222 | return controller_interface::CallbackReturn::ERROR;
223 | }
224 |
225 | for (const auto & interface_name : params_.interface_full_names) {
226 | command_interface_types_.push_back(interface_name);
227 | }
228 |
229 | return controller_interface::CallbackReturn::SUCCESS;
230 | }
231 |
232 | } // namespace @(pytroller_name)
233 |
234 | #include "pluginlib/class_list_macros.hpp"
235 |
236 | PLUGINLIB_EXPORT_CLASS(
237 | @(pytroller_name)::@(pytroller_class), controller_interface::ControllerInterface)
238 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/pytroller_tools/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------