diff --git a/README.md b/README.md
index 91403e2..06fd2ac 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ Snap for OAK-x cameras customized for Husarion robots
| `husarion-depthai.start` | Start the `husarion-depthai.daemon` service |
| `husarion-depthai.stop` | Stop the `husarion-depthai.daemon` service |
| `husarion-depthai` | Start the application in the foreground (run in the current terminal). Remember to stop the daemon first |
+| `husarion-depthai.image-view` | Preview the image from the camera |
## Setup FFMPEG
diff --git a/demo/rviz.launch.py b/demo/rviz.launch.py
index c1f202b..e00ea00 100644
--- a/demo/rviz.launch.py
+++ b/demo/rviz.launch.py
@@ -27,8 +27,8 @@ def launch_setup(context, *args, **kwargs):
name="republish",
arguments=[
"ffmpeg",
- # "in/ffmpeg:=camera/color/image_raw/ffmpeg",
- "in/ffmpeg:=abc/xyz/rgb/image_raw/ffmpeg",
+ "in/ffmpeg:=/image_raw/ffmpeg",
+ # "in/ffmpeg:=abc/xyz/rgb/image_raw/ffmpeg",
"raw",
"out:=camera/color/image_uncompressed",
],
diff --git a/snap/hooks/configure b/snap/hooks/configure
index b19934b..a9dc5dc 100755
--- a/snap/hooks/configure
+++ b/snap/hooks/configure
@@ -1,4 +1,4 @@
-#!/bin/sh -e
+#!/bin/bash -e
# The configure hook is called every time one the following actions happen:
# - initial snap installation
@@ -6,29 +6,54 @@
# - whenever the user runs snap set|unset to change a configuration option
# Define a function to log and echo messages
-log_and_echo() {
- local message="$1"
- # Log the message with logger
- logger -t "${SNAP_NAME}" "configure hook: $message"
- # Echo the message to standard error
- echo >&2 "$message"
-}
-
-log() {
- local message="$1"
- # Log the message with logger
- logger -t "${SNAP_NAME}" "configure hook: $message"
-}
+source $SNAP/usr/bin/utils.sh
-$SNAP/usr/bin/configure_hook_ros.sh
+# # Define the top-level key and the list of valid keys
+VALID_DRIVER_KEYS=(
+ "name"
+ "parent-frame"
+ "camera-model"
+ "cam-pos-x"
+ "cam-pos-y"
+ "cam-pos-z"
+ "cam-roll"
+ "cam-pitch"
+ "cam-yaw"
+ "params-file"
+ "ffmpeg-image-transport"
+)
-DEPTHAI_PARAMS_FILE="$(snapctl get driver.params-file)"
+VALID_FFMPEG_IMAGE_TRANSPORT_KEYS=(
+ "encoding"
+ "preset"
+ "tune"
+)
-# Check if the file exists
-if [ ! -f "$DEPTHAI_PARAMS_FILE" ]; then
- log_and_echo "$DEPTHAI_PARAMS_FILE does not exist."
- exit 1
-fi
+# common
+
+validate_keys "driver" VALID_DRIVER_KEYS[@]
+validate_keys "driver.ffmpeg-image-transport" VALID_FFMPEG_IMAGE_TRANSPORT_KEYS[@]
+
+# validate driver.name
+validate_regex "driver.name" '^[a-z_-]{1,10}$' "Possible values are text with a maximum length of 10 characters, containing only lowercase letters (a-z), "-" or '_'."
+
+# validate driver.parent-frame
+validate_regex "driver.parent-frame" '^[a-z_-]{1,40}$' "Possible values are text with a maximum length of 40 characters, containing only lowercase letters (a-z), "-" or '_'."
+
+# validate driver.camera-model
+VALID_CAMERA_MODEL_OPTIONS=("OAK-D" "OAK-D-LITE")
+validate_option "driver.camera-model" VALID_CAMERA_MODEL_OPTIONS[@]
+
+# validate driver.cam-x
+validate_float "driver.cam-pos-x"
+validate_float "driver.cam-pos-y"
+validate_float "driver.cam-pos-z"
+validate_float "driver.cam-roll"
+validate_float "driver.cam-pitch"
+validate_float "driver.cam-yaw"
+
+# validate driver.params-file
+validate_path "driver.params-file"
export LD_LIBRARY_PATH=$SNAP/usr/lib/$(uname -m)-linux-gnu/pulseaudio:$SNAP/usr/lib/$(uname -m)-linux-gnu/blas:$SNAP/usr/lib/$(uname -m)-linux-gnu/lapack:$LD_LIBRARY_PATH
@@ -39,8 +64,8 @@ if [ -n "$FFMPEG_ENCODING" ]; then
# Run ffmpeg -codecs and check if the codec is available
if ! ffmpeg -encoders 2>/dev/null | awk '{print $2}' | grep -q "$FFMPEG_ENCODING"; then
log_and_echo "Error: Codec $FFMPEG_ENCODING is not available:"
- # log_and_echo "Available codecs:"
- # ffmpeg -encoders 2>/dev/null | awk '/^ V/ {print $0}'
+ log_and_echo "Find available codecs here: $SNAP_COMMON/ffmpeg_codecs.txt"
+ ffmpeg -encoders 2>/dev/null | awk '/^ V/ {print $0}' > $SNAP_COMMON/ffmpeg_codecs.txt
exit 1
fi
@@ -60,6 +85,8 @@ else
yq -i './**.ros__parameters.ffmpeg_image_transport = {}' $SNAP_COMMON/ffmpeg_params.yaml
fi
+$SNAP/usr/bin/configure_hook_ros.sh
+
# restart services with new ROS 2 config
for service in daemon; do
if snapctl services ${SNAP_NAME}.${service} | grep -qw active; then
diff --git a/snap/hooks/connect-plug-ros-humble-ros-base b/snap/hooks/connect-plug-ros-humble-ros-base
index fbc9bc6..39d5c18 100755
--- a/snap/hooks/connect-plug-ros-humble-ros-base
+++ b/snap/hooks/connect-plug-ros-humble-ros-base
@@ -1,8 +1,8 @@
-#!/bin/sh -e
+#!/bin/bash -e
# now we can start the service
-# if snapctl services ${SNAP_NAME}.daemon | grep -q inactive; then
-# snapctl start --enable ${SNAP_NAME}.daemon 2>&1 || true
+# if snapctl services ${SNAP_NAME}.${SNAP_NAME} | grep -q inactive; then
+# snapctl start --enable ${SNAP_NAME}.${SNAP_NAME} 2>&1 || true
# fi
logger -t ${SNAP_NAME} "Plug 'ros-humble-ros-base' connected"
\ No newline at end of file
diff --git a/snap/hooks/disconnect-plug-ros-humble-ros-base b/snap/hooks/disconnect-plug-ros-humble-ros-base
index 35cc834..bc2c60d 100755
--- a/snap/hooks/disconnect-plug-ros-humble-ros-base
+++ b/snap/hooks/disconnect-plug-ros-humble-ros-base
@@ -1,4 +1,4 @@
-#!/bin/sh -e
+#!/bin/bash -e
logger -t ${SNAP_NAME} "Plug 'ros-humble-ros-base' disconnected"
snapctl stop --disable ${SNAP_NAME}.daemon 2>&1 || true
diff --git a/snap/hooks/install b/snap/hooks/install
index 9967502..505bfb9 100755
--- a/snap/hooks/install
+++ b/snap/hooks/install
@@ -22,14 +22,13 @@ snapctl set driver.params-file=$SNAP_COMMON/depthai_params.yaml
snapctl set driver.ffmpeg-image-transport.encoding=libx264
snapctl set driver.ffmpeg-image-transport.preset=ultrafast
snapctl set driver.ffmpeg-image-transport.tune=zerolatency
-snapctl set driver.namespace!
if ! snapctl is-connected raw-usb; then
log "Plug 'raw-usb' isn't connected, please run:"
log "sudo snap connect ${SNAP_NAME}:raw-usb"
fi
-# copy joy params
+# copy params
cp -r $SNAP/usr/share/husarion-depthai/config/*.yaml ${SNAP_COMMON}/
# # # copy meshes to shared folder
diff --git a/snap/local/launcher.sh b/snap/local/launcher.sh
index 613b28d..9509c92 100755
--- a/snap/local/launcher.sh
+++ b/snap/local/launcher.sh
@@ -28,8 +28,6 @@ OPTIONS="\
cam-pitch \
cam-yaw \
params-file \
- namespace \
- device-namespace \
"
LAUNCH_OPTIONS=""
diff --git a/snap/local/ros_common/check_daemon_running.sh b/snap/local/local-ros/check_daemon_running.sh
similarity index 53%
rename from snap/local/ros_common/check_daemon_running.sh
rename to snap/local/local-ros/check_daemon_running.sh
index 3f7b8d4..cbe1590 100755
--- a/snap/local/ros_common/check_daemon_running.sh
+++ b/snap/local/local-ros/check_daemon_running.sh
@@ -1,13 +1,7 @@
-#!/usr/bin/bash
+#!/bin/bash -e
# Define a function to log and echo messages
-log_and_echo() {
- local message="$1"
- # Log the message with logger
- logger -t "${SNAP_NAME}" "${SNAP_NAME}.check_daemon_running.sh: $message"
- # Echo the message to standard error
- echo >&2 "$message"
-}
+source $SNAP/usr/bin/utils.sh
if snapctl services ${SNAP_NAME}.daemon | grep -qw active; then
log_and_echo "to run ${SNAP_NAME} snap interactively, you need to stop the daemon first, run:"
diff --git a/snap/local/local-ros/configure_hook_ros.sh b/snap/local/local-ros/configure_hook_ros.sh
new file mode 100755
index 0000000..7483d15
--- /dev/null
+++ b/snap/local/local-ros/configure_hook_ros.sh
@@ -0,0 +1,176 @@
+#!/bin/bash -e
+
+# The configure hook is called every time one the following actions happen:
+# - initial snap installation
+# - snap refresh
+# - whenever the user runs snap set|unset to change a configuration option
+
+# Define a function to log and echo messages
+source $SNAP/usr/bin/utils.sh
+
+# Function to check the type of the provided XML file
+check_xml_profile_type() {
+ local xml_file="$1"
+
+ if [[ ! -f "$xml_file" ]]; then
+ log_and_echo "File '$xml_file' does not exist."
+ return 1
+ fi
+
+ local root_element
+ local namespace
+
+ # Extract the root element
+ root_element=$(yq '. | keys | .[1]' "$xml_file")
+
+ # Extract the namespace based on the root element
+ if [[ "$root_element" == "CycloneDDS" ]]; then
+ namespace=$(yq .CycloneDDS."+@xmlns" "$xml_file")
+ elif [[ "$root_element" == "profiles" ]]; then
+ namespace=$(yq .profiles."+@xmlns" "$xml_file")
+ else
+ namespace="unknown"
+ fi
+
+ # Remove quotes from the extracted values
+ root_element=${root_element//\"/}
+ namespace=${namespace//\"/}
+
+ if [[ "$root_element" == "profiles" ]] && [[ "$namespace" == "http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles" ]]; then
+ echo "rmw_fastrtps_cpp"
+ elif [[ "$root_element" == "CycloneDDS" ]] && [[ "$namespace" == "https://cdds.io/config" ]]; then
+ echo "rmw_cyclonedds_cpp"
+ else
+ exit 1
+ fi
+}
+
+VALID_ROS_KEYS=("localhost-only" "domain-id" "transport" "namespace")
+
+# Call the validation function
+validate_keys "ros" VALID_ROS_KEYS[@]
+
+ROS_LOCALHOST_ONLY="$(snapctl get ros.localhost-only)"
+ROS_DOMAIN_ID="$(snapctl get ros.domain-id)"
+
+# Make sure ROS_LOCALHOST_ONLY is valid
+VALID_ROS_LOCALHOST_ONLY_OPTIONS=(1 0)
+validate_option "ros.localhost-only" VALID_ROS_LOCALHOST_ONLY_OPTIONS[@]
+
+# Make sure ROS_DOMAIN_ID is valid
+# Make sure WEBUI_PORT is valid
+SUPPORTED_RANGE=(0 232)
+# Validate a specific port, for example, webui.port
+validate_number "ros.domain-id" SUPPORTED_RANGE[@]
+
+# Get the ros.transport setting using snapctl
+OPT="ros.transport"
+TRANSPORT_SETTING="$(snapctl get ${OPT})"
+
+# Check if TRANSPORT_SETTING is "builtin"
+if [ "$TRANSPORT_SETTING" == "builtin" ]; then
+ # Change the value to "rmw_fastrtps_cpp"
+ TRANSPORT_SETTING="rmw_fastrtps_cpp"
+fi
+
+# Only exit with status 1 if conditions are not met
+if [ "$TRANSPORT_SETTING" != "rmw_fastrtps_cpp" ] && [ "$TRANSPORT_SETTING" != "rmw_cyclonedds_cpp" ] && [ ! -f "${SNAP_COMMON}/${TRANSPORT_SETTING}.xml" ]; then
+ log_and_echo "'${SNAP_COMMON}/${TRANSPORT_SETTING}.xml' does not exist."
+ exit 1
+fi
+
+if [ "$TRANSPORT_SETTING" = "rmw_fastrtps_cpp" ] || [ "$TRANSPORT_SETTING" = "shm" ]; then
+ if ! snapctl is-connected shm-plug; then
+ log_and_echo "to use 'rmw_fastrtps_cpp' and 'shm' tranport shm-plug need to be connected, please run:"
+ log_and_echo "sudo snap connect ${SNAP_NAME}:shm-plug ${SNAP_NAME}:shm-slot"
+ exit 1
+ fi
+fi
+
+# Make sure ros-humble-ros-base is connected
+ROS_PLUG="ros-humble-ros-base"
+
+if ! snapctl is-connected ${ROS_PLUG}; then
+ log_and_echo "Plug '${ROS_PLUG}' isn't connected. Please run:"
+ log_and_echo "snap connect ${SNAP_NAME}:${ROS_PLUG} ${ROS_PLUG}:${ROS_PLUG}"
+ exit 1
+fi
+
+# Create the ${SNAP_COMMON}/ros.env file and export variables (for bash session running ROS2)
+ROS_ENV_FILE="${SNAP_COMMON}/ros.env"
+
+# Create the ${SNAP_COMMON}/ros.env file and export variables (for bash session running ROS2)
+ROS_SNAP_ARGS="${SNAP_COMMON}/ros_snap_args"
+
+echo "export ROS_DOMAIN_ID=${ROS_DOMAIN_ID}" > "${ROS_ENV_FILE}"
+echo "export ROS_LOCALHOST_ONLY=${ROS_LOCALHOST_ONLY}" >> "${ROS_ENV_FILE}"
+
+NAMESPACE=$(snapctl get ros.namespace)
+
+# Check if the namespace is set and not empty
+if [ -n "$NAMESPACE" ]; then
+ echo "ros.domain-id=${ROS_DOMAIN_ID} ros.localhost-only=${ROS_LOCALHOST_ONLY} ros.transport=${TRANSPORT_SETTING} ros.namespace=${NAMESPACE}" > "${ROS_SNAP_ARGS}"
+ echo "export ROS_NAMESPACE=${NAMESPACE}" >> "${ROS_ENV_FILE}"
+else
+ echo "ros.domain-id=${ROS_DOMAIN_ID} ros.localhost-only=${ROS_LOCALHOST_ONLY} ros.transport=${TRANSPORT_SETTING} ros.namespace!" > "${ROS_SNAP_ARGS}"
+ echo "unset ROS_NAMESPACE" >> "${ROS_ENV_FILE}"
+fi
+
+# Check the ros.transport setting and export the appropriate environment variable
+if [ "$TRANSPORT_SETTING" != "rmw_fastrtps_cpp" ] && [ "$TRANSPORT_SETTING" != "rmw_cyclonedds_cpp" ]; then
+ profile_type=$(check_xml_profile_type "${SNAP_COMMON}/${TRANSPORT_SETTING}.xml")
+ if [[ "$profile_type" == "rmw_fastrtps_cpp" ]]; then
+ echo "unset CYCLONEDDS_URI" >> "${ROS_ENV_FILE}"
+ echo "export RMW_IMPLEMENTATION=${profile_type}" >> "${ROS_ENV_FILE}"
+ echo "export FASTRTPS_DEFAULT_PROFILES_FILE=${SNAP_COMMON}/${TRANSPORT_SETTING}.xml" >> "${ROS_ENV_FILE}"
+ elif [[ "$profile_type" == "rmw_cyclonedds_cpp" ]]; then
+ echo "unset FASTRTPS_DEFAULT_PROFILES_FILE" >> "${ROS_ENV_FILE}"
+ echo "export RMW_IMPLEMENTATION=${profile_type}" >> "${ROS_ENV_FILE}"
+ echo "export CYCLONEDDS_URI=file://${SNAP_COMMON}/${TRANSPORT_SETTING}.xml" >> "${ROS_ENV_FILE}"
+ else
+ log_and_echo "'${TRANSPORT_SETTING}' is not a supported value for '${OPT}'. Possible values are: rmw_fastrtps_cpp, rmw_cyclonedds_cpp, or a valid profile XML file."
+ exit 1
+ fi
+elif [ "$TRANSPORT_SETTING" == "rmw_fastrtps_cpp" ] || [ "$TRANSPORT_SETTING" == "rmw_cyclonedds_cpp" ]; then
+ echo "unset CYCLONEDDS_URI" >> "${ROS_ENV_FILE}"
+ echo "unset FASTRTPS_DEFAULT_PROFILES_FILE" >> "${ROS_ENV_FILE}"
+ echo "export RMW_IMPLEMENTATION=${TRANSPORT_SETTING}" >> "${ROS_ENV_FILE}"
+fi
+
+# Define the path for the manage_ros_env.sh script
+MANAGE_SCRIPT="${SNAP_COMMON}/manage_ros_env.sh"
+
+# Create the manage_ros_env.sh script in ${SNAP_COMMON}
+cat << EOF > "${MANAGE_SCRIPT}"
+#!/bin/bash
+
+ROS_ENV_FILE="${SNAP_COMMON}/ros.env"
+SOURCE_LINE="source \${ROS_ENV_FILE}"
+
+add_source_to_bashrc() {
+ if ! grep -Fxq "\$SOURCE_LINE" ~/.bashrc; then
+ echo "\$SOURCE_LINE" >> ~/.bashrc
+ echo "Added '\$SOURCE_LINE' to ~/.bashrc"
+ else
+ echo "'\$SOURCE_LINE' is already in ~/.bashrc"
+ fi
+}
+
+remove_source_from_bashrc() {
+ sed -i "\|\$SOURCE_LINE|d" ~/.bashrc
+ echo "Removed '\$SOURCE_LINE' from ~/.bashrc"
+}
+
+case "\$1" in
+ remove)
+ remove_source_from_bashrc
+ ;;
+ add|*)
+ add_source_to_bashrc
+ ;;
+esac
+EOF
+
+# Make the manage_ros_env.sh script executable
+chmod +x "${MANAGE_SCRIPT}"
+
diff --git a/snap/local/ros_common/install_hook_ros.sh b/snap/local/local-ros/install_hook_ros.sh
similarity index 70%
rename from snap/local/ros_common/install_hook_ros.sh
rename to snap/local/local-ros/install_hook_ros.sh
index 9d48a98..86848fb 100755
--- a/snap/local/ros_common/install_hook_ros.sh
+++ b/snap/local/local-ros/install_hook_ros.sh
@@ -1,16 +1,12 @@
-#!/bin/sh -e
+#!/bin/bash -e
# Define a function to log messages
-log() {
- local message="$1"
- # Log the message with logger
- logger -t "${SNAP_NAME}" "install hook: $message"
-}
+source $SNAP/usr/bin/utils.sh
-
-snapctl set transport="udp"
-snapctl set ros-localhost-only=0
-snapctl set ros-domain-id=0
+snapctl set ros.transport="udp"
+snapctl set ros.localhost-only=0
+snapctl set ros.domain-id=0
+snapctl set ros.namespace! # unset
if ! snapctl is-connected ros-humble-ros-base; then
log "Plug 'ros-humble-ros-base' isn't connected, please run:"
diff --git a/snap/local/local-ros/ros_setup.sh b/snap/local/local-ros/ros_setup.sh
new file mode 100755
index 0000000..2fffe43
--- /dev/null
+++ b/snap/local/local-ros/ros_setup.sh
@@ -0,0 +1,5 @@
+#!/bin/bash -e
+
+source $SNAP_COMMON/ros.env
+
+exec $@
\ No newline at end of file
diff --git a/snap/local/ros_common/shm.xml b/snap/local/local-ros/shm.xml
similarity index 98%
rename from snap/local/ros_common/shm.xml
rename to snap/local/local-ros/shm.xml
index 1f9e585..4f2910f 100644
--- a/snap/local/ros_common/shm.xml
+++ b/snap/local/local-ros/shm.xml
@@ -15,4 +15,4 @@
false
-
\ No newline at end of file
+
diff --git a/snap/local/local-ros/udp-lo-cyclone.xml b/snap/local/local-ros/udp-lo-cyclone.xml
new file mode 100644
index 0000000..6cdc91c
--- /dev/null
+++ b/snap/local/local-ros/udp-lo-cyclone.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ lo
+ false
+
+
+ auto
+ 30
+
+
+
+
+
+
\ No newline at end of file
diff --git a/snap/local/local-ros/udp-lo.xml b/snap/local/local-ros/udp-lo.xml
new file mode 100644
index 0000000..fd0d0b2
--- /dev/null
+++ b/snap/local/local-ros/udp-lo.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ udp_transport_localhost
+ UDPv4
+
+ 127.0.0.1
+
+
+
+
+
+
+
+ udp_transport_localhost
+
+ false
+
+
+
diff --git a/snap/local/ros_common/udp.xml b/snap/local/local-ros/udp.xml
similarity index 98%
rename from snap/local/ros_common/udp.xml
rename to snap/local/local-ros/udp.xml
index 7ebb6f7..73e3e55 100644
--- a/snap/local/ros_common/udp.xml
+++ b/snap/local/local-ros/udp.xml
@@ -15,4 +15,4 @@
false
-
\ No newline at end of file
+
diff --git a/snap/local/local-ros/utils.sh b/snap/local/local-ros/utils.sh
new file mode 100755
index 0000000..eafb4f4
--- /dev/null
+++ b/snap/local/local-ros/utils.sh
@@ -0,0 +1,237 @@
+#!/bin/bash -e
+
+# Define a function to log and echo messages
+log_and_echo() {
+ local message="$1"
+ local script_name=$(basename "$0")
+ # Log the message with logger
+ logger -t "${SNAP_NAME}" "${script_name}: $message"
+ # Echo the message to standard error
+ echo -e >&2 "$message"
+}
+
+log() {
+ local message="$1"
+ local script_name=$(basename "$0")
+ # Log the message with logger
+ logger -t "${SNAP_NAME}" "${script_name}: $message"
+}
+
+is_integer() {
+ expr "$1" : '-\?[0-9][0-9]*$' >/dev/null 2>&1
+}
+
+# Function to validate the option values
+validate_option() {
+ local OPT=$1
+ local VALID_OPTIONS=("${!2}")
+
+ VALUE="$(snapctl get ${OPT})"
+
+ # Create an associative array to check valid options
+ declare -A valid_options_map
+ for option in "${VALID_OPTIONS[@]}"; do
+ valid_options_map["$option"]=1
+ done
+
+ # Join the valid options with newlines
+ JOINED_OPTIONS=$(printf "%s\n" "${VALID_OPTIONS[@]}")
+
+ if [ -n "${VALUE}" ]; then
+ if [[ -z "${valid_options_map[$VALUE]}" ]]; then
+ log_and_echo "'${VALUE}' is not a supported value for '${OPT}' parameter. Possible values are:\n${JOINED_OPTIONS}"
+ exit 1
+ fi
+ fi
+}
+
+# Function to validate configuration keys
+validate_keys() {
+ local top_level_key=$1
+ local valid_keys=("${!2}")
+
+ # Get the current configuration keys
+ local config_keys=$(snapctl get "$top_level_key" | yq '. | keys' | sed 's/- //g' | tr -d '"')
+
+ # Create an associative array to check valid keys
+ declare -A valid_keys_map
+ for key in "${valid_keys[@]}"; do
+ valid_keys_map["$key"]=1
+ done
+
+ # Join the valid options with newlines
+ JOINED_OPTIONS=$(printf "%s\n" "${valid_keys[@]}")
+
+ # Iterate over the keys in the configuration
+ for key in $config_keys; do
+ # Check if the key is in the list of valid keys
+ if [[ -z "${valid_keys_map[$key]}" ]]; then
+ log_and_echo "'${key}' is not a supported value for '${top_level_key}' key. Possible values are:\n${JOINED_OPTIONS}"
+ exit 1
+ fi
+ done
+}
+
+validate_number() {
+ local value_key=$1
+ local range=("${!2}")
+ local excluded_values=("${!3:-}")
+
+ # Get the value using snapctl
+ local value=$(snapctl get "$value_key")
+
+ # Extract the min and max range values
+ local min_value=${range[0]}
+ local max_value=${range[1]}
+
+ # Join the excluded values with newlines if they exist
+ local joined_excluded_values
+ local exclude_message
+ if [ -n "$excluded_values" ]; then
+ joined_excluded_values=$(printf "%s\n" "${excluded_values[@]}")
+ exclude_message=" excluding:\n${joined_excluded_values[*]}"
+ else
+ exclude_message=""
+ fi
+
+ # Check if the value is an integer
+ if ! [[ "$value" =~ ^[0-9]+$ ]]; then
+ log_and_echo "'${value}' is not a supported value for '${value_key}'. Possible values are integers between ${min_value} and ${max_value}.${exclude_message}"
+ exit 1
+ fi
+
+ # Check if the value is in the valid range
+ if [ "$value" -lt "$min_value" ] || [ "$value" -gt "$max_value" ]; then
+ log_and_echo "'${value}' is not a supported value for '${value_key}'. Possible values are integers between ${min_value} and ${max_value}.${exclude_message}"
+ exit 1
+ fi
+
+ # Check if the value is in the excluded list
+ if [ -n "$excluded_values" ]; then
+ for excluded_value in "${excluded_values[@]}"; do
+ if [ "$value" -eq "$excluded_value" ]; then
+ log_and_echo "'${value}' is not a supported value for '${value_key}'. Possible values are integers between ${min_value} and ${max_value}.${exclude_message}"
+ exit 1
+ fi
+ done
+ fi
+}
+
+validate_float() {
+ local value_key=$1
+
+ # Get the value using snapctl
+ local value=$(snapctl get "$value_key")
+
+ # Check if the value is a floating-point number
+ if ! [[ "$value" =~ ^-?[0-9]*\.?[0-9]+$ ]]; then
+ log_and_echo "'${value}' is not a supported value for '${value_key}'. Possible values are floating-point numbers."
+ exit 1
+ fi
+}
+
+validate_regex() {
+ local value_key=$1
+ local regex=$2
+ local error_message=$3
+
+ # Get the value using snapctl
+ local value=$(snapctl get "$value_key")
+
+ # Check if the value matches the regex
+ if ! [[ "$value" =~ $regex ]]; then
+ log_and_echo "'${value}' is not a supported value for '${value_key}'. ${error_message}"
+ exit 1
+ fi
+}
+
+validate_path() {
+ local value_key=$1
+
+ # Get the value using snapctl
+ local config_path=$(snapctl get "$value_key")
+
+ # Check if the path is a valid file
+ if [ ! -f "$config_path" ]; then
+ log_and_echo "The path specified in '$value_key' does not exist: '$config_path'."
+ exit 1
+ fi
+}
+
+validate_ipv4_addr() {
+ local value_key=$1
+
+ # Get the value using snapctl
+ local ip_address=$(snapctl get "$value_key")
+ local ip_address_regex='^(([0-9]{1,3}\.){3}[0-9]{1,3})$'
+
+ if [[ "$ip_address" =~ $ip_address_regex ]]; then
+ # Split the IP address into its parts
+ IFS='.' read -r -a octets <<< "$ip_address"
+
+ # Check each octet
+ for octet in "${octets[@]}"; do
+ if ((octet < 0 || octet > 255)); then
+ log_and_echo "Invalid format for '$value_key'. Each part of the IPv4 address must be between 0 and 255. Received: '$ip_address'."
+ exit 1
+ fi
+ done
+ else
+ log_and_echo "Invalid format for '$value_key'. Expected format: a valid IPv4 address. Received: '$ip_address'."
+ exit 1
+ fi
+}
+
+# Universal function to validate serial ports
+validate_serial_port() {
+ local port_key=$1
+
+ # Get the port value using snapctl
+ local port_value=$(snapctl get "$port_key")
+
+ # Check if the value is "auto" or a valid serial port
+ if [ "$port_value" != "auto" ] && [ ! -e "$port_value" ]; then
+ log_and_echo "'${port_value}' is not a valid value for '${port_key}'. It must be 'auto' or a valid serial port in the /dev/ directory."
+ exit 1
+ fi
+}
+
+# Function to find the ttyUSB* device for the specified USB Vendor and Product ID
+find_ttyUSB() {
+ # Extract port parameter, vendor, and product ID
+ PORT_PARAM="$1"
+ VENDOR_ID="$2"
+ PRODUCT_ID="$3"
+
+ # Get the serial-port value using snapctl
+ SERIAL_PORT=$(snapctl get "$PORT_PARAM")
+
+ if [ "$SERIAL_PORT" == "auto" ]; then
+ for device in /sys/bus/usb/devices/*; do
+ if [ -f "$device/idVendor" ]; then
+ current_vendor_id=$(cat "$device/idVendor")
+ if [ "$current_vendor_id" == "$VENDOR_ID" ]; then
+ if [ -z "$PRODUCT_ID" ] || ([ -f "$device/idProduct" ] && [ "$(cat "$device/idProduct")" == "$PRODUCT_ID" ]); then
+ # Look for ttyUSB device in the subdirectories
+ for subdir in "$device/"*; do
+ if [ -d "$subdir" ]; then
+ for tty in $(find "$subdir" -name "ttyUSB*" -print 2>/dev/null); do
+ if [ -e "$tty" ]; then
+ ttydev=$(basename "$tty")
+ echo "/dev/$ttydev"
+ return 0
+ fi
+ done
+ fi
+ done
+ fi
+ fi
+ fi
+ done
+ echo "Error: Device with ID $VENDOR_ID:${PRODUCT_ID:-*} not found."
+ return 1
+ else
+ echo "$SERIAL_PORT"
+ return 0
+ fi
+}
\ No newline at end of file
diff --git a/snap/local/ros_common/configure_hook_ros.sh b/snap/local/ros_common/configure_hook_ros.sh
deleted file mode 100755
index 15b60ee..0000000
--- a/snap/local/ros_common/configure_hook_ros.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/sh -e
-
-# The configure hook is called every time one the following actions happen:
-# - initial snap installation
-# - snap refresh
-# - whenever the user runs snap set|unset to change a configuration option
-
-# Define a function to log and echo messages
-log_and_echo() {
- local message="$1"
- # Log the message with logger
- logger -t "${SNAP_NAME}" "configure hook: $message"
- # Echo the message to standard error
- echo >&2 "$message"
-}
-
-log() {
- local message="$1"
- # Log the message with logger
- logger -t "${SNAP_NAME}" "configure hook: $message"
-}
-
-# Make sure ROS_LOCALHOST_ONLY is valid
-OPT="ros-localhost-only"
-ROS_LOCALHOST_ONLY="$(snapctl get ${OPT})"
-if [ -n "${ROS_LOCALHOST_ONLY}" ]; then
- case "${ROS_LOCALHOST_ONLY}" in
- 1) ;;
- 0) ;;
- *)
- log_and_echo "'${ROS_LOCALHOST_ONLY}' is not a supported value for '${OPT}'." \
- "Possible values are 0 or 1."
- exit 1
- ;;
- esac
-fi
-
-# Make sure ROS_DOMAIN_ID is valid
-OPT="ros-domain-id"
-ROS_DOMAIN_ID="$(snapctl get ${OPT})"
-
-is_integer() {
- expr "$1" : '-\?[0-9][0-9]*$' >/dev/null 2>&1
-}
-
-if ! is_integer "${ROS_DOMAIN_ID}" || [ "${ROS_DOMAIN_ID}" -lt 0 ] || [ "${ROS_DOMAIN_ID}" -gt 232 ]; then
- log_and_echo "'${ROS_DOMAIN_ID}' is not a supported value for '${OPT}'. Possible values are integers between 0 and 232."
- exit 1
-fi
-
-# Get the transport setting using snapctl
-OPT="transport"
-TRANSPORT_SETTING="$(snapctl get ${OPT})"
-
-# Only exit with status 1 if conditions are not met
-if [ "$TRANSPORT_SETTING" != "builtin" ] && [ ! -f "${SNAP_COMMON}/${TRANSPORT_SETTING}.xml" ]; then
- log_and_echo "'${SNAP_COMMON}/${TRANSPORT_SETTING}.xml' does not exist."
- exit 1
-fi
-
-if [ "$TRANSPORT_SETTING" = "builtin" ] || [ "$TRANSPORT_SETTING" = "shm" ]; then
- if ! snapctl is-connected shm-plug; then
- log_and_echo "to use 'builtin' and 'shm' tranport shm-plug need to be connected, please run:"
- log_and_echo "sudo snap connect ${SNAP_NAME}:shm-plug ${SNAP_NAME}:shm-slot"
- exit 1
- fi
-fi
-
-# # Make sure ros-humble-ros-base is connected
-# ROS_PLUG="ros-humble-ros-base"
-
-# if ! snapctl is-connected ${ROS_PLUG}; then
-# log_and_echo "Plug '${ROS_PLUG}' isn't connected. Please run:"
-# log_and_echo "snap connect ${SNAP_NAME}:${ROS_PLUG} ${ROS_PLUG}:${ROS_PLUG}"
-# exit 1
-# fi
diff --git a/snap/local/ros_common/ros_setup.sh b/snap/local/ros_common/ros_setup.sh
deleted file mode 100755
index ee6d3b5..0000000
--- a/snap/local/ros_common/ros_setup.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/bash -e
-
-log() {
- local message="$1"
- # Log the message with logger
- logger -t "${SNAP_NAME}" "ros_setup: $message"
-}
-
-TRANSPORT="$(snapctl get transport)"
-# watch the log with: "journalctl -t ${SNAP_NAME}"
-log "transport: ${TRANSPORT}"
-
-if [ "$TRANSPORT" = "builtin" ]; then
- log "using builtin transport setting"
-else
- PROFILE_FILE="${SNAP_COMMON}/${TRANSPORT}.xml"
- export FASTRTPS_DEFAULT_PROFILES_FILE=$PROFILE_FILE
- log "using ${PROFILE_FILE} transport setting"
- log "$(cat $FASTRTPS_DEFAULT_PROFILES_FILE)"
-fi
-
-export ROS_LOCALHOST_ONLY="$(snapctl get ros-localhost-only)"
-export ROS_DOMAIN_ID="$(snapctl get ros-domain-id)"
-
-log "ROS_LOCAHOST_ONLY=${ROS_LOCALHOST_ONLY}"
-log "ROS_DOMAIN_ID=${ROS_DOMAIN_ID}"
-
-exec $@
\ No newline at end of file
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 32fb515..c5b6d92 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -5,14 +5,22 @@ summary: OAK-x cameras driver for Husarion robots
icon: snap/gui/icon.png
description: |
The `husarion-depthai` snap contains all the necessary software to bring the OAK-x cameras up.
- It offers the following parameters:
+
+ **Parameters**
- * driver: `{...}`
- * ros-domain-id: `0`
- * ros-localhost-only: `0`
- * transport: `udp`
-
- The `driver` parameter is a dictionary that contains the following keys:
+ The snap provides the following configurable parameters (`param name`: `default value`):
+
+ * `driver`: `{...}`
+ * `ros`: `{...}`
+
+ The `ros` contains the following keys:
+
+ * `ros.domain-id`: `0` - Sets the `ROS_DOMAIN_ID` environment variable for the ROS driver.
+ * `ros.localhost-only`: `0` - Sets the `ROS_LOCALHOST_ONLY` environment variable for the ROS driver.
+ * `ros.transport`: `udp` - Configures DDS transport. Options are `udp`, `shm`, `builtin` (or `rmw_fastrtps_cpp`), `rmw_cyclonedds_cpp`. Corresponding DDS XML files can be found in the `/var/snap/rosbot-xl/common` directory (custom FastDDS setups can also be created here).
+ * `ros.namespace`: `(unset)` - Namespace for all topics and transforms.
+
+ The `driver` contains the following keys:
* name: `oak`
* parent-frame: `oak-d-base-frame`
@@ -27,7 +35,6 @@ description: |
* ffmpeg-image-transport.encoding: `libx264`
* ffmpeg-image-transport.preset: `ultrafast`
* ffmpeg-image-transport.tune: `zerolatency`
- * namespace: `(unset)`
To set the parameters, use the snap set command, e.g.,
@@ -139,70 +146,6 @@ parts:
- libcanberra-gtk3-module
- libglu1-mesa
- # ffmpeg:
- # plugin: colcon
- # source: https://github.com/ros-misc-utilities/ffmpeg_image_transport.git
- # # source-branch: v2.9.0-humble
- # build-packages:
- # - python3-catkin-pkg-modules
- # - python3-vcstool
- # stage-packages:
- # - ros-humble-image-transport
- # - ros-humble-image-transport-plugins
- # - ffmpeg
- # - ros-humble-cv-bridge
- # # needed to run ffmpeg without errors:
- # - libpulse-dev
- # - libblas3
- # - libjpeg-turbo8-dev
- # override-pull: |
- # craftctl default
- # cd $CRAFT_PART_SRC
- # vcs import $CRAFT_PART_SRC < $CRAFT_PART_SRC/ffmpeg_image_transport.repos
-
-
- # husarion-depthai:
- # plugin: colcon
- # source: https://github.com/luxonis/depthai-ros.git
- # source-branch: v2.9.0-humble
- # build-packages:
- # - python3-catkin-pkg-modules
- # - python3-vcstool
- # override-pull: |
- # craftctl default
-
- # cd $CRAFT_PART_SRC
-
- # git clone https://github.com/ros-misc-utilities/ffmpeg_image_transport.git
- # vcs import $CRAFT_PART_SRC < $CRAFT_PART_SRC/ffmpeg_image_transport/ffmpeg_image_transport.repos
-
- # # Set the snap version from the git tag
- # # The grade is set to 'stable' if the latest entry in the git history
- # # is the tag itself, otherwise set to devel
- # version="$(git describe --always --tags| sed -e 's/^v//;s/-/+git/;y/-/./')"
- # [ -n "$(echo $version | grep "+git")" ] && grade=devel || grade=stable
- # craftctl set version="$version"
- # craftctl set grade="$grade"
-
- # build-packages:
- # - python3-vcstool
- # - python3-pip
- # - python3-colcon-common-extensions
- # - python3-rosdep
- # - python3-catkin-pkg
-
- # ffmpeg:
- # plugin: colcon
- # source: https://github.com/ros-misc-utilities/ffmpeg_image_transport.git
- # build-packages:
- # - python3-vcstool
- # - python3-pip
- # - python3-colcon-common-extensions
- # - python3-rosdep
- # override-pull: |
- # craftctl default
-
- # vcs import $CRAFT_PART_SRC < $CRAFT_PART_SRC/ffmpeg_image_transport.repos
yq:
plugin: nil
override-build: |
@@ -222,7 +165,7 @@ parts:
# copy local scripts to the snap usr/bin
local-files-ros:
plugin: dump
- source: snap/local/ros_common/
+ source: snap/local/local-ros/
organize:
'*.sh': usr/bin/
'*.xml': usr/share/husarion-depthai/config/