diff --git a/README.md b/README.md index 812c665..e138d26 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ ## husarion-rplidar-snap +

+ +

+ Snap for SLAMTEC LIDARs customized for Husarion robots. [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/husarion-rplidar) diff --git a/snap/local/local-ros/check_daemon_running.sh b/snap/local/local-ros/check_daemon_running.sh deleted file mode 100755 index cbe1590..0000000 --- a/snap/local/local-ros/check_daemon_running.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -e - -# Define a function to log and echo messages -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:" - log_and_echo "sudo ${SNAP_NAME}.stop" - exit 1 -fi - -exec $@ \ No newline at end of file diff --git a/snap/local/local-ros/configure_hook_ros.sh b/snap/local/local-ros/configure_hook_ros.sh deleted file mode 100755 index 7483d15..0000000 --- a/snap/local/local-ros/configure_hook_ros.sh +++ /dev/null @@ -1,176 +0,0 @@ -#!/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/local-ros/install_hook_ros.sh b/snap/local/local-ros/install_hook_ros.sh deleted file mode 100755 index 86848fb..0000000 --- a/snap/local/local-ros/install_hook_ros.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -e - -# Define a function to log messages -source $SNAP/usr/bin/utils.sh - -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:" - log "sudo snap connect ${SNAP_NAME}:ros-humble-ros-base ros-humble-ros-base:ros-humble-ros-base" -fi - -if ! snapctl is-connected shm-plug; then - log "Plug 'shm-plug' isn't connected, please run:" - log "sudo snap connect ${SNAP_NAME}:shm-plug ${SNAP_NAME}:shm-slot" -fi - -# copy DDS config files to shared folder -cp -r $SNAP/usr/share/${SNAP_NAME}/config/*.xml ${SNAP_COMMON}/ \ No newline at end of file diff --git a/snap/local/local-ros/ros_setup.sh b/snap/local/local-ros/ros_setup.sh deleted file mode 100755 index 2fffe43..0000000 --- a/snap/local/local-ros/ros_setup.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -e - -source $SNAP_COMMON/ros.env - -exec $@ \ No newline at end of file diff --git a/snap/local/local-ros/shm.xml b/snap/local/local-ros/shm.xml deleted file mode 100644 index 4f2910f..0000000 --- a/snap/local/local-ros/shm.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - shm_transport - SHM - - - - - - - shm_transport - - false - - - diff --git a/snap/local/local-ros/udp-lo-cyclone.xml b/snap/local/local-ros/udp-lo-cyclone.xml deleted file mode 100644 index 6cdc91c..0000000 --- a/snap/local/local-ros/udp-lo-cyclone.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - 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 deleted file mode 100644 index fd0d0b2..0000000 --- a/snap/local/local-ros/udp-lo.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - udp_transport_localhost - UDPv4 - -
127.0.0.1
-
-
-
- - - - - udp_transport_localhost - - false - - -
diff --git a/snap/local/local-ros/udp.xml b/snap/local/local-ros/udp.xml deleted file mode 100644 index 73e3e55..0000000 --- a/snap/local/local-ros/udp.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - udp_transport - UDPv4 - - - - - - - udp_transport - - false - - - diff --git a/snap/local/local-ros/utils.sh b/snap/local/local-ros/utils.sh deleted file mode 100755 index eafb4f4..0000000 --- a/snap/local/local-ros/utils.sh +++ /dev/null @@ -1,237 +0,0 @@ -#!/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/start_launcher.sh b/snap/local/start_launcher.sh deleted file mode 100755 index a13d124..0000000 --- a/snap/local/start_launcher.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -e - -source $SNAP/usr/bin/utils.sh - -log "Start ${SNAP_NAME}.daemon service" -snapctl start --enable ${SNAP_NAME}.daemon 2>&1 || true - diff --git a/snap/local/stop_launcher.sh b/snap/local/stop_launcher.sh deleted file mode 100755 index 9b40d4e..0000000 --- a/snap/local/stop_launcher.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -e - -source $SNAP/usr/bin/utils.sh - -log "Stop ${SNAP_NAME}.daemon service" -snapctl stop --disable ${SNAP_NAME}.daemon 2>&1 || true diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 8d970a6..b432343 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -98,36 +98,31 @@ parts: craftctl set version="$version" craftctl set grade="stable" - yq: - plugin: nil - override-build: | - craftctl default - - YQ_VERSION="v4.35.1" - TARGETARCH=$CRAFT_ARCH_BUILD_FOR - curl -L "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${TARGETARCH}" -o $CRAFT_PART_BUILD/yq - override-prime: | - craftctl default - - cp $CRAFT_PART_BUILD/yq $CRAFT_PRIME/usr/bin/yq - chmod +x $CRAFT_PRIME/usr/bin/yq - build-packages: - - curl - - # copy local scripts to the snap usr/bin - local-files-ros: - plugin: dump - source: snap/local/local-ros/ - organize: - '*.sh': usr/bin/ - '*.xml': usr/share/husarion-rplidar/config/ - local-files: plugin: dump source: snap/local/ organize: '*.sh': usr/bin/ '*.py': usr/bin/ - # '*.yaml': usr/share/husarion-rplidar/config/ - # '*.xml': usr/share/husarion-rplidar/config/ - # '*.json': usr/share/husarion-rplidar/config/ \ No newline at end of file + + husarion-snap-common: + plugin: dump + source: https://github.com/husarion/husarion-snap-common + source-branch: "0.3.0" + source-type: git + build-environment: + - YQ_VERSION: "v4.35.1" + build-packages: + - curl + organize: + 'local-ros/*.sh': usr/bin/ + 'local-ros/*.xml': usr/share/husarion-snap-common/config/ + 'local-ros/ros.env': usr/share/husarion-snap-common/config/ + override-build: | + craftctl default + curl -L "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${CRAFT_ARCH_BUILD_FOR}" -o $CRAFT_PART_BUILD/yq + override-prime: | + craftctl default + cp $CRAFT_PART_BUILD/yq $CRAFT_PRIME/usr/bin/yq + chmod +x $CRAFT_PRIME/usr/bin/yq + rm -rf $CRAFT_PRIME/local-ros \ No newline at end of file