Skip to content

Commit

Permalink
feat: package-builder:v1 and development container (#1)
Browse files Browse the repository at this point in the history
* build: update aica-package.toml to package-builder:v1

* build: touchup package.xml metadata

* build: add devcontainer configuration

* build: control libraries v7.2.0 -> v7.4.1

* build: modulo v4.2.0 -> v4.2.1

* feat: add helper script to rename the template package

* docs: README
  • Loading branch information
eeberhard authored May 30, 2024
1 parent baef647 commit 6a69590
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 160 deletions.
26 changes: 26 additions & 0 deletions .devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"containerUser": "ros2",
"initializeCommand": [
"docker",
"build",
"--file=aica-package.toml",
"--target=development",
"--build-arg=config.signing.enabled=false",
"--tag=aica-technology/template-component-package:development",
"."
],
"image": "aica-technology/template-component-package:development",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/ros2/.devcontainer,type=bind,consistency=cached",
"workspaceFolder": "/home/ros2/.devcontainer",
"mounts": [
"source=${localWorkspaceFolder}/source,target=/home/ros2/ws/src,type=bind,consistency=cached"
],
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools-extension-pack",
"ms-python.python"
]
}
}
}
166 changes: 41 additions & 125 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,142 +1,56 @@
# AICA Component Template
[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/aica-technology/component-template)

A software development kit for creating custom components for AICA applications.

## AICA Components
# AICA Package Template

The AICA application framework enables modular programming of robotic systems through the dynamic composition of
Components.

Components are smart, run-time blocks that perform specific functions in an application. Components perform internal
computational logic on data, using input and output signals to communicate with other components.
A software development kit for creating custom components for AICA applications.

Simple components are either "on" or "off"; once created, they publish data on their outputs or execute callbacks
triggered by input data or other events. A simple component might listen on an input signal containing some Cartesian
position data, and re-publish the data with some offset or scaling applied.
Full documentation for creating, testing and packaging custom components is available at
https://docs.aica.tech/docs/category/custom-components.

