From ddc6ae47e846ecc8c958be23ffef53341830fbbd Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 18 Jul 2025 13:38:24 +0900 Subject: [PATCH 01/40] add: user creation in entrypoint --- haru-os/Dockerfile | 1 + haru-os/entrypoint.sh | 153 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 150 insertions(+), 4 deletions(-) diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index d30483f..2012820 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -9,6 +9,7 @@ RUN apt-get update && apt-get install -y \ alsa-utils \ clang build-essential cmake \ python3-pip python3-venv python3-colcon-common-extensions \ + gosu \ && rm -rf /var/lib/apt/lists/* # ROS basics diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index 13b6b4a..a37da2b 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -13,6 +13,146 @@ export QT_X11_NO_MITSHM=${QT_X11_NO_MITSHM:-1} export NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES:-all} export NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} +echo "ENTRYPOINT SCRIPT EXECUTION FOR user change STARTED AT $(date)" + +# Default USER_ID and GROUP_ID if not set +USER_ID=${USER_ID:-1000} +GROUP_ID=${GROUP_ID:-1000} +USERNAME="haru-eut" # Default username + +# user_removed=false + +# Check if user with USER_ID already exists +EXISTING_USERNAME=$(getent passwd "$USER_ID" | cut -d: -f1) + +# If the user exists and the username is different, remove the existing user (we need to free the UID) +if [ -n "$EXISTING_USERNAME" ] && [ "$EXISTING_USERNAME" != "$USERNAME" ]; then + echo "Removing user $EXISTING_USERNAME" + deluser --remove-home $EXISTING_USERNAME +# user_removed=true +fi + +# Create group if it doesn't exist +if ! getent group "$USERNAME" >/dev/null && ! getent group "$GROUP_ID" >/dev/null; then + groupadd --gid "$GROUP_ID" "$USERNAME" +elif ! getent group "$USERNAME" >/dev/null && getent group "$GROUP_ID" >/dev/null; then + EXISTING_GROUP_NAME=$(getent group "$GROUP_ID" | cut -d: -f1) + echo "Warning: Group with GID $GROUP_ID already exists with name $EXISTING_GROUP_NAME." + if [ "$USERNAME" != "$EXISTING_GROUP_NAME" ]; then + echo "Using existing group name $EXISTING_GROUP_NAME for user $USERNAME." + # Decide if you want to force USERNAME to EXISTING_GROUP_NAME or handle differently + fi + # For simplicity, we'll assume the GID is the primary concern. + # If USERNAME must be used, ensure groupadd doesn't conflict or handle renaming. +fi + +# Create user if it doesn't exist +if ! id -u "$USERNAME" >/dev/null 2>&1 && ! getent passwd "$USER_ID" >/dev/null; then + # Ensure the group for GID exists before creating user with it + if ! getent group "$GROUP_ID" >/dev/null; then + groupadd --gid "$GROUP_ID" "$USERNAME" # Or use a default group name if USERNAME is taken + fi + useradd --shell /bin/bash --uid "$USER_ID" --gid "$GROUP_ID" --create-home "$USERNAME" +elif ! id -u "$USERNAME" >/dev/null 2>&1 && getent passwd "$USER_ID" >/dev/null; then + # This condition implies: + # 1. The desired username "$USERNAME" (e.g., "haru-eut") is not registered. + # 2. The desired UID "$USER_ID" is already in use by a different user. + # This state should ideally be prevented by the initial cleanup logic. + # If this branch is reached, it indicates an unexpected or conflict state. + EXISTING_USER_NAME_WITH_UID=$(getent passwd "$USER_ID" | cut -d: -f1) + echo "Error: UID $USER_ID is already in use by user '$EXISTING_USER_NAME_WITH_UID'," \ + "but the desired username '$USERNAME' does not exist." >&2 + echo "This represents an unresolvable conflict based on the script's logic." >&2 + echo "The script expects that if UID $USER_ID is taken by a user other than '$USERNAME'," \ + "that user should have been removed earlier." >&2 + exit 1 +fi + +# Add user to sudoers with NOPASSWD +echo "Adding user $USERNAME to sudoers with NOPASSWD..." +echo "$USERNAME ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/$USERNAME" +chmod 0440 "/etc/sudoers.d/$USERNAME" + +# Change ownership of /ros2_ws +if [ -d "/ros2_ws" ]; then + echo "Changing ownership of /ros2_ws to $USERNAME ($USER_ID:$GROUP_ID)..." + chown -R "$USER_ID:$GROUP_ID" /ros2_ws +else + echo "Warning: /ros2_ws directory not found. Skipping chown." +fi + +# Ensure home directory permissions +USER_HOME="/home/$USERNAME" +if [ -d "$USER_HOME" ]; then + chown -R "$USER_ID:$GROUP_ID" "$USER_HOME" + # Ensure .bashrc exists and has correct permissions + BASHRC_FILE="$USER_HOME/.bashrc" + if [ ! -f "$BASHRC_FILE" ]; then + touch "$BASHRC_FILE" + chown "$USER_ID:$GROUP_ID" "$BASHRC_FILE" + fi + + echo "Updating $BASHRC_FILE for user $USERNAME..." + + # Lines to add to .bashrc + # Using grep -qxF to add line only if it doesn't exist + LINE_ROS_IP='export ROS_IP=$(ip route get 8.8.8.8 | awk -F'"'"'src '"'"' '"'"'NR==1{split($2,a," ");print a[1]}'"'"')' + grep -qxF "$LINE_ROS_IP" "$BASHRC_FILE" || echo "$LINE_ROS_IP" >> "$BASHRC_FILE" + + # ROS_DISTRO should be available as an environment variable in the container + # We write it as ${ROS_DISTRO} so it's evaluated when .bashrc is sourced + LINE_ROS_SETUP="source /opt/ros/\${ROS_DISTRO}/setup.bash" + grep -qxF "$LINE_ROS_SETUP" "$BASHRC_FILE" || echo "$LINE_ROS_SETUP" >> "$BASHRC_FILE" + + LINE_HARU_WS_SETUP="source /ros2_ws/install/setup.bash" + grep -qxF "$LINE_HARU_WS_SETUP" "$BASHRC_FILE" || echo "$LINE_HARU_WS_SETUP" >> "$BASHRC_FILE" + + LINE_COLCON_ARGCOMPLETE="source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash" + grep -qxF "$LINE_COLCON_ARGCOMPLETE" "$BASHRC_FILE" || echo "$LINE_COLCON_ARGCOMPLETE" >> "$BASHRC_FILE" + +else + echo "Warning: Home directory $USER_HOME not found for user $USERNAME." +fi + +# Optionally, recompile the workspace as USERNAME +# if [ -d "/ros2_ws/src" ]; then # Check if src directory exists, indicating a workspace to build +# echo "Recompiling workspace /ros2_ws as user $USERNAME..." +# # Ensure ROS_DISTRO is available. It should be an environment variable in the container. +# if [ -z "$ROS_DISTRO" ]; then +# echo "Error: ROS_DISTRO environment variable is not set. Cannot determine ROS setup path for recompilation." +# # Decide if you want to exit or attempt a default +# exit 1 +# fi + +# # Construct the command to run. +# # 1. Source the main ROS distribution's setup file. +# # 2. Source the user's .bashrc (which might add ROS_IP or source the local workspace). +# # 3. Change to the workspace directory. +# # 4. Execute colcon build. +# # Using \${ROS_DISTRO} ensures it's expanded by the shell invoked by gosu, using the container's ENV. +# COMMAND_TO_RUN="source /opt/ros/\${ROS_DISTRO}/setup.bash && \ +# source \"/home/$USERNAME/.bashrc\" && \ +# cd /ros2_ws && source ./install/setup.bash && \ +# echo 'Attempting to build workspace /ros2_ws as user $USERNAME' && \ +# echo 'Python3 path: '$(which python3) && \ +# echo 'PYTHONPATH: '\${PYTHONPATH} && \ +# colcon build --event-handlers console_direct+ --symlink-install" + +# echo "Executing recompilation command as $USERNAME: $COMMAND_TO_RUN" # For debugging + +# if gosu "$USERNAME" bash -c "$COMMAND_TO_RUN"; then +# echo "Workspace recompiled successfully." +# else +# echo "Warning: Workspace recompilation failed. The container will continue with the existing build (if any)." +# # Depending on requirements, you might want to exit here if build is critical: +# # exit 1 +# fi +# elif [ -d "/ros2_ws" ]; then +# echo "Info: /ros2_ws directory exists but /ros2_ws/src not found. Skipping recompilation." +# else +# echo "Info: /ros2_ws directory not found. Skipping recompilation." +# fi + # Set ROS export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:-0} echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" @@ -21,9 +161,14 @@ echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" source "/opt/ros/$ROS_DISTRO/setup.bash" # Source workspace overlay, if exists -if [ -f "/root/ros2_ws/install/setup.bash" ]; then - source "/root/ros2_ws/install/setup.bash" +if [ -f "/ros2_ws/install/setup.bash" ]; then + source "/ros2_ws/install/setup.bash" fi -# Execute whatever command was passed to the container -exec "$@" +# Execute the command passed to the entrypoint (e.g., from CMD in Dockerfile or command in docker-compose) +# using gosu to drop privileges +echo "Executing command as user $USERNAME ($USER_ID:$GROUP_ID): $@" +exec gosu "$USERNAME" "$@" + +# # Execute whatever command was passed to the container +# exec "$@" From c416dc88315c1664db41662f380aeea48b725c05 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Tue, 22 Jul 2025 19:58:05 +0900 Subject: [PATCH 02/40] Create default user in the docker build WIP --- haru-os/Dockerfile | 9 ++ haru-os/create_default_user.sh | 87 +++++++++++++++++++ haru-os/entrypoint.sh | 149 +-------------------------------- 3 files changed, 98 insertions(+), 147 deletions(-) create mode 100644 haru-os/create_default_user.sh diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index 2012820..8f49dda 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -29,10 +29,19 @@ RUN rm -f /etc/ros/rosdep/sources.list.d/20-default.list && \ rosdep update --include-eol-distros && \ rm -rf /var/lib/apt/lists/* +# #################################################################################################### +# User created during build - switch to non-root user +# #################################################################################################### +COPY create_default_user.sh /create_default_user.sh +RUN chmod +x /create_default_user.sh && /create_default_user.sh /bin/bash -c "echo 'User setup completed during build'" + # Copy the entrypoint script into the container COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # Use the custom entrypoint ENTRYPOINT ["/entrypoint.sh"] + +USER haru-eut + CMD ["/bin/bash"] \ No newline at end of file diff --git a/haru-os/create_default_user.sh b/haru-os/create_default_user.sh new file mode 100644 index 0000000..6d660bd --- /dev/null +++ b/haru-os/create_default_user.sh @@ -0,0 +1,87 @@ +echo "CREATE DEFAULT USER SCRIPT EXECUTION FOR user change STARTED AT $(date)" + +# Default USER_ID and GROUP_ID if not set +USER_ID=${USER_ID:-1000} +GROUP_ID=${GROUP_ID:-1000} +USERNAME="haru-eut" # Default username + +# user_removed=false + +# Check if user with USER_ID already exists +EXISTING_USERNAME=$(getent passwd "$USER_ID" | cut -d: -f1) + +# If the user exists and the username is different, remove the existing user (we need to free the UID) +if [ -n "$EXISTING_USERNAME" ] && [ "$EXISTING_USERNAME" != "$USERNAME" ]; then + echo "Removing user $EXISTING_USERNAME" + deluser --remove-home $EXISTING_USERNAME +# user_removed=true +fi + +# Create group if it doesn't exist +if ! getent group "$USERNAME" >/dev/null && ! getent group "$GROUP_ID" >/dev/null; then + groupadd --gid "$GROUP_ID" "$USERNAME" +elif ! getent group "$USERNAME" >/dev/null && getent group "$GROUP_ID" >/dev/null; then + EXISTING_GROUP_NAME=$(getent group "$GROUP_ID" | cut -d: -f1) + echo "Warning: Group with GID $GROUP_ID already exists with name $EXISTING_GROUP_NAME." + if [ "$USERNAME" != "$EXISTING_GROUP_NAME" ]; then + echo "Using existing group name $EXISTING_GROUP_NAME for user $USERNAME." + # Decide if you want to force USERNAME to EXISTING_GROUP_NAME or handle differently + fi + # For simplicity, we'll assume the GID is the primary concern. + # If USERNAME must be used, ensure groupadd doesn't conflict or handle renaming. +fi + +# Create user if it doesn't exist +if ! id -u "$USERNAME" >/dev/null 2>&1 && ! getent passwd "$USER_ID" >/dev/null; then + # Ensure the group for GID exists before creating user with it + if ! getent group "$GROUP_ID" >/dev/null; then + groupadd --gid "$GROUP_ID" "$USERNAME" # Or use a default group name if USERNAME is taken + fi + useradd --shell /bin/bash --uid "$USER_ID" --gid "$GROUP_ID" --create-home "$USERNAME" +elif ! id -u "$USERNAME" >/dev/null 2>&1 && getent passwd "$USER_ID" >/dev/null; then + # This condition implies: + # 1. The desired username "$USERNAME" (e.g., "haru-eut") is not registered. + # 2. The desired UID "$USER_ID" is already in use by a different user. + # This state should ideally be prevented by the initial cleanup logic. + # If this branch is reached, it indicates an unexpected or conflict state. + EXISTING_USER_NAME_WITH_UID=$(getent passwd "$USER_ID" | cut -d: -f1) + echo "Error: UID $USER_ID is already in use by user '$EXISTING_USER_NAME_WITH_UID'," \ + "but the desired username '$USERNAME' does not exist." >&2 + echo "This represents an unresolvable conflict based on the script's logic." >&2 + echo "The script expects that if UID $USER_ID is taken by a user other than '$USERNAME'," \ + "that user should have been removed earlier." >&2 + exit 1 +fi + +# Add user to relevant groups for sound and hardware access +echo "Adding user $USERNAME to audio, pulse, and pulse-access groups..." +if getent group audio >/dev/null; then + usermod -aG audio "$USERNAME" +else + echo "Warning: group audio not found. Creating audio group." + groupadd audio + usermod -aG audio "$USERNAME" +fi +if getent group pulse >/dev/null; then + usermod -aG pulse "$USERNAME" +else + echo "Warning: group pulse not found. Sound might be affected." +fi +if getent group pulse-access >/dev/null; then + usermod -aG pulse-access "$USERNAME" +else + echo "Warning: group pulse-access not found. Sound might be affected." +fi + +# Add user to sudoers with NOPASSWD +echo "Adding user $USERNAME to sudoers with NOPASSWD..." +echo "$USERNAME ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/$USERNAME" +chmod 0440 "/etc/sudoers.d/$USERNAME" + +# Change ownership of /ros2_ws +if [ -d "/ros2_ws" ]; then + echo "Changing ownership of /ros2_ws to $USERNAME ($USER_ID:$GROUP_ID)..." + chown -R "$USER_ID:$GROUP_ID" /ros2_ws +else + echo "Warning: /ros2_ws directory not found. Skipping chown." +fi \ No newline at end of file diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index a37da2b..94a2740 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -13,146 +13,6 @@ export QT_X11_NO_MITSHM=${QT_X11_NO_MITSHM:-1} export NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES:-all} export NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} -echo "ENTRYPOINT SCRIPT EXECUTION FOR user change STARTED AT $(date)" - -# Default USER_ID and GROUP_ID if not set -USER_ID=${USER_ID:-1000} -GROUP_ID=${GROUP_ID:-1000} -USERNAME="haru-eut" # Default username - -# user_removed=false - -# Check if user with USER_ID already exists -EXISTING_USERNAME=$(getent passwd "$USER_ID" | cut -d: -f1) - -# If the user exists and the username is different, remove the existing user (we need to free the UID) -if [ -n "$EXISTING_USERNAME" ] && [ "$EXISTING_USERNAME" != "$USERNAME" ]; then - echo "Removing user $EXISTING_USERNAME" - deluser --remove-home $EXISTING_USERNAME -# user_removed=true -fi - -# Create group if it doesn't exist -if ! getent group "$USERNAME" >/dev/null && ! getent group "$GROUP_ID" >/dev/null; then - groupadd --gid "$GROUP_ID" "$USERNAME" -elif ! getent group "$USERNAME" >/dev/null && getent group "$GROUP_ID" >/dev/null; then - EXISTING_GROUP_NAME=$(getent group "$GROUP_ID" | cut -d: -f1) - echo "Warning: Group with GID $GROUP_ID already exists with name $EXISTING_GROUP_NAME." - if [ "$USERNAME" != "$EXISTING_GROUP_NAME" ]; then - echo "Using existing group name $EXISTING_GROUP_NAME for user $USERNAME." - # Decide if you want to force USERNAME to EXISTING_GROUP_NAME or handle differently - fi - # For simplicity, we'll assume the GID is the primary concern. - # If USERNAME must be used, ensure groupadd doesn't conflict or handle renaming. -fi - -# Create user if it doesn't exist -if ! id -u "$USERNAME" >/dev/null 2>&1 && ! getent passwd "$USER_ID" >/dev/null; then - # Ensure the group for GID exists before creating user with it - if ! getent group "$GROUP_ID" >/dev/null; then - groupadd --gid "$GROUP_ID" "$USERNAME" # Or use a default group name if USERNAME is taken - fi - useradd --shell /bin/bash --uid "$USER_ID" --gid "$GROUP_ID" --create-home "$USERNAME" -elif ! id -u "$USERNAME" >/dev/null 2>&1 && getent passwd "$USER_ID" >/dev/null; then - # This condition implies: - # 1. The desired username "$USERNAME" (e.g., "haru-eut") is not registered. - # 2. The desired UID "$USER_ID" is already in use by a different user. - # This state should ideally be prevented by the initial cleanup logic. - # If this branch is reached, it indicates an unexpected or conflict state. - EXISTING_USER_NAME_WITH_UID=$(getent passwd "$USER_ID" | cut -d: -f1) - echo "Error: UID $USER_ID is already in use by user '$EXISTING_USER_NAME_WITH_UID'," \ - "but the desired username '$USERNAME' does not exist." >&2 - echo "This represents an unresolvable conflict based on the script's logic." >&2 - echo "The script expects that if UID $USER_ID is taken by a user other than '$USERNAME'," \ - "that user should have been removed earlier." >&2 - exit 1 -fi - -# Add user to sudoers with NOPASSWD -echo "Adding user $USERNAME to sudoers with NOPASSWD..." -echo "$USERNAME ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/$USERNAME" -chmod 0440 "/etc/sudoers.d/$USERNAME" - -# Change ownership of /ros2_ws -if [ -d "/ros2_ws" ]; then - echo "Changing ownership of /ros2_ws to $USERNAME ($USER_ID:$GROUP_ID)..." - chown -R "$USER_ID:$GROUP_ID" /ros2_ws -else - echo "Warning: /ros2_ws directory not found. Skipping chown." -fi - -# Ensure home directory permissions -USER_HOME="/home/$USERNAME" -if [ -d "$USER_HOME" ]; then - chown -R "$USER_ID:$GROUP_ID" "$USER_HOME" - # Ensure .bashrc exists and has correct permissions - BASHRC_FILE="$USER_HOME/.bashrc" - if [ ! -f "$BASHRC_FILE" ]; then - touch "$BASHRC_FILE" - chown "$USER_ID:$GROUP_ID" "$BASHRC_FILE" - fi - - echo "Updating $BASHRC_FILE for user $USERNAME..." - - # Lines to add to .bashrc - # Using grep -qxF to add line only if it doesn't exist - LINE_ROS_IP='export ROS_IP=$(ip route get 8.8.8.8 | awk -F'"'"'src '"'"' '"'"'NR==1{split($2,a," ");print a[1]}'"'"')' - grep -qxF "$LINE_ROS_IP" "$BASHRC_FILE" || echo "$LINE_ROS_IP" >> "$BASHRC_FILE" - - # ROS_DISTRO should be available as an environment variable in the container - # We write it as ${ROS_DISTRO} so it's evaluated when .bashrc is sourced - LINE_ROS_SETUP="source /opt/ros/\${ROS_DISTRO}/setup.bash" - grep -qxF "$LINE_ROS_SETUP" "$BASHRC_FILE" || echo "$LINE_ROS_SETUP" >> "$BASHRC_FILE" - - LINE_HARU_WS_SETUP="source /ros2_ws/install/setup.bash" - grep -qxF "$LINE_HARU_WS_SETUP" "$BASHRC_FILE" || echo "$LINE_HARU_WS_SETUP" >> "$BASHRC_FILE" - - LINE_COLCON_ARGCOMPLETE="source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash" - grep -qxF "$LINE_COLCON_ARGCOMPLETE" "$BASHRC_FILE" || echo "$LINE_COLCON_ARGCOMPLETE" >> "$BASHRC_FILE" - -else - echo "Warning: Home directory $USER_HOME not found for user $USERNAME." -fi - -# Optionally, recompile the workspace as USERNAME -# if [ -d "/ros2_ws/src" ]; then # Check if src directory exists, indicating a workspace to build -# echo "Recompiling workspace /ros2_ws as user $USERNAME..." -# # Ensure ROS_DISTRO is available. It should be an environment variable in the container. -# if [ -z "$ROS_DISTRO" ]; then -# echo "Error: ROS_DISTRO environment variable is not set. Cannot determine ROS setup path for recompilation." -# # Decide if you want to exit or attempt a default -# exit 1 -# fi - -# # Construct the command to run. -# # 1. Source the main ROS distribution's setup file. -# # 2. Source the user's .bashrc (which might add ROS_IP or source the local workspace). -# # 3. Change to the workspace directory. -# # 4. Execute colcon build. -# # Using \${ROS_DISTRO} ensures it's expanded by the shell invoked by gosu, using the container's ENV. -# COMMAND_TO_RUN="source /opt/ros/\${ROS_DISTRO}/setup.bash && \ -# source \"/home/$USERNAME/.bashrc\" && \ -# cd /ros2_ws && source ./install/setup.bash && \ -# echo 'Attempting to build workspace /ros2_ws as user $USERNAME' && \ -# echo 'Python3 path: '$(which python3) && \ -# echo 'PYTHONPATH: '\${PYTHONPATH} && \ -# colcon build --event-handlers console_direct+ --symlink-install" - -# echo "Executing recompilation command as $USERNAME: $COMMAND_TO_RUN" # For debugging - -# if gosu "$USERNAME" bash -c "$COMMAND_TO_RUN"; then -# echo "Workspace recompiled successfully." -# else -# echo "Warning: Workspace recompilation failed. The container will continue with the existing build (if any)." -# # Depending on requirements, you might want to exit here if build is critical: -# # exit 1 -# fi -# elif [ -d "/ros2_ws" ]; then -# echo "Info: /ros2_ws directory exists but /ros2_ws/src not found. Skipping recompilation." -# else -# echo "Info: /ros2_ws directory not found. Skipping recompilation." -# fi - # Set ROS export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:-0} echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" @@ -165,10 +25,5 @@ if [ -f "/ros2_ws/install/setup.bash" ]; then source "/ros2_ws/install/setup.bash" fi -# Execute the command passed to the entrypoint (e.g., from CMD in Dockerfile or command in docker-compose) -# using gosu to drop privileges -echo "Executing command as user $USERNAME ($USER_ID:$GROUP_ID): $@" -exec gosu "$USERNAME" "$@" - -# # Execute whatever command was passed to the container -# exec "$@" +# Execute whatever command was passed to the container +exec "$@" From 2640e4296d76ba6c92ad1dee235427d08e111fa8 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Tue, 29 Jul 2025 17:44:50 +0900 Subject: [PATCH 03/40] update: remove user creation --- haru-os/Dockerfile | 26 ++-------- haru-os/create_default_user.sh | 87 ---------------------------------- 2 files changed, 4 insertions(+), 109 deletions(-) delete mode 100644 haru-os/create_default_user.sh diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index 8f49dda..07b3f4c 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -4,7 +4,7 @@ ENV DEBIAN_FRONTEND=noninteractive # System basics RUN apt-get update && apt-get install -y \ - curl wget nano vim lsb-release git \ + curl wget nano vim lsb-release git openssh-client \ iproute2 net-tools iputils-ping \ alsa-utils \ clang build-essential cmake \ @@ -12,36 +12,18 @@ RUN apt-get update && apt-get install -y \ gosu \ && rm -rf /var/lib/apt/lists/* -# ROS basics +# Ros basics RUN apt update && apt install -y \ ros-${ROS_DISTRO}-rqt ros-${ROS_DISTRO}-rqt-common-plugins \ && rm -rf /var/lib/apt/lists/* # Haru basics -RUN wget -O - http://robotics.upo.es/repos/focal_apt/debian/conf/harurepo.gpg.key | apt-key add - -RUN wget -nc -P /etc/apt/sources.list.d/ http://robotics.upo.es/repos/focal_apt/debian/conf/harufocal.list -RUN echo "yaml https://raw.githubusercontent.com/robotics-upo/rosdistro/master/rosdep.yaml" | sudo tee /etc/ros/rosdep/sources.list.d/50-haru.list - -RUN rm -f /etc/ros/rosdep/sources.list.d/20-default.list && \ - apt-get update && apt-get install -y \ - python3-rosdep && \ - rosdep init && \ - rosdep update --include-eol-distros && \ - rm -rf /var/lib/apt/lists/* - -# #################################################################################################### -# User created during build - switch to non-root user -# #################################################################################################### -COPY create_default_user.sh /create_default_user.sh -RUN chmod +x /create_default_user.sh && /create_default_user.sh /bin/bash -c "echo 'User setup completed during build'" +RUN wget -O /usr/share/keyrings/harurepo.gpg https://robotics.upo.es/repos/noble_apt/debian/conf/harurepo.gpg.key && \ + wget -P /etc/apt/sources.list.d/ https://robotics.upo.es/repos/noble_apt/debian/conf/harunoble.list # Copy the entrypoint script into the container COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh - -# Use the custom entrypoint ENTRYPOINT ["/entrypoint.sh"] -USER haru-eut - CMD ["/bin/bash"] \ No newline at end of file diff --git a/haru-os/create_default_user.sh b/haru-os/create_default_user.sh deleted file mode 100644 index 6d660bd..0000000 --- a/haru-os/create_default_user.sh +++ /dev/null @@ -1,87 +0,0 @@ -echo "CREATE DEFAULT USER SCRIPT EXECUTION FOR user change STARTED AT $(date)" - -# Default USER_ID and GROUP_ID if not set -USER_ID=${USER_ID:-1000} -GROUP_ID=${GROUP_ID:-1000} -USERNAME="haru-eut" # Default username - -# user_removed=false - -# Check if user with USER_ID already exists -EXISTING_USERNAME=$(getent passwd "$USER_ID" | cut -d: -f1) - -# If the user exists and the username is different, remove the existing user (we need to free the UID) -if [ -n "$EXISTING_USERNAME" ] && [ "$EXISTING_USERNAME" != "$USERNAME" ]; then - echo "Removing user $EXISTING_USERNAME" - deluser --remove-home $EXISTING_USERNAME -# user_removed=true -fi - -# Create group if it doesn't exist -if ! getent group "$USERNAME" >/dev/null && ! getent group "$GROUP_ID" >/dev/null; then - groupadd --gid "$GROUP_ID" "$USERNAME" -elif ! getent group "$USERNAME" >/dev/null && getent group "$GROUP_ID" >/dev/null; then - EXISTING_GROUP_NAME=$(getent group "$GROUP_ID" | cut -d: -f1) - echo "Warning: Group with GID $GROUP_ID already exists with name $EXISTING_GROUP_NAME." - if [ "$USERNAME" != "$EXISTING_GROUP_NAME" ]; then - echo "Using existing group name $EXISTING_GROUP_NAME for user $USERNAME." - # Decide if you want to force USERNAME to EXISTING_GROUP_NAME or handle differently - fi - # For simplicity, we'll assume the GID is the primary concern. - # If USERNAME must be used, ensure groupadd doesn't conflict or handle renaming. -fi - -# Create user if it doesn't exist -if ! id -u "$USERNAME" >/dev/null 2>&1 && ! getent passwd "$USER_ID" >/dev/null; then - # Ensure the group for GID exists before creating user with it - if ! getent group "$GROUP_ID" >/dev/null; then - groupadd --gid "$GROUP_ID" "$USERNAME" # Or use a default group name if USERNAME is taken - fi - useradd --shell /bin/bash --uid "$USER_ID" --gid "$GROUP_ID" --create-home "$USERNAME" -elif ! id -u "$USERNAME" >/dev/null 2>&1 && getent passwd "$USER_ID" >/dev/null; then - # This condition implies: - # 1. The desired username "$USERNAME" (e.g., "haru-eut") is not registered. - # 2. The desired UID "$USER_ID" is already in use by a different user. - # This state should ideally be prevented by the initial cleanup logic. - # If this branch is reached, it indicates an unexpected or conflict state. - EXISTING_USER_NAME_WITH_UID=$(getent passwd "$USER_ID" | cut -d: -f1) - echo "Error: UID $USER_ID is already in use by user '$EXISTING_USER_NAME_WITH_UID'," \ - "but the desired username '$USERNAME' does not exist." >&2 - echo "This represents an unresolvable conflict based on the script's logic." >&2 - echo "The script expects that if UID $USER_ID is taken by a user other than '$USERNAME'," \ - "that user should have been removed earlier." >&2 - exit 1 -fi - -# Add user to relevant groups for sound and hardware access -echo "Adding user $USERNAME to audio, pulse, and pulse-access groups..." -if getent group audio >/dev/null; then - usermod -aG audio "$USERNAME" -else - echo "Warning: group audio not found. Creating audio group." - groupadd audio - usermod -aG audio "$USERNAME" -fi -if getent group pulse >/dev/null; then - usermod -aG pulse "$USERNAME" -else - echo "Warning: group pulse not found. Sound might be affected." -fi -if getent group pulse-access >/dev/null; then - usermod -aG pulse-access "$USERNAME" -else - echo "Warning: group pulse-access not found. Sound might be affected." -fi - -# Add user to sudoers with NOPASSWD -echo "Adding user $USERNAME to sudoers with NOPASSWD..." -echo "$USERNAME ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/$USERNAME" -chmod 0440 "/etc/sudoers.d/$USERNAME" - -# Change ownership of /ros2_ws -if [ -d "/ros2_ws" ]; then - echo "Changing ownership of /ros2_ws to $USERNAME ($USER_ID:$GROUP_ID)..." - chown -R "$USER_ID:$GROUP_ID" /ros2_ws -else - echo "Warning: /ros2_ws directory not found. Skipping chown." -fi \ No newline at end of file From b8fc361093f422e1f9317f933e6ea585438f2343 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Tue, 29 Jul 2025 18:27:19 +0900 Subject: [PATCH 04/40] update docker --- .env.example | 2 +- README.md | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 59080f4..cc600e0 100644 --- a/.env.example +++ b/.env.example @@ -6,4 +6,4 @@ NVIDIA_VISIBLE_DEVICES=all NVIDIA_DRIVER_CAPABILITIES=all # ROS -ROS_DOMAIN_ID=0 \ No newline at end of file +ROS_DOMAIN_ID=99 \ No newline at end of file diff --git a/README.md b/README.md index 9c9b0cc..a1bb822 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ export ROS_DOMAIN_ID=xxx Allow Docker GUI access: ``` -xhost +local:root +xhost +local:docker ``` ## Images and Applications @@ -26,10 +26,9 @@ docker build --rm -t haru/haru-os:ros2 -f haru-os/Dockerfile ./haru-os Run: ``` -docker run -it --rm --name haru-os --gpus all \ - --network host --ipc host \ +docker run -it --rm --name haru-os --network host --gpus all \ + --env-file .env.example \ -v /tmp/.X11-unix:/tmp/.X11-unix \ - -e DISPLAY=${DISPLAY} -e ROS_DOMAIN_ID=${ROS_DOMAIN_ID} \ haru/haru-os:ros2 ``` From 0d7df14aa7b49738ce59c383d6aa8e4cf084c2a4 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Tue, 29 Jul 2025 19:15:33 +0900 Subject: [PATCH 05/40] add: python3-vcstool --- haru-os/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index 07b3f4c..134a71e 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -8,12 +8,13 @@ RUN apt-get update && apt-get install -y \ iproute2 net-tools iputils-ping \ alsa-utils \ clang build-essential cmake \ - python3-pip python3-venv python3-colcon-common-extensions \ + python3-pip python3-venv \ gosu \ && rm -rf /var/lib/apt/lists/* # Ros basics RUN apt update && apt install -y \ + python3-vcstool python3-colcon-common-extensions \ ros-${ROS_DISTRO}-rqt ros-${ROS_DISTRO}-rqt-common-plugins \ && rm -rf /var/lib/apt/lists/* From 34c23297fbabfcceca49a6a09cf65315c1a093e3 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Wed, 30 Jul 2025 10:50:04 +0900 Subject: [PATCH 06/40] update entrypoint --- haru-os/entrypoint.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index 94a2740..910cd41 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -4,9 +4,11 @@ set -e # Set GUI export LIBGL_ALWAYS_INDIRECT=${LIBGL_ALWAYS_INDIRECT:-0} export DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:-/dev/null} -export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/tmp/runtime-root} +export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/tmp/runtime-$USER} mkdir -p $XDG_RUNTIME_DIR -chmod 700 $XDG_RUNTIME_DIR +if [ "$(stat -c %u "$XDG_RUNTIME_DIR")" -eq "$(id -u)" ]; then + chmod 700 "$XDG_RUNTIME_DIR" +fi export QT_X11_NO_MITSHM=${QT_X11_NO_MITSHM:-1} # Set CUDA From 093ecc9ae1d81ca4373fdac1a2d3b480b94de4c7 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Wed, 30 Jul 2025 13:56:30 +0900 Subject: [PATCH 07/40] fix: set rmw to cyclonedds by default to allow intra-user communication on the same machine --- .env.example | 1 + README.md | 10 +++------ docker-compose-haru.yaml | 47 ++++++++++++++++++++++++++++++---------- haru-os/Dockerfile | 1 + haru-os/entrypoint.sh | 3 +++ 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index cc600e0..8882687 100644 --- a/.env.example +++ b/.env.example @@ -6,4 +6,5 @@ NVIDIA_VISIBLE_DEVICES=all NVIDIA_DRIVER_CAPABILITIES=all # ROS +RMW_IMPLEMENTATION=rmw_cyclonedds_cpp ROS_DOMAIN_ID=99 \ No newline at end of file diff --git a/README.md b/README.md index a1bb822..61cfdca 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,6 @@ Install the [Docker Engine](https://docs.docker.com/engine/install/ubuntu/) and For CUDA support, also install the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installing-the-nvidia-container-toolkit). ## Setup -Set ROS variable with: -``` -export ROS_DOMAIN_ID=xxx -``` - Allow Docker GUI access: ``` xhost +local:docker @@ -26,7 +21,8 @@ docker build --rm -t haru/haru-os:ros2 -f haru-os/Dockerfile ./haru-os Run: ``` -docker run -it --rm --name haru-os --network host --gpus all \ +docker run -it --rm --name haru-os \ + --network host --ipc host --gpus all \ --env-file .env.example \ -v /tmp/.X11-unix:/tmp/.X11-unix \ haru/haru-os:ros2 @@ -34,5 +30,5 @@ docker run -it --rm --name haru-os --network host --gpus all \ Or compose: (recommended) ``` -docker compose -f docker-compose-haru.yaml --env-file .env up +docker compose -f docker-compose-haru.yaml --env-file .env.example up ``` diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index 44320ac..420c105 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -2,32 +2,55 @@ name: haru services: - test-publisher: + pub-root: image: haru/haru-os:ros2 - entrypoint: ["/entrypoint.sh"] - command: ["ros2", "topic", "pub", "-r", "1", "/hello", "std_msgs/msg/String", "data: 'Hello from Docker'"] + command: ["ros2", "topic", "pub", "/root", "std_msgs/msg/String", "{data: Hello from root}"] environment: - DISPLAY=${DISPLAY} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host ipc: host - devices: - - /dev/snd - - /dev/bus/usb volumes: - /tmp/.X11-unix:/tmp/.X11-unix - rqt: + pub-user: image: haru/haru-os:ros2 - entrypoint: ["/entrypoint.sh"] - command: ["rqt"] + user: 1000:1000 + command: ["ros2", "topic", "pub", "/user", "std_msgs/msg/String", "{data: Hello from user}"] environment: - DISPLAY=${DISPLAY} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host ipc: host - devices: - - /dev/snd - - /dev/bus/usb volumes: - /tmp/.X11-unix:/tmp/.X11-unix + + echo-root: + image: haru/haru-os:ros2 + command: ["ros2", "topic", "echo", "/user"] + environment: + - DISPLAY=${DISPLAY} + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + network_mode: host + ipc: host + volumes: + - /tmp/.X11-unix:/tmp/.X11-unix + restart: on-failure + + echo-user: + image: haru/haru-os:ros2 + user: 1000:1000 + command: ["ros2", "topic", "echo", "/root"] + environment: + - DISPLAY=${DISPLAY} + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + network_mode: host + ipc: host + volumes: + - /tmp/.X11-unix:/tmp/.X11-unix + restart: on-failure + diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index 134a71e..ffcd12e 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -16,6 +16,7 @@ RUN apt-get update && apt-get install -y \ RUN apt update && apt install -y \ python3-vcstool python3-colcon-common-extensions \ ros-${ROS_DISTRO}-rqt ros-${ROS_DISTRO}-rqt-common-plugins \ + ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ && rm -rf /var/lib/apt/lists/* # Haru basics diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index 910cd41..86ba83e 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -16,6 +16,9 @@ export NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES:-all} export NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} # Set ROS +export RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_cyclonedds_cpp} # default to rmw_cyclonedds_cpp and not rmw_fastrtps_cpp (i.e., disable shared memory transport to allow communication between different users, including root) +echo "[INFO] Using RMW: $RMW_IMPLEMENTATION" + export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:-0} echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" From 53e82ab47a188c53637f44512d25adbd5bc96161 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Wed, 30 Jul 2025 18:36:40 +0900 Subject: [PATCH 08/40] typo --- haru-os/entrypoint.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index 86ba83e..f00b50b 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -2,24 +2,24 @@ set -e # Set GUI -export LIBGL_ALWAYS_INDIRECT=${LIBGL_ALWAYS_INDIRECT:-0} -export DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:-/dev/null} -export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/tmp/runtime-$USER} +export LIBGL_ALWAYS_INDIRECT=${LIBGL_ALWAYS_INDIRECT:=0} +export DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:=/dev/null} +export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:=/tmp/runtime-$USER} mkdir -p $XDG_RUNTIME_DIR if [ "$(stat -c %u "$XDG_RUNTIME_DIR")" -eq "$(id -u)" ]; then chmod 700 "$XDG_RUNTIME_DIR" fi -export QT_X11_NO_MITSHM=${QT_X11_NO_MITSHM:-1} +export QT_X11_NO_MITSHM=${QT_X11_NO_MITSHM:=1} # Set CUDA -export NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES:-all} -export NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES:-compute,utility} +export NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES:=all} +export NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES:=compute,utility} # Set ROS -export RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_cyclonedds_cpp} # default to rmw_cyclonedds_cpp and not rmw_fastrtps_cpp (i.e., disable shared memory transport to allow communication between different users, including root) -echo "[INFO] Using RMW: $RMW_IMPLEMENTATION" +export RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:=rmw_cyclonedds_cpp} # default to rmw_cyclonedds_cpp and not rmw_fastrtps_cpp (i.e., disable shared memory transport to allow communication between different users, including root) +echo "[INFO] Using RMW_IMPLEMENTATION: $RMW_IMPLEMENTATION" -export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:-0} +export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:=0} echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" # Source ROS 2 distro environment From 730ea1aa241bf08f6cfcb7e180655cd18081da01 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Thu, 31 Jul 2025 16:32:32 +0900 Subject: [PATCH 09/40] add: workflow to push docker images to ghcr --- .github/workflows/docker-publish.yaml | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/docker-publish.yaml diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml new file mode 100644 index 0000000..a7f7698 --- /dev/null +++ b/.github/workflows/docker-publish.yaml @@ -0,0 +1,64 @@ +name: Build and Push Docker Images + +on: + push: + branches: + - '*' + +jobs: + build-and-push: + name: Build and Push to GHCR + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + strategy: + matrix: + component: [haru-os] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set variables + id: vars + run: | + OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + REPO=$(basename "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') + BRANCH=$(echo "${GITHUB_REF#refs/heads/}" | tr '/' '-' | tr '[:upper:]' '[:lower:]') + + echo "owner=$OWNER" >> $GITHUB_OUTPUT + echo "repo=$REPO" >> $GITHUB_OUTPUT + echo "branch=$BRANCH" >> $GITHUB_OUTPUT + + - name: Build and push ${{ matrix.component }} image + run: | + OWNER=${{ steps.vars.outputs.owner }} + REPO=${{ steps.vars.outputs.repo }} + BRANCH=${{ steps.vars.outputs.branch }} + + COMPONENT=${{ matrix.component }} + DOCKERFILE="${COMPONENT}/Dockerfile" + IMAGE_NAME="ghcr.io/${OWNER}/${COMPONENT}:${BRANCH}" + + # Tag with branch + echo "Building image $IMAGE_NAME from $DOCKERFILE" + docker build -t $IMAGE_NAME -f "${DOCKERFILE}" . + echo "Pushing image $IMAGE_NAME to GHCR" + docker push "${IMAGE_BASE}:${BRANCH}" + + # Also tag as :latest if branch is main + if [ "${BRANCH}" = "main" ]; then + docker tag $IMAGE_NAME "${IMAGE_BASE}:latest" + echo "Pushing $IMAGE_NAME as latest" + docker push "${IMAGE_BASE}:latest" + fi \ No newline at end of file From bb0c8b16a217ef5ec00b398dc91b1251505535cb Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Thu, 31 Jul 2025 16:33:39 +0900 Subject: [PATCH 10/40] typo: worflow revision --- .github/workflows/docker-publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index a7f7698..8eed1b2 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -52,7 +52,7 @@ jobs: # Tag with branch echo "Building image $IMAGE_NAME from $DOCKERFILE" - docker build -t $IMAGE_NAME -f "${DOCKERFILE}" . + docker build -t $IMAGE_NAME -f "${DOCKERFILE}" ./${COMPONENT} echo "Pushing image $IMAGE_NAME to GHCR" docker push "${IMAGE_BASE}:${BRANCH}" From eb3a78591dcb8691fe689c18d0c72793140a12c2 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Thu, 31 Jul 2025 16:42:41 +0900 Subject: [PATCH 11/40] typo: fix workflow --- .github/workflows/docker-publish.yaml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 8eed1b2..75e8b47 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -48,17 +48,19 @@ jobs: COMPONENT=${{ matrix.component }} DOCKERFILE="${COMPONENT}/Dockerfile" - IMAGE_NAME="ghcr.io/${OWNER}/${COMPONENT}:${BRANCH}" + + IMAGE_BASE="ghcr.io/${OWNER}/${COMPONENT}" + IMAGE_TAG="${BRANCH}" # Tag with branch - echo "Building image $IMAGE_NAME from $DOCKERFILE" - docker build -t $IMAGE_NAME -f "${DOCKERFILE}" ./${COMPONENT} - echo "Pushing image $IMAGE_NAME to GHCR" - docker push "${IMAGE_BASE}:${BRANCH}" + echo "Building image $IMAGE_BASE with tag $IMAGE_TAG from $DOCKERFILE" + docker build -t "${IMAGE_BASE}:${IMAGE_TAG}" -f "${DOCKERFILE}" ./${COMPONENT} + echo "Pushing image $IMAGE_NAME with tag $IMAGE_TAG to GHCR" + docker push "${IMAGE_BASE}:${IMAGE_TAG}" # Also tag as :latest if branch is main if [ "${BRANCH}" = "main" ]; then - docker tag $IMAGE_NAME "${IMAGE_BASE}:latest" + docker tag "${IMAGE_BASE}:${IMAGE_TAG}" "${IMAGE_BASE}:latest" echo "Pushing $IMAGE_NAME as latest" docker push "${IMAGE_BASE}:latest" fi \ No newline at end of file From 32e779e252be88c0dfb31ac3b49829c90b8f2803 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 15:06:51 +0900 Subject: [PATCH 12/40] update: haru-os image --- .env.example | 10 +++- .github/workflows/docker-publish.yaml | 66 ++++++++++++++++----------- docker-compose-haru.yaml | 2 +- haru-os/Dockerfile | 12 +++-- haru-os/custom_rosdep.yaml | 3 ++ 5 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 haru-os/custom_rosdep.yaml diff --git a/.env.example b/.env.example index 8882687..63a0531 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,8 @@ # DISPLAY -DISPLAY=${DISPLAY} +DISPLAY==:0 + +# AUDIO +AUDIO_DEVICE_ID=0 # CUDA NVIDIA_VISIBLE_DEVICES=all @@ -7,4 +10,7 @@ NVIDIA_DRIVER_CAPABILITIES=all # ROS RMW_IMPLEMENTATION=rmw_cyclonedds_cpp -ROS_DOMAIN_ID=99 \ No newline at end of file +ROS_DOMAIN_ID=99 + +# SIMULATOR +ROS_IP=xxx.xxx.xx.xxx \ No newline at end of file diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 75e8b47..fa3467f 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -4,6 +4,8 @@ on: push: branches: - '*' + tags: + - '*' jobs: build-and-push: @@ -33,34 +35,46 @@ jobs: id: vars run: | OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - REPO=$(basename "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') - BRANCH=$(echo "${GITHUB_REF#refs/heads/}" | tr '/' '-' | tr '[:upper:]' '[:lower:]') + + # Tag with branch name or GitHub tag + if [[ "${GITHUB_REF}" == refs/heads/* ]]; then + REF_TYPE="branch" + TAG=$(echo "${GITHUB_REF#refs/heads/}" | tr '/' '-' | tr '[:upper:]' '[:lower:]') + elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then + REF_TYPE="tag" + TAG=$(echo "${GITHUB_REF#refs/tags/}" | tr '/' '-' | tr '[:upper:]' '[:lower:]') + else + echo "Unsupported ref: ${GITHUB_REF}" + exit 1 + fi echo "owner=$OWNER" >> $GITHUB_OUTPUT - echo "repo=$REPO" >> $GITHUB_OUTPUT - echo "branch=$BRANCH" >> $GITHUB_OUTPUT + echo "ref_type=$REF_TYPE" >> $GITHUB_OUTPUT + echo "tag=$TAG" >> $GITHUB_OUTPUT - name: Build and push ${{ matrix.component }} image - run: | - OWNER=${{ steps.vars.outputs.owner }} - REPO=${{ steps.vars.outputs.repo }} - BRANCH=${{ steps.vars.outputs.branch }} - - COMPONENT=${{ matrix.component }} - DOCKERFILE="${COMPONENT}/Dockerfile" - - IMAGE_BASE="ghcr.io/${OWNER}/${COMPONENT}" - IMAGE_TAG="${BRANCH}" - - # Tag with branch - echo "Building image $IMAGE_BASE with tag $IMAGE_TAG from $DOCKERFILE" - docker build -t "${IMAGE_BASE}:${IMAGE_TAG}" -f "${DOCKERFILE}" ./${COMPONENT} - echo "Pushing image $IMAGE_NAME with tag $IMAGE_TAG to GHCR" - docker push "${IMAGE_BASE}:${IMAGE_TAG}" + uses: docker/build-push-action@v5 + with: + context: ./${{ matrix.component }} + file: ./${{ matrix.component }}/Dockerfile + push: true + tags: | + ghcr.io/${{ steps.vars.outputs.owner }}/${{ matrix.component }}:${{ steps.vars.outputs.tag }} + build-args: | + CLONE_METHOD=https + secrets: | + github_token=${{ secrets.GITHUB_TOKEN }} - # Also tag as :latest if branch is main - if [ "${BRANCH}" = "main" ]; then - docker tag "${IMAGE_BASE}:${IMAGE_TAG}" "${IMAGE_BASE}:latest" - echo "Pushing $IMAGE_NAME as latest" - docker push "${IMAGE_BASE}:latest" - fi \ No newline at end of file + - name: Tag and push :latest if main + if: steps.vars.outputs.ref_type == 'branch' && steps.vars.outputs.tag == 'main' + uses: docker/build-push-action@v5 + with: + context: ./${{ matrix.component }} + file: ./${{ matrix.component }}/Dockerfile + push: true + tags: | + ghcr.io/${{ steps.vars.outputs.owner }}/${{ matrix.component }}:latest + build-args: | + CLONE_METHOD=https + secrets: | + github_token=${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index 420c105..3d67da0 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -1,4 +1,4 @@ -name: haru +name: haru-os services: diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index ffcd12e..277dcc8 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -4,9 +4,10 @@ ENV DEBIAN_FRONTEND=noninteractive # System basics RUN apt-get update && apt-get install -y \ - curl wget nano vim lsb-release git openssh-client \ + curl wget unzip nano vim lsb-release \ + git openssh-client \ iproute2 net-tools iputils-ping \ - alsa-utils \ + alsa-utils pulseaudio-utils ffmpeg \ clang build-essential cmake \ python3-pip python3-venv \ gosu \ @@ -18,10 +19,15 @@ RUN apt update && apt install -y \ ros-${ROS_DISTRO}-rqt ros-${ROS_DISTRO}-rqt-common-plugins \ ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ && rm -rf /var/lib/apt/lists/* +COPY custom_rosdep.yaml /etc/ros/rosdep/custom_rosdep.yaml +RUN echo "yaml file:///etc/ros/rosdep/custom_rosdep.yaml" >> /etc/ros/rosdep/sources.list.d/20-custom.list && \ + rosdep update # Haru basics RUN wget -O /usr/share/keyrings/harurepo.gpg https://robotics.upo.es/repos/noble_apt/debian/conf/harurepo.gpg.key && \ - wget -P /etc/apt/sources.list.d/ https://robotics.upo.es/repos/noble_apt/debian/conf/harunoble.list + wget -P /etc/apt/sources.list.d/ https://robotics.upo.es/repos/noble_apt/debian/conf/harunoble.list && \ + echo "yaml https://raw.githubusercontent.com/robotics-upo/rosdistro/master/rosdep.yaml" >> /etc/ros/rosdep/sources.list.d/20-haru.list && \ + rosdep update # Copy the entrypoint script into the container COPY entrypoint.sh /entrypoint.sh diff --git a/haru-os/custom_rosdep.yaml b/haru-os/custom_rosdep.yaml new file mode 100644 index 0000000..b2edcd7 --- /dev/null +++ b/haru-os/custom_rosdep.yaml @@ -0,0 +1,3 @@ +ament_python: + ubuntu: + noble: [ros-jazzy-ament-cmake] \ No newline at end of file From da3faa6b419f35e6f3f63a63c9935fb9ffb31ac6 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 15:09:09 +0900 Subject: [PATCH 13/40] typo: fix --- docker-compose-haru.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index 3d67da0..cc3a121 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -6,7 +6,7 @@ services: image: haru/haru-os:ros2 command: ["ros2", "topic", "pub", "/root", "std_msgs/msg/String", "{data: Hello from root}"] environment: - - DISPLAY=${DISPLAY} + - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host @@ -19,7 +19,7 @@ services: user: 1000:1000 command: ["ros2", "topic", "pub", "/user", "std_msgs/msg/String", "{data: Hello from user}"] environment: - - DISPLAY=${DISPLAY} + - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host @@ -31,7 +31,7 @@ services: image: haru/haru-os:ros2 command: ["ros2", "topic", "echo", "/user"] environment: - - DISPLAY=${DISPLAY} + - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host @@ -45,7 +45,7 @@ services: user: 1000:1000 command: ["ros2", "topic", "echo", "/root"] environment: - - DISPLAY=${DISPLAY} + - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host From 3f11df9fc3207cd5b1294784d0886998aa5e33b7 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 15:29:52 +0900 Subject: [PATCH 14/40] add: wip haru-simulator (only haru systems work) --- .env.example | 5 +- README.md | 21 ++++++++ docker-compose-simulator.yaml | 98 +++++++++-------------------------- haru-simulator/Dockerfile | 53 +++++++++++++++---- 4 files changed, 91 insertions(+), 86 deletions(-) diff --git a/.env.example b/.env.example index 63a0531..4f16ec5 100644 --- a/.env.example +++ b/.env.example @@ -10,7 +10,4 @@ NVIDIA_DRIVER_CAPABILITIES=all # ROS RMW_IMPLEMENTATION=rmw_cyclonedds_cpp -ROS_DOMAIN_ID=99 - -# SIMULATOR -ROS_IP=xxx.xxx.xx.xxx \ No newline at end of file +ROS_DOMAIN_ID=99 \ No newline at end of file diff --git a/README.md b/README.md index 61cfdca..6eb174a 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,24 @@ Or compose: (recommended) ``` docker compose -f docker-compose-haru.yaml --env-file .env.example up ``` + +### Haru-Simulator +``` +docker build --rm --secret id=sshkey,src=$HOME/.ssh/id_ed25519 -t haru/haru-simulator:ros2 -f haru-simulator/Dockerfile ./haru-simulator +# or docker build --rm --secret id=github_token,src=$GITHUB_TOKEN -t haru/haru-simulator:ros2 -f haru-simulator/Dockerfile ./haru-simulator +``` + +Run: +``` +docker run -it --rm --name haru-os \ + --network host --ipc host --gpus all \ + --env-file .env.example \ + --device /dev/snd \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + haru/haru-simulator:ros2 +``` + +Or compose: (recommended) +``` +docker compose -f docker-compose-simulator.yaml --env-file .env.example up +``` \ No newline at end of file diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index fe3845d..96c21cf 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -1,84 +1,36 @@ -name: simulator +name: haru-simulator services: - ros-simulator: - image: haru-simulator - entrypoint: [ "/entrypoint.sh" ] - command: [ "/bin/bash", "-c", "roslaunch haru_unity unity_app_launcher.launch && tail -f /dev/null" ] - environment: - - DISPLAY=${DISPLAY} - - NVIDIA_DRIVER_CAPABILITIES=all - network_mode: host - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - runtime: nvidia - ros-publisher: - image: haru-os - entrypoint: [ "/entrypoint.sh" ] - command: [ "rostopic", "pub", "/hello", "std_msgs/String", "data: 'Hello from Docker'", "-r", "1" ] + # unity-app: + # image: haru/haru-simulator:ros2 + # command: ["/bin/bash", "-c", "export ROS_IP=$(hostname -I | awk '{print $1}') && ros2 launch haru_unity unity_app_launcher_launch.py && tail -f /dev/null"] + # environment: + # - DISPLAY=${DISPLAY} + # - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + # - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + # network_mode: host + # ipc: host + # devices: + # - /dev/snd + # - /dev/bus/usb + # volumes: + # - /tmp/.X11-unix:/tmp/.X11-unix + # runtime: nvidia + + haru-systems: + image: haru/haru-simulator:ros2 + command: ["ros2", "launch", "haru_unity", "haru_systems_launch.py", "audio_device_id:=${AUDIO_DEVICE_ID}"] environment: - - DISPLAY=${DISPLAY} - network_mode: host - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - - ros-publisher-cuda: - image: haru-os-cuda - entrypoint: [ "/entrypoint.sh" ] - command: [ "rostopic", "pub", "/hello_cuda", "std_msgs/String", "data: 'Hello from Docker with CUDA'", "-r", "1" ] - environment: - - DISPLAY=${DISPLAY} + - DISPLAY=${DISPLAY:-=:0} + - AUDIO_DEVICE_ID=${AUDIO_DEVICE_ID:-0} + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host + ipc: host devices: - /dev/snd - /dev/bus/usb volumes: - /tmp/.X11-unix:/tmp/.X11-unix runtime: nvidia - - ros-listener: - image: haru-os - entrypoint: [ "/entrypoint.sh" ] - command: [ "rostopic", "echo", "/hello" ] - environment: - - DISPLAY=${DISPLAY} - network_mode: host - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - - ros-listener-cuda: - image: haru-os-cuda - entrypoint: [ "/entrypoint.sh" ] - command: [ "rostopic", "echo", "/hello_cuda" ] - environment: - - DISPLAY=${DISPLAY} - network_mode: host - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - runtime: nvidia - - ros-rqt: - image: haru-os - entrypoint: [ "/entrypoint.sh" ] - command: [ "rqt" ] - environment: - - DISPLAY=${DISPLAY} - network_mode: host - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 45ed621..21d2809 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -1,19 +1,54 @@ -FROM haru-os-cuda +FROM haru/haru-os:ros2 # Install X11/OpenGL Dependencies RUN apt update && apt install -y \ libgl1 libglx0 libegl1 libxext6 libx11-6 libxrandr2 libxss1 libxcursor1 \ - libxcomposite1 libasound2 libxi6 libxtst6 libpulse0 libnss3 libxinerama1 \ + libxcomposite1 libasound2t64 libxi6 libxtst6 libpulse0 libnss3 libxinerama1 \ + libsdl2-dev libjpeg-dev \ x11-xserver-utils mesa-utils \ && rm -rf /var/lib/apt/lists/* -# Install ROS dependencies -RUN apt-get update && apt-get install -y \ - ros-noetic-xacro \ - ros-noetic-haru-unity ros-noetic-haru-utils \ - && rm -rf /var/lib/apt/lists/* +# Clone ROS dependencies +WORKDIR /ros2_ws/src + +ARG CLONE_METHOD=ssh +ENV CLONE_METHOD=${CLONE_METHOD} + +RUN --mount=type=secret,id=sshkey,required=false \ + --mount=type=secret,id=github_token,required=false \ + if [ "$CLONE_METHOD" = "https" ]; then \ + TOKEN=$(cat /run/secrets/github_token) && \ + git clone -b develop_ros2 https://x-access-token:${TOKEN}@github.com/haru-project/haru-unity.git && \ + git clone -b jazzy_debian https://x-access-token:${TOKEN}@github.com/haru-project/ROS-TCP-Endpoint.git && \ + git clone -b ros2 https://x-access-token:${TOKEN}@github.com/haru-project/strawberry-ros-msgs.git && \ + git clone -b fix_tuple_error https://x-access-token:${TOKEN}@github.com/haru-project/idmind-tabletop-ros-pkg.git; \ + else \ + mkdir -p $HOME/.ssh && cp /run/secrets/sshkey $HOME/.ssh/github && \ + echo "Host github.com" > $HOME/.ssh/config && \ + echo " IdentityFile $HOME/.ssh/github" >> $HOME/.ssh/config && \ + echo " StrictHostKeyChecking no" >> $HOME/.ssh/config && \ + git clone -b develop_ros2 git@github.com:haru-project/haru-unity.git && \ + git clone -b jazzy_debian git@github.com:haru-project/ROS-TCP-Endpoint.git && \ + git clone -b ros2 git@github.com:haru-project/strawberry-ros-msgs.git && \ + git clone -b fix_tuple_error git@github.com:haru-project/idmind-tabletop-ros-pkg.git; \ + fi + +# Download Unity App +RUN curl -L -o /ros2_ws/src/haru-unity/HVE_jazzy_linux.zip "https://www.dropbox.com/scl/fi/8jwe4mnp76h1l4elpfvqu/HVE_jazzy_linux.zip?rlkey=sg8zshsrndyxcic8v68gw6fxo&e=1&st=6mnx9pxf&dl=1" && \ + unzip /ros2_ws/src/haru-unity/HVE_jazzy_linux.zip -d /ros2_ws/src/haru-unity/scripts/unity_ve && \ + rm /ros2_ws/src/haru-unity/HVE_jazzy_linux.zip + +# Download robot resources +RUN cd /ros2_ws/src/idmind-tabletop-ros-pkg && \ + /bin/bash -c "bash scripts/download_resources.sh" + +# Build workspace +WORKDIR /ros2_ws + +RUN apt-get update && \ + /bin/bash -c "source /opt/ros/${ROS_DISTRO}/setup.bash && rosdep install --ignore-src --from-path src --rosdistro ${ROS_DISTRO} -y" && \ + rm -rf /var/lib/apt/lists/* -# Update resources -RUN ["/bin/bash", "-c", "source /entrypoint.sh && roslaunch haru_utils update_resources.launch"] +RUN ["/bin/bash", "-c", "source /opt/ros/${ROS_DISTRO}/setup.bash && colcon build --symlink-install"] CMD ["/bin/bash"] \ No newline at end of file From 241c6624f0de3702b4756cf6e4b95c12c379ac5b Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 15:36:11 +0900 Subject: [PATCH 15/40] update: working unity-app --- docker-compose-simulator.yaml | 42 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index 96c21cf..3b0ef35 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -2,30 +2,15 @@ name: haru-simulator services: - # unity-app: - # image: haru/haru-simulator:ros2 - # command: ["/bin/bash", "-c", "export ROS_IP=$(hostname -I | awk '{print $1}') && ros2 launch haru_unity unity_app_launcher_launch.py && tail -f /dev/null"] - # environment: - # - DISPLAY=${DISPLAY} - # - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - # - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} - # network_mode: host - # ipc: host - # devices: - # - /dev/snd - # - /dev/bus/usb - # volumes: - # - /tmp/.X11-unix:/tmp/.X11-unix - # runtime: nvidia - - haru-systems: + unity-app: image: haru/haru-simulator:ros2 - command: ["ros2", "launch", "haru_unity", "haru_systems_launch.py", "audio_device_id:=${AUDIO_DEVICE_ID}"] + command: ["/bin/bash", "-c", "export ROS_IP=$(hostname -I | awk '{print $1}') && ros2 launch haru_unity unity_app_launcher_launch.py && tail -f /dev/null"] environment: - - DISPLAY=${DISPLAY:-=:0} - - AUDIO_DEVICE_ID=${AUDIO_DEVICE_ID:-0} + - DISPLAY=${DISPLAY} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + - NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES} + - NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES} network_mode: host ipc: host devices: @@ -34,3 +19,20 @@ services: volumes: - /tmp/.X11-unix:/tmp/.X11-unix runtime: nvidia + + # haru-systems: + # image: haru/haru-simulator:ros2 + # command: ["ros2", "launch", "haru_unity", "haru_systems_launch.py", "audio_device_id:=${AUDIO_DEVICE_ID}"] + # environment: + # - DISPLAY=${DISPLAY:-=:0} + # - AUDIO_DEVICE_ID=${AUDIO_DEVICE_ID:-0} + # - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + # - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + # network_mode: host + # ipc: host + # devices: + # - /dev/snd + # - /dev/bus/usb + # volumes: + # - /tmp/.X11-unix:/tmp/.X11-unix + # runtime: nvidia From d8dd334783492702a29aa56cf73a4b21fa0c522e Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 15:36:44 +0900 Subject: [PATCH 16/40] update: build and push haru-simulator to ghcr --- .github/workflows/docker-publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index fa3467f..b0da622 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -18,7 +18,7 @@ jobs: strategy: matrix: - component: [haru-os] + component: [haru-os, haru-simulator] steps: - name: Checkout repository From 808bb48950ab45518ac21d60f6252116638a0dcc Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 16:03:57 +0900 Subject: [PATCH 17/40] fix: build haru-simulator from arg base image name --- .env.example | 2 +- .github/workflows/docker-publish.yaml | 2 ++ docker-compose-haru.yaml | 7 +++---- docker-compose-simulator.yaml | 4 ++-- haru-os/Dockerfile | 1 + haru-simulator/Dockerfile | 3 ++- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index 4f16ec5..93c5a7b 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ # DISPLAY -DISPLAY==:0 +DISPLAY=:0 # AUDIO AUDIO_DEVICE_ID=0 diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index b0da622..bb2dfea 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -62,6 +62,7 @@ jobs: ghcr.io/${{ steps.vars.outputs.owner }}/${{ matrix.component }}:${{ steps.vars.outputs.tag }} build-args: | CLONE_METHOD=https + BASE_IMAGE=ghcr.io/haru-project/haru-docker:${{ steps.vars.outputs.tag }} secrets: | github_token=${{ secrets.GITHUB_TOKEN }} @@ -76,5 +77,6 @@ jobs: ghcr.io/${{ steps.vars.outputs.owner }}/${{ matrix.component }}:latest build-args: | CLONE_METHOD=https + BASE_IMAGE=ghcr.io/haru-project/haru-docker:${{ steps.vars.outputs.tag }} secrets: | github_token=${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index cc3a121..fc25293 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -19,7 +19,7 @@ services: user: 1000:1000 command: ["ros2", "topic", "pub", "/user", "std_msgs/msg/String", "{data: Hello from user}"] environment: - - DISPLAY=${DISPLAY:-=:0} + - DISPLAY=${DISPLAY:-:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host @@ -31,7 +31,7 @@ services: image: haru/haru-os:ros2 command: ["ros2", "topic", "echo", "/user"] environment: - - DISPLAY=${DISPLAY:-=:0} + - DISPLAY=${DISPLAY:-:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host @@ -45,7 +45,7 @@ services: user: 1000:1000 command: ["ros2", "topic", "echo", "/root"] environment: - - DISPLAY=${DISPLAY:-=:0} + - DISPLAY=${DISPLAY:-:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host @@ -53,4 +53,3 @@ services: volumes: - /tmp/.X11-unix:/tmp/.X11-unix restart: on-failure - diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index 3b0ef35..11f04c7 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -6,7 +6,7 @@ services: image: haru/haru-simulator:ros2 command: ["/bin/bash", "-c", "export ROS_IP=$(hostname -I | awk '{print $1}') && ros2 launch haru_unity unity_app_launcher_launch.py && tail -f /dev/null"] environment: - - DISPLAY=${DISPLAY} + - DISPLAY=${DISPLAY:-:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} - NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES} @@ -24,7 +24,7 @@ services: # image: haru/haru-simulator:ros2 # command: ["ros2", "launch", "haru_unity", "haru_systems_launch.py", "audio_device_id:=${AUDIO_DEVICE_ID}"] # environment: - # - DISPLAY=${DISPLAY:-=:0} + # - DISPLAY=${DISPLAY:-:0} # - AUDIO_DEVICE_ID=${AUDIO_DEVICE_ID:-0} # - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} # - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index 277dcc8..48cdccd 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -17,6 +17,7 @@ RUN apt-get update && apt-get install -y \ RUN apt update && apt install -y \ python3-vcstool python3-colcon-common-extensions \ ros-${ROS_DISTRO}-rqt ros-${ROS_DISTRO}-rqt-common-plugins \ + ros-${ROS_DISTRO}-rviz2 ros-${ROS_DISTRO}-robot-state-publisher \ ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ && rm -rf /var/lib/apt/lists/* COPY custom_rosdep.yaml /etc/ros/rosdep/custom_rosdep.yaml diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 21d2809..2f35426 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -1,4 +1,5 @@ -FROM haru/haru-os:ros2 +ARG BASE_IMAGE=haru/haru-os:ros2 +FROM ${BASE_IMAGE} as base # Install X11/OpenGL Dependencies RUN apt update && apt install -y \ From 1d0f0abe90320677d5a0a503533ccf5a4cb9ddee Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 16:07:10 +0900 Subject: [PATCH 18/40] typo: base image name from ghcr --- .github/workflows/docker-publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index bb2dfea..1fe8f7d 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -62,7 +62,7 @@ jobs: ghcr.io/${{ steps.vars.outputs.owner }}/${{ matrix.component }}:${{ steps.vars.outputs.tag }} build-args: | CLONE_METHOD=https - BASE_IMAGE=ghcr.io/haru-project/haru-docker:${{ steps.vars.outputs.tag }} + BASE_IMAGE=ghcr.io/haru-project/haru-os:${{ steps.vars.outputs.tag }} secrets: | github_token=${{ secrets.GITHUB_TOKEN }} @@ -77,6 +77,6 @@ jobs: ghcr.io/${{ steps.vars.outputs.owner }}/${{ matrix.component }}:latest build-args: | CLONE_METHOD=https - BASE_IMAGE=ghcr.io/haru-project/haru-docker:${{ steps.vars.outputs.tag }} + BASE_IMAGE=ghcr.io/haru-project/haru-os:latest secrets: | github_token=${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From dbf2a40cf07184bf6310e9f09a685fc47ff4e69e Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 16:08:42 +0900 Subject: [PATCH 19/40] typo: docker linter --- haru-simulator/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 2f35426..336c983 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -1,5 +1,5 @@ ARG BASE_IMAGE=haru/haru-os:ros2 -FROM ${BASE_IMAGE} as base +FROM ${BASE_IMAGE} AS base # Install X11/OpenGL Dependencies RUN apt update && apt install -y \ From 5cb2a3497ee79adf8e2798e689df1a48fca4966c Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 19:19:12 +0900 Subject: [PATCH 20/40] add: web server --- docker-compose-simulator.yaml | 34 ++++++++++++++++++---------------- haru-simulator/Dockerfile | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index 11f04c7..fbe647d 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -20,19 +20,21 @@ services: - /tmp/.X11-unix:/tmp/.X11-unix runtime: nvidia - # haru-systems: - # image: haru/haru-simulator:ros2 - # command: ["ros2", "launch", "haru_unity", "haru_systems_launch.py", "audio_device_id:=${AUDIO_DEVICE_ID}"] - # environment: - # - DISPLAY=${DISPLAY:-:0} - # - AUDIO_DEVICE_ID=${AUDIO_DEVICE_ID:-0} - # - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - # - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} - # network_mode: host - # ipc: host - # devices: - # - /dev/snd - # - /dev/bus/usb - # volumes: - # - /tmp/.X11-unix:/tmp/.X11-unix - # runtime: nvidia + haru-web-server: + image: haru/haru-simulator:ros2 + working_dir: /app/haru2_web/ + command: ["/bin/bash", "-c", "cd haru_web && \ + ../.venv/bin/python3 -m streamlit run Home.py \ + --server.port 7000 \ + --server.address 0.0.0.0 \ + --server.headless true \ + --server.baseUrlPath '/haru_web'", + ] + environment: + - DISPLAY=${DISPLAY:-:0} + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + network_mode: host + ipc: host + volumes: + - /tmp/.X11-unix:/tmp/.X11-unix diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 336c983..049af04 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -52,4 +52,31 @@ RUN apt-get update && \ RUN ["/bin/bash", "-c", "source /opt/ros/${ROS_DISTRO}/setup.bash && colcon build --symlink-install"] +# Clone Web Server +WORKDIR /app +RUN --mount=type=secret,id=sshkey,required=false \ + --mount=type=secret,id=github_token,required=false \ + if [ "$CLONE_METHOD" = "https" ]; then \ + TOKEN=$(cat /run/secrets/github_token) && \ + git clone -b develop https://x-access-token:${TOKEN}@github.com/haru-project/haru2_web.git; \ + else \ + mkdir -p $HOME/.ssh && cp /run/secrets/sshkey $HOME/.ssh/github && \ + echo "Host github.com" > $HOME/.ssh/config && \ + echo " IdentityFile $HOME/.ssh/github" >> $HOME/.ssh/config && \ + echo " StrictHostKeyChecking no" >> $HOME/.ssh/config && \ + git clone -b develop git@github.com:haru-project/haru2_web.git; \ + fi + +WORKDIR /app/haru2_web + +RUN apt update && apt install -y \ + python3-noble-pyharu \ + && rm -rf /var/lib/apt/lists/* + +RUN python3 -m venv --system-site-packages .venv && \ + .venv/bin/pip install --upgrade pip setuptools wheel && \ + .venv/bin/pip install . + +WORKDIR /ros2_ws + CMD ["/bin/bash"] \ No newline at end of file From e8bee18c50a1beeaed13dc817a16b04ecff831c7 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 1 Aug 2025 19:25:43 +0900 Subject: [PATCH 21/40] update: workflow secrets --- .github/workflows/docker-publish.yaml | 4 ++-- haru-simulator/Dockerfile | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 1fe8f7d..f96fe9b 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -64,7 +64,7 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:${{ steps.vars.outputs.tag }} secrets: | - github_token=${{ secrets.GITHUB_TOKEN }} + github_token=${{ secrets.CLONE_HARU_GH_TOKEN }} - name: Tag and push :latest if main if: steps.vars.outputs.ref_type == 'branch' && steps.vars.outputs.tag == 'main' @@ -79,4 +79,4 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:latest secrets: | - github_token=${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + github_token=${{ secrets.CLONE_HARU_GH_TOKEN }} diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 049af04..8b80f66 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -52,8 +52,13 @@ RUN apt-get update && \ RUN ["/bin/bash", "-c", "source /opt/ros/${ROS_DISTRO}/setup.bash && colcon build --symlink-install"] -# Clone Web Server +# Install Web Server WORKDIR /app + +RUN apt update && apt install -y \ + python3-noble-pyharu \ + && rm -rf /var/lib/apt/lists/* + RUN --mount=type=secret,id=sshkey,required=false \ --mount=type=secret,id=github_token,required=false \ if [ "$CLONE_METHOD" = "https" ]; then \ @@ -69,10 +74,6 @@ RUN --mount=type=secret,id=sshkey,required=false \ WORKDIR /app/haru2_web -RUN apt update && apt install -y \ - python3-noble-pyharu \ - && rm -rf /var/lib/apt/lists/* - RUN python3 -m venv --system-site-packages .venv && \ .venv/bin/pip install --upgrade pip setuptools wheel && \ .venv/bin/pip install . From a1273612df4fa1080320385dac6634d8e2d7fd13 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Mon, 4 Aug 2025 11:35:59 +0900 Subject: [PATCH 22/40] update: remove audio device id --- .env.example | 3 --- 1 file changed, 3 deletions(-) diff --git a/.env.example b/.env.example index 93c5a7b..0e4a558 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,6 @@ # DISPLAY DISPLAY=:0 -# AUDIO -AUDIO_DEVICE_ID=0 - # CUDA NVIDIA_VISIBLE_DEVICES=all NVIDIA_DRIVER_CAPABILITIES=all From 01aab6cbbb818da4722c5c93090ea3db65eb6c29 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Wed, 6 Aug 2025 18:53:12 +0900 Subject: [PATCH 23/40] add: cyclone config for more paricipants as limit was reached --- haru-os/Dockerfile | 3 +++ haru-os/cyclonedds.xml | 25 +++++++++++++++++++++++++ haru-os/entrypoint.sh | 2 ++ 3 files changed, 30 insertions(+) create mode 100644 haru-os/cyclonedds.xml diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index 48cdccd..6195aa9 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -20,6 +20,9 @@ RUN apt update && apt install -y \ ros-${ROS_DISTRO}-rviz2 ros-${ROS_DISTRO}-robot-state-publisher \ ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ && rm -rf /var/lib/apt/lists/* + +COPY cyclonedds.xml /config/cyclonedds.xml + COPY custom_rosdep.yaml /etc/ros/rosdep/custom_rosdep.yaml RUN echo "yaml file:///etc/ros/rosdep/custom_rosdep.yaml" >> /etc/ros/rosdep/sources.list.d/20-custom.list && \ rosdep update diff --git a/haru-os/cyclonedds.xml b/haru-os/cyclonedds.xml new file mode 100644 index 0000000..8cff7be --- /dev/null +++ b/haru-os/cyclonedds.xml @@ -0,0 +1,25 @@ + + + + + + + + default + 65500B + + + + 999 + + + \ No newline at end of file diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index f00b50b..31c13e2 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -17,7 +17,9 @@ export NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES:=compute,utility} # Set ROS export RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:=rmw_cyclonedds_cpp} # default to rmw_cyclonedds_cpp and not rmw_fastrtps_cpp (i.e., disable shared memory transport to allow communication between different users, including root) +export CYCLONEDDS_URI=${CYCLONEDDS_URI:=file:///config/cyclonedds.xml} # cyclonedds config with higher MaxParticipants echo "[INFO] Using RMW_IMPLEMENTATION: $RMW_IMPLEMENTATION" +echo "[INFO] Using CYCLONEDDS_URI: $CYCLONEDDS_URI" export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:=0} echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" From cffca8e7cf40d464537557dd32022a0d01277b20 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Wed, 6 Aug 2025 20:45:50 +0900 Subject: [PATCH 24/40] fix: cyclonedss config --- haru-os/cyclonedds.xml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/haru-os/cyclonedds.xml b/haru-os/cyclonedds.xml index 8cff7be..ad19a38 100644 --- a/haru-os/cyclonedds.xml +++ b/haru-os/cyclonedds.xml @@ -5,21 +5,9 @@ xsi:schemaLocation="https://cdds.io/config https://raw.githubusercontent.com/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd" > - - - - - default - 65500B - - - 999 + + 999 \ No newline at end of file From c56a4b70235d521622cb093c753f048e559c16a6 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Thu, 7 Aug 2025 17:39:30 +0900 Subject: [PATCH 25/40] add: disable multicast on cyclonedds (need to set IP manually) (not for simulator) --- .env.example | 8 ++++++-- docker-compose-haru.yaml | 12 ++++++++++++ docker-compose-simulator.yaml | 4 ++-- haru-os/cyclonedds.xml | 9 +++++++++ haru-os/entrypoint.sh | 5 +++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 0e4a558..9ff7cdf 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,10 @@ DISPLAY=:0 NVIDIA_VISIBLE_DEVICES=all NVIDIA_DRIVER_CAPABILITIES=all +# CONNECTIVITY +ROBOT_IP= +PC_IP= + # ROS -RMW_IMPLEMENTATION=rmw_cyclonedds_cpp -ROS_DOMAIN_ID=99 \ No newline at end of file +ROS_DOMAIN_ID=99 +RMW_IMPLEMENTATION=rmw_cyclonedds_cpp \ No newline at end of file diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index fc25293..148ff0d 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -10,6 +10,9 @@ services: - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host + extra_hosts: + - robot:${ROBOT_IP:-192.168.10.10} + - pc:${PC_IP:-192.168.10.11} ipc: host volumes: - /tmp/.X11-unix:/tmp/.X11-unix @@ -23,6 +26,9 @@ services: - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host + extra_hosts: + - robot:${ROBOT_IP:-192.168.10.10} + - pc:${PC_IP:-192.168.10.11} ipc: host volumes: - /tmp/.X11-unix:/tmp/.X11-unix @@ -35,6 +41,9 @@ services: - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host + extra_hosts: + - robot:${ROBOT_IP:-192.168.10.10} + - pc:${PC_IP:-192.168.10.11} ipc: host volumes: - /tmp/.X11-unix:/tmp/.X11-unix @@ -49,6 +58,9 @@ services: - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host + extra_hosts: + - robot:${ROBOT_IP:-192.168.10.10} + - pc:${PC_IP:-192.168.10.11} ipc: host volumes: - /tmp/.X11-unix:/tmp/.X11-unix diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index fbe647d..59b1ecb 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -8,7 +8,7 @@ services: environment: - DISPLAY=${DISPLAY:-:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + - RMW_IMPLEMENTATION=rmw_fastrtps_cpp - NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES} - NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES} network_mode: host @@ -33,7 +33,7 @@ services: environment: - DISPLAY=${DISPLAY:-:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + - RMW_IMPLEMENTATION=rmw_fastrtps_cpp network_mode: host ipc: host volumes: diff --git a/haru-os/cyclonedds.xml b/haru-os/cyclonedds.xml index ad19a38..70522f8 100644 --- a/haru-os/cyclonedds.xml +++ b/haru-os/cyclonedds.xml @@ -5,7 +5,16 @@ xsi:schemaLocation="https://cdds.io/config https://raw.githubusercontent.com/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd" > + + + false + + + + + + 999 diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index 31c13e2..5a8e3e8 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -24,6 +24,11 @@ echo "[INFO] Using CYCLONEDDS_URI: $CYCLONEDDS_URI" export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:=0} echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" +# Show connectivity +echo "[INFO] Resolving hostnames from /etc/hosts" +echo "[INFO] robot resolves to: $(getent hosts robot | awk '{ print $1 }')" +echo "[INFO] pc resolves to: $(getent hosts pc | awk '{ print $1 }')" + # Source ROS 2 distro environment source "/opt/ros/$ROS_DISTRO/setup.bash" From e537dea8b03e14e21515c4891132ea6a974f02fb Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 8 Aug 2025 17:45:34 +0900 Subject: [PATCH 26/40] add: working setup with zenoh as middleware. check router thought (no simulator yet) --- .env.example | 6 +- docker-compose-haru.yaml | 30 +- haru-os/DEFAULT_RMW_ZENOH_ROUTER_CONFIG.json5 | 771 +++++++++++++++++ .../DEFAULT_RMW_ZENOH_SESSION_CONFIG.json5 | 778 ++++++++++++++++++ haru-os/Dockerfile | 5 +- ...OH_ROUTER_CONFIG_MULTICAST_MULTIHOST.json5 | 771 +++++++++++++++++ .../RMW_ZENOH_SESSION_CONFIG_MULTICAST.json5 | 778 ++++++++++++++++++ haru-os/entrypoint.sh | 19 +- 8 files changed, 3127 insertions(+), 31 deletions(-) create mode 100644 haru-os/DEFAULT_RMW_ZENOH_ROUTER_CONFIG.json5 create mode 100644 haru-os/DEFAULT_RMW_ZENOH_SESSION_CONFIG.json5 create mode 100644 haru-os/RMW_ZENOH_ROUTER_CONFIG_MULTICAST_MULTIHOST.json5 create mode 100644 haru-os/RMW_ZENOH_SESSION_CONFIG_MULTICAST.json5 diff --git a/.env.example b/.env.example index 9ff7cdf..ecf57e3 100644 --- a/.env.example +++ b/.env.example @@ -5,10 +5,6 @@ DISPLAY=:0 NVIDIA_VISIBLE_DEVICES=all NVIDIA_DRIVER_CAPABILITIES=all -# CONNECTIVITY -ROBOT_IP= -PC_IP= - # ROS ROS_DOMAIN_ID=99 -RMW_IMPLEMENTATION=rmw_cyclonedds_cpp \ No newline at end of file +RMW_IMPLEMENTATION=rmw_zenoh_cpp \ No newline at end of file diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index 148ff0d..5e2b444 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -2,6 +2,18 @@ name: haru-os services: + # zenoh-router: + # image: haru/haru-os:ros2 + # command: ["ros2", "run", "rmw_zenoh_cpp", "rmw_zenohd"] + # environment: + # - DISPLAY=${DISPLAY:-=:0} + # - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + # - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + # network_mode: host + # ipc: host + # volumes: + # - /tmp/.X11-unix:/tmp/.X11-unix + pub-root: image: haru/haru-os:ros2 command: ["ros2", "topic", "pub", "/root", "std_msgs/msg/String", "{data: Hello from root}"] @@ -10,9 +22,6 @@ services: - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host - extra_hosts: - - robot:${ROBOT_IP:-192.168.10.10} - - pc:${PC_IP:-192.168.10.11} ipc: host volumes: - /tmp/.X11-unix:/tmp/.X11-unix @@ -22,13 +31,10 @@ services: user: 1000:1000 command: ["ros2", "topic", "pub", "/user", "std_msgs/msg/String", "{data: Hello from user}"] environment: - - DISPLAY=${DISPLAY:-:0} + - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host - extra_hosts: - - robot:${ROBOT_IP:-192.168.10.10} - - pc:${PC_IP:-192.168.10.11} ipc: host volumes: - /tmp/.X11-unix:/tmp/.X11-unix @@ -37,13 +43,10 @@ services: image: haru/haru-os:ros2 command: ["ros2", "topic", "echo", "/user"] environment: - - DISPLAY=${DISPLAY:-:0} + - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host - extra_hosts: - - robot:${ROBOT_IP:-192.168.10.10} - - pc:${PC_IP:-192.168.10.11} ipc: host volumes: - /tmp/.X11-unix:/tmp/.X11-unix @@ -54,13 +57,10 @@ services: user: 1000:1000 command: ["ros2", "topic", "echo", "/root"] environment: - - DISPLAY=${DISPLAY:-:0} + - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} network_mode: host - extra_hosts: - - robot:${ROBOT_IP:-192.168.10.10} - - pc:${PC_IP:-192.168.10.11} ipc: host volumes: - /tmp/.X11-unix:/tmp/.X11-unix diff --git a/haru-os/DEFAULT_RMW_ZENOH_ROUTER_CONFIG.json5 b/haru-os/DEFAULT_RMW_ZENOH_ROUTER_CONFIG.json5 new file mode 100644 index 0000000..255ee8b --- /dev/null +++ b/haru-os/DEFAULT_RMW_ZENOH_ROUTER_CONFIG.json5 @@ -0,0 +1,771 @@ +/// This file attempts to list and document available configuration elements. +/// For a more complete view of the configuration's structure, check out `zenoh/src/config.rs`'s `Config` structure. +/// Note that the values here are correctly typed, but may not be sensible, so copying this file to change only the parts that matter to you is not good practice. +{ + /// The identifier (as unsigned 128bit integer in hexadecimal lowercase - leading zeros are not accepted) + /// that zenoh runtime will use. + /// If not set, a random unsigned 128bit integer will be used. + /// WARNING: this id must be unique in your zenoh network. + // id: "1234567890abcdef", + + /// The node's mode (router, peer or client) + mode: "router", + + /// Which endpoints to connect to. E.g. tcp/localhost:7447. + /// By configuring the endpoints, it is possible to tell zenoh which router/peer to connect to at startup. + /// + /// For TCP/UDP on Linux, it is possible additionally specify the interface to be connected to: + /// E.g. tcp/192.168.0.1:7447#iface=eth0, for connect only if the IP address is reachable via the interface eth0 + /// + /// It is also possible to specify a priority range and/or a reliability setting to be used on the link. + /// For example `tcp/localhost?prio=6-7;rel=0` assigns priorities "data_low" and "background" to the established link. + /// + /// For TCP and TLS links, it is possible to specify the TCP buffer sizes: + /// E.g. tcp/192.168.0.1:7447#so_sndbuf=65000;so_rcvbuf=65000 + /// For TCP, UDP, Quic and TLS links, it is possible to specify a `bind` address for the local socket: + /// E.g. tcp/192.168.0.1:7447#bind=192.168.0.1:0 + /// Note!: Currently it is unsupported to specify both `bind` and `iface`. + /// + /// For TCP/UDP links, it's possible to specify the DSCP field of the IP header: + /// E.g. tcp/192.168.0.1:7447#dscp=0x08 + connect: { + /// timeout waiting for all endpoints connected (0: no retry, -1: infinite timeout) + /// Accepts a single value (e.g. timeout_ms: 0) + /// or different values for router, peer and client (e.g. timeout_ms: { router: -1, peer: -1, client: 0 }). + timeout_ms: { router: -1, peer: -1, client: 0 }, + + /// The list of endpoints to connect to. + /// Accepts a single list (e.g. endpoints: ["tcp/10.10.10.10:7447", "tcp/11.11.11.11:7447"]) + /// or different lists for router, peer and client (e.g. endpoints: { router: ["tcp/10.10.10.10:7447"], peer: ["tcp/11.11.11.11:7447"] }). + /// + /// See https://docs.rs/zenoh/latest/zenoh/config/struct.EndPoint.html + endpoints: [ + // "/
" + ], + + /// Global connect configuration, + /// Accepts a single value or different values for router, peer and client. + /// The configuration can also be specified for the separate endpoint + /// it will override the global one + /// E.g. tcp/192.168.0.1:7447#retry_period_init_ms=20000;retry_period_max_ms=10000" + + /// exit from application, if timeout exceed + exit_on_failure: { router: false, peer: false, client: true }, + /// connect establishing retry configuration + retry: { + /// initial wait timeout until next connect try + period_init_ms: 1000, + /// maximum wait timeout until next connect try + period_max_ms: 4000, + /// increase factor for the next timeout until nexti connect try + period_increase_factor: 2, + }, + }, + + /// Which endpoints to listen on. E.g. tcp/0.0.0.0:7447. + /// By configuring the endpoints, it is possible to tell zenoh which are the endpoints that other routers, + /// peers, or client can use to establish a zenoh session. + /// + /// For TCP/UDP on Linux, it is possible additionally specify the interface to be listened to: + /// E.g. tcp/0.0.0.0:7447#iface=eth0, for listen connection only on eth0 + /// + /// It is also possible to specify a priority range and/or a reliability setting to be used on the link. + /// For example `tcp/localhost?prio=6-7;rel=0` assigns priorities "data_low" and "background" to the established link. + /// + /// For TCP and TLS links, it is possible to specify the TCP buffer sizes: + /// E.g. tcp/192.168.0.1:7447#so_sndbuf=65000;so_rcvbuf=65000 + /// + /// For TCP/UDP links, it's possible to specify the DSCP field of the IP header: + /// E.g. tcp/192.168.0.1:7447#dscp=0x08 + listen: { + /// timeout waiting for all listen endpoints (0: no retry, -1: infinite timeout) + /// Accepts a single value (e.g. timeout_ms: 0) + /// or different values for router, peer and client (e.g. timeout_ms: { router: -1, peer: -1, client: 0 }). + timeout_ms: 0, + + /// The list of endpoints to listen on. + /// Accepts a single list (e.g. endpoints: ["tcp/[::]:7447", "udp/[::]:7447"]) + /// or different lists for router, peer and client (e.g. endpoints: { router: ["tcp/[::]:7447"], peer: ["tcp/[::]:0"] }). + /// + /// See https://docs.rs/zenoh/latest/zenoh/config/struct.EndPoint.html + endpoints: [ + "tcp/[::]:7447" + ], + + /// Global listen configuration, + /// Accepts a single value or different values for router, peer and client. + /// The configuration can also be specified for the separate endpoint + /// it will override the global one + /// E.g. tcp/192.168.0.1:7447#exit_on_failure=false;retry_period_max_ms=1000" + + /// exit from application, if timeout exceed + exit_on_failure: true, + /// listen retry configuration + retry: { + /// initial wait timeout until next try + period_init_ms: 1000, + /// maximum wait timeout until next try + period_max_ms: 4000, + /// increase factor for the next timeout until next try + period_increase_factor: 2, + }, + }, + /// Configure the session open behavior. + open: { + /// Configure the conditions to be met before session open returns. + return_conditions: { + /// Session open waits to connect to scouted peers and routers before returning. + /// When set to false, first publications and queries after session open from peers may be lost. + connect_scouted: true, + /// Session open waits to receive initial declares from connected peers before returning. + /// Setting to false may cause extra traffic at startup from peers. + declares: true, + }, + }, + /// Configure the scouting mechanisms and their behaviours + scouting: { + /// In client mode, the period in milliseconds dedicated to scouting for a router before failing. + timeout: 3000, + /// In peer mode, the maximum period in milliseconds dedicated to scouting remote peers before attempting other operations. + delay: 500, + /// The multicast scouting configuration. + multicast: { + /// Whether multicast scouting is enabled or not + /// + /// ROS setting: disable multicast discovery by default + enabled: false, + /// The socket which should be used for multicast scouting + address: "224.0.0.224:7446", + /// The network interface which should be used for multicast scouting + interface: "auto", // If not set or set to "auto" the interface if picked automatically + /// The time-to-live on multicast scouting packets + ttl: 1, + /// Which type of Zenoh instances to automatically establish sessions with upon discovery on UDP multicast. + /// Accepts a single value (e.g. autoconnect: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router, peer or client mode (e.g. autoconnect: { router: [], peer: ["router", "peer"] }). + /// Each value is a list of: "peer", "router" and/or "client". + autoconnect: { router: [], peer: ["router", "peer"], client: ["router"] }, + /// Strategy for autoconnection, mainly to avoid nodes connecting to each other redundantly. + /// Possible options are: + /// - "always": always attempt to autoconnect, may result in redundant connections. + /// - "greater-zid": attempt to connect to another node only if its own zid is greater than the other's. + /// If both nodes use this strategy, only one will attempt the connection. + /// This strategy may not be suited if one of the nodes is not reachable by the other one, for example + /// because of a private IP. + /// Accepts a single value (e.g. autoconnect: "always") which applies whatever node would be auto-connected to, + /// or different values for router and/or peer depending on the type of node detected + /// (e.g. autoconnect_strategy : { to_router: "always", to_peer: "greater-zid" }), + /// or different values for router or peer mode + /// (e.g. autoconnect_strategy : { peer: { to_router: "always", to_peer: "greater-zid" } }). + autoconnect_strategy: { router: { to_router: "always", to_peer: "always" } }, + /// Whether or not to listen for scout messages on UDP multicast and reply to them. + listen: true, + }, + /// The gossip scouting configuration. Note that instances in "client" mode do not participate in gossip. + gossip: { + /// Whether gossip scouting is enabled or not + enabled: true, + /// When true, gossip scouting information are propagated multiple hops to all nodes in the local network. + /// When false, gossip scouting information are only propagated to the next hop. + /// Activating multihop gossip implies more scouting traffic and a lower scalability. + /// It mostly makes sense when using "linkstate" routing mode where all nodes in the subsystem don't have + /// direct connectivity with each other. + multihop: false, + /// Which type of Zenoh instances to send gossip messages to. + /// Accepts a single value (e.g. target: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router or peer mode (e.g. target: { router: ["router", "peer"], peer: ["router"] }). + /// Each value is a list of "peer" and/or "router". + /// ROS setting: by default all peers rely on the router to discover each other. Thus configuring the peer to send gossip + /// messages only to the router is sufficient and avoids unecessary traffic between Nodes at launch time. + target: { router: ["router", "peer"], peer: ["router"]}, + /// Which type of Zenoh instances to automatically establish sessions with upon discovery on gossip. + /// Accepts a single value (e.g. autoconnect: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router or peer mode (e.g. autoconnect: { router: [], peer: ["router", "peer"] }). + /// Each value is a list of: "peer" and/or "router". + autoconnect: { router: [], peer: ["router", "peer"] }, + /// Strategy for autoconnection, mainly to avoid nodes connecting to each other redundantly. + /// Possible options are: + /// - "always": always attempt to autoconnect, may result in redundant connection which will then be closed. + /// - "greater-zid": attempt to connect to another node only if its own zid is greater than the other's. + /// If both nodes use this strategy, only one will attempt the connection. + /// This strategy may not be suited if one of the nodes is not reachable by the other one, for example + /// because of a private IP. + /// Accepts a single value (e.g. autoconnect: "always") which applies whatever node would be auto-connected to, + /// or different values for router and/or peer depending on the type of node detected + /// (e.g. autoconnect_strategy : { to_router: "always", to_peer: "greater-zid" }), + /// or different values for router or peer mode + /// (e.g. autoconnect_strategy : { peer: { to_router: "always", to_peer: "greater-zid" } }). + autoconnect_strategy: { router: { to_router: "always", to_peer: "always" } }, + }, + }, + + /// Configuration of data messages timestamps management. + timestamping: { + /// Whether data messages should be timestamped if not already. + /// Accepts a single boolean value or different values for router, peer and client. + /// + /// ROS setting: PublicationCache which is required for transient_local durability + /// only works when time-stamping is enabled. + enabled: { router: true, peer: true, client: true }, + /// Whether data messages with timestamps in the future should be dropped or not. + /// If set to false (default), messages with timestamps in the future are retimestamped. + /// Timestamps are ignored if timestamping is disabled. + drop_future_timestamp: false, + }, + + /// The default timeout to apply to queries in milliseconds. + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together. + /// Note that only action-related service get_result is hard-coded with an infinite timeout. + queries_default_timeout: 60000, + + /// The routing strategy to use and it's configuration. + routing: { + /// The routing strategy to use in routers and it's configuration. + router: { + /// When set to true a router will forward data between two peers + /// directly connected to it if it detects that those peers are not + /// connected to each other. + /// The failover brokering only works if gossip discovery is enabled + /// and peers are configured with gossip target "router". + /// ROS setting: disabled by default because it serves no purpose when each peer connects directly to all others, + /// and it introduces additional management overhead and extra messages during system startup. + peers_failover_brokering: false, + /// Linkstate mode configuration. + linkstate: { + /// Weights of the outgoing transports in linkstate mode. + /// If none of the two endpoint nodes of a transport specifies its weight, a weight of 100 is applied. + /// If only one of the two endpoint nodes of a transport specifies its weight, the specified weight is applied. + /// If both endpoint nodes of a transport specify its weight, the greater weight is applied. + // transport_weights: [ + // { dst_zid: "1", weight: "10" }, + // { dst_zid: "2", weight: "200" }, + // ] + } + }, + /// The routing strategy to use in peers and it's configuration. + peer: { + /// The routing strategy to use in peers. ("peer_to_peer" or "linkstate"). + /// This option needs to be set to the same value in all peers and routers of the subsystem. + mode: "peer_to_peer", + /// Linkstate mode configuration (only taken into account if mode == "linkstate"). + linkstate: { + /// Weights of the outgoing transports in linkstate mode. + /// If none of the two endpoint nodes of a transport specifies its weight, a weight of 100 is applied. + /// If only one of the two endpoint nodes of a transport specifies its weight, the specified weight is applied. + /// If both endpoint nodes of a transport specify its weight, the greater weight is applied. + // transport_weights: [ + // { dst_zid: "1", weight: "10" }, + // { dst_zid: "2", weight: "200" }, + // ] + } + }, + /// The interests-based routing configuration. + /// This configuration applies regardless of the mode (router, peer or client). + interests: { + /// The timeout to wait for incoming interests declarations in milliseconds. + /// The expiration of this timeout implies that the discovery protocol might be incomplete, + /// leading to potential loss of messages, queries or liveliness tokens. + timeout: 10000, + }, + }, + + // /// Overwrite QoS options for Zenoh messages by key expression (ignores Zenoh API QoS config for overwritten values) + // qos: { + // /// Overwrite QoS options for PUT and DELETE messages + // publication: [ + // { + // /// PUT and DELETE messages on key expressions that are included by these key expressions + // /// will have their QoS options overwritten by the given config. + // key_exprs: ["demo/**", "example/key"], + // /// Configurations that will be applied on the publisher. + // /// Options that are supplied here will overwrite the configuration given in Zenoh API + // config: { + // congestion_control: "block", + // priority: "data_high", + // express: true, + // reliability: "best_effort", + // allowed_destination: "remote", + // }, + // }, + // ], + // /// Overwrite QoS options for messages sent and received from/to the network + // /// This allows more fine grained rules (per network card, etc...) but is + // /// less performant than the publication option above. + // network: [ + // { + // /// Optional Id, has to be unique. + // id: "lo0_en0_qos_overwrite", + // // Optional list of ZIDs on which qos will be overwritten when communicating with. + // // zids: ["38a4829bce9166ee"], + // // Optional list of interfaces, if not specified, will be applied to all interfaces. + // interfaces: [ + // "lo0", + // "en0", + // ], + // /// Optional list of link protocols. Transports with at least one of these links will have their qos overwritten. + // /// If absent, the overwrite will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// List of message types to apply to. + // messages: [ + // "put", // put publications + // "delete" // delete publications + // "query", // get queries + // "reply", // replies to queries + // ], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the rules will be applied to both flows. + // flows: ["egress", "ingress"], + // key_exprs: ["test/demo"], + // overwrite: { + // /// Optional new priority value, if not specified priority of the messages will stay unchanged. + // priority: "real_time", + // /// Optional new congestion control value, if not specified congestion control of the messages will stay unchanged. + // congestion_control: "block", + // /// Optional new express value, if not specified express flag of the messages will stay unchanged. + // express: true + // }, + // }, + // ], + // }, + + // /// The declarations aggregation strategy. + // aggregation: { + // /// A list of key-expressions for which all included subscribers will be aggregated into. + // subscribers: [ + // // key_expression + // ], + // /// A list of key-expressions for which all included publishers will be aggregated into. + // publishers: [ + // // key_expression + // ], + // }, + + // /// Namespace prefix. + // /// If specified, all outgoing key expressions will be automatically prefixed with specified string, + // /// and all incoming key expressions will be stripped of specified prefix. + // /// The namespace prefix should satisfy all key expression constraints + // /// and additionally it can not contain wild characters ('*'). + // /// Namespace is applied to the session. + // /// E. g. if session has a namespace of "1" then session.put("my/keyexpr", my_message), + // /// will put a message into 1/my/keyexpr. Same applies to all other operations within this session. + // namespace: "my/namespace", + + // /// The downsampling declaration. + // downsampling: [ + // { + // /// Optional Id, has to be unique + // "id": "wlan0egress", + // /// Optional list of network interfaces messages will be processed on, the rest will be passed as is. + // /// If absent, the rules will be applied to all interfaces. An empty list is invalid. + // interfaces: [ "wlan0" ], + // /// Optional list of link protocols. Transports with at least one of these links will have their messages filtered. + // /// If absent, the rules will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the rules will be applied to both flows. + // flow: ["ingress", "egress"], + // /// List of message type on which downsampling will be applied. Must not be empty. + // messages: [ + // /// Publication (Put and Delete) + // "push", + // /// Get + // "query", + // /// Queryable Reply to a Query + // "reply" + // ], + // /// A list of downsampling rules: key_expression and the maximum frequency in Hertz + // rules: [ + // { key_expr: "demo/example/zenoh-rs-pub", freq: 0.1 }, + // ], + // }, + // ], + + // /// Configure access control (ACL) rules + // access_control: { + // /// [true/false] acl will be activated only if this is set to true + // "enabled": false, + // /// [deny/allow] default permission is deny (even if this is left empty or not specified) + // "default_permission": "deny", + // /// Rule set for permissions allowing or denying access to key-expressions + // "rules": + // [ + // { + // /// Id has to be unique within the rule set + // "id": "rule1", + // "messages": [ + // "put", "delete", "declare_subscriber", + // "query", "reply", "declare_queryable", + // "liveliness_token", "liveliness_query", "declare_liveliness_subscriber", + // ], + // "flows":["egress","ingress"], + // "permission": "allow", + // "key_exprs": [ + // "test/demo" + // ], + // }, + // { + // "id": "rule2", + // "messages": [ + // "put", "delete", "declare_subscriber", + // "query", "reply", "declare_queryable", + // ], + // "flows":["ingress"], + // "permission": "allow", + // "key_exprs": [ + // "**" + // ], + // }, + // ], + // /// List of combinations of subjects. + // /// + // /// If a subject property (i.e. username, certificate common name or interface) is empty + // /// it is interpreted as a wildcard. Moreover, a subject property cannot be an empty list. + // "subjects": + // [ + // { + // /// Id has to be unique within the subjects list + // "id": "subject1", + // /// Subjects can be interfaces + // "interfaces": [ + // "lo0", + // "en0", + // ], + // /// Subjects can be cert_common_names when using TLS or Quic + // "cert_common_names": [ + // "example.zenoh.io" + // ], + // /// Subjects can be usernames when using user/password authentication + // "usernames": [ + // "zenoh-example" + // ], + // /// This instance translates internally to this filter: + // /// (interface="lo0" && cert_common_name="example.zenoh.io" && username="zenoh-example") || + // /// (interface="en0" && cert_common_name="example.zenoh.io" && username="zenoh-example") + // }, + // { + // "id": "subject2", + // "interfaces": [ + // "lo0", + // "en0", + // ], + // "cert_common_names": [ + // "example2.zenoh.io" + // ], + // /// This instance translates internally to this filter: + // /// (interface="lo0" && cert_common_name="example2.zenoh.io") || + // /// (interface="en0" && cert_common_name="example2.zenoh.io") + // }, + // { + // "id": "subject3", + // /// An empty subject combination is a wildcard + // }, + // { + // "id": "subject4", + // /// link protocols can also be used to identify transports to filter messages on. + // /// If absent, the rules will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // }, + // ], + // /// The policies list associates rules to subjects + // "policies": + // [ + // /// Each policy associates one or multiple rules to one or multiple subject combinations + // { + // /// Id is optional. If provided, it has to be unique within the policies list + // "id": "policy1", + // /// Rules and Subjects are identified with their unique IDs declared above + // "rules": ["rule1"], + // "subjects": ["subject1", "subject2"], + // }, + // { + // "rules": ["rule2"], + // "subjects": ["subject3", "subject4"], + // }, + // ] + //}, + + // low_pass_filter: [ + // { + // /// Optional Id, has to be unique + // "id": "filter1", + // /// Optional list of network interfaces messages will be processed on, the rest will not be filtered. + // /// If absent, the filter will be applied to all interfaces. + // interfaces: [ "wlan0" ], + // /// Optional list of link protocols. Transports with at least one of these links will have their messages filtered. + // /// If absent, the rule will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the filter will be applied to both flows. + // flow: ["ingress", "egress"], + // /// List of message type on which the filter will be applied. Must not be empty. + // messages: [ + // "put", + // "delete", + // "query", + // "reply" + // ], + // /// List of key_expressions which matching messages will be filtered + // key_exprs: [ + // "demo/**", + // ], + // /// Inclusive max size of serialized payload + serialized attachment + // size_limit: 8192, + // }, + // ], + + /// Configure internal transport parameters + transport: { + unicast: { + /// Timeout in milliseconds when opening a link + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together + open_timeout: 60000, + /// Timeout in milliseconds when accepting a link + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together + accept_timeout: 60000, + /// Maximum number of links in pending state while performing the handshake for accepting it + /// ROS setting: increase the value to support a large number of Nodes starting all together + accept_pending: 10000, + /// Maximum number of transports that can be simultaneously alive for a single zenoh sessions + /// ROS setting: increase the value to support a large number of Nodes starting all together + max_sessions: 10000, + /// Maximum number of incoming links that are admitted per transport + max_links: 1, + /// Enables the LowLatency transport + /// This option does not make LowLatency transport mandatory, the actual implementation of transport + /// used will depend on Establish procedure and other party's settings + /// + /// NOTE: Currently, the LowLatency transport doesn't preserve QoS prioritization. + /// NOTE: Due to the note above, 'lowlatency' is incompatible with 'qos' option, so in order to + /// enable 'lowlatency' you need to explicitly disable 'qos'. + /// NOTE: LowLatency transport does not support the fragmentation, so the message size should be + /// smaller than the tx batch_size. + lowlatency: false, + /// Enables QoS on unicast communications. + qos: { + enabled: true, + }, + /// Enables compression on unicast communications. + /// Compression capabilities are negotiated during session establishment. + /// If both Zenoh nodes support compression, then compression is activated. + compression: { + enabled: false, + }, + }, + /// WARNING: multicast communication does not perform any negotiation upon group joining. + /// Because of that, it is important that all transport parameters are the same to make + /// sure all your nodes in the system can communicate. One common parameter to configure + /// is "transport/link/tx/batch_size" since its default value depends on the actual platform + /// when operating on multicast. + /// E.g., the batch size on Linux and Windows is 65535 bytes, on Mac OS X is 9216, and anything else is 8192. + multicast: { + /// JOIN message transmission interval in milliseconds. + join_interval: 2500, + /// Maximum number of multicast sessions. + max_sessions: 1000, + /// Enables QoS on multicast communication. + /// Default to false for Zenoh-to-Zenoh-Pico out-of-the-box compatibility. + qos: { + enabled: false, + }, + /// Enables compression on multicast communication. + /// Default to false for Zenoh-to-Zenoh-Pico out-of-the-box compatibility. + compression: { + enabled: false, + }, + }, + link: { + /// An optional whitelist of protocols to be used for accepting and opening sessions. If not + /// configured, all the supported protocols are automatically whitelisted. The supported + /// protocols are: ["tcp" , "udp", "tls", "quic", "ws", "unixsock-stream", "vsock"] For + /// example, to only enable "tls" and "quic": protocols: ["tls", "quic"], + /// + /// Configure the zenoh TX parameters of a link + tx: { + /// The resolution in bits to be used for the message sequence numbers. + /// When establishing a session with another Zenoh instance, the lowest value of the two instances will be used. + /// Accepted values: 8bit, 16bit, 32bit, 64bit. + sequence_number_resolution: "32bit", + /// Link lease duration in milliseconds to announce to other zenoh nodes + /// ROS setting: increase the value to avoid lease expiration at launch time with a large number of Nodes starting all together + lease: 60000, + /// Number of keep-alive messages in a link lease duration. If no data is sent, keep alive + /// messages will be sent at the configured time interval. + /// NOTE: In order to consider eventual packet loss and transmission latency and jitter, + /// set the actual keep_alive interval to one fourth of the lease time: i.e. send + /// 4 keep_alive messages in a lease period. Changing the lease time will have the + /// keep_alive messages sent more or less often. + /// This is in-line with the ITU-T G.8013/Y.1731 specification on continuous connectivity + /// check which considers a link as failed when no messages are received in 3.5 times the + /// target interval. + /// ROS setting: decrease the value since Nodes are communicating over the loopback + /// where keep-alive messages have less chances to be lost. + keep_alive: 2, + /// Batch size in bytes is expressed as a 16bit unsigned integer. + /// Therefore, the maximum batch size is 2^16-1 (i.e. 65535). + /// The default batch size value is the maximum batch size: 65535. + batch_size: 65535, + /// Each zenoh link has a transmission queue that can be configured + queue: { + /// The size of each priority queue indicates the number of batches a given queue can contain. + /// NOTE: the number of batches in each priority must be included between 1 and 16. Different values will result in an error. + /// The amount of memory being allocated for each queue is then SIZE_XXX * BATCH_SIZE. + /// In the case of the transport link MTU being smaller than the ZN_BATCH_SIZE, + /// then amount of memory being allocated for each queue is SIZE_XXX * LINK_MTU. + /// If qos is false, then only the DATA priority will be allocated. + size: { + control: 2, + real_time: 2, + interactive_high: 2, + interactive_low: 2, + data_high: 2, + data: 2, + data_low: 2, + background: 2, + }, + /// Congestion occurs when the queue is empty (no available batch). + congestion_control: { + /// Behavior pushing CongestionControl::Drop messages to the queue. + drop: { + /// The maximum time in microseconds to wait for an available batch before dropping a droppable message if still no batch is available. + wait_before_drop: 1000, + /// The maximum deadline limit for multi-fragment messages. + max_wait_before_drop_fragments: 50000, + }, + /// Behavior pushing CongestionControl::Block messages to the queue. + block: { + /// The maximum time in microseconds to wait for an available batch before closing the transport session when sending a blocking message + /// if still no batch is available. + /// ROS setting: unlike DEFAULT_RMW_ZENOH_SESSION_CONFIG.json5, no change here: + /// as the router is routing messages to outside the robot, possibly over WiFi, + /// keeping a lower value ensure the router is not blocked for too long in case of congestioned WiFi. + wait_before_close: 5000000, + }, + }, + /// Perform batching of messages if they are smaller of the batch_size + batching: { + /// Perform adaptive batching of messages if they are smaller of the batch_size. + /// When the network is detected to not be fast enough to transmit every message individually, many small messages may be + /// batched together and sent all at once on the wire reducing the overall network overhead. This is typically of a high-throughput + /// scenario mainly composed of small messages. In other words, batching is activated by the network back-pressure. + enabled: true, + /// The maximum time limit (in ms) a message should be retained for batching when back-pressure happens. + time_limit: 1, + }, + allocation: { + /// Mode for memory allocation of batches in the priority queues. + /// - "init": batches are allocated at queue initialization time. + /// - "lazy": batches are allocated when needed up to the maximum number of batches configured in the size configuration parameter. + mode: "lazy", + }, + }, + }, + /// Configure the zenoh RX parameters of a link + rx: { + /// Receiving buffer size in bytes for each link + /// The default the rx_buffer_size value is the same as the default batch size: 65535. + /// For very high throughput scenarios, the rx_buffer_size can be increased to accommodate + /// more in-flight data. This is particularly relevant when dealing with large messages. + /// E.g. for 16MiB rx_buffer_size set the value to: 16777216. + buffer_size: 65535, + /// Maximum size of the defragmentation buffer at receiver end. + /// Fragmented messages that are larger than the configured size will be dropped. + /// The default value is 1GiB. This would work in most scenarios. + /// NOTE: reduce the value if you are operating on a memory constrained device. + max_message_size: 1073741824, + }, + /// Configure TLS specific parameters + tls: { + /// Path to the certificate of the certificate authority used to validate either the server + /// or the client's keys and certificates, depending on the node's mode. If not specified + /// on router mode then the default WebPKI certificates are used instead. + root_ca_certificate: null, + /// Path to the TLS listening side private key + listen_private_key: null, + /// Path to the TLS listening side public certificate + listen_certificate: null, + /// Enables mTLS (mutual authentication), client authentication + enable_mtls: false, + /// Path to the TLS connecting side private key + connect_private_key: null, + /// Path to the TLS connecting side certificate + connect_certificate: null, + // Whether or not to verify the matching between hostname/dns and certificate when connecting, + // if set to false zenoh will disregard the common names of the certificates when verifying servers. + // This could be dangerous because your CA can have signed a server cert for foo.com, that's later being used to host a server at baz.com. If you wan't your + // ca to verify that the server at baz.com is actually baz.com, let this be true (default). + verify_name_on_connect: true, + // Whether or not to close links when remote certificates expires. + // If set to true, links that require certificates (tls/quic) will automatically disconnect when the time of expiration of the remote certificate chain is reached + // note that mTLS (client authentication) is required for a listener to disconnect a client on expiration + close_link_on_expiration: false, + /// Optional configuration for TCP system buffers sizes for TLS links + /// + /// Configure TCP read buffer size (bytes) + // so_rcvbuf: 123456, + /// Configure TCP write buffer size (bytes) + // so_sndbuf: 123456, + }, + // // Configure optional TCP link specific parameters + // tcp: { + // /// Optional configuration for TCP system buffers sizes for TCP links + // /// + // /// Configure TCP read buffer size (bytes) + // // so_rcvbuf: 123456, + // /// Configure TCP write buffer size (bytes) + // // so_sndbuf: 123456, + // } + }, + /// Shared memory configuration. + /// NOTE: shared memory can be used only if zenoh is compiled with "shared-memory" feature, otherwise + /// settings in this section have no effect. + shared_memory: { + /// Whether shared memory is enabled or not. + /// If set to `true`, the SHM buffer optimization support will be announced to other parties. (default `true`). + /// This option doesn't make SHM buffer optimization mandatory, the real support depends on other party setting. + /// A probing procedure for shared memory is performed upon session opening. To enable zenoh to operate + /// over shared memory (and to not fallback on network mode), shared memory needs to be enabled also on the + /// subscriber side. By doing so, the probing procedure will succeed and shared memory will operate as expected. + /// + /// ROS setting: disabled by default until fully tested + enabled: false, + /// SHM resources initialization mode (default "lazy"). + /// - "lazy": SHM subsystem internals will be initialized lazily upon the first SHM buffer + /// allocation or reception. This setting provides better startup time and optimizes resource usage, + /// but produces extra latency at the first SHM buffer interaction. + /// - "init": SHM subsystem internals will be initialized upon Session opening. This setting sacrifices + /// startup time, but guarantees no latency impact when first SHM buffer is processed. + mode: "lazy", + }, + auth: { + /// The configuration of authentication. + /// A password implies a username is required. + usrpwd: { + user: null, + password: null, + /// The path to a file containing the user password dictionary + dictionary_file: null, + }, + pubkey: { + public_key_pem: null, + private_key_pem: null, + public_key_file: null, + private_key_file: null, + key_size: null, + known_keys_file: null, + }, + }, + }, + + /// Configure the Admin Space + /// Unstable: this configuration part works as advertised, but may change in a future release + adminspace: { + /// Enables the admin space + enabled: true, + /// read and/or write permissions on the admin space + permissions: { + read: true, + write: false, + }, + }, + +} diff --git a/haru-os/DEFAULT_RMW_ZENOH_SESSION_CONFIG.json5 b/haru-os/DEFAULT_RMW_ZENOH_SESSION_CONFIG.json5 new file mode 100644 index 0000000..9d261b8 --- /dev/null +++ b/haru-os/DEFAULT_RMW_ZENOH_SESSION_CONFIG.json5 @@ -0,0 +1,778 @@ +/// This file attempts to list and document available configuration elements. +/// For a more complete view of the configuration's structure, check out `zenoh/src/config.rs`'s `Config` structure. +/// Note that the values here are correctly typed, but may not be sensible, so copying this file to change only the parts that matter to you is not good practice. +{ + /// The identifier (as unsigned 128bit integer in hexadecimal lowercase - leading zeros are not accepted) + /// that zenoh runtime will use. + /// If not set, a random unsigned 128bit integer will be used. + /// WARNING: this id must be unique in your zenoh network. + // id: "1234567890abcdef", + + /// The node's mode (router, peer or client) + mode: "peer", + + /// Which endpoints to connect to. E.g. tcp/localhost:7447. + /// By configuring the endpoints, it is possible to tell zenoh which router/peer to connect to at startup. + /// + /// For TCP/UDP on Linux, it is possible additionally specify the interface to be connected to: + /// E.g. tcp/192.168.0.1:7447#iface=eth0, for connect only if the IP address is reachable via the interface eth0 + /// + /// It is also possible to specify a priority range and/or a reliability setting to be used on the link. + /// For example `tcp/localhost?prio=6-7;rel=0` assigns priorities "data_low" and "background" to the established link. + /// + /// For TCP and TLS links, it is possible to specify the TCP buffer sizes: + /// E.g. tcp/192.168.0.1:7447#so_sndbuf=65000;so_rcvbuf=65000 + /// For TCP, UDP, Quic and TLS links, it is possible to specify a `bind` address for the local socket: + /// E.g. tcp/192.168.0.1:7447#bind=192.168.0.1:0 + /// Note!: Currently it is unsupported to specify both `bind` and `iface`. + /// + /// For TCP/UDP links, it's possible to specify the DSCP field of the IP header: + /// E.g. tcp/192.168.0.1:7447#dscp=0x08 + connect: { + /// timeout waiting for all endpoints connected (0: no retry, -1: infinite timeout) + /// Accepts a single value (e.g. timeout_ms: 0) + /// or different values for router, peer and client (e.g. timeout_ms: { router: -1, peer: -1, client: 0 }). + timeout_ms: { router: -1, peer: -1, client: 0 }, + + /// The list of endpoints to connect to. + /// Accepts a single list (e.g. endpoints: ["tcp/10.10.10.10:7447", "tcp/11.11.11.11:7447"]) + /// or different lists for router, peer and client (e.g. endpoints: { router: ["tcp/10.10.10.10:7447"], peer: ["tcp/11.11.11.11:7447"] }). + /// + /// See https://docs.rs/zenoh/latest/zenoh/config/struct.EndPoint.html + /// + /// ROS setting: By default connect to the Zenoh router on localhost on port 7447. + endpoints: [ + "tcp/localhost:7447" + ], + + /// Global connect configuration, + /// Accepts a single value or different values for router, peer and client. + /// The configuration can also be specified for the separate endpoint + /// it will override the global one + /// E.g. tcp/192.168.0.1:7447#retry_period_init_ms=20000;retry_period_max_ms=10000" + + /// exit from application, if timeout exceed + exit_on_failure: { router: false, peer: false, client: true }, + /// connect establishing retry configuration + retry: { + /// initial wait timeout until next connect try + period_init_ms: 1000, + /// maximum wait timeout until next connect try + period_max_ms: 4000, + /// increase factor for the next timeout until nexti connect try + period_increase_factor: 2, + }, + }, + + /// Which endpoints to listen on. E.g. tcp/0.0.0.0:7447. + /// By configuring the endpoints, it is possible to tell zenoh which are the endpoints that other routers, + /// peers, or client can use to establish a zenoh session. + /// + /// For TCP/UDP on Linux, it is possible additionally specify the interface to be listened to: + /// E.g. tcp/0.0.0.0:7447#iface=eth0, for listen connection only on eth0 + /// + /// It is also possible to specify a priority range and/or a reliability setting to be used on the link. + /// For example `tcp/localhost?prio=6-7;rel=0` assigns priorities "data_low" and "background" to the established link. + /// + /// For TCP and TLS links, it is possible to specify the TCP buffer sizes: + /// E.g. tcp/192.168.0.1:7447#so_sndbuf=65000;so_rcvbuf=65000 + /// + /// For TCP/UDP links, it's possible to specify the DSCP field of the IP header: + /// E.g. tcp/192.168.0.1:7447#dscp=0x08 + listen: { + /// timeout waiting for all listen endpoints (0: no retry, -1: infinite timeout) + /// Accepts a single value (e.g. timeout_ms: 0) + /// or different values for router, peer and client (e.g. timeout_ms: { router: -1, peer: -1, client: 0 }). + timeout_ms: 0, + + /// The list of endpoints to listen on. + /// Accepts a single list (e.g. endpoints: ["tcp/[::]:7447", "udp/[::]:7447"]) + /// or different lists for router, peer and client (e.g. endpoints: { router: ["tcp/[::]:7447"], peer: ["tcp/[::]:0"] }). + /// + /// See https://docs.rs/zenoh/latest/zenoh/config/struct.EndPoint.html + /// + /// ROS setting: By default accept incoming connections only from localhost (i.e. from colocalized Nodes). + /// All communications with other hosts are routed by the Zenoh router. + endpoints: [ + "tcp/localhost:0" + ], + + /// Global listen configuration, + /// Accepts a single value or different values for router, peer and client. + /// The configuration can also be specified for the separate endpoint + /// it will override the global one + /// E.g. tcp/192.168.0.1:7447#exit_on_failure=false;retry_period_max_ms=1000" + + /// exit from application, if timeout exceed + exit_on_failure: true, + /// listen retry configuration + retry: { + /// initial wait timeout until next try + period_init_ms: 1000, + /// maximum wait timeout until next try + period_max_ms: 4000, + /// increase factor for the next timeout until next try + period_increase_factor: 2, + }, + }, + /// Configure the session open behavior. + open: { + /// Configure the conditions to be met before session open returns. + return_conditions: { + /// Session open waits to connect to scouted peers and routers before returning. + /// When set to false, first publications and queries after session open from peers may be lost. + connect_scouted: true, + /// Session open waits to receive initial declares from connected peers before returning. + /// Setting to false may cause extra traffic at startup from peers. + declares: true, + }, + }, + /// Configure the scouting mechanisms and their behaviours + scouting: { + /// In client mode, the period in milliseconds dedicated to scouting for a router before failing. + timeout: 3000, + /// In peer mode, the maximum period in milliseconds dedicated to scouting remote peers before attempting other operations. + delay: 500, + /// The multicast scouting configuration. + multicast: { + /// Whether multicast scouting is enabled or not + /// + /// ROS setting: disable multicast discovery by default + enabled: false, + /// The socket which should be used for multicast scouting + address: "224.0.0.224:7446", + /// The network interface which should be used for multicast scouting + interface: "auto", // If not set or set to "auto" the interface if picked automatically + /// The time-to-live on multicast scouting packets + ttl: 1, + /// Which type of Zenoh instances to automatically establish sessions with upon discovery on UDP multicast. + /// Accepts a single value (e.g. autoconnect: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router, peer or client mode (e.g. autoconnect: { router: [], peer: ["router", "peer"] }). + /// Each value is a list of: "peer", "router" and/or "client". + autoconnect: { router: [], peer: ["router", "peer"], client: ["router"] }, + /// Strategy for autoconnection, mainly to avoid nodes connecting to each other redundantly. + /// Possible options are: + /// - "always": always attempt to autoconnect, may result in redundant connections. + /// - "greater-zid": attempt to connect to another node only if its own zid is greater than the other's. + /// If both nodes use this strategy, only one will attempt the connection. + /// This strategy may not be suited if one of the nodes is not reachable by the other one, for example + /// because of a private IP. + /// Accepts a single value (e.g. autoconnect: "always") which applies whatever node would be auto-connected to, + /// or different values for router and/or peer depending on the type of node detected + /// (e.g. autoconnect_strategy : { to_router: "always", to_peer: "greater-zid" }), + /// or different values for router or peer mode + /// (e.g. autoconnect_strategy : { peer: { to_router: "always", to_peer: "greater-zid" } }). + /// ROS setting: by default all peers rely on the router to discover each other. Thus configuring the peer to send gossip + /// messages only to the router is sufficient and avoids unecessary traffic between Nodes at launch time. + autoconnect_strategy: { peer: { to_router: "always", to_peer: "greater-zid" } }, + /// Whether or not to listen for scout messages on UDP multicast and reply to them. + listen: true, + }, + /// The gossip scouting configuration. Note that instances in "client" mode do not participate in gossip. + gossip: { + /// Whether gossip scouting is enabled or not + enabled: true, + /// When true, gossip scouting information are propagated multiple hops to all nodes in the local network. + /// When false, gossip scouting information are only propagated to the next hop. + /// Activating multihop gossip implies more scouting traffic and a lower scalability. + /// It mostly makes sense when using "linkstate" routing mode where all nodes in the subsystem don't have + /// direct connectivity with each other. + multihop: false, + /// Which type of Zenoh instances to send gossip messages to. + /// Accepts a single value (e.g. target: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router or peer mode (e.g. target: { router: ["router", "peer"], peer: ["router"] }). + /// Each value is a list of "peer" and/or "router". + /// ROS setting: by default all peers rely on the router to discover each other. Thus configuring the peer to send gossip + /// messages only to the router is sufficient and avoids unecessary traffic between Nodes at launch time. + target: { router: ["router", "peer"], peer: ["router"]}, + /// Which type of Zenoh instances to automatically establish sessions with upon discovery on gossip. + /// Accepts a single value (e.g. autoconnect: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router or peer mode (e.g. autoconnect: { router: [], peer: ["router", "peer"] }). + /// Each value is a list of: "peer" and/or "router". + autoconnect: { router: [], peer: ["router", "peer"] }, + /// Strategy for autoconnection, mainly to avoid nodes connecting to each other redundantly. + /// Possible options are: + /// - "always": always attempt to autoconnect, may result in redundant connection which will then be closed. + /// - "greater-zid": attempt to connect to another node only if its own zid is greater than the other's. + /// If both nodes use this strategy, only one will attempt the connection. + /// This strategy may not be suited if one of the nodes is not reachable by the other one, for example + /// because of a private IP. + /// Accepts a single value (e.g. autoconnect: "always") which applies whatever node would be auto-connected to, + /// or different values for router and/or peer depending on the type of node detected + /// (e.g. autoconnect_strategy : { to_router: "always", to_peer: "greater-zid" }), + /// or different values for router or peer mode + /// (e.g. autoconnect_strategy : { peer: { to_router: "always", to_peer: "greater-zid" } }). + /// ROS setting: as by default all peers will interconnect to each other over the loopback interface, + /// they are all reachable to each other. Hence using "greater-zid" for peers connecting to + /// other peers is sufficient and avoids unecessary double connections between peers at startup. + autoconnect_strategy: { peer: { to_router: "always", to_peer: "greater-zid" } }, + }, + }, + + /// Configuration of data messages timestamps management. + timestamping: { + /// Whether data messages should be timestamped if not already. + /// Accepts a single boolean value or different values for router, peer and client. + /// + /// ROS setting: PublicationCache which is required for transient_local durability + /// only works when time-stamping is enabled. + enabled: { router: true, peer: true, client: true }, + /// Whether data messages with timestamps in the future should be dropped or not. + /// If set to false (default), messages with timestamps in the future are retimestamped. + /// Timestamps are ignored if timestamping is disabled. + drop_future_timestamp: false, + }, + + /// The default timeout to apply to queries in milliseconds. + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together. + /// Note that only action-related service get_result is hard-coded with an infinite timeout. + queries_default_timeout: 60000, + + /// The routing strategy to use and it's configuration. + routing: { + /// The routing strategy to use in routers and it's configuration. + router: { + /// When set to true a router will forward data between two peers + /// directly connected to it if it detects that those peers are not + /// connected to each other. + /// The failover brokering only works if gossip discovery is enabled + /// and peers are configured with gossip target "router". + peers_failover_brokering: true, + /// Linkstate mode configuration. + linkstate: { + /// Weights of the outgoing transports in linkstate mode. + /// If none of the two endpoint nodes of a transport specifies its weight, a weight of 100 is applied. + /// If only one of the two endpoint nodes of a transport specifies its weight, the specified weight is applied. + /// If both endpoint nodes of a transport specify its weight, the greater weight is applied. + // transport_weights: [ + // { dst_zid: "1", weight: "10" }, + // { dst_zid: "2", weight: "200" }, + // ] + } + }, + /// The routing strategy to use in peers and it's configuration. + peer: { + /// The routing strategy to use in peers. ("peer_to_peer" or "linkstate"). + /// This option needs to be set to the same value in all peers and routers of the subsystem. + mode: "peer_to_peer", + /// Linkstate mode configuration (only taken into account if mode == "linkstate"). + linkstate: { + /// Weights of the outgoing transports in linkstate mode. + /// If none of the two endpoint nodes of a transport specifies its weight, a weight of 100 is applied. + /// If only one of the two endpoint nodes of a transport specifies its weight, the specified weight is applied. + /// If both endpoint nodes of a transport specify its weight, the greater weight is applied. + // transport_weights: [ + // { dst_zid: "1", weight: "10" }, + // { dst_zid: "2", weight: "200" }, + // ] + } + }, + /// The interests-based routing configuration. + /// This configuration applies regardless of the mode (router, peer or client). + interests: { + /// The timeout to wait for incoming interests declarations in milliseconds. + /// The expiration of this timeout implies that the discovery protocol might be incomplete, + /// leading to potential loss of messages, queries or liveliness tokens. + timeout: 10000, + }, + }, + + // /// Overwrite QoS options for Zenoh messages by key expression (ignores Zenoh API QoS config for overwritten values) + // qos: { + // /// Overwrite QoS options for PUT and DELETE messages + // publication: [ + // { + // /// PUT and DELETE messages on key expressions that are included by these key expressions + // /// will have their QoS options overwritten by the given config. + // key_exprs: ["demo/**", "example/key"], + // /// Configurations that will be applied on the publisher. + // /// Options that are supplied here will overwrite the configuration given in Zenoh API + // config: { + // congestion_control: "block", + // priority: "data_high", + // express: true, + // reliability: "best_effort", + // allowed_destination: "remote", + // }, + // }, + // ], + // /// Overwrite QoS options for messages sent and received from/to the network + // /// This allows more fine grained rules (per network card, etc...) but is + // /// less performant than the publication option above. + // network: [ + // { + // /// Optional Id, has to be unique. + // id: "lo0_en0_qos_overwrite", + // // Optional list of ZIDs on which qos will be overwritten when communicating with. + // // zids: ["38a4829bce9166ee"], + // // Optional list of interfaces, if not specified, will be applied to all interfaces. + // interfaces: [ + // "lo0", + // "en0", + // ], + // /// Optional list of link protocols. Transports with at least one of these links will have their qos overwritten. + // /// If absent, the overwrite will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// List of message types to apply to. + // messages: [ + // "put", // put publications + // "delete" // delete publications + // "query", // get queries + // "reply", // replies to queries + // ], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the rules will be applied to both flows. + // flows: ["egress", "ingress"], + // key_exprs: ["test/demo"], + // overwrite: { + // /// Optional new priority value, if not specified priority of the messages will stay unchanged. + // priority: "real_time", + // /// Optional new congestion control value, if not specified congestion control of the messages will stay unchanged. + // congestion_control: "block", + // /// Optional new express value, if not specified express flag of the messages will stay unchanged. + // express: true + // }, + // }, + // ], + // }, + + // /// The declarations aggregation strategy. + // aggregation: { + // /// A list of key-expressions for which all included subscribers will be aggregated into. + // subscribers: [ + // // key_expression + // ], + // /// A list of key-expressions for which all included publishers will be aggregated into. + // publishers: [ + // // key_expression + // ], + // }, + + // /// Namespace prefix. + // /// If specified, all outgoing key expressions will be automatically prefixed with specified string, + // /// and all incoming key expressions will be stripped of specified prefix. + // /// The namespace prefix should satisfy all key expression constraints + // /// and additionally it can not contain wild characters ('*'). + // /// Namespace is applied to the session. + // /// E. g. if session has a namespace of "1" then session.put("my/keyexpr", my_message), + // /// will put a message into 1/my/keyexpr. Same applies to all other operations within this session. + // namespace: "my/namespace", + + // /// The downsampling declaration. + // downsampling: [ + // { + // /// Optional Id, has to be unique + // "id": "wlan0egress", + // /// Optional list of network interfaces messages will be processed on, the rest will be passed as is. + // /// If absent, the rules will be applied to all interfaces. An empty list is invalid. + // interfaces: [ "wlan0" ], + // /// Optional list of link protocols. Transports with at least one of these links will have their messages filtered. + // /// If absent, the rules will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the rules will be applied to both flows. + // flow: ["ingress", "egress"], + // /// List of message type on which downsampling will be applied. Must not be empty. + // messages: [ + // /// Publication (Put and Delete) + // "push", + // /// Get + // "query", + // /// Queryable Reply to a Query + // "reply" + // ], + // /// A list of downsampling rules: key_expression and the maximum frequency in Hertz + // rules: [ + // { key_expr: "demo/example/zenoh-rs-pub", freq: 0.1 }, + // ], + // }, + // ], + + // /// Configure access control (ACL) rules + // access_control: { + // /// [true/false] acl will be activated only if this is set to true + // "enabled": false, + // /// [deny/allow] default permission is deny (even if this is left empty or not specified) + // "default_permission": "deny", + // /// Rule set for permissions allowing or denying access to key-expressions + // "rules": + // [ + // { + // /// Id has to be unique within the rule set + // "id": "rule1", + // "messages": [ + // "put", "delete", "declare_subscriber", + // "query", "reply", "declare_queryable", + // "liveliness_token", "liveliness_query", "declare_liveliness_subscriber", + // ], + // "flows":["egress","ingress"], + // "permission": "allow", + // "key_exprs": [ + // "test/demo" + // ], + // }, + // { + // "id": "rule2", + // "messages": [ + // "put", "delete", "declare_subscriber", + // "query", "reply", "declare_queryable", + // ], + // "flows":["ingress"], + // "permission": "allow", + // "key_exprs": [ + // "**" + // ], + // }, + // ], + // /// List of combinations of subjects. + // /// + // /// If a subject property (i.e. username, certificate common name or interface) is empty + // /// it is interpreted as a wildcard. Moreover, a subject property cannot be an empty list. + // "subjects": + // [ + // { + // /// Id has to be unique within the subjects list + // "id": "subject1", + // /// Subjects can be interfaces + // "interfaces": [ + // "lo0", + // "en0", + // ], + // /// Subjects can be cert_common_names when using TLS or Quic + // "cert_common_names": [ + // "example.zenoh.io" + // ], + // /// Subjects can be usernames when using user/password authentication + // "usernames": [ + // "zenoh-example" + // ], + // /// This instance translates internally to this filter: + // /// (interface="lo0" && cert_common_name="example.zenoh.io" && username="zenoh-example") || + // /// (interface="en0" && cert_common_name="example.zenoh.io" && username="zenoh-example") + // }, + // { + // "id": "subject2", + // "interfaces": [ + // "lo0", + // "en0", + // ], + // "cert_common_names": [ + // "example2.zenoh.io" + // ], + // /// This instance translates internally to this filter: + // /// (interface="lo0" && cert_common_name="example2.zenoh.io") || + // /// (interface="en0" && cert_common_name="example2.zenoh.io") + // }, + // { + // "id": "subject3", + // /// An empty subject combination is a wildcard + // }, + // { + // "id": "subject4", + // /// link protocols can also be used to identify transports to filter messages on. + // /// If absent, the rules will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // }, + // ], + // /// The policies list associates rules to subjects + // "policies": + // [ + // /// Each policy associates one or multiple rules to one or multiple subject combinations + // { + // /// Id is optional. If provided, it has to be unique within the policies list + // "id": "policy1", + // /// Rules and Subjects are identified with their unique IDs declared above + // "rules": ["rule1"], + // "subjects": ["subject1", "subject2"], + // }, + // { + // "rules": ["rule2"], + // "subjects": ["subject3", "subject4"], + // }, + // ] + //}, + + // low_pass_filter: [ + // { + // /// Optional Id, has to be unique + // "id": "filter1", + // /// Optional list of network interfaces messages will be processed on, the rest will not be filtered. + // /// If absent, the filter will be applied to all interfaces. + // interfaces: [ "wlan0" ], + // /// Optional list of link protocols. Transports with at least one of these links will have their messages filtered. + // /// If absent, the rule will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the filter will be applied to both flows. + // flow: ["ingress", "egress"], + // /// List of message type on which the filter will be applied. Must not be empty. + // messages: [ + // "put", + // "delete", + // "query", + // "reply" + // ], + // /// List of key_expressions which matching messages will be filtered + // key_exprs: [ + // "demo/**", + // ], + // /// Inclusive max size of serialized payload + serialized attachment + // size_limit: 8192, + // }, + // ], + + /// Configure internal transport parameters + transport: { + unicast: { + /// Timeout in milliseconds when opening a link + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together + open_timeout: 60000, + /// Timeout in milliseconds when accepting a link + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together + accept_timeout: 60000, + /// Maximum number of links in pending state while performing the handshake for accepting it + /// ROS setting: increase the value to support a large number of Nodes starting all together + accept_pending: 10000, + /// Maximum number of transports that can be simultaneously alive for a single zenoh sessions + /// ROS setting: increase the value to support a large number of Nodes starting all together + max_sessions: 10000, + /// Maximum number of incoming links that are admitted per transport + max_links: 1, + /// Enables the LowLatency transport + /// This option does not make LowLatency transport mandatory, the actual implementation of transport + /// used will depend on Establish procedure and other party's settings + /// + /// NOTE: Currently, the LowLatency transport doesn't preserve QoS prioritization. + /// NOTE: Due to the note above, 'lowlatency' is incompatible with 'qos' option, so in order to + /// enable 'lowlatency' you need to explicitly disable 'qos'. + /// NOTE: LowLatency transport does not support the fragmentation, so the message size should be + /// smaller than the tx batch_size. + lowlatency: false, + /// Enables QoS on unicast communications. + qos: { + enabled: true, + }, + /// Enables compression on unicast communications. + /// Compression capabilities are negotiated during session establishment. + /// If both Zenoh nodes support compression, then compression is activated. + compression: { + enabled: false, + }, + }, + /// WARNING: multicast communication does not perform any negotiation upon group joining. + /// Because of that, it is important that all transport parameters are the same to make + /// sure all your nodes in the system can communicate. One common parameter to configure + /// is "transport/link/tx/batch_size" since its default value depends on the actual platform + /// when operating on multicast. + /// E.g., the batch size on Linux and Windows is 65535 bytes, on Mac OS X is 9216, and anything else is 8192. + multicast: { + /// JOIN message transmission interval in milliseconds. + join_interval: 2500, + /// Maximum number of multicast sessions. + max_sessions: 1000, + /// Enables QoS on multicast communication. + /// Default to false for Zenoh-to-Zenoh-Pico out-of-the-box compatibility. + qos: { + enabled: false, + }, + /// Enables compression on multicast communication. + /// Default to false for Zenoh-to-Zenoh-Pico out-of-the-box compatibility. + compression: { + enabled: false, + }, + }, + link: { + /// An optional whitelist of protocols to be used for accepting and opening sessions. If not + /// configured, all the supported protocols are automatically whitelisted. The supported + /// protocols are: ["tcp" , "udp", "tls", "quic", "ws", "unixsock-stream", "vsock"] For + /// example, to only enable "tls" and "quic": protocols: ["tls", "quic"], + /// + /// Configure the zenoh TX parameters of a link + tx: { + /// The resolution in bits to be used for the message sequence numbers. + /// When establishing a session with another Zenoh instance, the lowest value of the two instances will be used. + /// Accepted values: 8bit, 16bit, 32bit, 64bit. + sequence_number_resolution: "32bit", + /// Link lease duration in milliseconds to announce to other zenoh nodes + /// ROS setting: increase the value to avoid lease expiration at launch time with a large number of Nodes starting all together + lease: 60000, + /// Number of keep-alive messages in a link lease duration. If no data is sent, keep alive + /// messages will be sent at the configured time interval. + /// NOTE: In order to consider eventual packet loss and transmission latency and jitter, + /// set the actual keep_alive interval to one fourth of the lease time: i.e. send + /// 4 keep_alive messages in a lease period. Changing the lease time will have the + /// keep_alive messages sent more or less often. + /// This is in-line with the ITU-T G.8013/Y.1731 specification on continuous connectivity + /// check which considers a link as failed when no messages are received in 3.5 times the + /// target interval. + /// ROS setting: decrease the value since Nodes are communicating over the loopback + /// where keep-alive messages have less chances to be lost. + keep_alive: 2, + /// Batch size in bytes is expressed as a 16bit unsigned integer. + /// Therefore, the maximum batch size is 2^16-1 (i.e. 65535). + /// The default batch size value is the maximum batch size: 65535. + batch_size: 65535, + /// Each zenoh link has a transmission queue that can be configured + queue: { + /// The size of each priority queue indicates the number of batches a given queue can contain. + /// NOTE: the number of batches in each priority must be included between 1 and 16. Different values will result in an error. + /// The amount of memory being allocated for each queue is then SIZE_XXX * BATCH_SIZE. + /// In the case of the transport link MTU being smaller than the ZN_BATCH_SIZE, + /// then amount of memory being allocated for each queue is SIZE_XXX * LINK_MTU. + /// If qos is false, then only the DATA priority will be allocated. + size: { + control: 2, + real_time: 2, + interactive_high: 2, + interactive_low: 2, + data_high: 2, + data: 2, + data_low: 2, + background: 2, + }, + /// Congestion occurs when the queue is empty (no available batch). + congestion_control: { + /// Behavior pushing CongestionControl::Drop messages to the queue. + drop: { + /// The maximum time in microseconds to wait for an available batch before dropping a droppable message if still no batch is available. + wait_before_drop: 1000, + /// The maximum deadline limit for multi-fragment messages. + max_wait_before_drop_fragments: 50000, + }, + /// Behavior pushing CongestionControl::Block messages to the queue. + block: { + /// The maximum time in microseconds to wait for an available batch before closing the transport session when sending a blocking message + /// if still no batch is available. + /// ROS setting: increase the value to avoid unecessary link closure at launch time where congestion is likely + /// to occur even over the loopback since all the Nodes are starting at the same time. + wait_before_close: 60000000, + }, + }, + /// Perform batching of messages if they are smaller of the batch_size + batching: { + /// Perform adaptive batching of messages if they are smaller of the batch_size. + /// When the network is detected to not be fast enough to transmit every message individually, many small messages may be + /// batched together and sent all at once on the wire reducing the overall network overhead. This is typically of a high-throughput + /// scenario mainly composed of small messages. In other words, batching is activated by the network back-pressure. + enabled: true, + /// The maximum time limit (in ms) a message should be retained for batching when back-pressure happens. + time_limit: 1, + }, + allocation: { + /// Mode for memory allocation of batches in the priority queues. + /// - "init": batches are allocated at queue initialization time. + /// - "lazy": batches are allocated when needed up to the maximum number of batches configured in the size configuration parameter. + mode: "lazy", + }, + }, + }, + /// Configure the zenoh RX parameters of a link + rx: { + /// Receiving buffer size in bytes for each link + /// The default the rx_buffer_size value is the same as the default batch size: 65535. + /// For very high throughput scenarios, the rx_buffer_size can be increased to accommodate + /// more in-flight data. This is particularly relevant when dealing with large messages. + /// E.g. for 16MiB rx_buffer_size set the value to: 16777216. + buffer_size: 65535, + /// Maximum size of the defragmentation buffer at receiver end. + /// Fragmented messages that are larger than the configured size will be dropped. + /// The default value is 1GiB. This would work in most scenarios. + /// NOTE: reduce the value if you are operating on a memory constrained device. + max_message_size: 1073741824, + }, + /// Configure TLS specific parameters + tls: { + /// Path to the certificate of the certificate authority used to validate either the server + /// or the client's keys and certificates, depending on the node's mode. If not specified + /// on router mode then the default WebPKI certificates are used instead. + root_ca_certificate: null, + /// Path to the TLS listening side private key + listen_private_key: null, + /// Path to the TLS listening side public certificate + listen_certificate: null, + /// Enables mTLS (mutual authentication), client authentication + enable_mtls: false, + /// Path to the TLS connecting side private key + connect_private_key: null, + /// Path to the TLS connecting side certificate + connect_certificate: null, + // Whether or not to verify the matching between hostname/dns and certificate when connecting, + // if set to false zenoh will disregard the common names of the certificates when verifying servers. + // This could be dangerous because your CA can have signed a server cert for foo.com, that's later being used to host a server at baz.com. If you wan't your + // ca to verify that the server at baz.com is actually baz.com, let this be true (default). + verify_name_on_connect: true, + // Whether or not to close links when remote certificates expires. + // If set to true, links that require certificates (tls/quic) will automatically disconnect when the time of expiration of the remote certificate chain is reached + // note that mTLS (client authentication) is required for a listener to disconnect a client on expiration + close_link_on_expiration: false, + /// Optional configuration for TCP system buffers sizes for TLS links + /// + /// Configure TCP read buffer size (bytes) + // so_rcvbuf: 123456, + /// Configure TCP write buffer size (bytes) + // so_sndbuf: 123456, + }, + // // Configure optional TCP link specific parameters + // tcp: { + // /// Optional configuration for TCP system buffers sizes for TCP links + // /// + // /// Configure TCP read buffer size (bytes) + // // so_rcvbuf: 123456, + // /// Configure TCP write buffer size (bytes) + // // so_sndbuf: 123456, + // } + }, + /// Shared memory configuration. + /// NOTE: shared memory can be used only if zenoh is compiled with "shared-memory" feature, otherwise + /// settings in this section have no effect. + shared_memory: { + /// Whether shared memory is enabled or not. + /// If set to `true`, the SHM buffer optimization support will be announced to other parties. (default `true`). + /// This option doesn't make SHM buffer optimization mandatory, the real support depends on other party setting. + /// A probing procedure for shared memory is performed upon session opening. To enable zenoh to operate + /// over shared memory (and to not fallback on network mode), shared memory needs to be enabled also on the + /// subscriber side. By doing so, the probing procedure will succeed and shared memory will operate as expected. + /// + /// ROS setting: disabled by default until fully tested + enabled: false, + /// SHM resources initialization mode (default "lazy"). + /// - "lazy": SHM subsystem internals will be initialized lazily upon the first SHM buffer + /// allocation or reception. This setting provides better startup time and optimizes resource usage, + /// but produces extra latency at the first SHM buffer interaction. + /// - "init": SHM subsystem internals will be initialized upon Session opening. This setting sacrifices + /// startup time, but guarantees no latency impact when first SHM buffer is processed. + mode: "lazy", + }, + auth: { + /// The configuration of authentication. + /// A password implies a username is required. + usrpwd: { + user: null, + password: null, + /// The path to a file containing the user password dictionary + dictionary_file: null, + }, + pubkey: { + public_key_pem: null, + private_key_pem: null, + public_key_file: null, + private_key_file: null, + key_size: null, + known_keys_file: null, + }, + }, + }, + + /// Configure the Admin Space + /// Unstable: this configuration part works as advertised, but may change in a future release + adminspace: { + /// Enables the admin space + enabled: true, + /// read and/or write permissions on the admin space + permissions: { + read: true, + write: false, + }, + }, + +} diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index 6195aa9..d111f9f 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -18,10 +18,11 @@ RUN apt update && apt install -y \ python3-vcstool python3-colcon-common-extensions \ ros-${ROS_DISTRO}-rqt ros-${ROS_DISTRO}-rqt-common-plugins \ ros-${ROS_DISTRO}-rviz2 ros-${ROS_DISTRO}-robot-state-publisher \ - ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ + ros-${ROS_DISTRO}-rmw-cyclonedds-cpp ros-${ROS_DISTRO}-rmw-zenoh-cpp \ && rm -rf /var/lib/apt/lists/* -COPY cyclonedds.xml /config/cyclonedds.xml +COPY RMW_ZENOH_ROUTER_CONFIG_MULTICAST_MULTIHOST.json5 /config/RMW_ZENOH_ROUTER_CONFIG.json5 +COPY RMW_ZENOH_SESSION_CONFIG_MULTICAST.json5 /config/RMW_ZENOH_SESSION_CONFIG.json5 COPY custom_rosdep.yaml /etc/ros/rosdep/custom_rosdep.yaml RUN echo "yaml file:///etc/ros/rosdep/custom_rosdep.yaml" >> /etc/ros/rosdep/sources.list.d/20-custom.list && \ diff --git a/haru-os/RMW_ZENOH_ROUTER_CONFIG_MULTICAST_MULTIHOST.json5 b/haru-os/RMW_ZENOH_ROUTER_CONFIG_MULTICAST_MULTIHOST.json5 new file mode 100644 index 0000000..e05676f --- /dev/null +++ b/haru-os/RMW_ZENOH_ROUTER_CONFIG_MULTICAST_MULTIHOST.json5 @@ -0,0 +1,771 @@ +/// This file attempts to list and document available configuration elements. +/// For a more complete view of the configuration's structure, check out `zenoh/src/config.rs`'s `Config` structure. +/// Note that the values here are correctly typed, but may not be sensible, so copying this file to change only the parts that matter to you is not good practice. +{ + /// The identifier (as unsigned 128bit integer in hexadecimal lowercase - leading zeros are not accepted) + /// that zenoh runtime will use. + /// If not set, a random unsigned 128bit integer will be used. + /// WARNING: this id must be unique in your zenoh network. + // id: "1234567890abcdef", + + /// The node's mode (router, peer or client) + mode: "router", + + /// Which endpoints to connect to. E.g. tcp/localhost:7447. + /// By configuring the endpoints, it is possible to tell zenoh which router/peer to connect to at startup. + /// + /// For TCP/UDP on Linux, it is possible additionally specify the interface to be connected to: + /// E.g. tcp/192.168.0.1:7447#iface=eth0, for connect only if the IP address is reachable via the interface eth0 + /// + /// It is also possible to specify a priority range and/or a reliability setting to be used on the link. + /// For example `tcp/localhost?prio=6-7;rel=0` assigns priorities "data_low" and "background" to the established link. + /// + /// For TCP and TLS links, it is possible to specify the TCP buffer sizes: + /// E.g. tcp/192.168.0.1:7447#so_sndbuf=65000;so_rcvbuf=65000 + /// For TCP, UDP, Quic and TLS links, it is possible to specify a `bind` address for the local socket: + /// E.g. tcp/192.168.0.1:7447#bind=192.168.0.1:0 + /// Note!: Currently it is unsupported to specify both `bind` and `iface`. + /// + /// For TCP/UDP links, it's possible to specify the DSCP field of the IP header: + /// E.g. tcp/192.168.0.1:7447#dscp=0x08 + connect: { + /// timeout waiting for all endpoints connected (0: no retry, -1: infinite timeout) + /// Accepts a single value (e.g. timeout_ms: 0) + /// or different values for router, peer and client (e.g. timeout_ms: { router: -1, peer: -1, client: 0 }). + timeout_ms: { router: -1, peer: -1, client: 0 }, + + /// The list of endpoints to connect to. + /// Accepts a single list (e.g. endpoints: ["tcp/10.10.10.10:7447", "tcp/11.11.11.11:7447"]) + /// or different lists for router, peer and client (e.g. endpoints: { router: ["tcp/10.10.10.10:7447"], peer: ["tcp/11.11.11.11:7447"] }). + /// + /// See https://docs.rs/zenoh/latest/zenoh/config/struct.EndPoint.html + endpoints: [ + // "/
" + ], + + /// Global connect configuration, + /// Accepts a single value or different values for router, peer and client. + /// The configuration can also be specified for the separate endpoint + /// it will override the global one + /// E.g. tcp/192.168.0.1:7447#retry_period_init_ms=20000;retry_period_max_ms=10000" + + /// exit from application, if timeout exceed + exit_on_failure: { router: false, peer: false, client: true }, + /// connect establishing retry configuration + retry: { + /// initial wait timeout until next connect try + period_init_ms: 1000, + /// maximum wait timeout until next connect try + period_max_ms: 4000, + /// increase factor for the next timeout until nexti connect try + period_increase_factor: 2, + }, + }, + + /// Which endpoints to listen on. E.g. tcp/0.0.0.0:7447. + /// By configuring the endpoints, it is possible to tell zenoh which are the endpoints that other routers, + /// peers, or client can use to establish a zenoh session. + /// + /// For TCP/UDP on Linux, it is possible additionally specify the interface to be listened to: + /// E.g. tcp/0.0.0.0:7447#iface=eth0, for listen connection only on eth0 + /// + /// It is also possible to specify a priority range and/or a reliability setting to be used on the link. + /// For example `tcp/localhost?prio=6-7;rel=0` assigns priorities "data_low" and "background" to the established link. + /// + /// For TCP and TLS links, it is possible to specify the TCP buffer sizes: + /// E.g. tcp/192.168.0.1:7447#so_sndbuf=65000;so_rcvbuf=65000 + /// + /// For TCP/UDP links, it's possible to specify the DSCP field of the IP header: + /// E.g. tcp/192.168.0.1:7447#dscp=0x08 + listen: { + /// timeout waiting for all listen endpoints (0: no retry, -1: infinite timeout) + /// Accepts a single value (e.g. timeout_ms: 0) + /// or different values for router, peer and client (e.g. timeout_ms: { router: -1, peer: -1, client: 0 }). + timeout_ms: 0, + + /// The list of endpoints to listen on. + /// Accepts a single list (e.g. endpoints: ["tcp/[::]:7447", "udp/[::]:7447"]) + /// or different lists for router, peer and client (e.g. endpoints: { router: ["tcp/[::]:7447"], peer: ["tcp/[::]:0"] }). + /// + /// See https://docs.rs/zenoh/latest/zenoh/config/struct.EndPoint.html + endpoints: [ + "tcp/0.0.0.0:7447" + ], + + /// Global listen configuration, + /// Accepts a single value or different values for router, peer and client. + /// The configuration can also be specified for the separate endpoint + /// it will override the global one + /// E.g. tcp/192.168.0.1:7447#exit_on_failure=false;retry_period_max_ms=1000" + + /// exit from application, if timeout exceed + exit_on_failure: true, + /// listen retry configuration + retry: { + /// initial wait timeout until next try + period_init_ms: 1000, + /// maximum wait timeout until next try + period_max_ms: 4000, + /// increase factor for the next timeout until next try + period_increase_factor: 2, + }, + }, + /// Configure the session open behavior. + open: { + /// Configure the conditions to be met before session open returns. + return_conditions: { + /// Session open waits to connect to scouted peers and routers before returning. + /// When set to false, first publications and queries after session open from peers may be lost. + connect_scouted: true, + /// Session open waits to receive initial declares from connected peers before returning. + /// Setting to false may cause extra traffic at startup from peers. + declares: true, + }, + }, + /// Configure the scouting mechanisms and their behaviours + scouting: { + /// In client mode, the period in milliseconds dedicated to scouting for a router before failing. + timeout: 3000, + /// In peer mode, the maximum period in milliseconds dedicated to scouting remote peers before attempting other operations. + delay: 500, + /// The multicast scouting configuration. + multicast: { + /// Whether multicast scouting is enabled or not + /// + /// ROS setting: disable multicast discovery by default + enabled: true, + /// The socket which should be used for multicast scouting + address: "224.0.0.224:7446", + /// The network interface which should be used for multicast scouting + interface: "auto", // If not set or set to "auto" the interface if picked automatically + /// The time-to-live on multicast scouting packets + ttl: 1, + /// Which type of Zenoh instances to automatically establish sessions with upon discovery on UDP multicast. + /// Accepts a single value (e.g. autoconnect: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router, peer or client mode (e.g. autoconnect: { router: [], peer: ["router", "peer"] }). + /// Each value is a list of: "peer", "router" and/or "client". + autoconnect: { router: [], peer: ["router", "peer"], client: ["router"] }, + /// Strategy for autoconnection, mainly to avoid nodes connecting to each other redundantly. + /// Possible options are: + /// - "always": always attempt to autoconnect, may result in redundant connections. + /// - "greater-zid": attempt to connect to another node only if its own zid is greater than the other's. + /// If both nodes use this strategy, only one will attempt the connection. + /// This strategy may not be suited if one of the nodes is not reachable by the other one, for example + /// because of a private IP. + /// Accepts a single value (e.g. autoconnect: "always") which applies whatever node would be auto-connected to, + /// or different values for router and/or peer depending on the type of node detected + /// (e.g. autoconnect_strategy : { to_router: "always", to_peer: "greater-zid" }), + /// or different values for router or peer mode + /// (e.g. autoconnect_strategy : { peer: { to_router: "always", to_peer: "greater-zid" } }). + autoconnect_strategy: { router: { to_router: "always", to_peer: "always" } }, + /// Whether or not to listen for scout messages on UDP multicast and reply to them. + listen: true, + }, + /// The gossip scouting configuration. Note that instances in "client" mode do not participate in gossip. + gossip: { + /// Whether gossip scouting is enabled or not + enabled: true, + /// When true, gossip scouting information are propagated multiple hops to all nodes in the local network. + /// When false, gossip scouting information are only propagated to the next hop. + /// Activating multihop gossip implies more scouting traffic and a lower scalability. + /// It mostly makes sense when using "linkstate" routing mode where all nodes in the subsystem don't have + /// direct connectivity with each other. + multihop: false, + /// Which type of Zenoh instances to send gossip messages to. + /// Accepts a single value (e.g. target: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router or peer mode (e.g. target: { router: ["router", "peer"], peer: ["router"] }). + /// Each value is a list of "peer" and/or "router". + /// ROS setting: by default all peers rely on the router to discover each other. Thus configuring the peer to send gossip + /// messages only to the router is sufficient and avoids unecessary traffic between Nodes at launch time. + target: { router: ["router", "peer"], peer: ["router"]}, + /// Which type of Zenoh instances to automatically establish sessions with upon discovery on gossip. + /// Accepts a single value (e.g. autoconnect: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router or peer mode (e.g. autoconnect: { router: [], peer: ["router", "peer"] }). + /// Each value is a list of: "peer" and/or "router". + autoconnect: { router: [], peer: ["router", "peer"] }, + /// Strategy for autoconnection, mainly to avoid nodes connecting to each other redundantly. + /// Possible options are: + /// - "always": always attempt to autoconnect, may result in redundant connection which will then be closed. + /// - "greater-zid": attempt to connect to another node only if its own zid is greater than the other's. + /// If both nodes use this strategy, only one will attempt the connection. + /// This strategy may not be suited if one of the nodes is not reachable by the other one, for example + /// because of a private IP. + /// Accepts a single value (e.g. autoconnect: "always") which applies whatever node would be auto-connected to, + /// or different values for router and/or peer depending on the type of node detected + /// (e.g. autoconnect_strategy : { to_router: "always", to_peer: "greater-zid" }), + /// or different values for router or peer mode + /// (e.g. autoconnect_strategy : { peer: { to_router: "always", to_peer: "greater-zid" } }). + autoconnect_strategy: { router: { to_router: "always", to_peer: "always" } }, + }, + }, + + /// Configuration of data messages timestamps management. + timestamping: { + /// Whether data messages should be timestamped if not already. + /// Accepts a single boolean value or different values for router, peer and client. + /// + /// ROS setting: PublicationCache which is required for transient_local durability + /// only works when time-stamping is enabled. + enabled: { router: true, peer: true, client: true }, + /// Whether data messages with timestamps in the future should be dropped or not. + /// If set to false (default), messages with timestamps in the future are retimestamped. + /// Timestamps are ignored if timestamping is disabled. + drop_future_timestamp: false, + }, + + /// The default timeout to apply to queries in milliseconds. + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together. + /// Note that only action-related service get_result is hard-coded with an infinite timeout. + queries_default_timeout: 60000, + + /// The routing strategy to use and it's configuration. + routing: { + /// The routing strategy to use in routers and it's configuration. + router: { + /// When set to true a router will forward data between two peers + /// directly connected to it if it detects that those peers are not + /// connected to each other. + /// The failover brokering only works if gossip discovery is enabled + /// and peers are configured with gossip target "router". + /// ROS setting: disabled by default because it serves no purpose when each peer connects directly to all others, + /// and it introduces additional management overhead and extra messages during system startup. + peers_failover_brokering: false, + /// Linkstate mode configuration. + linkstate: { + /// Weights of the outgoing transports in linkstate mode. + /// If none of the two endpoint nodes of a transport specifies its weight, a weight of 100 is applied. + /// If only one of the two endpoint nodes of a transport specifies its weight, the specified weight is applied. + /// If both endpoint nodes of a transport specify its weight, the greater weight is applied. + // transport_weights: [ + // { dst_zid: "1", weight: "10" }, + // { dst_zid: "2", weight: "200" }, + // ] + } + }, + /// The routing strategy to use in peers and it's configuration. + peer: { + /// The routing strategy to use in peers. ("peer_to_peer" or "linkstate"). + /// This option needs to be set to the same value in all peers and routers of the subsystem. + mode: "peer_to_peer", + /// Linkstate mode configuration (only taken into account if mode == "linkstate"). + linkstate: { + /// Weights of the outgoing transports in linkstate mode. + /// If none of the two endpoint nodes of a transport specifies its weight, a weight of 100 is applied. + /// If only one of the two endpoint nodes of a transport specifies its weight, the specified weight is applied. + /// If both endpoint nodes of a transport specify its weight, the greater weight is applied. + // transport_weights: [ + // { dst_zid: "1", weight: "10" }, + // { dst_zid: "2", weight: "200" }, + // ] + } + }, + /// The interests-based routing configuration. + /// This configuration applies regardless of the mode (router, peer or client). + interests: { + /// The timeout to wait for incoming interests declarations in milliseconds. + /// The expiration of this timeout implies that the discovery protocol might be incomplete, + /// leading to potential loss of messages, queries or liveliness tokens. + timeout: 10000, + }, + }, + + // /// Overwrite QoS options for Zenoh messages by key expression (ignores Zenoh API QoS config for overwritten values) + // qos: { + // /// Overwrite QoS options for PUT and DELETE messages + // publication: [ + // { + // /// PUT and DELETE messages on key expressions that are included by these key expressions + // /// will have their QoS options overwritten by the given config. + // key_exprs: ["demo/**", "example/key"], + // /// Configurations that will be applied on the publisher. + // /// Options that are supplied here will overwrite the configuration given in Zenoh API + // config: { + // congestion_control: "block", + // priority: "data_high", + // express: true, + // reliability: "best_effort", + // allowed_destination: "remote", + // }, + // }, + // ], + // /// Overwrite QoS options for messages sent and received from/to the network + // /// This allows more fine grained rules (per network card, etc...) but is + // /// less performant than the publication option above. + // network: [ + // { + // /// Optional Id, has to be unique. + // id: "lo0_en0_qos_overwrite", + // // Optional list of ZIDs on which qos will be overwritten when communicating with. + // // zids: ["38a4829bce9166ee"], + // // Optional list of interfaces, if not specified, will be applied to all interfaces. + // interfaces: [ + // "lo0", + // "en0", + // ], + // /// Optional list of link protocols. Transports with at least one of these links will have their qos overwritten. + // /// If absent, the overwrite will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// List of message types to apply to. + // messages: [ + // "put", // put publications + // "delete" // delete publications + // "query", // get queries + // "reply", // replies to queries + // ], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the rules will be applied to both flows. + // flows: ["egress", "ingress"], + // key_exprs: ["test/demo"], + // overwrite: { + // /// Optional new priority value, if not specified priority of the messages will stay unchanged. + // priority: "real_time", + // /// Optional new congestion control value, if not specified congestion control of the messages will stay unchanged. + // congestion_control: "block", + // /// Optional new express value, if not specified express flag of the messages will stay unchanged. + // express: true + // }, + // }, + // ], + // }, + + // /// The declarations aggregation strategy. + // aggregation: { + // /// A list of key-expressions for which all included subscribers will be aggregated into. + // subscribers: [ + // // key_expression + // ], + // /// A list of key-expressions for which all included publishers will be aggregated into. + // publishers: [ + // // key_expression + // ], + // }, + + // /// Namespace prefix. + // /// If specified, all outgoing key expressions will be automatically prefixed with specified string, + // /// and all incoming key expressions will be stripped of specified prefix. + // /// The namespace prefix should satisfy all key expression constraints + // /// and additionally it can not contain wild characters ('*'). + // /// Namespace is applied to the session. + // /// E. g. if session has a namespace of "1" then session.put("my/keyexpr", my_message), + // /// will put a message into 1/my/keyexpr. Same applies to all other operations within this session. + // namespace: "my/namespace", + + // /// The downsampling declaration. + // downsampling: [ + // { + // /// Optional Id, has to be unique + // "id": "wlan0egress", + // /// Optional list of network interfaces messages will be processed on, the rest will be passed as is. + // /// If absent, the rules will be applied to all interfaces. An empty list is invalid. + // interfaces: [ "wlan0" ], + // /// Optional list of link protocols. Transports with at least one of these links will have their messages filtered. + // /// If absent, the rules will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the rules will be applied to both flows. + // flow: ["ingress", "egress"], + // /// List of message type on which downsampling will be applied. Must not be empty. + // messages: [ + // /// Publication (Put and Delete) + // "push", + // /// Get + // "query", + // /// Queryable Reply to a Query + // "reply" + // ], + // /// A list of downsampling rules: key_expression and the maximum frequency in Hertz + // rules: [ + // { key_expr: "demo/example/zenoh-rs-pub", freq: 0.1 }, + // ], + // }, + // ], + + // /// Configure access control (ACL) rules + // access_control: { + // /// [true/false] acl will be activated only if this is set to true + // "enabled": false, + // /// [deny/allow] default permission is deny (even if this is left empty or not specified) + // "default_permission": "deny", + // /// Rule set for permissions allowing or denying access to key-expressions + // "rules": + // [ + // { + // /// Id has to be unique within the rule set + // "id": "rule1", + // "messages": [ + // "put", "delete", "declare_subscriber", + // "query", "reply", "declare_queryable", + // "liveliness_token", "liveliness_query", "declare_liveliness_subscriber", + // ], + // "flows":["egress","ingress"], + // "permission": "allow", + // "key_exprs": [ + // "test/demo" + // ], + // }, + // { + // "id": "rule2", + // "messages": [ + // "put", "delete", "declare_subscriber", + // "query", "reply", "declare_queryable", + // ], + // "flows":["ingress"], + // "permission": "allow", + // "key_exprs": [ + // "**" + // ], + // }, + // ], + // /// List of combinations of subjects. + // /// + // /// If a subject property (i.e. username, certificate common name or interface) is empty + // /// it is interpreted as a wildcard. Moreover, a subject property cannot be an empty list. + // "subjects": + // [ + // { + // /// Id has to be unique within the subjects list + // "id": "subject1", + // /// Subjects can be interfaces + // "interfaces": [ + // "lo0", + // "en0", + // ], + // /// Subjects can be cert_common_names when using TLS or Quic + // "cert_common_names": [ + // "example.zenoh.io" + // ], + // /// Subjects can be usernames when using user/password authentication + // "usernames": [ + // "zenoh-example" + // ], + // /// This instance translates internally to this filter: + // /// (interface="lo0" && cert_common_name="example.zenoh.io" && username="zenoh-example") || + // /// (interface="en0" && cert_common_name="example.zenoh.io" && username="zenoh-example") + // }, + // { + // "id": "subject2", + // "interfaces": [ + // "lo0", + // "en0", + // ], + // "cert_common_names": [ + // "example2.zenoh.io" + // ], + // /// This instance translates internally to this filter: + // /// (interface="lo0" && cert_common_name="example2.zenoh.io") || + // /// (interface="en0" && cert_common_name="example2.zenoh.io") + // }, + // { + // "id": "subject3", + // /// An empty subject combination is a wildcard + // }, + // { + // "id": "subject4", + // /// link protocols can also be used to identify transports to filter messages on. + // /// If absent, the rules will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // }, + // ], + // /// The policies list associates rules to subjects + // "policies": + // [ + // /// Each policy associates one or multiple rules to one or multiple subject combinations + // { + // /// Id is optional. If provided, it has to be unique within the policies list + // "id": "policy1", + // /// Rules and Subjects are identified with their unique IDs declared above + // "rules": ["rule1"], + // "subjects": ["subject1", "subject2"], + // }, + // { + // "rules": ["rule2"], + // "subjects": ["subject3", "subject4"], + // }, + // ] + //}, + + // low_pass_filter: [ + // { + // /// Optional Id, has to be unique + // "id": "filter1", + // /// Optional list of network interfaces messages will be processed on, the rest will not be filtered. + // /// If absent, the filter will be applied to all interfaces. + // interfaces: [ "wlan0" ], + // /// Optional list of link protocols. Transports with at least one of these links will have their messages filtered. + // /// If absent, the rule will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the filter will be applied to both flows. + // flow: ["ingress", "egress"], + // /// List of message type on which the filter will be applied. Must not be empty. + // messages: [ + // "put", + // "delete", + // "query", + // "reply" + // ], + // /// List of key_expressions which matching messages will be filtered + // key_exprs: [ + // "demo/**", + // ], + // /// Inclusive max size of serialized payload + serialized attachment + // size_limit: 8192, + // }, + // ], + + /// Configure internal transport parameters + transport: { + unicast: { + /// Timeout in milliseconds when opening a link + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together + open_timeout: 60000, + /// Timeout in milliseconds when accepting a link + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together + accept_timeout: 60000, + /// Maximum number of links in pending state while performing the handshake for accepting it + /// ROS setting: increase the value to support a large number of Nodes starting all together + accept_pending: 10000, + /// Maximum number of transports that can be simultaneously alive for a single zenoh sessions + /// ROS setting: increase the value to support a large number of Nodes starting all together + max_sessions: 10000, + /// Maximum number of incoming links that are admitted per transport + max_links: 1, + /// Enables the LowLatency transport + /// This option does not make LowLatency transport mandatory, the actual implementation of transport + /// used will depend on Establish procedure and other party's settings + /// + /// NOTE: Currently, the LowLatency transport doesn't preserve QoS prioritization. + /// NOTE: Due to the note above, 'lowlatency' is incompatible with 'qos' option, so in order to + /// enable 'lowlatency' you need to explicitly disable 'qos'. + /// NOTE: LowLatency transport does not support the fragmentation, so the message size should be + /// smaller than the tx batch_size. + lowlatency: false, + /// Enables QoS on unicast communications. + qos: { + enabled: true, + }, + /// Enables compression on unicast communications. + /// Compression capabilities are negotiated during session establishment. + /// If both Zenoh nodes support compression, then compression is activated. + compression: { + enabled: false, + }, + }, + /// WARNING: multicast communication does not perform any negotiation upon group joining. + /// Because of that, it is important that all transport parameters are the same to make + /// sure all your nodes in the system can communicate. One common parameter to configure + /// is "transport/link/tx/batch_size" since its default value depends on the actual platform + /// when operating on multicast. + /// E.g., the batch size on Linux and Windows is 65535 bytes, on Mac OS X is 9216, and anything else is 8192. + multicast: { + /// JOIN message transmission interval in milliseconds. + join_interval: 2500, + /// Maximum number of multicast sessions. + max_sessions: 1000, + /// Enables QoS on multicast communication. + /// Default to false for Zenoh-to-Zenoh-Pico out-of-the-box compatibility. + qos: { + enabled: false, + }, + /// Enables compression on multicast communication. + /// Default to false for Zenoh-to-Zenoh-Pico out-of-the-box compatibility. + compression: { + enabled: false, + }, + }, + link: { + /// An optional whitelist of protocols to be used for accepting and opening sessions. If not + /// configured, all the supported protocols are automatically whitelisted. The supported + /// protocols are: ["tcp" , "udp", "tls", "quic", "ws", "unixsock-stream", "vsock"] For + /// example, to only enable "tls" and "quic": protocols: ["tls", "quic"], + /// + /// Configure the zenoh TX parameters of a link + tx: { + /// The resolution in bits to be used for the message sequence numbers. + /// When establishing a session with another Zenoh instance, the lowest value of the two instances will be used. + /// Accepted values: 8bit, 16bit, 32bit, 64bit. + sequence_number_resolution: "32bit", + /// Link lease duration in milliseconds to announce to other zenoh nodes + /// ROS setting: increase the value to avoid lease expiration at launch time with a large number of Nodes starting all together + lease: 60000, + /// Number of keep-alive messages in a link lease duration. If no data is sent, keep alive + /// messages will be sent at the configured time interval. + /// NOTE: In order to consider eventual packet loss and transmission latency and jitter, + /// set the actual keep_alive interval to one fourth of the lease time: i.e. send + /// 4 keep_alive messages in a lease period. Changing the lease time will have the + /// keep_alive messages sent more or less often. + /// This is in-line with the ITU-T G.8013/Y.1731 specification on continuous connectivity + /// check which considers a link as failed when no messages are received in 3.5 times the + /// target interval. + /// ROS setting: decrease the value since Nodes are communicating over the loopback + /// where keep-alive messages have less chances to be lost. + keep_alive: 2, + /// Batch size in bytes is expressed as a 16bit unsigned integer. + /// Therefore, the maximum batch size is 2^16-1 (i.e. 65535). + /// The default batch size value is the maximum batch size: 65535. + batch_size: 65535, + /// Each zenoh link has a transmission queue that can be configured + queue: { + /// The size of each priority queue indicates the number of batches a given queue can contain. + /// NOTE: the number of batches in each priority must be included between 1 and 16. Different values will result in an error. + /// The amount of memory being allocated for each queue is then SIZE_XXX * BATCH_SIZE. + /// In the case of the transport link MTU being smaller than the ZN_BATCH_SIZE, + /// then amount of memory being allocated for each queue is SIZE_XXX * LINK_MTU. + /// If qos is false, then only the DATA priority will be allocated. + size: { + control: 2, + real_time: 2, + interactive_high: 2, + interactive_low: 2, + data_high: 2, + data: 2, + data_low: 2, + background: 2, + }, + /// Congestion occurs when the queue is empty (no available batch). + congestion_control: { + /// Behavior pushing CongestionControl::Drop messages to the queue. + drop: { + /// The maximum time in microseconds to wait for an available batch before dropping a droppable message if still no batch is available. + wait_before_drop: 1000, + /// The maximum deadline limit for multi-fragment messages. + max_wait_before_drop_fragments: 50000, + }, + /// Behavior pushing CongestionControl::Block messages to the queue. + block: { + /// The maximum time in microseconds to wait for an available batch before closing the transport session when sending a blocking message + /// if still no batch is available. + /// ROS setting: unlike DEFAULT_RMW_ZENOH_SESSION_CONFIG.json5, no change here: + /// as the router is routing messages to outside the robot, possibly over WiFi, + /// keeping a lower value ensure the router is not blocked for too long in case of congestioned WiFi. + wait_before_close: 5000000, + }, + }, + /// Perform batching of messages if they are smaller of the batch_size + batching: { + /// Perform adaptive batching of messages if they are smaller of the batch_size. + /// When the network is detected to not be fast enough to transmit every message individually, many small messages may be + /// batched together and sent all at once on the wire reducing the overall network overhead. This is typically of a high-throughput + /// scenario mainly composed of small messages. In other words, batching is activated by the network back-pressure. + enabled: true, + /// The maximum time limit (in ms) a message should be retained for batching when back-pressure happens. + time_limit: 1, + }, + allocation: { + /// Mode for memory allocation of batches in the priority queues. + /// - "init": batches are allocated at queue initialization time. + /// - "lazy": batches are allocated when needed up to the maximum number of batches configured in the size configuration parameter. + mode: "lazy", + }, + }, + }, + /// Configure the zenoh RX parameters of a link + rx: { + /// Receiving buffer size in bytes for each link + /// The default the rx_buffer_size value is the same as the default batch size: 65535. + /// For very high throughput scenarios, the rx_buffer_size can be increased to accommodate + /// more in-flight data. This is particularly relevant when dealing with large messages. + /// E.g. for 16MiB rx_buffer_size set the value to: 16777216. + buffer_size: 65535, + /// Maximum size of the defragmentation buffer at receiver end. + /// Fragmented messages that are larger than the configured size will be dropped. + /// The default value is 1GiB. This would work in most scenarios. + /// NOTE: reduce the value if you are operating on a memory constrained device. + max_message_size: 1073741824, + }, + /// Configure TLS specific parameters + tls: { + /// Path to the certificate of the certificate authority used to validate either the server + /// or the client's keys and certificates, depending on the node's mode. If not specified + /// on router mode then the default WebPKI certificates are used instead. + root_ca_certificate: null, + /// Path to the TLS listening side private key + listen_private_key: null, + /// Path to the TLS listening side public certificate + listen_certificate: null, + /// Enables mTLS (mutual authentication), client authentication + enable_mtls: false, + /// Path to the TLS connecting side private key + connect_private_key: null, + /// Path to the TLS connecting side certificate + connect_certificate: null, + // Whether or not to verify the matching between hostname/dns and certificate when connecting, + // if set to false zenoh will disregard the common names of the certificates when verifying servers. + // This could be dangerous because your CA can have signed a server cert for foo.com, that's later being used to host a server at baz.com. If you wan't your + // ca to verify that the server at baz.com is actually baz.com, let this be true (default). + verify_name_on_connect: true, + // Whether or not to close links when remote certificates expires. + // If set to true, links that require certificates (tls/quic) will automatically disconnect when the time of expiration of the remote certificate chain is reached + // note that mTLS (client authentication) is required for a listener to disconnect a client on expiration + close_link_on_expiration: false, + /// Optional configuration for TCP system buffers sizes for TLS links + /// + /// Configure TCP read buffer size (bytes) + // so_rcvbuf: 123456, + /// Configure TCP write buffer size (bytes) + // so_sndbuf: 123456, + }, + // // Configure optional TCP link specific parameters + // tcp: { + // /// Optional configuration for TCP system buffers sizes for TCP links + // /// + // /// Configure TCP read buffer size (bytes) + // // so_rcvbuf: 123456, + // /// Configure TCP write buffer size (bytes) + // // so_sndbuf: 123456, + // } + }, + /// Shared memory configuration. + /// NOTE: shared memory can be used only if zenoh is compiled with "shared-memory" feature, otherwise + /// settings in this section have no effect. + shared_memory: { + /// Whether shared memory is enabled or not. + /// If set to `true`, the SHM buffer optimization support will be announced to other parties. (default `true`). + /// This option doesn't make SHM buffer optimization mandatory, the real support depends on other party setting. + /// A probing procedure for shared memory is performed upon session opening. To enable zenoh to operate + /// over shared memory (and to not fallback on network mode), shared memory needs to be enabled also on the + /// subscriber side. By doing so, the probing procedure will succeed and shared memory will operate as expected. + /// + /// ROS setting: disabled by default until fully tested + enabled: false, + /// SHM resources initialization mode (default "lazy"). + /// - "lazy": SHM subsystem internals will be initialized lazily upon the first SHM buffer + /// allocation or reception. This setting provides better startup time and optimizes resource usage, + /// but produces extra latency at the first SHM buffer interaction. + /// - "init": SHM subsystem internals will be initialized upon Session opening. This setting sacrifices + /// startup time, but guarantees no latency impact when first SHM buffer is processed. + mode: "lazy", + }, + auth: { + /// The configuration of authentication. + /// A password implies a username is required. + usrpwd: { + user: null, + password: null, + /// The path to a file containing the user password dictionary + dictionary_file: null, + }, + pubkey: { + public_key_pem: null, + private_key_pem: null, + public_key_file: null, + private_key_file: null, + key_size: null, + known_keys_file: null, + }, + }, + }, + + /// Configure the Admin Space + /// Unstable: this configuration part works as advertised, but may change in a future release + adminspace: { + /// Enables the admin space + enabled: true, + /// read and/or write permissions on the admin space + permissions: { + read: true, + write: false, + }, + }, + +} diff --git a/haru-os/RMW_ZENOH_SESSION_CONFIG_MULTICAST.json5 b/haru-os/RMW_ZENOH_SESSION_CONFIG_MULTICAST.json5 new file mode 100644 index 0000000..8d15e25 --- /dev/null +++ b/haru-os/RMW_ZENOH_SESSION_CONFIG_MULTICAST.json5 @@ -0,0 +1,778 @@ +/// This file attempts to list and document available configuration elements. +/// For a more complete view of the configuration's structure, check out `zenoh/src/config.rs`'s `Config` structure. +/// Note that the values here are correctly typed, but may not be sensible, so copying this file to change only the parts that matter to you is not good practice. +{ + /// The identifier (as unsigned 128bit integer in hexadecimal lowercase - leading zeros are not accepted) + /// that zenoh runtime will use. + /// If not set, a random unsigned 128bit integer will be used. + /// WARNING: this id must be unique in your zenoh network. + // id: "1234567890abcdef", + + /// The node's mode (router, peer or client) + mode: "client", + + /// Which endpoints to connect to. E.g. tcp/localhost:7447. + /// By configuring the endpoints, it is possible to tell zenoh which router/peer to connect to at startup. + /// + /// For TCP/UDP on Linux, it is possible additionally specify the interface to be connected to: + /// E.g. tcp/192.168.0.1:7447#iface=eth0, for connect only if the IP address is reachable via the interface eth0 + /// + /// It is also possible to specify a priority range and/or a reliability setting to be used on the link. + /// For example `tcp/localhost?prio=6-7;rel=0` assigns priorities "data_low" and "background" to the established link. + /// + /// For TCP and TLS links, it is possible to specify the TCP buffer sizes: + /// E.g. tcp/192.168.0.1:7447#so_sndbuf=65000;so_rcvbuf=65000 + /// For TCP, UDP, Quic and TLS links, it is possible to specify a `bind` address for the local socket: + /// E.g. tcp/192.168.0.1:7447#bind=192.168.0.1:0 + /// Note!: Currently it is unsupported to specify both `bind` and `iface`. + /// + /// For TCP/UDP links, it's possible to specify the DSCP field of the IP header: + /// E.g. tcp/192.168.0.1:7447#dscp=0x08 + connect: { + /// timeout waiting for all endpoints connected (0: no retry, -1: infinite timeout) + /// Accepts a single value (e.g. timeout_ms: 0) + /// or different values for router, peer and client (e.g. timeout_ms: { router: -1, peer: -1, client: 0 }). + timeout_ms: { router: -1, peer: -1, client: 0 }, + + /// The list of endpoints to connect to. + /// Accepts a single list (e.g. endpoints: ["tcp/10.10.10.10:7447", "tcp/11.11.11.11:7447"]) + /// or different lists for router, peer and client (e.g. endpoints: { router: ["tcp/10.10.10.10:7447"], peer: ["tcp/11.11.11.11:7447"] }). + /// + /// See https://docs.rs/zenoh/latest/zenoh/config/struct.EndPoint.html + /// + /// ROS setting: By default connect to the Zenoh router on localhost on port 7447. + endpoints: [ + "tcp/localhost:7447" + ], + + /// Global connect configuration, + /// Accepts a single value or different values for router, peer and client. + /// The configuration can also be specified for the separate endpoint + /// it will override the global one + /// E.g. tcp/192.168.0.1:7447#retry_period_init_ms=20000;retry_period_max_ms=10000" + + /// exit from application, if timeout exceed + exit_on_failure: { router: false, peer: false, client: true }, + /// connect establishing retry configuration + retry: { + /// initial wait timeout until next connect try + period_init_ms: 1000, + /// maximum wait timeout until next connect try + period_max_ms: 4000, + /// increase factor for the next timeout until nexti connect try + period_increase_factor: 2, + }, + }, + + /// Which endpoints to listen on. E.g. tcp/0.0.0.0:7447. + /// By configuring the endpoints, it is possible to tell zenoh which are the endpoints that other routers, + /// peers, or client can use to establish a zenoh session. + /// + /// For TCP/UDP on Linux, it is possible additionally specify the interface to be listened to: + /// E.g. tcp/0.0.0.0:7447#iface=eth0, for listen connection only on eth0 + /// + /// It is also possible to specify a priority range and/or a reliability setting to be used on the link. + /// For example `tcp/localhost?prio=6-7;rel=0` assigns priorities "data_low" and "background" to the established link. + /// + /// For TCP and TLS links, it is possible to specify the TCP buffer sizes: + /// E.g. tcp/192.168.0.1:7447#so_sndbuf=65000;so_rcvbuf=65000 + /// + /// For TCP/UDP links, it's possible to specify the DSCP field of the IP header: + /// E.g. tcp/192.168.0.1:7447#dscp=0x08 + listen: { + /// timeout waiting for all listen endpoints (0: no retry, -1: infinite timeout) + /// Accepts a single value (e.g. timeout_ms: 0) + /// or different values for router, peer and client (e.g. timeout_ms: { router: -1, peer: -1, client: 0 }). + timeout_ms: 0, + + /// The list of endpoints to listen on. + /// Accepts a single list (e.g. endpoints: ["tcp/[::]:7447", "udp/[::]:7447"]) + /// or different lists for router, peer and client (e.g. endpoints: { router: ["tcp/[::]:7447"], peer: ["tcp/[::]:0"] }). + /// + /// See https://docs.rs/zenoh/latest/zenoh/config/struct.EndPoint.html + /// + /// ROS setting: By default accept incoming connections only from localhost (i.e. from colocalized Nodes). + /// All communications with other hosts are routed by the Zenoh router. + endpoints: [ + "tcp/localhost:0" + ], + + /// Global listen configuration, + /// Accepts a single value or different values for router, peer and client. + /// The configuration can also be specified for the separate endpoint + /// it will override the global one + /// E.g. tcp/192.168.0.1:7447#exit_on_failure=false;retry_period_max_ms=1000" + + /// exit from application, if timeout exceed + exit_on_failure: true, + /// listen retry configuration + retry: { + /// initial wait timeout until next try + period_init_ms: 1000, + /// maximum wait timeout until next try + period_max_ms: 4000, + /// increase factor for the next timeout until next try + period_increase_factor: 2, + }, + }, + /// Configure the session open behavior. + open: { + /// Configure the conditions to be met before session open returns. + return_conditions: { + /// Session open waits to connect to scouted peers and routers before returning. + /// When set to false, first publications and queries after session open from peers may be lost. + connect_scouted: true, + /// Session open waits to receive initial declares from connected peers before returning. + /// Setting to false may cause extra traffic at startup from peers. + declares: true, + }, + }, + /// Configure the scouting mechanisms and their behaviours + scouting: { + /// In client mode, the period in milliseconds dedicated to scouting for a router before failing. + timeout: 3000, + /// In peer mode, the maximum period in milliseconds dedicated to scouting remote peers before attempting other operations. + delay: 500, + /// The multicast scouting configuration. + multicast: { + /// Whether multicast scouting is enabled or not + /// + /// ROS setting: disable multicast discovery by default + enabled: true, + /// The socket which should be used for multicast scouting + address: "224.0.0.224:7446", + /// The network interface which should be used for multicast scouting + interface: "auto", // If not set or set to "auto" the interface if picked automatically + /// The time-to-live on multicast scouting packets + ttl: 1, + /// Which type of Zenoh instances to automatically establish sessions with upon discovery on UDP multicast. + /// Accepts a single value (e.g. autoconnect: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router, peer or client mode (e.g. autoconnect: { router: [], peer: ["router", "peer"] }). + /// Each value is a list of: "peer", "router" and/or "client". + autoconnect: { router: [], peer: ["router", "peer"], client: ["router"] }, + /// Strategy for autoconnection, mainly to avoid nodes connecting to each other redundantly. + /// Possible options are: + /// - "always": always attempt to autoconnect, may result in redundant connections. + /// - "greater-zid": attempt to connect to another node only if its own zid is greater than the other's. + /// If both nodes use this strategy, only one will attempt the connection. + /// This strategy may not be suited if one of the nodes is not reachable by the other one, for example + /// because of a private IP. + /// Accepts a single value (e.g. autoconnect: "always") which applies whatever node would be auto-connected to, + /// or different values for router and/or peer depending on the type of node detected + /// (e.g. autoconnect_strategy : { to_router: "always", to_peer: "greater-zid" }), + /// or different values for router or peer mode + /// (e.g. autoconnect_strategy : { peer: { to_router: "always", to_peer: "greater-zid" } }). + /// ROS setting: by default all peers rely on the router to discover each other. Thus configuring the peer to send gossip + /// messages only to the router is sufficient and avoids unecessary traffic between Nodes at launch time. + autoconnect_strategy: { peer: { to_router: "always", to_peer: "greater-zid" } }, + /// Whether or not to listen for scout messages on UDP multicast and reply to them. + listen: true, + }, + /// The gossip scouting configuration. Note that instances in "client" mode do not participate in gossip. + gossip: { + /// Whether gossip scouting is enabled or not + enabled: true, + /// When true, gossip scouting information are propagated multiple hops to all nodes in the local network. + /// When false, gossip scouting information are only propagated to the next hop. + /// Activating multihop gossip implies more scouting traffic and a lower scalability. + /// It mostly makes sense when using "linkstate" routing mode where all nodes in the subsystem don't have + /// direct connectivity with each other. + multihop: false, + /// Which type of Zenoh instances to send gossip messages to. + /// Accepts a single value (e.g. target: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router or peer mode (e.g. target: { router: ["router", "peer"], peer: ["router"] }). + /// Each value is a list of "peer" and/or "router". + /// ROS setting: by default all peers rely on the router to discover each other. Thus configuring the peer to send gossip + /// messages only to the router is sufficient and avoids unecessary traffic between Nodes at launch time. + target: { router: ["router", "peer"], peer: ["router"]}, + /// Which type of Zenoh instances to automatically establish sessions with upon discovery on gossip. + /// Accepts a single value (e.g. autoconnect: ["router", "peer"]) which applies whatever the configured "mode" is, + /// or different values for router or peer mode (e.g. autoconnect: { router: [], peer: ["router", "peer"] }). + /// Each value is a list of: "peer" and/or "router". + autoconnect: { router: [], peer: ["router", "peer"] }, + /// Strategy for autoconnection, mainly to avoid nodes connecting to each other redundantly. + /// Possible options are: + /// - "always": always attempt to autoconnect, may result in redundant connection which will then be closed. + /// - "greater-zid": attempt to connect to another node only if its own zid is greater than the other's. + /// If both nodes use this strategy, only one will attempt the connection. + /// This strategy may not be suited if one of the nodes is not reachable by the other one, for example + /// because of a private IP. + /// Accepts a single value (e.g. autoconnect: "always") which applies whatever node would be auto-connected to, + /// or different values for router and/or peer depending on the type of node detected + /// (e.g. autoconnect_strategy : { to_router: "always", to_peer: "greater-zid" }), + /// or different values for router or peer mode + /// (e.g. autoconnect_strategy : { peer: { to_router: "always", to_peer: "greater-zid" } }). + /// ROS setting: as by default all peers will interconnect to each other over the loopback interface, + /// they are all reachable to each other. Hence using "greater-zid" for peers connecting to + /// other peers is sufficient and avoids unecessary double connections between peers at startup. + autoconnect_strategy: { peer: { to_router: "always", to_peer: "greater-zid" } }, + }, + }, + + /// Configuration of data messages timestamps management. + timestamping: { + /// Whether data messages should be timestamped if not already. + /// Accepts a single boolean value or different values for router, peer and client. + /// + /// ROS setting: PublicationCache which is required for transient_local durability + /// only works when time-stamping is enabled. + enabled: { router: true, peer: true, client: true }, + /// Whether data messages with timestamps in the future should be dropped or not. + /// If set to false (default), messages with timestamps in the future are retimestamped. + /// Timestamps are ignored if timestamping is disabled. + drop_future_timestamp: false, + }, + + /// The default timeout to apply to queries in milliseconds. + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together. + /// Note that only action-related service get_result is hard-coded with an infinite timeout. + queries_default_timeout: 60000, + + /// The routing strategy to use and it's configuration. + routing: { + /// The routing strategy to use in routers and it's configuration. + router: { + /// When set to true a router will forward data between two peers + /// directly connected to it if it detects that those peers are not + /// connected to each other. + /// The failover brokering only works if gossip discovery is enabled + /// and peers are configured with gossip target "router". + peers_failover_brokering: true, + /// Linkstate mode configuration. + linkstate: { + /// Weights of the outgoing transports in linkstate mode. + /// If none of the two endpoint nodes of a transport specifies its weight, a weight of 100 is applied. + /// If only one of the two endpoint nodes of a transport specifies its weight, the specified weight is applied. + /// If both endpoint nodes of a transport specify its weight, the greater weight is applied. + // transport_weights: [ + // { dst_zid: "1", weight: "10" }, + // { dst_zid: "2", weight: "200" }, + // ] + } + }, + /// The routing strategy to use in peers and it's configuration. + peer: { + /// The routing strategy to use in peers. ("peer_to_peer" or "linkstate"). + /// This option needs to be set to the same value in all peers and routers of the subsystem. + mode: "peer_to_peer", + /// Linkstate mode configuration (only taken into account if mode == "linkstate"). + linkstate: { + /// Weights of the outgoing transports in linkstate mode. + /// If none of the two endpoint nodes of a transport specifies its weight, a weight of 100 is applied. + /// If only one of the two endpoint nodes of a transport specifies its weight, the specified weight is applied. + /// If both endpoint nodes of a transport specify its weight, the greater weight is applied. + // transport_weights: [ + // { dst_zid: "1", weight: "10" }, + // { dst_zid: "2", weight: "200" }, + // ] + } + }, + /// The interests-based routing configuration. + /// This configuration applies regardless of the mode (router, peer or client). + interests: { + /// The timeout to wait for incoming interests declarations in milliseconds. + /// The expiration of this timeout implies that the discovery protocol might be incomplete, + /// leading to potential loss of messages, queries or liveliness tokens. + timeout: 10000, + }, + }, + + // /// Overwrite QoS options for Zenoh messages by key expression (ignores Zenoh API QoS config for overwritten values) + // qos: { + // /// Overwrite QoS options for PUT and DELETE messages + // publication: [ + // { + // /// PUT and DELETE messages on key expressions that are included by these key expressions + // /// will have their QoS options overwritten by the given config. + // key_exprs: ["demo/**", "example/key"], + // /// Configurations that will be applied on the publisher. + // /// Options that are supplied here will overwrite the configuration given in Zenoh API + // config: { + // congestion_control: "block", + // priority: "data_high", + // express: true, + // reliability: "best_effort", + // allowed_destination: "remote", + // }, + // }, + // ], + // /// Overwrite QoS options for messages sent and received from/to the network + // /// This allows more fine grained rules (per network card, etc...) but is + // /// less performant than the publication option above. + // network: [ + // { + // /// Optional Id, has to be unique. + // id: "lo0_en0_qos_overwrite", + // // Optional list of ZIDs on which qos will be overwritten when communicating with. + // // zids: ["38a4829bce9166ee"], + // // Optional list of interfaces, if not specified, will be applied to all interfaces. + // interfaces: [ + // "lo0", + // "en0", + // ], + // /// Optional list of link protocols. Transports with at least one of these links will have their qos overwritten. + // /// If absent, the overwrite will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// List of message types to apply to. + // messages: [ + // "put", // put publications + // "delete" // delete publications + // "query", // get queries + // "reply", // replies to queries + // ], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the rules will be applied to both flows. + // flows: ["egress", "ingress"], + // key_exprs: ["test/demo"], + // overwrite: { + // /// Optional new priority value, if not specified priority of the messages will stay unchanged. + // priority: "real_time", + // /// Optional new congestion control value, if not specified congestion control of the messages will stay unchanged. + // congestion_control: "block", + // /// Optional new express value, if not specified express flag of the messages will stay unchanged. + // express: true + // }, + // }, + // ], + // }, + + // /// The declarations aggregation strategy. + // aggregation: { + // /// A list of key-expressions for which all included subscribers will be aggregated into. + // subscribers: [ + // // key_expression + // ], + // /// A list of key-expressions for which all included publishers will be aggregated into. + // publishers: [ + // // key_expression + // ], + // }, + + // /// Namespace prefix. + // /// If specified, all outgoing key expressions will be automatically prefixed with specified string, + // /// and all incoming key expressions will be stripped of specified prefix. + // /// The namespace prefix should satisfy all key expression constraints + // /// and additionally it can not contain wild characters ('*'). + // /// Namespace is applied to the session. + // /// E. g. if session has a namespace of "1" then session.put("my/keyexpr", my_message), + // /// will put a message into 1/my/keyexpr. Same applies to all other operations within this session. + // namespace: "my/namespace", + + // /// The downsampling declaration. + // downsampling: [ + // { + // /// Optional Id, has to be unique + // "id": "wlan0egress", + // /// Optional list of network interfaces messages will be processed on, the rest will be passed as is. + // /// If absent, the rules will be applied to all interfaces. An empty list is invalid. + // interfaces: [ "wlan0" ], + // /// Optional list of link protocols. Transports with at least one of these links will have their messages filtered. + // /// If absent, the rules will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the rules will be applied to both flows. + // flow: ["ingress", "egress"], + // /// List of message type on which downsampling will be applied. Must not be empty. + // messages: [ + // /// Publication (Put and Delete) + // "push", + // /// Get + // "query", + // /// Queryable Reply to a Query + // "reply" + // ], + // /// A list of downsampling rules: key_expression and the maximum frequency in Hertz + // rules: [ + // { key_expr: "demo/example/zenoh-rs-pub", freq: 0.1 }, + // ], + // }, + // ], + + // /// Configure access control (ACL) rules + // access_control: { + // /// [true/false] acl will be activated only if this is set to true + // "enabled": false, + // /// [deny/allow] default permission is deny (even if this is left empty or not specified) + // "default_permission": "deny", + // /// Rule set for permissions allowing or denying access to key-expressions + // "rules": + // [ + // { + // /// Id has to be unique within the rule set + // "id": "rule1", + // "messages": [ + // "put", "delete", "declare_subscriber", + // "query", "reply", "declare_queryable", + // "liveliness_token", "liveliness_query", "declare_liveliness_subscriber", + // ], + // "flows":["egress","ingress"], + // "permission": "allow", + // "key_exprs": [ + // "test/demo" + // ], + // }, + // { + // "id": "rule2", + // "messages": [ + // "put", "delete", "declare_subscriber", + // "query", "reply", "declare_queryable", + // ], + // "flows":["ingress"], + // "permission": "allow", + // "key_exprs": [ + // "**" + // ], + // }, + // ], + // /// List of combinations of subjects. + // /// + // /// If a subject property (i.e. username, certificate common name or interface) is empty + // /// it is interpreted as a wildcard. Moreover, a subject property cannot be an empty list. + // "subjects": + // [ + // { + // /// Id has to be unique within the subjects list + // "id": "subject1", + // /// Subjects can be interfaces + // "interfaces": [ + // "lo0", + // "en0", + // ], + // /// Subjects can be cert_common_names when using TLS or Quic + // "cert_common_names": [ + // "example.zenoh.io" + // ], + // /// Subjects can be usernames when using user/password authentication + // "usernames": [ + // "zenoh-example" + // ], + // /// This instance translates internally to this filter: + // /// (interface="lo0" && cert_common_name="example.zenoh.io" && username="zenoh-example") || + // /// (interface="en0" && cert_common_name="example.zenoh.io" && username="zenoh-example") + // }, + // { + // "id": "subject2", + // "interfaces": [ + // "lo0", + // "en0", + // ], + // "cert_common_names": [ + // "example2.zenoh.io" + // ], + // /// This instance translates internally to this filter: + // /// (interface="lo0" && cert_common_name="example2.zenoh.io") || + // /// (interface="en0" && cert_common_name="example2.zenoh.io") + // }, + // { + // "id": "subject3", + // /// An empty subject combination is a wildcard + // }, + // { + // "id": "subject4", + // /// link protocols can also be used to identify transports to filter messages on. + // /// If absent, the rules will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // }, + // ], + // /// The policies list associates rules to subjects + // "policies": + // [ + // /// Each policy associates one or multiple rules to one or multiple subject combinations + // { + // /// Id is optional. If provided, it has to be unique within the policies list + // "id": "policy1", + // /// Rules and Subjects are identified with their unique IDs declared above + // "rules": ["rule1"], + // "subjects": ["subject1", "subject2"], + // }, + // { + // "rules": ["rule2"], + // "subjects": ["subject3", "subject4"], + // }, + // ] + //}, + + // low_pass_filter: [ + // { + // /// Optional Id, has to be unique + // "id": "filter1", + // /// Optional list of network interfaces messages will be processed on, the rest will not be filtered. + // /// If absent, the filter will be applied to all interfaces. + // interfaces: [ "wlan0" ], + // /// Optional list of link protocols. Transports with at least one of these links will have their messages filtered. + // /// If absent, the rule will be applied to all transports. An empty list is invalid. + // link_protocols: [ "tcp", "udp", "tls", "quic", "ws", "serial", "unixsock-stream", "unixpipe", "vsock"], + // /// Optional list of data flows messages will be processed on ("egress" and/or "ingress"). + // /// If absent, the filter will be applied to both flows. + // flow: ["ingress", "egress"], + // /// List of message type on which the filter will be applied. Must not be empty. + // messages: [ + // "put", + // "delete", + // "query", + // "reply" + // ], + // /// List of key_expressions which matching messages will be filtered + // key_exprs: [ + // "demo/**", + // ], + // /// Inclusive max size of serialized payload + serialized attachment + // size_limit: 8192, + // }, + // ], + + /// Configure internal transport parameters + transport: { + unicast: { + /// Timeout in milliseconds when opening a link + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together + open_timeout: 60000, + /// Timeout in milliseconds when accepting a link + /// ROS setting: increase the value to avoid timeout at launch time with a large number of Nodes starting all together + accept_timeout: 60000, + /// Maximum number of links in pending state while performing the handshake for accepting it + /// ROS setting: increase the value to support a large number of Nodes starting all together + accept_pending: 10000, + /// Maximum number of transports that can be simultaneously alive for a single zenoh sessions + /// ROS setting: increase the value to support a large number of Nodes starting all together + max_sessions: 10000, + /// Maximum number of incoming links that are admitted per transport + max_links: 1, + /// Enables the LowLatency transport + /// This option does not make LowLatency transport mandatory, the actual implementation of transport + /// used will depend on Establish procedure and other party's settings + /// + /// NOTE: Currently, the LowLatency transport doesn't preserve QoS prioritization. + /// NOTE: Due to the note above, 'lowlatency' is incompatible with 'qos' option, so in order to + /// enable 'lowlatency' you need to explicitly disable 'qos'. + /// NOTE: LowLatency transport does not support the fragmentation, so the message size should be + /// smaller than the tx batch_size. + lowlatency: false, + /// Enables QoS on unicast communications. + qos: { + enabled: true, + }, + /// Enables compression on unicast communications. + /// Compression capabilities are negotiated during session establishment. + /// If both Zenoh nodes support compression, then compression is activated. + compression: { + enabled: false, + }, + }, + /// WARNING: multicast communication does not perform any negotiation upon group joining. + /// Because of that, it is important that all transport parameters are the same to make + /// sure all your nodes in the system can communicate. One common parameter to configure + /// is "transport/link/tx/batch_size" since its default value depends on the actual platform + /// when operating on multicast. + /// E.g., the batch size on Linux and Windows is 65535 bytes, on Mac OS X is 9216, and anything else is 8192. + multicast: { + /// JOIN message transmission interval in milliseconds. + join_interval: 2500, + /// Maximum number of multicast sessions. + max_sessions: 1000, + /// Enables QoS on multicast communication. + /// Default to false for Zenoh-to-Zenoh-Pico out-of-the-box compatibility. + qos: { + enabled: false, + }, + /// Enables compression on multicast communication. + /// Default to false for Zenoh-to-Zenoh-Pico out-of-the-box compatibility. + compression: { + enabled: false, + }, + }, + link: { + /// An optional whitelist of protocols to be used for accepting and opening sessions. If not + /// configured, all the supported protocols are automatically whitelisted. The supported + /// protocols are: ["tcp" , "udp", "tls", "quic", "ws", "unixsock-stream", "vsock"] For + /// example, to only enable "tls" and "quic": protocols: ["tls", "quic"], + /// + /// Configure the zenoh TX parameters of a link + tx: { + /// The resolution in bits to be used for the message sequence numbers. + /// When establishing a session with another Zenoh instance, the lowest value of the two instances will be used. + /// Accepted values: 8bit, 16bit, 32bit, 64bit. + sequence_number_resolution: "32bit", + /// Link lease duration in milliseconds to announce to other zenoh nodes + /// ROS setting: increase the value to avoid lease expiration at launch time with a large number of Nodes starting all together + lease: 60000, + /// Number of keep-alive messages in a link lease duration. If no data is sent, keep alive + /// messages will be sent at the configured time interval. + /// NOTE: In order to consider eventual packet loss and transmission latency and jitter, + /// set the actual keep_alive interval to one fourth of the lease time: i.e. send + /// 4 keep_alive messages in a lease period. Changing the lease time will have the + /// keep_alive messages sent more or less often. + /// This is in-line with the ITU-T G.8013/Y.1731 specification on continuous connectivity + /// check which considers a link as failed when no messages are received in 3.5 times the + /// target interval. + /// ROS setting: decrease the value since Nodes are communicating over the loopback + /// where keep-alive messages have less chances to be lost. + keep_alive: 2, + /// Batch size in bytes is expressed as a 16bit unsigned integer. + /// Therefore, the maximum batch size is 2^16-1 (i.e. 65535). + /// The default batch size value is the maximum batch size: 65535. + batch_size: 65535, + /// Each zenoh link has a transmission queue that can be configured + queue: { + /// The size of each priority queue indicates the number of batches a given queue can contain. + /// NOTE: the number of batches in each priority must be included between 1 and 16. Different values will result in an error. + /// The amount of memory being allocated for each queue is then SIZE_XXX * BATCH_SIZE. + /// In the case of the transport link MTU being smaller than the ZN_BATCH_SIZE, + /// then amount of memory being allocated for each queue is SIZE_XXX * LINK_MTU. + /// If qos is false, then only the DATA priority will be allocated. + size: { + control: 2, + real_time: 2, + interactive_high: 2, + interactive_low: 2, + data_high: 2, + data: 2, + data_low: 2, + background: 2, + }, + /// Congestion occurs when the queue is empty (no available batch). + congestion_control: { + /// Behavior pushing CongestionControl::Drop messages to the queue. + drop: { + /// The maximum time in microseconds to wait for an available batch before dropping a droppable message if still no batch is available. + wait_before_drop: 1000, + /// The maximum deadline limit for multi-fragment messages. + max_wait_before_drop_fragments: 50000, + }, + /// Behavior pushing CongestionControl::Block messages to the queue. + block: { + /// The maximum time in microseconds to wait for an available batch before closing the transport session when sending a blocking message + /// if still no batch is available. + /// ROS setting: increase the value to avoid unecessary link closure at launch time where congestion is likely + /// to occur even over the loopback since all the Nodes are starting at the same time. + wait_before_close: 60000000, + }, + }, + /// Perform batching of messages if they are smaller of the batch_size + batching: { + /// Perform adaptive batching of messages if they are smaller of the batch_size. + /// When the network is detected to not be fast enough to transmit every message individually, many small messages may be + /// batched together and sent all at once on the wire reducing the overall network overhead. This is typically of a high-throughput + /// scenario mainly composed of small messages. In other words, batching is activated by the network back-pressure. + enabled: true, + /// The maximum time limit (in ms) a message should be retained for batching when back-pressure happens. + time_limit: 1, + }, + allocation: { + /// Mode for memory allocation of batches in the priority queues. + /// - "init": batches are allocated at queue initialization time. + /// - "lazy": batches are allocated when needed up to the maximum number of batches configured in the size configuration parameter. + mode: "lazy", + }, + }, + }, + /// Configure the zenoh RX parameters of a link + rx: { + /// Receiving buffer size in bytes for each link + /// The default the rx_buffer_size value is the same as the default batch size: 65535. + /// For very high throughput scenarios, the rx_buffer_size can be increased to accommodate + /// more in-flight data. This is particularly relevant when dealing with large messages. + /// E.g. for 16MiB rx_buffer_size set the value to: 16777216. + buffer_size: 65535, + /// Maximum size of the defragmentation buffer at receiver end. + /// Fragmented messages that are larger than the configured size will be dropped. + /// The default value is 1GiB. This would work in most scenarios. + /// NOTE: reduce the value if you are operating on a memory constrained device. + max_message_size: 1073741824, + }, + /// Configure TLS specific parameters + tls: { + /// Path to the certificate of the certificate authority used to validate either the server + /// or the client's keys and certificates, depending on the node's mode. If not specified + /// on router mode then the default WebPKI certificates are used instead. + root_ca_certificate: null, + /// Path to the TLS listening side private key + listen_private_key: null, + /// Path to the TLS listening side public certificate + listen_certificate: null, + /// Enables mTLS (mutual authentication), client authentication + enable_mtls: false, + /// Path to the TLS connecting side private key + connect_private_key: null, + /// Path to the TLS connecting side certificate + connect_certificate: null, + // Whether or not to verify the matching between hostname/dns and certificate when connecting, + // if set to false zenoh will disregard the common names of the certificates when verifying servers. + // This could be dangerous because your CA can have signed a server cert for foo.com, that's later being used to host a server at baz.com. If you wan't your + // ca to verify that the server at baz.com is actually baz.com, let this be true (default). + verify_name_on_connect: true, + // Whether or not to close links when remote certificates expires. + // If set to true, links that require certificates (tls/quic) will automatically disconnect when the time of expiration of the remote certificate chain is reached + // note that mTLS (client authentication) is required for a listener to disconnect a client on expiration + close_link_on_expiration: false, + /// Optional configuration for TCP system buffers sizes for TLS links + /// + /// Configure TCP read buffer size (bytes) + // so_rcvbuf: 123456, + /// Configure TCP write buffer size (bytes) + // so_sndbuf: 123456, + }, + // // Configure optional TCP link specific parameters + // tcp: { + // /// Optional configuration for TCP system buffers sizes for TCP links + // /// + // /// Configure TCP read buffer size (bytes) + // // so_rcvbuf: 123456, + // /// Configure TCP write buffer size (bytes) + // // so_sndbuf: 123456, + // } + }, + /// Shared memory configuration. + /// NOTE: shared memory can be used only if zenoh is compiled with "shared-memory" feature, otherwise + /// settings in this section have no effect. + shared_memory: { + /// Whether shared memory is enabled or not. + /// If set to `true`, the SHM buffer optimization support will be announced to other parties. (default `true`). + /// This option doesn't make SHM buffer optimization mandatory, the real support depends on other party setting. + /// A probing procedure for shared memory is performed upon session opening. To enable zenoh to operate + /// over shared memory (and to not fallback on network mode), shared memory needs to be enabled also on the + /// subscriber side. By doing so, the probing procedure will succeed and shared memory will operate as expected. + /// + /// ROS setting: disabled by default until fully tested + enabled: false, + /// SHM resources initialization mode (default "lazy"). + /// - "lazy": SHM subsystem internals will be initialized lazily upon the first SHM buffer + /// allocation or reception. This setting provides better startup time and optimizes resource usage, + /// but produces extra latency at the first SHM buffer interaction. + /// - "init": SHM subsystem internals will be initialized upon Session opening. This setting sacrifices + /// startup time, but guarantees no latency impact when first SHM buffer is processed. + mode: "lazy", + }, + auth: { + /// The configuration of authentication. + /// A password implies a username is required. + usrpwd: { + user: null, + password: null, + /// The path to a file containing the user password dictionary + dictionary_file: null, + }, + pubkey: { + public_key_pem: null, + private_key_pem: null, + public_key_file: null, + private_key_file: null, + key_size: null, + known_keys_file: null, + }, + }, + }, + + /// Configure the Admin Space + /// Unstable: this configuration part works as advertised, but may change in a future release + adminspace: { + /// Enables the admin space + enabled: true, + /// read and/or write permissions on the admin space + permissions: { + read: true, + write: false, + }, + }, + +} diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index 5a8e3e8..ff456a0 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -16,18 +16,19 @@ export NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES:=all} export NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES:=compute,utility} # Set ROS -export RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:=rmw_cyclonedds_cpp} # default to rmw_cyclonedds_cpp and not rmw_fastrtps_cpp (i.e., disable shared memory transport to allow communication between different users, including root) -export CYCLONEDDS_URI=${CYCLONEDDS_URI:=file:///config/cyclonedds.xml} # cyclonedds config with higher MaxParticipants -echo "[INFO] Using RMW_IMPLEMENTATION: $RMW_IMPLEMENTATION" -echo "[INFO] Using CYCLONEDDS_URI: $CYCLONEDDS_URI" - export ROS_DOMAIN_ID=${ROS_DOMAIN_ID:=0} echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" -# Show connectivity -echo "[INFO] Resolving hostnames from /etc/hosts" -echo "[INFO] robot resolves to: $(getent hosts robot | awk '{ print $1 }')" -echo "[INFO] pc resolves to: $(getent hosts pc | awk '{ print $1 }')" +export RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:=rmw_fastrtps_cpp} +echo "[INFO] Using RMW_IMPLEMENTATION: $RMW_IMPLEMENTATION" + +# If using RMW Zenoh, apply additional configuration +if [ "$RMW_IMPLEMENTATION" = "rmw_zenoh_cpp" ]; then + export ZENOH_ROUTER_CONFIG_URI=${ZENOH_ROUTER_CONFIG_URI:=/config/RMW_ZENOH_ROUTER_CONFIG.json5} + export ZENOH_SESSION_CONFIG_URI=${ZENOH_SESSION_CONFIG_URI:=/config/RMW_ZENOH_SESSION_CONFIG.json5} + echo "[INFO] Using ZENOH_ROUTER_CONFIG_URI: $ZENOH_ROUTER_CONFIG_URI" + echo "[INFO] Using ZENOH_SESSION_CONFIG_URI: $ZENOH_SESSION_CONFIG_URI" +fi # Source ROS 2 distro environment source "/opt/ros/$ROS_DISTRO/setup.bash" From d80a99340237944a9c3dc1d3c5eed2a1e349ba06 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Tue, 12 Aug 2025 11:30:34 +0900 Subject: [PATCH 27/40] disable shm transport for containers communication --- .env.example | 2 +- docker-compose-haru.yaml | 20 ++++--------------- docker-compose-simulator.yaml | 4 ++-- haru-os/Dockerfile | 1 + ...onedds.xml => cyclonedds_no_multicast.xml} | 0 haru-os/entrypoint.sh | 12 +++++++++++ 6 files changed, 20 insertions(+), 19 deletions(-) rename haru-os/{cyclonedds.xml => cyclonedds_no_multicast.xml} (100%) diff --git a/.env.example b/.env.example index ecf57e3..9b19111 100644 --- a/.env.example +++ b/.env.example @@ -7,4 +7,4 @@ NVIDIA_DRIVER_CAPABILITIES=all # ROS ROS_DOMAIN_ID=99 -RMW_IMPLEMENTATION=rmw_zenoh_cpp \ No newline at end of file +RMW_IMPLEMENTATION=rmw_fastrtps_cpp \ No newline at end of file diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index 5e2b444..a01416d 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -2,25 +2,13 @@ name: haru-os services: - # zenoh-router: - # image: haru/haru-os:ros2 - # command: ["ros2", "run", "rmw_zenoh_cpp", "rmw_zenohd"] - # environment: - # - DISPLAY=${DISPLAY:-=:0} - # - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - # - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} - # network_mode: host - # ipc: host - # volumes: - # - /tmp/.X11-unix:/tmp/.X11-unix - pub-root: image: haru/haru-os:ros2 command: ["ros2", "topic", "pub", "/root", "std_msgs/msg/String", "{data: Hello from root}"] environment: - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} network_mode: host ipc: host volumes: @@ -33,7 +21,7 @@ services: environment: - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} network_mode: host ipc: host volumes: @@ -45,7 +33,7 @@ services: environment: - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} network_mode: host ipc: host volumes: @@ -59,7 +47,7 @@ services: environment: - DISPLAY=${DISPLAY:-=:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION} + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} network_mode: host ipc: host volumes: diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index 59b1ecb..83e2f54 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -8,7 +8,7 @@ services: environment: - DISPLAY=${DISPLAY:-:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=rmw_fastrtps_cpp + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} - NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES} - NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES} network_mode: host @@ -33,7 +33,7 @@ services: environment: - DISPLAY=${DISPLAY:-:0} - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=rmw_fastrtps_cpp + - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} network_mode: host ipc: host volumes: diff --git a/haru-os/Dockerfile b/haru-os/Dockerfile index d111f9f..6f9da09 100644 --- a/haru-os/Dockerfile +++ b/haru-os/Dockerfile @@ -21,6 +21,7 @@ RUN apt update && apt install -y \ ros-${ROS_DISTRO}-rmw-cyclonedds-cpp ros-${ROS_DISTRO}-rmw-zenoh-cpp \ && rm -rf /var/lib/apt/lists/* +COPY cyclonedds_no_multicast.xml /config/cyclonedds.xml COPY RMW_ZENOH_ROUTER_CONFIG_MULTICAST_MULTIHOST.json5 /config/RMW_ZENOH_ROUTER_CONFIG.json5 COPY RMW_ZENOH_SESSION_CONFIG_MULTICAST.json5 /config/RMW_ZENOH_SESSION_CONFIG.json5 diff --git a/haru-os/cyclonedds.xml b/haru-os/cyclonedds_no_multicast.xml similarity index 100% rename from haru-os/cyclonedds.xml rename to haru-os/cyclonedds_no_multicast.xml diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index ff456a0..1e0a571 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -22,6 +22,18 @@ echo "[INFO] Using ROS_DOMAIN_ID: $ROS_DOMAIN_ID" export RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:=rmw_fastrtps_cpp} echo "[INFO] Using RMW_IMPLEMENTATION: $RMW_IMPLEMENTATION" +# If using RMW Fast DDS, apply additional configuration +if [ "$RMW_IMPLEMENTATION" = "rmw_fastrtps_cpp" ]; then + export FASTDDS_BUILTIN_TRANSPORTS=${FASTDDS_BUILTIN_TRANSPORTS:=UDPv4} + echo "[INFO] Using FASTDDS_BUILTIN_TRANSPORTS: $FASTDDS_BUILTIN_TRANSPORTS" +fi + +# If using RMW Cyclone DDS, apply additional configuration +if [ "$RMW_IMPLEMENTATION" = "rmw_cyclonedds_cpp" ]; then + export CYCLONEDDS_URI=${CYCLONEDDS_URI:=file:///config/cyclonedds.xml} + echo "[INFO] Using CYCLONEDDS_URI: $CYCLONEDDS_URI" +fi + # If using RMW Zenoh, apply additional configuration if [ "$RMW_IMPLEMENTATION" = "rmw_zenoh_cpp" ]; then export ZENOH_ROUTER_CONFIG_URI=${ZENOH_ROUTER_CONFIG_URI:=/config/RMW_ZENOH_ROUTER_CONFIG.json5} From 9561f0f40e1e985d2ccf141165cc7de2396aac4f Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Tue, 12 Aug 2025 16:51:08 +0900 Subject: [PATCH 28/40] typo: branch master instead of main --- .github/workflows/docker-publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index f96fe9b..788906d 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -66,8 +66,8 @@ jobs: secrets: | github_token=${{ secrets.CLONE_HARU_GH_TOKEN }} - - name: Tag and push :latest if main - if: steps.vars.outputs.ref_type == 'branch' && steps.vars.outputs.tag == 'main' + - name: Tag and push :latest if master + if: steps.vars.outputs.ref_type == 'branch' && steps.vars.outputs.tag == 'master' uses: docker/build-push-action@v5 with: context: ./${{ matrix.component }} From 4a122dc326d1a5eda25972c076d977e90a9f5c49 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Tue, 12 Aug 2025 18:21:34 +0900 Subject: [PATCH 29/40] typo: update token var --- .github/workflows/docker-publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 788906d..d8809f5 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -64,7 +64,7 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:${{ steps.vars.outputs.tag }} secrets: | - github_token=${{ secrets.CLONE_HARU_GH_TOKEN }} + github_token=${{ secrets.CI_TOKEN }} - name: Tag and push :latest if master if: steps.vars.outputs.ref_type == 'branch' && steps.vars.outputs.tag == 'master' @@ -79,4 +79,4 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:latest secrets: | - github_token=${{ secrets.CLONE_HARU_GH_TOKEN }} + github_token=${{ secrets.CI_TOKEN }} From 74fb4d01a26830d78209e5f3045fc39c9f13c616 Mon Sep 17 00:00:00 2001 From: MAEDA Chikara Antoine <91732614+AntoineBlanot@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:31:33 +0900 Subject: [PATCH 30/40] Update docker-publish.yaml --- .github/workflows/docker-publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index d8809f5..6d1496b 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -64,7 +64,7 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:${{ steps.vars.outputs.tag }} secrets: | - github_token=${{ secrets.CI_TOKEN }} + github_token=${{ secrets.GITHUB_TOKEN }} - name: Tag and push :latest if master if: steps.vars.outputs.ref_type == 'branch' && steps.vars.outputs.tag == 'master' @@ -79,4 +79,4 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:latest secrets: | - github_token=${{ secrets.CI_TOKEN }} + github_token=${{ secrets.GITHUB_TOKEN }} From f89a6d0c68b7a2d8bb2d50f035cb5319be77e845 Mon Sep 17 00:00:00 2001 From: MAEDA Chikara Antoine <91732614+AntoineBlanot@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:44:33 +0900 Subject: [PATCH 31/40] Update docker-publish.yaml --- .github/workflows/docker-publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 6d1496b..d8809f5 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -64,7 +64,7 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:${{ steps.vars.outputs.tag }} secrets: | - github_token=${{ secrets.GITHUB_TOKEN }} + github_token=${{ secrets.CI_TOKEN }} - name: Tag and push :latest if master if: steps.vars.outputs.ref_type == 'branch' && steps.vars.outputs.tag == 'master' @@ -79,4 +79,4 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:latest secrets: | - github_token=${{ secrets.GITHUB_TOKEN }} + github_token=${{ secrets.CI_TOKEN }} From 4eee0184301fedb39559ea710e0ed860cbb4e975 Mon Sep 17 00:00:00 2001 From: MAEDA Chikara Antoine Date: Thu, 14 Aug 2025 10:57:25 +0900 Subject: [PATCH 32/40] update: haru-simulator image uses .repos file --- haru-simulator/.repos | 26 ++++++++++++++++++++++++++ haru-simulator/Dockerfile | 15 +++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 haru-simulator/.repos diff --git a/haru-simulator/.repos b/haru-simulator/.repos new file mode 100644 index 0000000..81665e1 --- /dev/null +++ b/haru-simulator/.repos @@ -0,0 +1,26 @@ +repositories: + + haru-unity: + type: git + url: git@github.com:haru-project/haru-unity.git + version: develop_ros2 + + ROS-TCP-Endpoint: + type: git + url: git@github.com:haru-project/ROS-TCP-Endpoint.git + version: jazzy_debian + + idmind-tabletop-ros-pkg: + type: git + url: git@github.com:haru-project/idmind-tabletop-ros-pkg.git + version: fix_tuple_error + + strawberry-ros-msgs: + type: git + url: git@github.com:haru-project/strawberry-ros-msgs.git + version: ros2 + + haru-speech-msgs: + type: git + url: git@github.com:haru-project/haru-speech-msgs.git + version: master \ No newline at end of file diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 8b80f66..42bde56 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -12,6 +12,8 @@ RUN apt update && apt install -y \ # Clone ROS dependencies WORKDIR /ros2_ws/src +COPY .repos .repos + ARG CLONE_METHOD=ssh ENV CLONE_METHOD=${CLONE_METHOD} @@ -19,19 +21,16 @@ RUN --mount=type=secret,id=sshkey,required=false \ --mount=type=secret,id=github_token,required=false \ if [ "$CLONE_METHOD" = "https" ]; then \ TOKEN=$(cat /run/secrets/github_token) && \ - git clone -b develop_ros2 https://x-access-token:${TOKEN}@github.com/haru-project/haru-unity.git && \ - git clone -b jazzy_debian https://x-access-token:${TOKEN}@github.com/haru-project/ROS-TCP-Endpoint.git && \ - git clone -b ros2 https://x-access-token:${TOKEN}@github.com/haru-project/strawberry-ros-msgs.git && \ - git clone -b fix_tuple_error https://x-access-token:${TOKEN}@github.com/haru-project/idmind-tabletop-ros-pkg.git; \ + cp .repos .repos.https && \ + sed -i "s|git@github.com:|https://x-access-token:${TOKEN}@github.com/|g" .repos.https && \ + vcs import src < .repos.https && \ + rm -f .repos.https; \ else \ mkdir -p $HOME/.ssh && cp /run/secrets/sshkey $HOME/.ssh/github && \ echo "Host github.com" > $HOME/.ssh/config && \ echo " IdentityFile $HOME/.ssh/github" >> $HOME/.ssh/config && \ echo " StrictHostKeyChecking no" >> $HOME/.ssh/config && \ - git clone -b develop_ros2 git@github.com:haru-project/haru-unity.git && \ - git clone -b jazzy_debian git@github.com:haru-project/ROS-TCP-Endpoint.git && \ - git clone -b ros2 git@github.com:haru-project/strawberry-ros-msgs.git && \ - git clone -b fix_tuple_error git@github.com:haru-project/idmind-tabletop-ros-pkg.git; \ + vcs import src < .repos; \ fi # Download Unity App From 20c90ddfa4447a2b2cf37e8a6a03da7e4b46264b Mon Sep 17 00:00:00 2001 From: MAEDA Chikara Antoine Date: Thu, 14 Aug 2025 11:04:32 +0900 Subject: [PATCH 33/40] fix: typo in Dockerfile --- haru-simulator/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 42bde56..2edfd1d 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -10,7 +10,7 @@ RUN apt update && apt install -y \ && rm -rf /var/lib/apt/lists/* # Clone ROS dependencies -WORKDIR /ros2_ws/src +WORKDIR /ros2_ws COPY .repos .repos From 4c03e3ea7265fcb5939ae0f8d6ca731e5fc3f068 Mon Sep 17 00:00:00 2001 From: MAEDA Chikara Antoine Date: Thu, 14 Aug 2025 11:06:46 +0900 Subject: [PATCH 34/40] fix: typo in Dockerfile --- haru-simulator/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 2edfd1d..8a3a3c7 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -10,7 +10,7 @@ RUN apt update && apt install -y \ && rm -rf /var/lib/apt/lists/* # Clone ROS dependencies -WORKDIR /ros2_ws +WORKDIR /ros2_ws/src COPY .repos .repos @@ -23,14 +23,14 @@ RUN --mount=type=secret,id=sshkey,required=false \ TOKEN=$(cat /run/secrets/github_token) && \ cp .repos .repos.https && \ sed -i "s|git@github.com:|https://x-access-token:${TOKEN}@github.com/|g" .repos.https && \ - vcs import src < .repos.https && \ + vcs import . < .repos.https && \ rm -f .repos.https; \ else \ mkdir -p $HOME/.ssh && cp /run/secrets/sshkey $HOME/.ssh/github && \ echo "Host github.com" > $HOME/.ssh/config && \ echo " IdentityFile $HOME/.ssh/github" >> $HOME/.ssh/config && \ echo " StrictHostKeyChecking no" >> $HOME/.ssh/config && \ - vcs import src < .repos; \ + vcs import . < .repos; \ fi # Download Unity App From b93b6d17f846c8081076dcddd140c5f065c19aba Mon Sep 17 00:00:00 2001 From: MAEDA Chikara Antoine Date: Thu, 14 Aug 2025 11:34:47 +0900 Subject: [PATCH 35/40] update: use CD_TOKEN to write private repos --- .github/workflows/docker-publish.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index d8809f5..69ec99e 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -29,7 +29,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.CD_TOKEN }} - name: Set variables id: vars @@ -64,7 +64,7 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:${{ steps.vars.outputs.tag }} secrets: | - github_token=${{ secrets.CI_TOKEN }} + github_token=${{ secrets.CD_TOKEN }} - name: Tag and push :latest if master if: steps.vars.outputs.ref_type == 'branch' && steps.vars.outputs.tag == 'master' @@ -79,4 +79,4 @@ jobs: CLONE_METHOD=https BASE_IMAGE=ghcr.io/haru-project/haru-os:latest secrets: | - github_token=${{ secrets.CI_TOKEN }} + github_token=${{ secrets.CD_TOKEN }} From dbbbf91f90cc455eab817016a7d3c2e69dd0e730 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Thu, 14 Aug 2025 13:11:15 +0900 Subject: [PATCH 36/40] update: change action runner to self-hosted --- .github/workflows/docker-publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 69ec99e..1c4d4f0 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -10,7 +10,7 @@ on: jobs: build-and-push: name: Build and Push to GHCR - runs-on: ubuntu-latest + runs-on: self-hosted permissions: contents: read From 041b29569bd0066c1fa3b76200c6104d07306d63 Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Thu, 14 Aug 2025 16:05:32 +0900 Subject: [PATCH 37/40] update: revert to ubuntu-latest runner --- .github/workflows/docker-publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 1c4d4f0..69ec99e 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -10,7 +10,7 @@ on: jobs: build-and-push: name: Build and Push to GHCR - runs-on: self-hosted + runs-on: ubuntu-latest permissions: contents: read From dba3281ba50cfcd9ec88bb6c0c56cbf62df983dd Mon Sep 17 00:00:00 2001 From: Chikara Maeda Date: Fri, 15 Aug 2025 15:18:04 +0900 Subject: [PATCH 38/40] typo: fix docker env --- .env.example | 2 +- docker-compose-haru.yaml | 20 ++++---------------- docker-compose-simulator.yaml | 12 ++---------- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/.env.example b/.env.example index 9b19111..683affa 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ # DISPLAY -DISPLAY=:0 +DISPLAY=${DISPLAY:-=:0} # CUDA NVIDIA_VISIBLE_DEVICES=all diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index a01416d..0dc5159 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -5,10 +5,7 @@ services: pub-root: image: haru/haru-os:ros2 command: ["ros2", "topic", "pub", "/root", "std_msgs/msg/String", "{data: Hello from root}"] - environment: - - DISPLAY=${DISPLAY:-=:0} - - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} + env_file: .env.example network_mode: host ipc: host volumes: @@ -18,10 +15,7 @@ services: image: haru/haru-os:ros2 user: 1000:1000 command: ["ros2", "topic", "pub", "/user", "std_msgs/msg/String", "{data: Hello from user}"] - environment: - - DISPLAY=${DISPLAY:-=:0} - - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} + env_file: .env.example network_mode: host ipc: host volumes: @@ -30,10 +24,7 @@ services: echo-root: image: haru/haru-os:ros2 command: ["ros2", "topic", "echo", "/user"] - environment: - - DISPLAY=${DISPLAY:-=:0} - - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} + env_file: .env.example network_mode: host ipc: host volumes: @@ -44,10 +35,7 @@ services: image: haru/haru-os:ros2 user: 1000:1000 command: ["ros2", "topic", "echo", "/root"] - environment: - - DISPLAY=${DISPLAY:-=:0} - - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} + env_file: .env.example network_mode: host ipc: host volumes: diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index 83e2f54..407fb72 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -5,12 +5,7 @@ services: unity-app: image: haru/haru-simulator:ros2 command: ["/bin/bash", "-c", "export ROS_IP=$(hostname -I | awk '{print $1}') && ros2 launch haru_unity unity_app_launcher_launch.py && tail -f /dev/null"] - environment: - - DISPLAY=${DISPLAY:-:0} - - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} - - NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES} - - NVIDIA_DRIVER_CAPABILITIES=${NVIDIA_DRIVER_CAPABILITIES} + env_file: .env.example network_mode: host ipc: host devices: @@ -30,10 +25,7 @@ services: --server.headless true \ --server.baseUrlPath '/haru_web'", ] - environment: - - DISPLAY=${DISPLAY:-:0} - - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} - - RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp} + env_file: .env.example network_mode: host ipc: host volumes: From 5cde479c8d7113e016abfd997054ac2fccc5c3d3 Mon Sep 17 00:00:00 2001 From: MAEDA Chikara Antoine Date: Tue, 16 Sep 2025 20:01:46 +0900 Subject: [PATCH 39/40] typo: change docker tag from ros2 to local --- README.md | 10 +-- docker-compose-haru.yaml | 8 +- docker-compose-simulator.yaml | 4 +- docker-compose-virtual.yaml | 142 ---------------------------------- haru-simulator/Dockerfile | 16 ++-- 5 files changed, 16 insertions(+), 164 deletions(-) delete mode 100644 docker-compose-virtual.yaml diff --git a/README.md b/README.md index 6eb174a..5d30742 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ xhost +local:docker ### Haru-OS ``` -docker build --rm -t haru/haru-os:ros2 -f haru-os/Dockerfile ./haru-os +docker build --rm -t haru/haru-os:local -f haru-os/Dockerfile ./haru-os ``` Run: @@ -25,7 +25,7 @@ docker run -it --rm --name haru-os \ --network host --ipc host --gpus all \ --env-file .env.example \ -v /tmp/.X11-unix:/tmp/.X11-unix \ - haru/haru-os:ros2 + haru/haru-os:local ``` Or compose: (recommended) @@ -35,8 +35,8 @@ docker compose -f docker-compose-haru.yaml --env-file .env.example up ### Haru-Simulator ``` -docker build --rm --secret id=sshkey,src=$HOME/.ssh/id_ed25519 -t haru/haru-simulator:ros2 -f haru-simulator/Dockerfile ./haru-simulator -# or docker build --rm --secret id=github_token,src=$GITHUB_TOKEN -t haru/haru-simulator:ros2 -f haru-simulator/Dockerfile ./haru-simulator +docker build --rm --ssh default=$SSH_AUTH_SOCK -t haru/haru-simulator:local -f haru-simulator/Dockerfile ./haru-simulator +# or docker build --rm --secret id=github_token,src=$GITHUB_TOKEN -t haru/haru-simulator:local -f haru-simulator/Dockerfile ./haru-simulator ``` Run: @@ -46,7 +46,7 @@ docker run -it --rm --name haru-os \ --env-file .env.example \ --device /dev/snd \ -v /tmp/.X11-unix:/tmp/.X11-unix \ - haru/haru-simulator:ros2 + haru/haru-simulator:local ``` Or compose: (recommended) diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index 0dc5159..f735c72 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -3,7 +3,7 @@ name: haru-os services: pub-root: - image: haru/haru-os:ros2 + image: haru/haru-os:local command: ["ros2", "topic", "pub", "/root", "std_msgs/msg/String", "{data: Hello from root}"] env_file: .env.example network_mode: host @@ -12,7 +12,7 @@ services: - /tmp/.X11-unix:/tmp/.X11-unix pub-user: - image: haru/haru-os:ros2 + image: haru/haru-os:local user: 1000:1000 command: ["ros2", "topic", "pub", "/user", "std_msgs/msg/String", "{data: Hello from user}"] env_file: .env.example @@ -22,7 +22,7 @@ services: - /tmp/.X11-unix:/tmp/.X11-unix echo-root: - image: haru/haru-os:ros2 + image: haru/haru-os:local command: ["ros2", "topic", "echo", "/user"] env_file: .env.example network_mode: host @@ -32,7 +32,7 @@ services: restart: on-failure echo-user: - image: haru/haru-os:ros2 + image: haru/haru-os:local user: 1000:1000 command: ["ros2", "topic", "echo", "/root"] env_file: .env.example diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index 407fb72..ec31a2c 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -3,7 +3,7 @@ name: haru-simulator services: unity-app: - image: haru/haru-simulator:ros2 + image: haru/haru-simulator:local command: ["/bin/bash", "-c", "export ROS_IP=$(hostname -I | awk '{print $1}') && ros2 launch haru_unity unity_app_launcher_launch.py && tail -f /dev/null"] env_file: .env.example network_mode: host @@ -16,7 +16,7 @@ services: runtime: nvidia haru-web-server: - image: haru/haru-simulator:ros2 + image: haru/haru-simulator:local working_dir: /app/haru2_web/ command: ["/bin/bash", "-c", "cd haru_web && \ ../.venv/bin/python3 -m streamlit run Home.py \ diff --git a/docker-compose-virtual.yaml b/docker-compose-virtual.yaml deleted file mode 100644 index 4e239f1..0000000 --- a/docker-compose-virtual.yaml +++ /dev/null @@ -1,142 +0,0 @@ -name: virtual - -networks: - rosnet: - name: rosnet - driver: bridge - -services: - ros-master: - image: haru-os - entrypoint: ["/entrypoint.sh"] - command: ["roscore"] - environment: - - DISPLAY=${DISPLAY} - - ROS_MASTER_URI=http://ros-master:11311 - - ROS_HOSTNAME=ros-master - networks: - - rosnet - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - healthcheck: - test: ["CMD-SHELL", "rosnode list || exit 0"] - interval: 5s - retries: 5 - start_period: 10s - timeout: 5s - - ros-haru-simulator: - image: haru-simulator - entrypoint: ["/entrypoint.sh"] - command: [ "/bin/bash", "-c", "roslaunch haru_unity unity_app_launcher.launch && tail -f /dev/null" ] - environment: - - DISPLAY=${DISPLAY} - - NVIDIA_DRIVER_CAPABILITIES=all - - ROS_MASTER_URI=http://ros-master:11311 - - ROS_HOSTNAME=ros-haru-simulator - network_mode: rosnet - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - runtime: nvidia - depends_on: - ros-master: - condition: service_healthy - - ros-publisher: - image: haru-os - entrypoint: ["/entrypoint.sh"] - command: ["rostopic", "pub", "/hello", "std_msgs/String", "data: 'Hello from Docker'", "-r", "1"] - environment: - - DISPLAY=${DISPLAY} - - ROS_MASTER_URI=http://ros-master:11311 - - ROS_HOSTNAME=ros-publisher - network_mode: rosnet - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - depends_on: - ros-master: - condition: service_healthy - - ros-publisher-cuda: - image: haru-os-cuda - entrypoint: ["/entrypoint.sh"] - command: ["rostopic", "pub", "/hello_cuda", "std_msgs/String", "data: 'Hello from Docker with CUDA'", "-r", "1"] - environment: - - DISPLAY=${DISPLAY} - - ROS_MASTER_URI=http://ros-master:11311 - - ROS_HOSTNAME=ros-publisher-cuda - network_mode: rosnet - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - runtime: nvidia - depends_on: - ros-master: - condition: service_healthy - - ros-listener: - image: haru-os - entrypoint: ["/entrypoint.sh"] - command: ["rostopic", "echo", "/hello"] - environment: - - DISPLAY=${DISPLAY} - - ROS_MASTER_URI=http://ros-master:11311 - - ROS_HOSTNAME=ros-listener - network_mode: rosnet - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - depends_on: - ros-master: - condition: service_healthy - - ros-listener-cuda: - image: haru-os-cuda - entrypoint: ["/entrypoint.sh"] - command: ["rostopic", "echo", "/hello_cuda"] - environment: - - DISPLAY=${DISPLAY} - - ROS_MASTER_URI=http://ros-master:11311 - - ROS_HOSTNAME=ros-listener-cuda - network_mode: rosnet - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - runtime: nvidia - depends_on: - ros-master: - condition: service_healthy - - ros-rqt: - image: haru-os - entrypoint: ["/entrypoint.sh"] - command: ["rqt"] - environment: - - DISPLAY=${DISPLAY} - - ROS_MASTER_URI=http://ros-master:11311 - - ROS_HOSTNAME=ros-rqt - networks: - - rosnet - devices: - - /dev/snd - - /dev/bus/usb - volumes: - - /tmp/.X11-unix:/tmp/.X11-unix - depends_on: - ros-master: - condition: service_healthy diff --git a/haru-simulator/Dockerfile b/haru-simulator/Dockerfile index 8a3a3c7..dc894b9 100644 --- a/haru-simulator/Dockerfile +++ b/haru-simulator/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=haru/haru-os:ros2 +ARG BASE_IMAGE=haru/haru-os:local FROM ${BASE_IMAGE} AS base # Install X11/OpenGL Dependencies @@ -17,7 +17,7 @@ COPY .repos .repos ARG CLONE_METHOD=ssh ENV CLONE_METHOD=${CLONE_METHOD} -RUN --mount=type=secret,id=sshkey,required=false \ +RUN --mount=type=ssh,required=false \ --mount=type=secret,id=github_token,required=false \ if [ "$CLONE_METHOD" = "https" ]; then \ TOKEN=$(cat /run/secrets/github_token) && \ @@ -26,10 +26,7 @@ RUN --mount=type=secret,id=sshkey,required=false \ vcs import . < .repos.https && \ rm -f .repos.https; \ else \ - mkdir -p $HOME/.ssh && cp /run/secrets/sshkey $HOME/.ssh/github && \ - echo "Host github.com" > $HOME/.ssh/config && \ - echo " IdentityFile $HOME/.ssh/github" >> $HOME/.ssh/config && \ - echo " StrictHostKeyChecking no" >> $HOME/.ssh/config && \ + ssh-keyscan github.com >> ~/.ssh/known_hosts && \ vcs import . < .repos; \ fi @@ -58,16 +55,13 @@ RUN apt update && apt install -y \ python3-noble-pyharu \ && rm -rf /var/lib/apt/lists/* -RUN --mount=type=secret,id=sshkey,required=false \ +RUN --mount=type=ssh,required=false \ --mount=type=secret,id=github_token,required=false \ if [ "$CLONE_METHOD" = "https" ]; then \ TOKEN=$(cat /run/secrets/github_token) && \ git clone -b develop https://x-access-token:${TOKEN}@github.com/haru-project/haru2_web.git; \ else \ - mkdir -p $HOME/.ssh && cp /run/secrets/sshkey $HOME/.ssh/github && \ - echo "Host github.com" > $HOME/.ssh/config && \ - echo " IdentityFile $HOME/.ssh/github" >> $HOME/.ssh/config && \ - echo " StrictHostKeyChecking no" >> $HOME/.ssh/config && \ + ssh-keyscan github.com >> ~/.ssh/known_hosts && \ git clone -b develop git@github.com:haru-project/haru2_web.git; \ fi From 6d91fa6af9322c6eb47ccaf41be44e2625b59564 Mon Sep 17 00:00:00 2001 From: MAEDA Chikara Antoine Date: Wed, 17 Sep 2025 15:17:31 +0900 Subject: [PATCH 40/40] add: control audio device from env variable --- .env.example | 3 +++ docker-compose-haru.yaml | 1 - docker-compose-simulator.yaml | 12 ++++++++---- haru-os/entrypoint.sh | 8 ++++++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 683affa..0b2ae19 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,9 @@ # DISPLAY DISPLAY=${DISPLAY:-=:0} +# AUDIO +AUDIO_CARD=0 + # CUDA NVIDIA_VISIBLE_DEVICES=all NVIDIA_DRIVER_CAPABILITIES=all diff --git a/docker-compose-haru.yaml b/docker-compose-haru.yaml index f735c72..c1614fd 100644 --- a/docker-compose-haru.yaml +++ b/docker-compose-haru.yaml @@ -1,7 +1,6 @@ name: haru-os services: - pub-root: image: haru/haru-os:local command: ["ros2", "topic", "pub", "/root", "std_msgs/msg/String", "{data: Hello from root}"] diff --git a/docker-compose-simulator.yaml b/docker-compose-simulator.yaml index ec31a2c..546e678 100644 --- a/docker-compose-simulator.yaml +++ b/docker-compose-simulator.yaml @@ -1,7 +1,6 @@ name: haru-simulator services: - unity-app: image: haru/haru-simulator:local command: ["/bin/bash", "-c", "export ROS_IP=$(hostname -I | awk '{print $1}') && ros2 launch haru_unity unity_app_launcher_launch.py && tail -f /dev/null"] @@ -10,12 +9,17 @@ services: ipc: host devices: - /dev/snd - - /dev/bus/usb volumes: - /tmp/.X11-unix:/tmp/.X11-unix - runtime: nvidia + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] - haru-web-server: + web-server: image: haru/haru-simulator:local working_dir: /app/haru2_web/ command: ["/bin/bash", "-c", "cd haru_web && \ diff --git a/haru-os/entrypoint.sh b/haru-os/entrypoint.sh index 1e0a571..8d12d69 100644 --- a/haru-os/entrypoint.sh +++ b/haru-os/entrypoint.sh @@ -11,6 +11,14 @@ if [ "$(stat -c %u "$XDG_RUNTIME_DIR")" -eq "$(id -u)" ]; then fi export QT_X11_NO_MITSHM=${QT_X11_NO_MITSHM:=1} +# Set AUDIO +export AUDIO_CARD=${AUDIO_CARD:=0} +echo "[INFO] Using AUDIO_CARD (default ALSA card): $AUDIO_CARD" +cat > /etc/asound.conf <