diff --git a/README.md b/README.md index 14a7c54..c68f9b1 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,54 @@ You can then download and run the script: ```bash bash <(curl https://raw.githubusercontent.com/3LawsRobotics/3laws/master/install_ros2.sh) ``` +## Docker runtime environment + +A sample Docker project is provided to assist users who prefer to install and execute Supervisor within a controlled environment. The project is not designed to meet any production-grade requirements, but rather to provide a starting point for further development. + +From the root directory of this project, run `./docker/build_docker.bash ` to create the runtime Docker image. Replace `` with either `humble` or `jazzy`. When building is complete, run `./docker/run_docker.bash ` to start the container. + +The start script creates a docker managed volume mounted at `~/.3laws`. The volume is used to store Supervisor configurations so that all savings and progresses persists across restarts of the container. + +In alternative to manually starting the container via the provided script, it is possible to call the script directly from a ROS 2 launch file: + +```python +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +from launch.actions import ExecuteProcess + + +def generate_launch_description(): + DOCKER_RUN_SCRIPT = os.path.join( + , + "run_docker.bash", + ) + + launchdesc = LaunchDescription( + [ + ExecuteProcess( + cmd=[DOCKER_RUN_SCRIPT], + shell=True, + output="screen", + name="lll_supervisor_docker", + ) + ] + ) + + return launchdesc + +``` + + +IMPORTANT: When running the container for the first time, the Supervisor node will fail as no configuration file exists yet. Configure Supervisor through the Control Panel and restart the container. + +When running the provided container, a `rosbridge_websocket` node and 3Laws Control Panel are started by the `entrypoint.sh` in the background inside dedicated `screen` sessions. At this point, you can follow the configuration steps described in the [Official Documentation](https://docs.3laws.io/en/latest/). + +Once the Supervisor configuration is complete, you can start the Supervisor from within the container via +```bash +ros2 launch lll_supervisor supervisor.launch.py +``` ## Repo maintainer diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100755 index 0000000..1c3f37f --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,31 @@ +ARG ROS_DISTRO=humble +ARG BASE_IMAGE=ros:$ROS_DISTRO-ros-core +FROM $BASE_IMAGE + +## Sup options +ARG ASSET_ID +ARG ASSET_NAME + +## Install some packages +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + sudo \ + software-properties-common \ + screen \ + vim \ + ros-$ROS_DISTRO-rosbridge-server + +## Get Supervisor +ENV GH_REPO="https://api.github.com/repos/3LawsRobotics/3laws" + +## Install Supervisor +WORKDIR /tmp +ARG CURL_ARGS="-LJO#" +ARG GH_ASSET="$GH_REPO/releases/assets/$ASSET_ID" +RUN curl $CURL_ARGS -s -H 'Accept: application/octet-stream' "$GH_ASSET" +RUN sudo apt install -f ./"$ASSET_NAME" -y --no-install-recommends + +## Add entrypoint +WORKDIR / +COPY entrypoint.sh /root/entrypoint.sh +ENTRYPOINT ["/root/entrypoint.sh"] diff --git a/docker/build_docker.bash b/docker/build_docker.bash new file mode 100755 index 0000000..fe8bdb4 --- /dev/null +++ b/docker/build_docker.bash @@ -0,0 +1,34 @@ +#!/bin/bash + +DOCKER_IMAGE_NAME="lll-supervisor-runtime" + +# Get ROS_DISTRO from first argument, error if not provided +if [ -z "$1" ]; then + echo "Usage: $0 . Specify jazzy|humble" + exit 1 +fi + +# Check if provided ROS_DISTRO is either "jazzy" or "humble" +if [ "$1" != "jazzy" ] && [ "$1" != "humble" ]; then + echo "Error - supported ROS_DISTRO values are: jazzy|humble" + exit 1 +fi +TAG=$1 + +GH_REPO="https://api.github.com/repos/3LawsRobotics/3laws/releases/latest" +PACKAGE_NAME="lll-supervisor-full-${TAG}" +REGEX_QUERY="${PACKAGE_NAME}_[0-9]\+\.[0-9]\+\.[0-9]\+-[0-9]\+_amd64_ubuntu[0-9]\+.[0-9]\+" + +# Read asset tags. +RESPONSE=$(curl -s -H "application/vnd.github+json" $GH_REPO) +ASSET_NAME=$(echo "$RESPONSE" | grep -o "name.:.\+${REGEX_QUERY}.deb" | cut -d ":" -f2- | cut -d "\"" -f2-) +ASSET_ID=$(echo "$RESPONSE" | grep -C3 "name.:.\+$REGEX_QUERY" | grep -w id | tr : = | tr -cd '[[:alnum:]]=' | cut -d'=' -f2-) + +echo "Building Docker image $DOCKER_IMAGE_NAME:$TAG with asset $ASSET_NAME (id: $ASSET_ID)" + +docker build --rm \ + --build-arg ROS_DISTRO="$TAG" \ + --build-arg ASSET_ID="$ASSET_ID" \ + --build-arg ASSET_NAME="$ASSET_NAME" \ + -t "$DOCKER_IMAGE_NAME:$TAG" \ + -f "$(pwd)/Dockerfile" . diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000..64a9aa0 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e # Exit immediately if a command exits with a non-zero status + +screen -dmS lll_rosbridge bash -lc "source /opt/ros/$ROS_DISTRO/setup.bash && exec ros2 launch rosbridge_server rosbridge_websocket_launch.xml port:=9091" + +screen -dmS lll_control_panel /opt/3laws/control_panel/control-panel-backend 8000 /opt/3laws/control_panel/build/ + +source /opt/ros/humble/setup.bash && exec "$@" diff --git a/docker/run_docker.bash b/docker/run_docker.bash new file mode 100755 index 0000000..7838964 --- /dev/null +++ b/docker/run_docker.bash @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e # Exit on error, print commands, and treat unset variables as an error + +TAG="$1" +IMAGE_NAME="lll-supervisor-runtime" + +# Get all tags for the image +tags=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${IMAGE_NAME}:" | cut -d: -f2) + +# Check if TAG exists in tags +if echo "$tags" | grep -q "^${TAG}$"; then + IMAGE_TAG=$TAG +else + echo "Selected tag '$TAG' not found. Available tags are:" + echo "$tags" + exit 1 +fi + +DOCKER_HOME=/ +DOCKER_LLL_CONFIG_DIR="$DOCKER_HOME/.3laws/config" +IMAGE_NAME="lll-supervisor-runtime" + +# Check if Docker image exists +if ! docker image inspect $IMAGE_NAME:$IMAGE_TAG >/dev/null 2>&1; then + echo "Docker image $IMAGE_NAME:$IMAGE_TAG not found. Run build_docker.bash first to create image." + exit 1 +fi + +docker volume create lll_supervisor_config +docker run --privileged --rm -it \ + --name lll-supervisor \ + --net=host \ + -v lll_supervisor_config:/$DOCKER_LLL_CONFIG_DIR/ \ + "$IMAGE_NAME":"$IMAGE_TAG" bash