Alternately, lifecycle components have managed states and can be configured, activated, or deactivated in stages.
When fully active, they perform a periodic computation step.
An example of a lifecycle component might be a motion generator that outputs a trajectory command when active, where
the trajectory can be paused and resumed by triggering deactivation and activation.
You will need to have Docker installed in order to develop or build your component package
(see [Docker installation](https://docs.docker.com/get-docker/)).

## Creating a component package
## Create a custom package repository

To create a component package, create a new repository in GitHub using this repository as a template.

![Use this template menu](docs/creation-1.png)
![Create new repository meny](docs/creation-2.png)
![Create new repository menu](docs/creation-2.png)

Alternatively, you can also clone this repository locally and create a new repository from it.

```bash
git clone git@github.com:aica-technology/component-template.git my_component_package
```

If you want to create a second package in your current repository, you will need to create a new folder in `source` and add it in your `aica-package.toml` file.

## Component package structure

We store components in the `source` folder, each component in its own subfolder.

### `./include/<pkg_name>/`

This directory should contain all the C++ headers (`.hpp`) of the package.

### `./src/`

This directory should contain all the C++ source files (`.cpp`) of the package.
They should specify the functions defined in the header files.

### `./<pkg_name>/`

This directory should be a Python module with the same name as the package containing all Python implementations.

### `./test/`

The test directory should contain unit test files for the component package.
Test sources should be named as `test_*.cpp` in the [cpp_tests](./source/template_component_package/test/cpp_tests) folder
for C++ tests using `gtest`, and as `test_*.py` in [python_tests](./source/template_component_package/test/python_tests) for
Python tests using `pytest`.

### `./CMakeLists.txt`

Contains package build instructions. Change this file when adding components as described in
[Writing C++ Components](#writing-c-components) and [Writing Python Components](#writing-python-components).

### `./package.xml`

This is a ROS 2 specific file that describe the package metadata and dependencies.
See the [ROS 2 documentation](https://docs.ros.org/en/foxy/Tutorials/Creating-Your-First-ROS2-Package.html) for
additional details.

### `./setup.cfg`

Contains the entrypoints as described in [Writing Python Components](#writing-python-components).

### `./requirements.txt`

A [pip-formatted file](https://pip.pypa.io/en/stable/reference/requirements-file-format/) containing the Python dependencies of the package.

## Writing C++ components

A component should generally inherit from `modulo_components::Component` for simple, unmanaged instances
or `modulo_components::LifecycleComponent` for managed lifecycle instances.

When inheriting from `modulo_components::LifecycleComponent`, lifecycle transition callback functions can be
overridden to implement custom behaviours when the lifecycle state changes. Some common transition functions include:

- `on_configure_callback`
- `on_activate_callback`
- `on_deactivate_callback`
## Rename the template component package

The lifecycle component also provides a step function callback which is executed periodically.
This template repository uses the placeholder package name `template_component_package`. The first thing you should
do is rename the package to a meaningful custom name. This can be accomplished using the included bash script
[`rename_package.sh`](./rename_package.sh) (use `./rename_package.sh --help` for more information). For example:

- `on_step_callback`.

The base component classes provide additional virtual functions that can be overridden, as well as many utilities to
add signals, parameters and other behaviours. See the modulo component API documentation for more information.

For the AICA application framework to discover the components, they must be registered.

To register the class as a component, the following macro is needed at the end of the source file,
where `template_component_package` is replaced by the local package name and `CPPComponent` is replaced by the component name:

```cpp
#include "rclcpp_components/register_node_macro.hpp"

RCLCPP_COMPONENTS_REGISTER_NODE(template_component_package::CPPComponent)
```
Then, add the following lines to CMakeLists.txt to generate and register the component library target.
The target name `cpp_component` can be replaced with any unique identifier for the particular component.
```cmake
ament_auto_add_library(cpp_component SHARED ${PROJECT_SOURCE_DIR}/src/CPPComponent.cpp)
rclcpp_components_register_nodes(cpp_component "${PROJECT_NAME}::CPPComponent")
list(APPEND COMPONENTS cpp_component)
```bash
./rename_package.sh my_custom_package [--dry-run]
```

## Writing Python components
If you want to include a second ROS package within the AICA package, you will need to create a new package folder in `source`
and add it in your `aica-package.toml` file under `[build.packages.name_of_new_package]` accordingly.

Similar to C++, a Python component is a class that inherits from either `modulo_components.component.Component` or
`modulo_components.lifecycle_component.LifecycleComponent`.
## Configure the package development environment

Refer to the modulo component API documentation for more information.
This template uses a devContainer configuration ([`.devcontainer.json`](./.devcontainer.json)) with base AICA Docker images
for a seamless integrated development experience.

To register the Python class as a component, add an entrypoint to setup.cfg under the `python_components` field.
Replace `template_component_package` and `PyComponent` with the package and component name respectively.
Using VSCode and the Dev Containers extension, after creating and renaming the template package, simply open the repository
in a devcontainer using the "Reopen in Container" command.

```conf
python_components =
template_component_package::PyComponent = template_component_package.py_component:PyComponent
Other IDEs such as JetBrains can similarly be configured to use development containers.

```
If any changes are made to aica-package.toml (including any package names or dependencies), remember to rebuild the devcontainer.

## Building your component package

You will need to have Docker installed in order to build your component package (see [Docker installation](https://docs.docker.com/get-docker/)).

Then you can build your component using the following command:
You can build your package using the following command:

```bash
docker build -f aica-package.toml .
Expand All @@ -145,9 +59,18 @@ docker build -f aica-package.toml .
We use a custom Docker frontend instead of a Dockerfile, so all configuration of the build is stored in `aica-package.toml`.
As we are using `docker build` to build you can pass any Docker argument, like `-t <image_name>` to tag the image or `--platform <platform>` to build for a specific platform. Note that you can only build one platform at a time at the moment.

### `aica-package.toml` configuration
## Testing your component package

See the example [aica-package.toml](./aica-package.toml) or the [docs](https://docs.aica.tech/) for more details about the configuration.
You can invoke any unit tests in your package by changing the docker build stage to `test`, e.g.:

```bash
docker build -f aica-package.toml --target test .
```

## Package configuration with `aica-package.toml`

All build configurations, metadata and dependencies are defined in [aica-package.toml](./aica-package.toml). Refer to the
[docs](https://docs.aica.tech/docs/reference/custom-components/aica-package-toml) for more details about the syntax.

### CLI configuration

Expand All @@ -161,11 +84,11 @@ Example:
docker build -f aica-package.toml --build-arg config.build.cmake_args.SOME_FLAG=Release .
```

## Installing external dependencies
### Installing external dependencies

As the build is done in a Docker container, you will need to install external dependencies through `aica-package.toml`.

### System libraries
#### System libraries

You can add system libraries by adding the list of packages to install through `apt`:

Expand All @@ -176,7 +99,7 @@ libyaml-cpp-dev = "*"

Note that the `*` is currently ignored but might be used in the future to specify a version.

### Python packages
#### Python packages

`aica-package.toml` will automatically install any Python packages specified in a `requirements.txt` file stored in your component package folder. However, you can change the name of that file or specify the packages to install directly in `aica-package.toml`:

Expand All @@ -188,14 +111,7 @@ file = "requirements.txt"
numpy = "1.0.0"
```

## Testing your component package

You can test your component package by changing the docker build stage to `test`, e.g.:

```bash
docker build -f aica-package.toml --target test .
```

## Using your component package

Now that you have built your component package, you can use it in your application. See the [AICA documentation](https://docs.aica.tech/docs/getting-started/installation#configuring-a-runtime-image-with-add-on-packages) for more details.
After you have built and tagged your package as a docker image, you can use it in your application. See the
[AICA documentation](https://docs.aica.tech/docs/getting-started/installation#configuring-a-runtime-image-with-add-on-packages) for more details.
68 changes: 35 additions & 33 deletions aica-package.toml
Original file line number Diff line number Diff line change
@@ -1,46 +1,48 @@
#syntax=ghcr.io/aica-technology/package-builder:v0.0.9
#syntax=ghcr.io/aica-technology/package-builder:v1

[metadata]
version = "0.0.1"
## add a description of the AICA package
# description = "My custom package"

## a metadata name for the collection is required when bundling multiple packages together.
## the collection name should be formatted like a Docker image name,
## and the ROS equivalent collection name must be formatted in lower_snake_case
# [metadata.collection]
# name = "template-custom-collection"
# ros-name = "template_custom_collection"

[build]
type = "ros"
image = "v1.0.1-iron"

[build.cmake_args]
# add any cmake args here, e.g.
[build.cmake-args]
## add any cmake args here, e.g.
# SOME_FLAG = "ON"
# you can override them in the CLI with `--build-arg config.build.cmake_args.SOME_FLAG=OFF`
## you can override them in the CLI with `--build-arg config.build.cmake_args.SOME_FLAG=OFF`

[build.environment.aica]
image = "iron"
[build.dependencies]
"@aica/foss/control-libraries" = "v7.4.1"
"@aica/foss/modulo" = "v4.2.1"

[build.environment.aica.libraries]
"@aica/foss/control-libraries" = "v7.2.0"

[build.environment.aica.ros]
"@aica/foss/modulo" = "v3.2.0"
[build.packages.template-component-package]
source = "./source/template_component_package"

[build.environment.apt]
# add any apt packages here, e.g.
## add any required apt dependencies for the custom package.
## these packages will be available during the build but also in the final image,
## so they are useful for dynamic libraries
# [build.packages.template-component-package.dependencies.apt]
# libyaml-cpp-dev = "*"
# these packages will be available during the build but not in the final image,
# so they are useful for headers or static libraries but not for dynamic libraries

[build.packages.component]
source = "./source/template_component_package"
## by default, we will install any pip packages in `${package_source}/requirements.txt`
## but you can customize the name of the requirements file, e.g.
# [build.packages.template-component-package.dependencies.pip]
# file = "custom_requirements.txt"

[build.packages.component.dependencies.apt]
# add any apt packages here, e.g.
# libyaml-cpp-dev = "*"
# these packages will be available during the build but also in the final image,
# so they are useful for dynamic libraries

# [build.packages.component.dependencies.pip]
# by default, we will install any packages in `${source}/requirements.txt`
# but you can customize the name of the file, e.g.
# file = "requirements.txt"
# otherwise you can specify the packages directly, e.g.
# [build.packages.component.dependencies.pip.packages]
## otherwise you can specify the pip packages directly
# [build.packages.template-component-package.dependencies.pip.packages]
# numpy = "1.0.0"


# add other components here, e.g.
# [build.packages.component2]
# source = "./source/new_component_package"
## if the AICA package contains multiple ROS packages, repeat the process for each ROS package.
# [build.packages.other-component-package]
# source = "./source/other_component_package"
Loading

0 comments on commit 6a69590

Please sign in to comment.