Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attribute SafetyLevel to the component Stacks #179

Open
wants to merge 17 commits into
base: 2.0.0_stable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Publications:

- Features

- [Deployment features](docu/deployment.md)
- [Compare your model with a Specification](docu/CompareSpec.md)
- [Introspection at design time](docu/simulateRuntime.md)
- [Models combination](docu/ModelCombine.md)
Expand Down
93 changes: 93 additions & 0 deletions docu/deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
## How to get deployment artifacts


### Preparation

Please be sure that the tool is installed and your workspace setup, see the [installation guide](../README.md) for further details.

Import the example project pub_sub_ros2 (from the [ros-model-examples](https://github.com/ipa-nhg/ros-model-examples) repository) to the workbench of your application:

```
pub_sub_ros2
```

Import the example project agriculture_demo_sprint2 (from the [ros-model-examples](https://github.com/ipa-nhg/ros-model-examples) repository) to the workbench of your application:

```
agriculture_demo_sprint2
```

Install docker and docker-compose

### Get get deployment artifacts

To create deployment artifacts you need to right click "*.rossystem" file.

#### Rossystem without parameters
In this case, you need to right click "communication.rossystem". Then you need to choose "Deployment Artifacts Generator".

A dialog will be open and ask you to select a ROS Distro. In this case, the rossystem doesn't contain parameters, so you only need to choose a ROS Distro.

This process is as shown blow.

![alt text](images/rostooling_deployment.gif)

Then deployment artifacts will automatically created, as you can find under "src-gen" folder

![alt text](images/pub_sub_ros2_deployment_gen.png)


#### Rossystem with parameters

In some case, you need to choose device ports based on parameters defined in a rossystem.

For example, if you want to run teleop with a joystick in a docker container, docker need to know which port is the joystick connect to.

In this case, you need to right click "communication.rossystem". Then you need to choose "Deployment Artifacts Generator".

A dialog will be open and ask you to select a ROS Distro. In this case, the rossystem contains parameters, you need to choose the corresponding parameter to the joystick port value.

This process is as shown blow.

![alt text](images/rostooling_deployment_jackal.gif)

Then deployment artifacts will automatically created.

![alt text](images/jackal_deployment_gen.png)

### Use github action to generate docker images

Once you get deployment artifacts, you can use git action to build and release docker images automatically.

If you already have a repository in github, you can use it. If not, you need to create a repository. For example, we use ("rossystem-deployment" repository)[https://github.com/ipa-rwu/rossystem-deployment.

You also need a dockerhub account. Once you have dockerhub account, you need to add your Docker ID as a secret to GitHub.
1. Add your Docker ID as a secret to GitHub. Navigate to your GitHub repository and click Settings > Secrets > New secret.
2. Create DOCKER_USERNAME and DOCKER_PASSWORD

The result should looks like below.

![alt text](images/github_secrets.png)

In this repository, you need to create a folder ".github/workflows". Then you need to put a file with suffix "_workflow.yml" from a generated folder to this folder. For example, you can put "communication_foxy_workflow.yml" or "agriculture_robot_sprint3_noetic_workflow.yml" under ".github/workflows" folder, as shown below.

![alt text](images/github_workflows_folder.png)


Then you need to put the generated folder in this repository. For example, we put "communication_ros2" and "agriculture_robot_sprint3" in the repository. The locations of these two folder as shown below.

![alt text](images/rossystem_deployment_repo.png)

You can push them to github. Github action will build docker image and push images in dockerhub.

### Start the system

Before starting the system, you need to modify "docker-compose" file. You need to add your docker account as prefix before a docker image name.
For example, we add "kogrob2" (docker hub account name) in front of "communication_foxy:latest", as shown below.

![alt text](images/docker-compose.png)

Then you can use the command below to start the system.
```
docker-compose up
```
Binary file added docu/images/docker-compose.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docu/images/github_secrets.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docu/images/github_workflows_folder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docu/images/jackal_deployment_gen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docu/images/pub_sub_ros2_deployment_gen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docu/images/rossystem_deployment_repo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docu/images/rostooling_deployment.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docu/images/rostooling_deployment_jackal.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import de.fraunhofer.ipa.rossystem.deployment.RosInstallCompiler
import de.fraunhofer.ipa.rossystem.deployment.DockerComposeCompiler
import de.fraunhofer.ipa.rossystem.deployment.DockerContainerCompiler
import de.fraunhofer.ipa.rossystem.deployment.GitActionCompiler
import de.fraunhofer.ipa.rossystem.deployment.DeploymentHelpers
import rossystem.RosSystem;
import java.util.HashMap
import java.util.Map
Expand Down Expand Up @@ -44,12 +45,12 @@ class DeploymentArtifactsGenerator extends AbstractGenerator {
RosInstallCompiler rosintall_compiler = new RosInstallCompiler()
DockerComposeCompiler dockercompose_compiler = new DockerComposeCompiler()
GitActionCompiler gitaction_compiler = new GitActionCompiler()


DeploymentHelpers generator_helper = new DeploymentHelpers()

String ros_distro
String system_folder_prefix
Integer ros_version
String system_prefix
String stack_prefix
Map<String, List<String>> device_map = new HashMap<String, List<String>>

def get_ros_distro(String distro) {
Expand Down Expand Up @@ -85,26 +86,26 @@ class DeploymentArtifactsGenerator extends AbstractGenerator {
}
]
for (system : resource.allContents.toIterable.filter(RosSystem)){
system_prefix = create_system_prefix(system)
system_folder_prefix = create_system_prefix(system)
if (system.componentStack.size==0){
fsa.generateFile(system_prefix +"/Dockerfile",docker_compiler.compile_toDockerContainer(system, null, ros_distro, ros_version))
fsa.generateFile(system_prefix +"/extra_layer/" + system.getName().toLowerCase + ".rosinstall",rosintall_compiler.compile_toRosInstall(system,null))
fsa.generateFile(system_prefix +"/extra_layer/Dockerfile",docker_compiler.compile_toDockerImageExtraLayer(system, null,ros_distro, ros_version))
fsa.generateFile(system_folder_prefix +"/Dockerfile",docker_compiler.compile_toDockerContainer(system, null, ros_distro, ros_version))
fsa.generateFile(system_folder_prefix +"/extra_layer/" + system.getName().toLowerCase + ".rosinstall",rosintall_compiler.compile_toRosInstall(system,null))
fsa.generateFile(system_folder_prefix +"/extra_layer/Dockerfile",docker_compiler.compile_toDockerImageExtraLayer(system, null,ros_distro, ros_version))
} else {
for (stack : system.componentStack){
stack_prefix = String.join("/", system_prefix, system.name.toLowerCase+'_'+stack.name.toLowerCase)
fsa.generateFile(String.join("/", stack_prefix, "Dockerfile"),docker_compiler.compile_toDockerContainer(system, stack, ros_distro, ros_version))
fsa.generateFile(String.join("/", stack_prefix, "extra_layer", stack.name.toLowerCase+".rosinstall"),rosintall_compiler.compile_toRosInstall(system,stack))
fsa.generateFile(String.join("/", stack_prefix, "extra_layer", "Dockerfile"),docker_compiler.compile_toDockerImageExtraLayer(system,stack, ros_distro, ros_version))
val stack_folder_prefix = String.join("/", system_folder_prefix, system.name.toLowerCase+'_'+stack.name.toLowerCase)
fsa.generateFile(String.join("/", stack_folder_prefix, "Dockerfile"),docker_compiler.compile_toDockerContainer(system, stack, ros_distro, ros_version))
fsa.generateFile(String.join("/", stack_folder_prefix, "extra_layer", stack.name.toLowerCase+".rosinstall"),rosintall_compiler.compile_toRosInstall(system,stack))
fsa.generateFile(String.join("/", stack_folder_prefix, "extra_layer", "Dockerfile"),docker_compiler.compile_toDockerImageExtraLayer(system,stack, ros_distro, ros_version))
}
}

fsa.generateFile(String.join("/", system_prefix, "docker-compose.yml"),dockercompose_compiler.compile_toDockerCompose(system, ros_distro, ros_version, device_map))
fsa.generateFile(String.join("/", system_folder_prefix, "docker-compose.yml"),dockercompose_compiler.compile_toDockerCompose(system, ros_distro, ros_version, device_map))
}

// git action workflow
for (system : resource.allContents.toIterable.filter(RosSystem)){
fsa.generateFile(String.join("/", system_prefix, system.getName().toLowerCase + "_workflow.yml") ,gitaction_compiler.compile_toGitAction(system, ros_version))
fsa.generateFile(String.join("/", system_folder_prefix, generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro) + "_workflow.yml") ,gitaction_compiler.compile_toGitAction(system, ros_version, ros_distro))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package de.fraunhofer.ipa.rossystem.deployment

import componentInterface.ComponentInterface
import de.fraunhofer.ipa.rossystem.generator.GeneratorHelpers
import java.util.ArrayList
import java.util.HashSet
import java.util.List
import java.util.Set
import ros.Dependency
import ros.PackageDependency
import ros.impl.PackageImpl
import rossystem.ComponentStack
import rossystem.RosSystem

class DeploymentHelpers extends GeneratorHelpers {
List<ComponentInterface> ComponentsList
PackageImpl component_package
Set<String> Repos

def get_uniqe_name(String prefix, String ros_distro) {
return prefix + "_" + ros_distro
}

def get_folder_name(String prefix, String ros_distro) {
if(ros_distro=="foxy") {
return prefix + "_ros2"
}
else{
return prefix
}
}

def Set<String> listOfRepos(Object subsystem) {
new ArrayList()
ComponentsList = new ArrayList<ComponentInterface>();
if (subsystem.class.toString.contains("RosSystemImpl")){
ComponentsList = (subsystem as RosSystem).rosComponent
} else if (subsystem.class.toString.contains("ComponentStackImpl")) {
ComponentsList = (subsystem as ComponentStack).rosComponent
}

Repos = new HashSet<String>();
for (ComponentInterface component: ComponentsList){
component_package = null;
component_package = get_pkg(component);
if (component_package !== null){
if (component_package.fromGitRepo !== null){
Repos.add(component_package.fromGitRepo);
}
if (!component_package.dependency.empty){
for (Dependency depend: component_package.dependency){
if ((depend as PackageDependency).package !== null){
if ((depend as PackageDependency).package.fromGitRepo !== null){
Repos.add((depend as PackageDependency).package.fromGitRepo);
}
}
}
}}}
return Repos;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package de.fraunhofer.ipa.rossystem.deployment

import rossystem.RosSystem
import de.fraunhofer.ipa.rossystem.generator.GeneratorHelpers
import de.fraunhofer.ipa.rossystem.deployment.DeploymentHelpers
import java.util.Map
import java.util.List

class DockerComposeCompiler {

GeneratorHelpers generator_helper = new GeneratorHelpers()
DeploymentHelpers generator_helper = new DeploymentHelpers()

def create_devices(List<String> ports)'''
«IF ports.size() > 0»
Expand All @@ -31,26 +31,26 @@ services:
- ros

«IF system.getComponentStack().isEmpty()»
«" "»«system.name.toLowerCase»:
image: "«system.name.toLowerCase»:latest"
«" "»«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»:
image: "«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»:latest"
depends_on:
- ros-master
environment:
- "ROS_MASTER_URI=http://ros-master:11311"
- "ROS_HOSTNAME=«system.name.toLowerCase»"
- "ROS_HOSTNAME=«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»"
networks:
- ros
«create_devices(device_map.get(system.name))»
command: stdbuf -o L roslaunch «system.name.toLowerCase» «system.name.toLowerCase».launch --wait
«ELSE»
«FOR stack:system.componentStack»
«" "»«system.name.toLowerCase»_«stack.name.toLowerCase»:
image: "«system.name.toLowerCase»_«stack.name.toLowerCase»:latest"
«" "»«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»_«stack.name.toLowerCase»:
image: "«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»_«stack.name.toLowerCase»:latest"
depends_on:
- ros-master
environment:
- "ROS_MASTER_URI=http://ros-master:11311"
- "ROS_HOSTNAME=«stack.name.toLowerCase»"
- "ROS_HOSTNAME=«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»_«stack.name.toLowerCase»"
networks:
- ros
«create_devices(device_map.get(stack.name))»
Expand All @@ -59,8 +59,22 @@ services:
«ENDFOR»
«ENDIF»
«ELSE»
Todo: complete docker compose file for ros2
«ENDIF»
version: "3.3"
services:
«IF system.getComponentStack().isEmpty()»
«" "»«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»:
image: "«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»:latest"
«create_devices(device_map.get(system.name))»
command: stdbuf -o L ros2 launch «system.name.toLowerCase» «system.name.toLowerCase».launch.py
«ELSE»
«FOR stack:system.componentStack»
«" "»«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»_«stack.name.toLowerCase»:
image: "«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»_«stack.name.toLowerCase»:latest"
«create_devices(device_map.get(stack.name))»
command: stdbuf -o L ros2 launch «system.name.toLowerCase»_«stack.name.toLowerCase» «stack.name.toLowerCase».launch.py

«ENDFOR»
«ENDIF»
«ENDIF»
'''
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,39 @@ package de.fraunhofer.ipa.rossystem.deployment

import rossystem.RosSystem
import rossystem.ComponentStack
import de.fraunhofer.ipa.rossystem.generator.GeneratorHelpers
import de.fraunhofer.ipa.rossystem.deployment.DeploymentHelpers

class DockerContainerCompiler {
GeneratorHelpers generator_helper = new GeneratorHelpers()
DeploymentHelpers generator_helper = new DeploymentHelpers()

def dockerfile_header(Integer ros_version) '''
# syntax=docker/dockerfile:experimental
ARG SUFFIX=
ARG BUILDER_SUFFIX=:ros«ros_version»
ARG PREFIX=
ARG PREFIX=
'''
def compile_toDockerContainer(RosSystem system, ComponentStack stack, String ros_distro, Integer ros_version) '''«generator_helper.init_pkg()»
«dockerfile_header(ros_version)»
«IF stack===null»
«IF generator_helper.listOfRepos(system).isEmpty()»
FROM ros:«ros_distro»-ros-core as base
«ELSE»
FROM ${PREFIX}extra_layer_«system.name.toLowerCase»${SUFFIX} as base
FROM ${PREFIX}extra_layer_«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»${SUFFIX} as base
«ENDIF»
«ELSE»
«IF generator_helper.listOfRepos(stack).isEmpty()»
FROM ros:«ros_distro»-ros-core as base
«ELSE»
FROM ${PREFIX}extra_layer_«stack.name.toLowerCase»${SUFFIX} as base
FROM ${PREFIX}extra_layer_«generator_helper.get_uniqe_name(system.name.toLowerCase, ros_distro)»_«stack.name.toLowerCase»${SUFFIX} as base
«ENDIF»
«ENDIF»
FROM ${PREFIX}builder${BUILDER_SUFFIX} as builder

FROM base as build
COPY . /root/ws/src/«IF stack===null»«system.name.toLowerCase»«ELSE»«system.name.toLowerCase»_«stack.name.toLowerCase»«ENDIF»/
COPY . /root/ws/src/«IF stack===null»«system.name.toLowerCase»«ELSE»«system.name.toLowerCase»_«stack.name.toLowerCase»«ENDIF»«IF ros_version===2»_ros2«ENDIF»/
RUN --mount=type=bind,from=builder,target=/builder \
apt-get update -qq && \
«IF ros_version===2»/builder/workspace.bash builder_setup && \«ENDIF»
/builder/workspace.bash build_workspace /root/ws && \
rm -rf /var/lib/apt/lists/*

Expand Down Expand Up @@ -63,7 +64,11 @@ RUN --mount=type=bind,from=builder,target=/builder --mount=type=bind,target=/roo
COPY --from=install /opt/ros/$ROS_DISTRO /opt/ros/$ROS_DISTRO

FROM deploy as launch
«IF ros_version==1»
«IF stack===null»CMD ["roslaunch", "«system.name»", "«system.name».launch"]«ELSE»CMD ["roslaunch", "«system.name.toLowerCase»_«stack.name.toLowerCase»", "«stack.name.toLowerCase».launch"]«ENDIF»
«ELSE»
«IF stack===null»CMD ["ros2", "launch", "«system.name.toLowerCase»", "«system.name.toLowerCase».launch.py"]«ELSE»CMD ["ros2", "launch", "«system.name.toLowerCase»_«stack.name.toLowerCase»", "«stack.name.toLowerCase».launch.py"]«ENDIF»
«ENDIF»
'''

def compile_toDockerImageExtraLayer(RosSystem system, ComponentStack stack, String ros_distro, Integer ros_version) '''«generator_helper.init_pkg()»
Expand All @@ -76,6 +81,7 @@ FROM base as pre_build
COPY * /root/ws/src/
RUN --mount=type=bind,from=builder,target=/builder \
apt-get update -qq && \
«IF ros_version===2»/builder/workspace.bash builder_setup && \«ENDIF»
/builder/workspace.bash update_list /root/ws && \
rm -rf /var/lib/apt/lists/*

Expand Down Expand Up @@ -103,7 +109,7 @@ RUN --mount=type=bind,from=builder,target=/builder \
/builder/workspace.bash install_depends /root/ws && \
rm -rf /var/lib/apt/lists/*

FROM pre_build as deploy
FROM build as deploy
RUN --mount=type=bind,from=builder,target=/builder \
--mount=type=bind,target=/root/ws,from=install,source=/root/ws \
apt-get update -qq && \
Expand Down
Loading