From 82571f00f00a1c8e034199f9bfa33ac7b028ba22 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 15:50:23 +0100 Subject: [PATCH 01/11] feat(lib): add shared Scripts API helper for host-side wrappers Introduces lib/scripts-api.sh, a sourced bash library that centralises the poll-and-wait logic for calling the gateway Scripts API. All host-side wrapper scripts across the sensor_diagnostics, turtlebot3, and moveit demos will source this file rather than duplicating the implementation. Provides: execute_script(), list_scripts(), check_gateway(), and dependency checks for curl and jq at source time. Configurable via GATEWAY_URL, POLL_INTERVAL, and MAX_WAIT environment variables. --- lib/scripts-api.sh | 100 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 lib/scripts-api.sh diff --git a/lib/scripts-api.sh b/lib/scripts-api.sh new file mode 100644 index 0000000..8b801cd --- /dev/null +++ b/lib/scripts-api.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# Shared helper for calling gateway Scripts API +# Source this from host-side wrapper scripts: +# SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# source "${SCRIPT_DIR}/../../lib/scripts-api.sh" + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" +POLL_INTERVAL="${POLL_INTERVAL:-1}" +MAX_WAIT="${MAX_WAIT:-120}" + +# Check dependencies +for cmd in curl jq; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "Required tool '$cmd' is not installed." + exit 1 + fi +done + +# Check gateway is reachable +check_gateway() { + if ! curl -sf "${API_BASE}/health" > /dev/null 2>&1; then + echo "Gateway not available at ${GATEWAY_URL}" + exit 1 + fi +} + +# Execute a script via Scripts API and poll until completion +# Usage: execute_script [description] +# entity_type: "components" or "apps" +execute_script() { + local entity_type="$1" + local entity_id="$2" + local script_id="$3" + local description="${4:-$script_id}" + + check_gateway + + echo "Executing ${description} via Scripts API..." + local result + result=$(curl -sf -X POST "${API_BASE}/${entity_type}/${entity_id}/scripts/${script_id}/executions" \ + -H "Content-Type: application/json" \ + -d '{"execution_type": "now"}' 2>&1) + + if [ $? -ne 0 ]; then + echo "Failed to start script execution." + echo "Check that the script '${script_id}' exists:" + echo " curl ${API_BASE}/${entity_type}/${entity_id}/scripts | jq" + exit 1 + fi + + local exec_id + exec_id=$(echo "$result" | jq -r '.id') + if [ -z "$exec_id" ] || [ "$exec_id" = "null" ]; then + echo "Failed to start script execution:" + echo "$result" | jq '.' 2>/dev/null || echo "$result" + exit 1 + fi + + echo "Execution started: $exec_id" + + # Poll until done + local elapsed=0 + while [ $elapsed -lt $MAX_WAIT ]; do + local exec_data + exec_data=$(curl -sf "${API_BASE}/${entity_type}/${entity_id}/scripts/${script_id}/executions/${exec_id}") + local status + status=$(echo "$exec_data" | jq -r '.status') + + case "$status" in + completed) + echo "Done." + return 0 + ;; + failed|terminated) + echo "Script ${status}!" + echo "$exec_data" | jq -r '.error // empty' 2>/dev/null + return 1 + ;; + *) + sleep "$POLL_INTERVAL" + elapsed=$((elapsed + POLL_INTERVAL)) + ;; + esac + done + + echo "Timeout waiting for script execution (${MAX_WAIT}s)" + return 1 +} + +# List available scripts for an entity +# Usage: list_scripts +list_scripts() { + local entity_type="$1" + local entity_id="$2" + + check_gateway + + curl -sf "${API_BASE}/${entity_type}/${entity_id}/scripts" | jq '.items[] | {id, name, description}' 2>/dev/null +} From 87aaed10737706047ec62ad49df7c97ca46ef713 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 15:56:15 +0100 Subject: [PATCH 02/11] feat(sensor_diagnostics): add Scripts API support - 7 container-side scripts in auto-discovery layout (compute-unit entity) - Enable scripts section in medkit_params.yaml - COPY container_scripts in Dockerfile - Rewrite host-side scripts as Scripts API wrappers - New scripts: run-diagnostics, inject-fault-scenario --- demos/sensor_diagnostics/Dockerfile | 4 ++ .../config/medkit_params.yaml | 7 ++ .../compute-unit/inject-drift/metadata.json | 5 ++ .../compute-unit/inject-drift/script.bash | 12 ++++ .../compute-unit/inject-failure/metadata.json | 5 ++ .../compute-unit/inject-failure/script.bash | 12 ++++ .../inject-fault-scenario/metadata.json | 5 ++ .../inject-fault-scenario/script.bash | 27 +++++++ .../compute-unit/inject-nan/metadata.json | 5 ++ .../compute-unit/inject-nan/script.bash | 21 ++++++ .../compute-unit/inject-noise/metadata.json | 5 ++ .../compute-unit/inject-noise/script.bash | 17 +++++ .../compute-unit/restore-normal/metadata.json | 5 ++ .../compute-unit/restore-normal/script.bash | 65 +++++++++++++++++ .../run-diagnostics/metadata.json | 5 ++ .../compute-unit/run-diagnostics/script.bash | 33 +++++++++ demos/sensor_diagnostics/inject-drift.sh | 28 ++------ demos/sensor_diagnostics/inject-failure.sh | 23 ++---- .../inject-fault-scenario.sh | 7 ++ demos/sensor_diagnostics/inject-nan.sh | 42 ++--------- demos/sensor_diagnostics/inject-noise.sh | 36 ++-------- demos/sensor_diagnostics/restore-normal.sh | 72 ++----------------- demos/sensor_diagnostics/run-diagnostics.sh | 7 ++ 23 files changed, 272 insertions(+), 176 deletions(-) create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/metadata.json create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/script.bash create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/metadata.json create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/script.bash create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/metadata.json create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/metadata.json create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/metadata.json create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/metadata.json create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/script.bash create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/metadata.json create mode 100644 demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash create mode 100644 demos/sensor_diagnostics/inject-fault-scenario.sh create mode 100644 demos/sensor_diagnostics/run-diagnostics.sh diff --git a/demos/sensor_diagnostics/Dockerfile b/demos/sensor_diagnostics/Dockerfile index 7f2f9be..c6d2654 100644 --- a/demos/sensor_diagnostics/Dockerfile +++ b/demos/sensor_diagnostics/Dockerfile @@ -48,6 +48,10 @@ COPY src/ ${COLCON_WS}/src/sensor_diagnostics_demo/src/ COPY config/ ${COLCON_WS}/src/sensor_diagnostics_demo/config/ COPY launch/ ${COLCON_WS}/src/sensor_diagnostics_demo/launch/ +# Copy scripts into auto-discovery layout for Scripts API +COPY container_scripts/ /var/lib/ros2_medkit/scripts/ +RUN find /var/lib/ros2_medkit/scripts -name "*.bash" -exec chmod +x {} \; + # Build all packages (skip test dependencies that aren't in ros-base) WORKDIR ${COLCON_WS} RUN bash -c "source /opt/ros/jazzy/setup.bash && \ diff --git a/demos/sensor_diagnostics/config/medkit_params.yaml b/demos/sensor_diagnostics/config/medkit_params.yaml index 744776e..1b83adb 100644 --- a/demos/sensor_diagnostics/config/medkit_params.yaml +++ b/demos/sensor_diagnostics/config/medkit_params.yaml @@ -27,6 +27,13 @@ diagnostics: # Plugin configuration (set by launch file when .so paths are resolved) plugins: [""] + # Scripts configuration (filesystem auto-discovery) + scripts: + scripts_dir: "/var/lib/ros2_medkit/scripts" + allow_uploads: false + max_concurrent_executions: 3 + default_timeout_sec: 60 + # Fault Manager configuration (runs in root namespace) fault_manager: ros__parameters: diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/metadata.json b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/metadata.json new file mode 100644 index 0000000..9a6aaea --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject Drift", + "description": "Inject sensor drift: set drift_rate=0.1 on lidar-sim", + "format": "bash" +} diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/script.bash new file mode 100644 index 0000000..fc37aad --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/script.bash @@ -0,0 +1,12 @@ +#!/bin/bash +# Inject sensor drift: set drift_rate=0.1 on lidar-sim +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/drift_rate" \ + -H "Content-Type: application/json" -d '{"value": 0.1}' + +echo "Drift injected: lidar-sim drift_rate=0.1" +exit 0 diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/metadata.json b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/metadata.json new file mode 100644 index 0000000..599f327 --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject Failure", + "description": "Inject sensor failure: set failure_probability=1.0 on imu-sim", + "format": "bash" +} diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/script.bash new file mode 100644 index 0000000..5a21f7d --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/script.bash @@ -0,0 +1,12 @@ +#!/bin/bash +# Inject sensor failure: set failure_probability=1.0 on imu-sim +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 1.0}' + +echo "Failure injected: imu-sim failure_probability=1.0" +exit 0 diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/metadata.json b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/metadata.json new file mode 100644 index 0000000..6876969 --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject Fault Scenario", + "description": "Inject composite fault scenario: NaN values on lidar, IMU, GPS and black frames on camera simultaneously", + "format": "bash" +} diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash new file mode 100644 index 0000000..c1971c1 --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash @@ -0,0 +1,27 @@ +#!/bin/bash +# Inject composite fault scenario: NaN values on lidar, IMU, GPS and black frames on camera simultaneously +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +echo "Injecting composite fault scenario..." + +curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' +echo "lidar-sim: inject_nan=true" + +curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' +echo "imu-sim: inject_nan=true" + +curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' +echo "gps-sim: inject_nan=true" + +curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ + -H "Content-Type: application/json" -d '{"value": true}' +echo "camera-sim: inject_black_frames=true" + +echo "Composite fault scenario injected on all sensors" +exit 0 diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/metadata.json b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/metadata.json new file mode 100644 index 0000000..9cf4a14 --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject NaN", + "description": "Inject NaN values on lidar-sim, imu-sim, and gps-sim simultaneously", + "format": "bash" +} diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash new file mode 100644 index 0000000..d5afee2 --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash @@ -0,0 +1,21 @@ +#!/bin/bash +# Inject NaN values on lidar-sim, imu-sim, and gps-sim simultaneously +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' +echo "lidar-sim: inject_nan=true" + +curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' +echo "imu-sim: inject_nan=true" + +curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' +echo "gps-sim: inject_nan=true" + +echo "NaN injection enabled on lidar-sim, imu-sim, gps-sim" +exit 0 diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/metadata.json b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/metadata.json new file mode 100644 index 0000000..165b41f --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject Noise", + "description": "Inject high noise: noise_stddev=0.5 on lidar-sim and noise_level=0.3 on camera-sim", + "format": "bash" +} diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash new file mode 100644 index 0000000..5ce739b --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash @@ -0,0 +1,17 @@ +#!/bin/bash +# Inject high noise: noise_stddev=0.5 on lidar-sim and noise_level=0.3 on camera-sim +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ + -H "Content-Type: application/json" -d '{"value": 0.5}' +echo "lidar-sim: noise_stddev=0.5" + +curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ + -H "Content-Type: application/json" -d '{"value": 0.3}' +echo "camera-sim: noise_level=0.3" + +echo "High noise injected on lidar-sim and camera-sim" +exit 0 diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/metadata.json b/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/metadata.json new file mode 100644 index 0000000..a045ef8 --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Restore Normal", + "description": "Reset all sensor parameters to defaults and clear all active faults", + "format": "bash" +} diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/script.bash new file mode 100644 index 0000000..ad419a9 --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/script.bash @@ -0,0 +1,65 @@ +#!/bin/bash +# Reset all sensor parameters to defaults and clear all active faults +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +echo "Restoring all sensors to normal operation..." + +# LiDAR +curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ + -H "Content-Type: application/json" -d '{"value": 0.01}' +curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' +curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": false}' +curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/drift_rate" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' +echo "lidar-sim: restored to defaults" + +# IMU +curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/accel_noise_stddev" \ + -H "Content-Type: application/json" -d '{"value": 0.01}' +curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' +curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": false}' +curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/drift_rate" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' +echo "imu-sim: restored to defaults" + +# GPS +curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/position_noise_stddev" \ + -H "Content-Type: application/json" -d '{"value": 2.0}' +curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' +curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": false}' +curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/drift_rate" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' +echo "gps-sim: restored to defaults" + +# Camera +curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' +curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' +curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ + -H "Content-Type: application/json" -d '{"value": false}' +echo "camera-sim: restored to defaults" + +# Clear all faults +curl -s -X DELETE "${API_BASE}/apps/diagnostic-bridge/faults/LIDAR_SIM" > /dev/null 2>&1 || true +curl -s -X DELETE "${API_BASE}/apps/diagnostic-bridge/faults/CAMERA_SIM" > /dev/null 2>&1 || true +curl -s -X DELETE "${API_BASE}/apps/diagnostic-bridge/faults/IMU_SIM" > /dev/null 2>&1 || true +curl -s -X DELETE "${API_BASE}/apps/diagnostic-bridge/faults/GPS_SIM" > /dev/null 2>&1 || true +curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/SENSOR_TIMEOUT" > /dev/null 2>&1 || true +curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/SENSOR_NAN" > /dev/null 2>&1 || true +curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/SENSOR_OUT_OF_RANGE" > /dev/null 2>&1 || true +curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/RATE_DEGRADED" > /dev/null 2>&1 || true +curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/NO_FIX" > /dev/null 2>&1 || true +echo "All faults cleared" + +echo "All sensors restored to normal operation" +exit 0 diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/metadata.json b/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/metadata.json new file mode 100644 index 0000000..7149962 --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Run Diagnostics", + "description": "Check health of all 4 sensors by querying their data endpoints and reporting active faults", + "format": "bash" +} diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash new file mode 100644 index 0000000..8a5b81b --- /dev/null +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash @@ -0,0 +1,33 @@ +#!/bin/bash +# Check health of all 4 sensors by querying their data endpoints and reporting active faults +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +SENSORS=("lidar-sim/data/scan" "imu-sim/data/imu" "gps-sim/data/fix" "camera-sim/data/image") +ERRORS=0 + +for sensor_path in "${SENSORS[@]}"; do + app=$(echo "$sensor_path" | cut -d/ -f1) + echo "Checking $app..." + RESPONSE=$(curl -sf "${API_BASE}/apps/${sensor_path}" 2>&1) + if [ $? -ne 0 ]; then + echo " FAIL: No response from $app" + ERRORS=$((ERRORS + 1)) + else + echo " OK: $app responding" + fi +done + +# Also check faults +FAULT_COUNT=$(curl -sf "${API_BASE}/faults" | jq '.items | length' 2>/dev/null || echo "?") +echo "Active faults: $FAULT_COUNT" + +if [ $ERRORS -gt 0 ]; then + echo "DIAGNOSTICS FAILED: $ERRORS sensor(s) not responding" + exit 1 +fi + +echo "All sensors healthy" +exit 0 diff --git a/demos/sensor_diagnostics/inject-drift.sh b/demos/sensor_diagnostics/inject-drift.sh index 0cb30d2..013d8ee 100755 --- a/demos/sensor_diagnostics/inject-drift.sh +++ b/demos/sensor_diagnostics/inject-drift.sh @@ -1,25 +1,7 @@ #!/bin/bash -# Inject sensor drift fault - demonstrates LEGACY fault reporting path -# LiDAR drift → DiagnosticArray → /diagnostics → diagnostic-bridge → FaultManager +# Inject sensor drift +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" -API_BASE="${GATEWAY_URL}/api/v1" - -echo "Injecting DRIFT fault (Legacy path: LiDAR → diagnostic-bridge)..." -echo "" - -# LiDAR drift: uses legacy diagnostics path -echo "[LEGACY PATH] Setting LiDAR drift_rate to 0.1 m/s..." -echo " Fault path: lidar-sim → /diagnostics topic → diagnostic-bridge → FaultManager" -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/drift_rate" \ - -H "Content-Type: application/json" \ - -d '{"value": 0.1}' - -echo "" -echo "✓ Drift enabled! LiDAR readings will gradually shift over time." -echo "" -echo "Fault codes expected (auto-generated from diagnostic name):" -echo " - LIDAR_SIM (DRIFTING status, WARN severity)" -echo "" -echo "Watch the drift accumulate with: curl ${GATEWAY_URL}/api/v1/apps/lidar-sim/data/scan | jq '.ranges[:5]'" -echo "Check faults with: curl ${GATEWAY_URL}/api/v1/faults | jq" +execute_script "components" "compute-unit" "inject-drift" "Inject sensor drift" diff --git a/demos/sensor_diagnostics/inject-failure.sh b/demos/sensor_diagnostics/inject-failure.sh index 71ff173..57bd171 100755 --- a/demos/sensor_diagnostics/inject-failure.sh +++ b/demos/sensor_diagnostics/inject-failure.sh @@ -1,20 +1,7 @@ #!/bin/bash -# Inject sensor failure (timeout) fault - demonstrates MODERN fault reporting path -# IMU sensor → anomaly-detector → FaultManager (via ReportFault service) +# Inject sensor failure +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" -API_BASE="${GATEWAY_URL}/api/v1" - -echo "Injecting SENSOR FAILURE fault (Modern path: IMU → anomaly-detector)..." - -# Set high failure probability - IMU will stop publishing -echo "Setting IMU failure_probability to 1.0 (complete failure)..." -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" \ - -d '{"value": 1.0}' - -echo "" -echo "✓ IMU failure injected!" -echo " Fault reporting path: imu-sim → anomaly-detector → /fault_manager/report_fault" -echo " The anomaly detector should report SENSOR_TIMEOUT fault directly to FaultManager." -echo " Check faults with: curl ${API_BASE}/faults | jq" +execute_script "components" "compute-unit" "inject-failure" "Inject sensor failure" diff --git a/demos/sensor_diagnostics/inject-fault-scenario.sh b/demos/sensor_diagnostics/inject-fault-scenario.sh new file mode 100644 index 0000000..9e33e4c --- /dev/null +++ b/demos/sensor_diagnostics/inject-fault-scenario.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Inject composite fault scenario +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" + +execute_script "components" "compute-unit" "inject-fault-scenario" "Inject composite fault scenario" diff --git a/demos/sensor_diagnostics/inject-nan.sh b/demos/sensor_diagnostics/inject-nan.sh index 94f0931..b06d26c 100755 --- a/demos/sensor_diagnostics/inject-nan.sh +++ b/demos/sensor_diagnostics/inject-nan.sh @@ -1,39 +1,7 @@ #!/bin/bash -# Inject NaN values fault - demonstrates BOTH fault reporting paths +# Inject NaN values +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" -API_BASE="${GATEWAY_URL}/api/v1" - -echo "Injecting NaN VALUES fault (demonstrates both fault reporting paths)..." -echo "" - -# LEGACY PATH: LiDAR publishes DiagnosticArray → diagnostic_bridge → FaultManager -echo "[LEGACY PATH] Enabling LiDAR inject_nan..." -echo " Fault path: lidar-sim → /diagnostics topic → diagnostic-bridge → FaultManager" -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" \ - -d '{"value": true}' -echo "" - -# MODERN PATH: IMU/GPS → anomaly-detector → FaultManager (direct service call) -echo "[MODERN PATH] Enabling IMU inject_nan..." -echo " Fault path: imu-sim → anomaly-detector → /fault_manager/report_fault" -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" \ - -d '{"value": true}' -echo "" - -echo "[MODERN PATH] Enabling GPS inject_nan..." -echo " Fault path: gps-sim → anomaly-detector → /fault_manager/report_fault" -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" \ - -d '{"value": true}' - -echo "" -echo "✓ NaN injection enabled on multiple sensors!" -echo "" -echo "Fault codes expected:" -echo " - LIDAR_SIM (from diagnostic-bridge, auto-generated from diagnostic name)" -echo " - SENSOR_NAN (from anomaly-detector)" -echo "" -echo "Check faults with: curl ${API_BASE}/faults | jq" +execute_script "components" "compute-unit" "inject-nan" "Inject NaN values" diff --git a/demos/sensor_diagnostics/inject-noise.sh b/demos/sensor_diagnostics/inject-noise.sh index a4fdfe7..ef1ddda 100755 --- a/demos/sensor_diagnostics/inject-noise.sh +++ b/demos/sensor_diagnostics/inject-noise.sh @@ -1,33 +1,7 @@ #!/bin/bash -# Inject high noise fault - demonstrates LEGACY fault reporting path -# LiDAR/Camera → DiagnosticArray → /diagnostics → diagnostic-bridge → FaultManager +# Inject high noise +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" -API_BASE="${GATEWAY_URL}/api/v1" - -echo "Injecting HIGH NOISE fault (Legacy path: LiDAR/Camera → diagnostic-bridge)..." -echo "" - -# LiDAR: increase noise stddev (uses legacy diagnostics path) -echo "[LEGACY PATH] Setting LiDAR noise_stddev to 0.5 (very noisy)..." -echo " Fault path: lidar-sim → /diagnostics topic → diagnostic-bridge → FaultManager" -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ - -H "Content-Type: application/json" \ - -d '{"value": 0.5}' -echo "" - -# Camera: add noise (uses legacy diagnostics path) -echo "[LEGACY PATH] Setting Camera noise_level to 0.3..." -echo " Fault path: camera-sim → /diagnostics topic → diagnostic-bridge → FaultManager" -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ - -H "Content-Type: application/json" \ - -d '{"value": 0.3}' - -echo "" -echo "✓ High noise injected on LiDAR and Camera!" -echo "" -echo "Fault codes expected (auto-generated from diagnostic names):" -echo " - LIDAR_SIM (HIGH_NOISE status)" -echo " - CAMERA_SIM (HIGH_NOISE status)" -echo "" -echo "Check faults with: curl ${API_BASE}/faults | jq" +execute_script "components" "compute-unit" "inject-noise" "Inject high noise" diff --git a/demos/sensor_diagnostics/restore-normal.sh b/demos/sensor_diagnostics/restore-normal.sh index 8e009fc..7bf413a 100755 --- a/demos/sensor_diagnostics/restore-normal.sh +++ b/demos/sensor_diagnostics/restore-normal.sh @@ -1,69 +1,7 @@ #!/bin/bash -# Restore normal sensor operation (clear all faults) +# Restore normal operation +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" -API_BASE="${GATEWAY_URL}/api/v1" - -echo "Restoring NORMAL operation..." - -# LiDAR -echo "Resetting LiDAR parameters..." -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ - -H "Content-Type: application/json" -d '{"value": 0.01}' -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": false}' -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/drift_rate" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' - -# IMU -echo "Resetting IMU parameters..." -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/accel_noise_stddev" \ - -H "Content-Type: application/json" -d '{"value": 0.01}' -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": false}' -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/drift_rate" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' - -# GPS -echo "Resetting GPS parameters..." -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/position_noise_stddev" \ - -H "Content-Type: application/json" -d '{"value": 2.0}' -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": false}' -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/drift_rate" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' - -# Camera -echo "Resetting Camera parameters..." -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ - -H "Content-Type: application/json" -d '{"value": false}' - -# Clear all faults from FaultManager -# All sensors now publish to /diagnostics, so all faults come through diagnostic-bridge -echo "" -echo "Clearing all faults from FaultManager..." -curl -s -X DELETE "${API_BASE}/apps/diagnostic-bridge/faults/LIDAR_SIM" > /dev/null 2>&1 -curl -s -X DELETE "${API_BASE}/apps/diagnostic-bridge/faults/CAMERA_SIM" > /dev/null 2>&1 -curl -s -X DELETE "${API_BASE}/apps/diagnostic-bridge/faults/IMU_SIM" > /dev/null 2>&1 -curl -s -X DELETE "${API_BASE}/apps/diagnostic-bridge/faults/GPS_SIM" > /dev/null 2>&1 - -# Faults from anomaly-detector (modern path for anomaly detection) -curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/SENSOR_TIMEOUT" > /dev/null 2>&1 -curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/SENSOR_NAN" > /dev/null 2>&1 -curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/SENSOR_OUT_OF_RANGE" > /dev/null 2>&1 -curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/RATE_DEGRADED" > /dev/null 2>&1 -curl -s -X DELETE "${API_BASE}/apps/anomaly-detector/faults/NO_FIX" > /dev/null 2>&1 - -echo "" -echo "✓ Normal operation restored! All fault injections and faults cleared." -echo " Verify with: curl ${API_BASE}/faults | jq" +execute_script "components" "compute-unit" "restore-normal" "Restore normal operation" diff --git a/demos/sensor_diagnostics/run-diagnostics.sh b/demos/sensor_diagnostics/run-diagnostics.sh new file mode 100644 index 0000000..aac9d34 --- /dev/null +++ b/demos/sensor_diagnostics/run-diagnostics.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Run sensor diagnostics +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" + +execute_script "components" "compute-unit" "run-diagnostics" "Run sensor diagnostics" From 511ce6929a435ec52dc3dc42cfe7e8278e0a229e Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 15:58:50 +0100 Subject: [PATCH 03/11] docs(sensor_diagnostics): add Scripts API documentation to README Document the Scripts API endpoint for calling inject/restore scripts via REST, add host prerequisites note for curl/jq, and extend the Scripts table with run-diagnostics.sh and inject-fault-scenario.sh. --- demos/sensor_diagnostics/README.md | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/demos/sensor_diagnostics/README.md b/demos/sensor_diagnostics/README.md index 976ee02..a2fac97 100644 --- a/demos/sensor_diagnostics/README.md +++ b/demos/sensor_diagnostics/README.md @@ -15,6 +15,8 @@ This demo showcases ros2_medkit's data monitoring, configuration management, and ## Quick Start +> **Host prerequisites:** The host-side scripts (`check-demo.sh`, `inject-*.sh`, `restore-normal.sh`) require `curl` and `jq` to be installed on your machine. + ### Using Docker (Recommended) ```bash @@ -139,6 +141,42 @@ Sensor Topics → anomaly_detector monitors | `inject-failure.sh` | IMU | Modern | Complete sensor timeout | | `restore-normal.sh` | All | Both | Clears all faults | +## Scripts API + +The inject and restore scripts run inside the container and are also callable directly via the gateway REST API using the Scripts endpoint. This lets you trigger fault scenarios programmatically without needing the shell scripts on the host. + +### List Available Scripts + +```bash +curl http://localhost:8080/api/v1/components/compute-unit/scripts | jq +``` + +### Execute a Script + +```bash +curl -X POST http://localhost:8080/api/v1/components/compute-unit/scripts/inject-nan/executions \ + -H "Content-Type: application/json" \ + -d '{"execution_type":"now"}' | jq +``` + +### Check Execution Status + +```bash +curl http://localhost:8080/api/v1/components/compute-unit/scripts/inject-nan/executions/{exec_id} | jq +``` + +### Available Scripts + +| Script | Description | +|--------|-------------| +| `run-diagnostics` | Check health of all sensors | +| `inject-fault-scenario` | Composite fault injection (all sensors) | +| `inject-drift` | Inject sensor drift on LiDAR | +| `inject-failure` | Inject sensor failure on IMU | +| `inject-nan` | Inject NaN values on LiDAR, IMU, GPS | +| `inject-noise` | Inject high noise on LiDAR and Camera | +| `restore-normal` | Reset all sensors and clear faults | + ## API Examples ### Read Sensor Data @@ -207,12 +245,16 @@ curl http://localhost:8080/api/v1/faults | jq | `run-demo.sh` | Start Docker services (daemon mode) | | `stop-demo.sh` | Stop Docker services | | `check-demo.sh` | Interactive API demonstration and exploration | +| `run-diagnostics.sh` | Check health of all sensors | +| `inject-fault-scenario.sh` | Composite fault injection across all sensors | | `inject-noise.sh` | Inject high noise fault | | `inject-failure.sh` | Cause sensor timeout | | `inject-nan.sh` | Inject NaN values | | `inject-drift.sh` | Enable sensor drift | | `restore-normal.sh` | Clear all faults | +> **Note:** The inject and restore scripts (`inject-*.sh`, `restore-normal.sh`) are also available via the [Scripts API](#scripts-api) - callable as REST endpoints without requiring the host-side scripts. + ## Sensor Parameters ### LiDAR (`/sensors/lidar_sim`) From 37e3abd0412cb51ad206aa8e4aca1a8118934eec Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 16:03:50 +0100 Subject: [PATCH 04/11] feat(turtlebot3): add Scripts API support - 5 container-side scripts in auto-discovery layout (nav2-stack entity) - Enable scripts section in medkit_params.yaml - Add jq to Dockerfile, COPY container_scripts - Rewrite host-side scripts as Scripts API wrappers - New scripts: nav-health-check, reset-navigation - Update README with Scripts API documentation --- demos/turtlebot3_integration/Dockerfile | 5 ++ demos/turtlebot3_integration/README.md | 40 +++++++++++ .../config/medkit_params.yaml | 7 ++ .../inject-localization-failure/metadata.json | 5 ++ .../inject-localization-failure/script.bash | 36 ++++++++++ .../inject-nav-failure/metadata.json | 5 ++ .../nav2-stack/inject-nav-failure/script.bash | 44 ++++++++++++ .../nav2-stack/nav-health-check/metadata.json | 5 ++ .../nav2-stack/nav-health-check/script.bash | 36 ++++++++++ .../nav2-stack/reset-navigation/metadata.json | 5 ++ .../nav2-stack/reset-navigation/script.bash | 27 ++++++++ .../nav2-stack/restore-normal/metadata.json | 5 ++ .../nav2-stack/restore-normal/script.bash | 38 +++++++++++ .../inject-localization-failure.sh | 54 ++------------- .../inject-nav-failure.sh | 68 ++----------------- .../nav-health-check.sh | 7 ++ .../reset-navigation.sh | 7 ++ .../turtlebot3_integration/restore-normal.sh | 61 ++--------------- 18 files changed, 287 insertions(+), 168 deletions(-) create mode 100644 demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/metadata.json create mode 100755 demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash create mode 100644 demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/metadata.json create mode 100755 demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/script.bash create mode 100644 demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/metadata.json create mode 100755 demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/script.bash create mode 100644 demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/metadata.json create mode 100755 demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash create mode 100644 demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/metadata.json create mode 100755 demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/script.bash create mode 100755 demos/turtlebot3_integration/nav-health-check.sh create mode 100755 demos/turtlebot3_integration/reset-navigation.sh diff --git a/demos/turtlebot3_integration/Dockerfile b/demos/turtlebot3_integration/Dockerfile index 7831984..33e0434 100644 --- a/demos/turtlebot3_integration/Dockerfile +++ b/demos/turtlebot3_integration/Dockerfile @@ -42,6 +42,7 @@ RUN apt-get update && apt-get install -y \ libsqlite3-dev \ git \ curl \ + jq \ && rm -rf /var/lib/apt/lists/* # Clone ros2_medkit from GitHub (gateway + dependencies + plugins) @@ -68,6 +69,10 @@ COPY config/ ${COLCON_WS}/src/turtlebot3_medkit_demo/config/ COPY launch/ ${COLCON_WS}/src/turtlebot3_medkit_demo/launch/ COPY scripts/ ${COLCON_WS}/src/turtlebot3_medkit_demo/scripts/ +# Copy scripts into auto-discovery layout for Scripts API +COPY container_scripts/ /var/lib/ros2_medkit/scripts/ +RUN find /var/lib/ros2_medkit/scripts -name "*.bash" -exec chmod +x {} \; + # Build ros2_medkit and demo package WORKDIR ${COLCON_WS} RUN bash -c "source /opt/ros/jazzy/setup.bash && \ diff --git a/demos/turtlebot3_integration/README.md b/demos/turtlebot3_integration/README.md index 67e52df..9a8dd6a 100644 --- a/demos/turtlebot3_integration/README.md +++ b/demos/turtlebot3_integration/README.md @@ -356,6 +356,42 @@ This demo uses two fault reporting paths: | Nav2 Controller | diagnostic_bridge | Path following errors | | TurtleBot3 | diagnostic_bridge | Motor/sensor issues | +## Scripts API + +The inject and restore scripts run inside the container and are callable via the gateway REST API. This lets you trigger fault scenarios programmatically or from the web UI. + +> **Host prerequisites:** The host-side scripts require `curl` and `jq`. + +### List Available Scripts + +```bash +curl http://localhost:8080/api/v1/components/nav2-stack/scripts | jq +``` + +### Execute a Script + +```bash +curl -X POST http://localhost:8080/api/v1/components/nav2-stack/scripts/inject-nav-failure/executions \ + -H "Content-Type: application/json" \ + -d '{"execution_type":"now"}' | jq +``` + +### Check Execution Status + +```bash +curl http://localhost:8080/api/v1/components/nav2-stack/scripts/inject-nav-failure/executions/{exec_id} | jq +``` + +### Available Scripts + +| Script | Description | +|--------|-------------| +| `nav-health-check` | Check health of Nav2 stack | +| `reset-navigation` | Cancel goals and reset AMCL | +| `inject-localization-failure` | Inject AMCL localization failure | +| `inject-nav-failure` | Inject navigation failure (unreachable goal) | +| `restore-normal` | Reset parameters and clear faults | + ## Fault Injection Scenarios This demo includes scripts to inject various fault conditions for testing fault management. @@ -487,10 +523,14 @@ demos/turtlebot3_integration/ | `send-nav-goal.sh [x] [y] [yaw]` | Send navigation goal via SOVD API | | `check-entities.sh` | Explore SOVD entity hierarchy | | `check-faults.sh` | View active faults from gateway | +| `nav-health-check.sh` | Check Nav2 stack health | +| `reset-navigation.sh` | Cancel goals and reset AMCL | | `inject-nav-failure.sh` | Inject navigation failure (unreachable goal) | | `inject-localization-failure.sh` | Inject localization failure (AMCL reset) | | `restore-normal.sh` | Restore normal operation and clear faults | +> **Note:** The inject, restore, and diagnostic scripts are also available via the [Scripts API](#scripts-api) - callable as REST endpoints without requiring the host-side scripts. + ## Manual Setup (Alternative) If you prefer not to use Docker: diff --git a/demos/turtlebot3_integration/config/medkit_params.yaml b/demos/turtlebot3_integration/config/medkit_params.yaml index 36d2575..9a6f352 100644 --- a/demos/turtlebot3_integration/config/medkit_params.yaml +++ b/demos/turtlebot3_integration/config/medkit_params.yaml @@ -28,6 +28,13 @@ diagnostics: # Plugin configuration (set by launch file when .so paths are resolved) plugins: [""] + # Scripts configuration (filesystem auto-discovery) + scripts: + scripts_dir: "/var/lib/ros2_medkit/scripts" + allow_uploads: false + max_concurrent_executions: 3 + default_timeout_sec: 60 + # Fault Manager configuration (runs in root namespace) fault_manager: ros__parameters: diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/metadata.json b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/metadata.json new file mode 100644 index 0000000..0c8df34 --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject Localization Failure", + "description": "Reinitialize AMCL global localization (scatters particles) then send a navigation goal under high uncertainty", + "format": "bash" +} diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash new file mode 100755 index 0000000..a6e09cb --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash @@ -0,0 +1,36 @@ +#!/bin/bash +# Inject localization failure: reinitialize AMCL then send a navigation goal under high uncertainty +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +echo "Reinitializing AMCL global localization (scatters particles)..." +curl -s -X POST "${API_BASE}/apps/amcl/operations/reinitialize_global_localization/executions" \ + -H "Content-Type: application/json" \ + -d '{}' + +echo "" +echo "Waiting for particles to scatter..." +sleep 2 + +echo "Sending navigation goal with high localization uncertainty..." +curl -s -X POST "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" \ + -H "Content-Type: application/json" \ + -d '{ + "goal": { + "pose": { + "header": {"frame_id": "map"}, + "pose": { + "position": {"x": 2.0, "y": 0.0, "z": 0.0}, + "orientation": {"w": 1.0} + } + } + } + }' + +echo "" +echo "Localization failure injected." +echo "AMCL has been reinitialized - localization uncertainty is high." +echo "Monitor faults at: ${API_BASE}/faults" +exit 0 diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/metadata.json b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/metadata.json new file mode 100644 index 0000000..0045127 --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject Navigation Failure", + "description": "Send a navigation goal to an unreachable location (100, 100) far outside the turtlebot3_world map", + "format": "bash" +} diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/script.bash new file mode 100755 index 0000000..11a7181 --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/script.bash @@ -0,0 +1,44 @@ +#!/bin/bash +# Inject navigation failure: send goal to unreachable location far outside map bounds +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +echo "Sending navigation goal to (100.0, 100.0) - far outside map..." +RESPONSE=$(curl -s -X POST "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" \ + -H "Content-Type: application/json" \ + -d '{ + "goal": { + "pose": { + "header": {"frame_id": "map"}, + "pose": { + "position": {"x": 100.0, "y": 100.0, "z": 0.0}, + "orientation": {"w": 1.0} + } + } + } + }') + +echo "${RESPONSE}" | jq '.' 2>/dev/null || echo "${RESPONSE}" + +EXEC_ID=$(echo "${RESPONSE}" | jq -r '.execution_id // .id // empty' 2>/dev/null) + +if [ -n "${EXEC_ID}" ] && [ "${EXEC_ID}" != "null" ]; then + echo "" + echo "Waiting for navigation to fail (checking status)..." + for _ in $(seq 1 10); do + sleep 2 + STATUS=$(curl -s "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions/${EXEC_ID}" \ + | jq -r '.status' 2>/dev/null) + echo " Status: ${STATUS}" + if [ "${STATUS}" = "failed" ] || [ "${STATUS}" = "completed" ]; then + break + fi + done +fi + +echo "" +echo "Navigation failure injected." +echo "Monitor faults at: ${API_BASE}/faults" +exit 0 diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/metadata.json b/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/metadata.json new file mode 100644 index 0000000..3b209b0 --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Nav2 Health Check", + "description": "Check nav2 stack health: verify amcl, bt-navigator, controller-server, and planner-server are responding", + "format": "bash" +} diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/script.bash new file mode 100755 index 0000000..1cc0e3b --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/script.bash @@ -0,0 +1,36 @@ +#!/bin/bash +# Check nav2 stack health: verify key apps are responding via gateway +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +APPS=("amcl" "bt-navigator" "controller-server" "planner-server") +ERRORS=0 + +echo "Checking nav2 stack health..." +echo "" + +for app in "${APPS[@]}"; do + echo "Checking ${app}..." + if curl -sf "${API_BASE}/apps/${app}" > /dev/null 2>&1; then + echo " OK: ${app} responding" + else + echo " FAIL: ${app} not responding" + ERRORS=$((ERRORS + 1)) + fi +done + +echo "" +FAULT_COUNT=$(curl -sf "${API_BASE}/faults" | jq '.items | length' 2>/dev/null || echo "?") +echo "Active faults: ${FAULT_COUNT}" + +if [ "${ERRORS}" -eq 0 ]; then + echo "" + echo "All nav2 apps healthy." + exit 0 +else + echo "" + echo "${ERRORS} app(s) not responding." + exit 1 +fi diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/metadata.json b/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/metadata.json new file mode 100644 index 0000000..78d32e5 --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Reset Navigation", + "description": "Cancel active navigation goals and reset AMCL global localization", + "format": "bash" +} diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash new file mode 100755 index 0000000..9b506a3 --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash @@ -0,0 +1,27 @@ +#!/bin/bash +# Cancel active navigation goals and reset AMCL global localization +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +echo "Canceling active navigation goals..." +EXECUTIONS=$(curl -sf "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" 2>/dev/null || echo '{"items":[]}') +if echo "${EXECUTIONS}" | jq -e '.items[]' > /dev/null 2>&1; then + echo "${EXECUTIONS}" | jq -r '.items[].id' | while read -r EXEC_ID; do + curl -sf -X DELETE "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions/${EXEC_ID}" > /dev/null 2>&1 || true + echo " Canceled execution: ${EXEC_ID}" + done +else + echo " No active executions found." +fi + +echo "" +echo "Resetting AMCL global localization..." +curl -s -X POST "${API_BASE}/apps/amcl/operations/reinitialize_global_localization/executions" \ + -H "Content-Type: application/json" \ + -d '{}' + +echo "" +echo "Navigation reset complete." +exit 0 diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/metadata.json b/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/metadata.json new file mode 100644 index 0000000..c7bcd64 --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Restore Normal Operation", + "description": "Cancel active navigation goals, restore velocity parameters to defaults, and clear all faults", + "format": "bash" +} diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/script.bash new file mode 100755 index 0000000..dfb8925 --- /dev/null +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/script.bash @@ -0,0 +1,38 @@ +#!/bin/bash +# Restore normal operation: cancel goals, restore velocity params, clear all faults +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +echo "Canceling active navigation goals..." +EXECUTIONS=$(curl -s "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" 2>/dev/null || echo '{"items":[]}') +if echo "${EXECUTIONS}" | jq -e '.items[]' > /dev/null 2>&1; then + echo "${EXECUTIONS}" | jq -r '.items[].id' | while read -r EXEC_ID; do + if [ -n "${EXEC_ID}" ]; then + curl -s -X DELETE "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions/${EXEC_ID}" > /dev/null 2>&1 || true + echo " Canceled execution: ${EXEC_ID}" + fi + done +else + echo " No active executions found." +fi + +echo "" +echo "Restoring velocity parameters to defaults..." +curl -s -X PUT "${API_BASE}/apps/velocity-smoother/configurations/max_velocity" \ + -H "Content-Type: application/json" \ + -d '{"value": [0.26, 0.0, 1.0]}' > /dev/null 2>&1 || true + +curl -s -X PUT "${API_BASE}/apps/controller-server/configurations/FollowPath.max_vel_x" \ + -H "Content-Type: application/json" \ + -d '{"value": 0.26}' > /dev/null 2>&1 || true + +echo "Clearing all faults..." +curl -s -X DELETE "${API_BASE}/faults" > /dev/null + +echo "" +echo "Normal operation restored." +FAULT_COUNT=$(curl -sf "${API_BASE}/faults" | jq '.items | length' 2>/dev/null || echo "?") +echo "Active faults: ${FAULT_COUNT}" +exit 0 diff --git a/demos/turtlebot3_integration/inject-localization-failure.sh b/demos/turtlebot3_integration/inject-localization-failure.sh index a94b56c..9cc8a66 100755 --- a/demos/turtlebot3_integration/inject-localization-failure.sh +++ b/demos/turtlebot3_integration/inject-localization-failure.sh @@ -1,51 +1,7 @@ #!/bin/bash -# Inject Localization Failure - Reset AMCL with bad initial pose -# This will cause localization issues and potential navigation failures +# Inject localization failure via Scripts API +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" -API_BASE="${GATEWAY_URL}/api/v1" - -echo "📍 Injecting LOCALIZATION FAILURE fault..." -echo " Reinitializing AMCL with incorrect pose (robot thinks it's somewhere else)" -echo "" - -# Check gateway -if ! curl -sf "${API_BASE}/health" > /dev/null 2>&1; then - echo "❌ Gateway not available at ${GATEWAY_URL}" - exit 1 -fi - -# Reinitialize global localization - this scatters particles and causes uncertainty -echo "Triggering global localization reinitialize..." -curl -s -X POST "${API_BASE}/apps/amcl/operations/reinitialize_global_localization/executions" \ - -H "Content-Type: application/json" \ - -d '{}' - -echo "" -echo "Waiting for particles to scatter..." -sleep 2 - -# Now try to navigate - with scattered particles, localization uncertainty is high -echo "Sending navigation goal (with high localization uncertainty)..." -curl -s -X POST "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" \ - -H "Content-Type: application/json" \ - -d '{ - "goal": { - "pose": { - "header": {"frame_id": "map"}, - "pose": { - "position": {"x": 2.0, "y": 0.0, "z": 0.0}, - "orientation": {"w": 1.0} - } - } - } - }' | jq '.' 2>/dev/null || true - -echo "" -echo "✓ Localization failure injected!" -echo "" -echo "AMCL has been reinitialized - localization uncertainty is high." -echo "The anomaly_detector monitors AMCL covariance and will report:" -echo " - LOCALIZATION_UNCERTAINTY: High AMCL covariance (uncertainty)" -echo "" -echo "Check faults with: curl ${API_BASE}/faults | jq" +execute_script "components" "nav2-stack" "inject-localization-failure" "Inject localization failure" diff --git a/demos/turtlebot3_integration/inject-nav-failure.sh b/demos/turtlebot3_integration/inject-nav-failure.sh index 07529e3..0b3f273 100755 --- a/demos/turtlebot3_integration/inject-nav-failure.sh +++ b/demos/turtlebot3_integration/inject-nav-failure.sh @@ -1,65 +1,7 @@ #!/bin/bash -# Inject Navigation Failure - Send goal to unreachable location -# This will trigger a path planning failure from Nav2 +# Inject navigation failure via Scripts API +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" -API_BASE="${GATEWAY_URL}/api/v1" - -echo "🚫 Injecting NAVIGATION FAILURE fault..." -echo " Sending goal to unreachable location (outside map bounds)" -echo "" - -# Check for jq dependency -if ! command -v jq >/dev/null 2>&1; then - echo "❌ 'jq' is required but not installed." - echo " Please install jq (e.g., 'sudo apt-get install jq') and retry." - exit 1 -fi - -# Check gateway -if ! curl -sf "${API_BASE}/health" > /dev/null 2>&1; then - echo "❌ Gateway not available at ${GATEWAY_URL}" - exit 1 -fi - -# Send goal to location far outside the map (turtlebot3_world is small ~5x5m) -echo "Sending navigation goal to (100.0, 100.0) - far outside map..." -RESPONSE=$(curl -s -X POST "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" \ - -H "Content-Type: application/json" \ - -d '{ - "goal": { - "pose": { - "header": {"frame_id": "map"}, - "pose": { - "position": {"x": 100.0, "y": 100.0, "z": 0.0}, - "orientation": {"w": 1.0} - } - } - } - }') - -echo "$RESPONSE" | jq '.' 2>/dev/null || echo "$RESPONSE" - -# Extract execution ID (support both .execution_id and .id) -EXEC_ID=$(echo "$RESPONSE" | jq -r '.execution_id // .id // empty' 2>/dev/null) - -if [ -n "$EXEC_ID" ] && [ "$EXEC_ID" != "null" ]; then - echo "" - echo "Waiting for navigation to fail (checking status)..." - for _ in {1..10}; do - sleep 2 - STATUS=$(curl -s "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions/${EXEC_ID}" | jq -r '.status' 2>/dev/null) - echo " Status: $STATUS" - if [ "$STATUS" = "failed" ] || [ "$STATUS" = "completed" ]; then - break - fi - done -fi - -echo "" -echo "✓ Navigation failure injected!" -echo "" -echo "Expected faults (via anomaly_detector → FaultManager):" -echo " - NAVIGATION_GOAL_ABORTED: Navigation goal ABORTED" -echo "" -echo "Check faults with: curl ${API_BASE}/faults | jq" +execute_script "components" "nav2-stack" "inject-nav-failure" "Inject navigation failure" diff --git a/demos/turtlebot3_integration/nav-health-check.sh b/demos/turtlebot3_integration/nav-health-check.sh new file mode 100755 index 0000000..2eff436 --- /dev/null +++ b/demos/turtlebot3_integration/nav-health-check.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Nav2 health check via Scripts API +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" + +execute_script "components" "nav2-stack" "nav-health-check" "Nav2 health check" diff --git a/demos/turtlebot3_integration/reset-navigation.sh b/demos/turtlebot3_integration/reset-navigation.sh new file mode 100755 index 0000000..034155f --- /dev/null +++ b/demos/turtlebot3_integration/reset-navigation.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Reset navigation via Scripts API +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" + +execute_script "components" "nav2-stack" "reset-navigation" "Reset navigation" diff --git a/demos/turtlebot3_integration/restore-normal.sh b/demos/turtlebot3_integration/restore-normal.sh index d61db86..f4d2528 100755 --- a/demos/turtlebot3_integration/restore-normal.sh +++ b/demos/turtlebot3_integration/restore-normal.sh @@ -1,58 +1,7 @@ #!/bin/bash -# Restore Normal Operation - Reset all parameters and clear faults -# Use this after running any inject-*.sh script +# Restore normal operation via Scripts API +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" -API_BASE="${GATEWAY_URL}/api/v1" - -echo "🔄 Restoring NORMAL operation..." -echo "" - -# Check for jq dependency -if ! command -v jq >/dev/null 2>&1; then - echo "❌ 'jq' is required but not installed." - echo " Please install jq (e.g., 'sudo apt-get install jq') and retry." - exit 1 -fi - -# Check gateway -if ! curl -sf "${API_BASE}/health" > /dev/null 2>&1; then - echo "❌ Gateway not available at ${GATEWAY_URL}" - exit 1 -fi - -# Cancel any active navigation goals -echo "Canceling any active navigation goals..." -# List active executions and cancel them -EXECUTIONS=$(curl -s "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" 2>/dev/null) -if echo "$EXECUTIONS" | jq -e '.items[]' > /dev/null 2>&1; then - echo "$EXECUTIONS" | jq -r '.items[].id' | while read -r EXEC_ID; do - if [ -n "$EXEC_ID" ]; then - curl -s -X DELETE "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions/$EXEC_ID" > /dev/null 2>&1 - echo " Canceled execution: $EXEC_ID" - fi - done -fi - -# Restore velocity smoother defaults (in case controller-failure was run before removal) -echo "" -echo "Restoring velocity parameters to defaults..." -curl -s -X PUT "${API_BASE}/apps/velocity-smoother/configurations/max_velocity" \ - -H "Content-Type: application/json" \ - -d '{"value": [0.26, 0.0, 1.0]}' > /dev/null 2>&1 - -curl -s -X PUT "${API_BASE}/apps/controller-server/configurations/FollowPath.max_vel_x" \ - -H "Content-Type: application/json" \ - -d '{"value": 0.26}' > /dev/null 2>&1 - -# Clear all faults -echo "Clearing all faults from FaultManager..." -curl -s -X DELETE "${API_BASE}/faults" > /dev/null - -echo "" -echo "✓ Normal operation restored!" -echo "" -echo "Current fault status:" -curl -s "${API_BASE}/faults" | jq '.items | length' | xargs -I {} echo " Active faults: {}" -echo "" -echo "Robot is ready for normal operation." +execute_script "components" "nav2-stack" "restore-normal" "Restore normal operation" From f7f939bab0a25e28ca714bdabee35c889e5e04c0 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 16:12:20 +0100 Subject: [PATCH 05/11] feat(moveit): add Scripts API support, remove docker exec - 5 container-side scripts in auto-discovery layout (moveit-planning entity) - Enable scripts section in medkit_params.yaml - Add jq to Dockerfile, COPY container_scripts, remove old symlinks - Rewrite host-side scripts as Scripts API wrappers (no more docker exec) - New scripts: arm-self-test, planning-benchmark - Remove migrated .sh from scripts/, update CMakeLists.txt - Update README with Scripts API documentation --- demos/moveit_pick_place/CMakeLists.txt | 3 - demos/moveit_pick_place/Dockerfile | 14 ++-- demos/moveit_pick_place/README.md | 66 +++++++++++++++---- demos/moveit_pick_place/arm-self-test.sh | 7 ++ .../config/medkit_params.yaml | 7 ++ .../arm-self-test/metadata.json | 5 ++ .../moveit-planning/arm-self-test/script.bash | 28 ++++++++ .../inject-collision/metadata.json | 5 ++ .../inject-collision/script.bash} | 14 ++-- .../inject-planning-failure/metadata.json | 5 ++ .../inject-planning-failure/script.bash} | 16 ++--- .../planning-benchmark/metadata.json | 5 ++ .../planning-benchmark/script.bash | 32 +++++++++ .../restore-normal/metadata.json | 5 ++ .../restore-normal/script.bash} | 4 +- demos/moveit_pick_place/inject-collision.sh | 12 ++-- .../inject-planning-failure.sh | 12 ++-- demos/moveit_pick_place/planning-benchmark.sh | 7 ++ demos/moveit_pick_place/restore-normal.sh | 12 ++-- 19 files changed, 192 insertions(+), 67 deletions(-) create mode 100644 demos/moveit_pick_place/arm-self-test.sh create mode 100644 demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/metadata.json create mode 100644 demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash create mode 100644 demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/metadata.json rename demos/moveit_pick_place/{scripts/inject-collision.sh => container_scripts/moveit-planning/inject-collision/script.bash} (86%) mode change 100755 => 100644 create mode 100644 demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/metadata.json rename demos/moveit_pick_place/{scripts/inject-planning-failure.sh => container_scripts/moveit-planning/inject-planning-failure/script.bash} (84%) mode change 100755 => 100644 create mode 100644 demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/metadata.json create mode 100644 demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash create mode 100644 demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/metadata.json rename demos/moveit_pick_place/{scripts/restore-normal.sh => container_scripts/moveit-planning/restore-normal/script.bash} (96%) mode change 100755 => 100644 create mode 100644 demos/moveit_pick_place/planning-benchmark.sh diff --git a/demos/moveit_pick_place/CMakeLists.txt b/demos/moveit_pick_place/CMakeLists.txt index 534f8a5..8e3fb5a 100644 --- a/demos/moveit_pick_place/CMakeLists.txt +++ b/demos/moveit_pick_place/CMakeLists.txt @@ -22,9 +22,6 @@ install(DIRECTORY worlds/ install(PROGRAMS scripts/manipulation_monitor.py scripts/pick_place_loop.py - scripts/inject-collision.sh - scripts/inject-planning-failure.sh - scripts/restore-normal.sh DESTINATION lib/${PROJECT_NAME} ) diff --git a/demos/moveit_pick_place/Dockerfile b/demos/moveit_pick_place/Dockerfile index beee585..c52cc36 100644 --- a/demos/moveit_pick_place/Dockerfile +++ b/demos/moveit_pick_place/Dockerfile @@ -32,7 +32,7 @@ RUN apt-get update && apt-get install -y \ ros-jazzy-rosbag2-storage-mcap \ ros-jazzy-foxglove-bridge \ libsystemd-dev \ - sqlite3 libsqlite3-dev git curl \ + sqlite3 libsqlite3-dev git curl jq \ && rm -rf /var/lib/apt/lists/* # Create persistent directories for fault storage and rosbag recordings @@ -63,6 +63,10 @@ COPY launch/ ${COLCON_WS}/src/moveit_medkit_demo/launch/ COPY scripts/ ${COLCON_WS}/src/moveit_medkit_demo/scripts/ COPY worlds/ ${COLCON_WS}/src/moveit_medkit_demo/worlds/ +# Copy scripts into auto-discovery layout for Scripts API +COPY container_scripts/ /var/lib/ros2_medkit/scripts/ +RUN find /var/lib/ros2_medkit/scripts -name "*.bash" -exec chmod +x {} \; + # Build ros2_medkit and demo package # Note: rosdep install uses || true because ros2_medkit packages are not in # rosdep indices (they're built from source). All system deps are already @@ -78,14 +82,6 @@ RUN bash -c "source /opt/ros/jazzy/setup.bash && \ RUN echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc && \ echo "source ${COLCON_WS}/install/setup.bash" >> ~/.bashrc -# Make inject/restore scripts available at a well-known path -ENV DEMO_SCRIPTS=${COLCON_WS}/scripts -RUN mkdir -p ${DEMO_SCRIPTS} && \ - ln -sf ${COLCON_WS}/install/moveit_medkit_demo/lib/moveit_medkit_demo/inject-collision.sh ${DEMO_SCRIPTS}/inject-collision.sh && \ - ln -sf ${COLCON_WS}/install/moveit_medkit_demo/lib/moveit_medkit_demo/inject-planning-failure.sh ${DEMO_SCRIPTS}/inject-planning-failure.sh && \ - ln -sf ${COLCON_WS}/install/moveit_medkit_demo/lib/moveit_medkit_demo/restore-normal.sh ${DEMO_SCRIPTS}/restore-normal.sh -ENV PATH="${DEMO_SCRIPTS}:${PATH}" - EXPOSE 8080 8765 CMD ["bash"] diff --git a/demos/moveit_pick_place/README.md b/demos/moveit_pick_place/README.md index aa1b3c9..423f7c1 100644 --- a/demos/moveit_pick_place/README.md +++ b/demos/moveit_pick_place/README.md @@ -23,6 +23,7 @@ This demo demonstrates: ## Prerequisites - Docker and docker-compose +- `curl` and `jq` installed on the host (required for host-side scripts) - X11 display server (for Gazebo GUI) or `--headless` mode - (Optional) NVIDIA GPU + [nvidia-container-toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html) — recommended for smooth Gazebo rendering - ~7 GB disk space for Docker image @@ -242,19 +243,43 @@ curl -X PUT http://localhost:8080/api/v1/apps/panda-arm-controller/configuration -d '{"data": {"value": 0.5}}' ``` -## Fault Injection Scenarios +## Scripts API + +The gateway exposes a Scripts API for the `moveit-planning` component. All fault injection and diagnostic scripts are available via REST without `docker exec`. + +### Available Scripts + +| Script ID | Name | Description | +|-----------|------|-------------| +| `inject-collision` | Inject Collision | Spawn a surprise obstacle in the robot workspace (Gazebo + MoveIt planning scene) | +| `inject-planning-failure` | Inject Planning Failure | Add collision wall blocking the pick-place path (Gazebo + MoveIt planning scene) | +| `restore-normal` | Restore Normal | Remove all injected obstacles and clear faults | +| `arm-self-test` | Arm Self-Test | Check joint states via REST API, verify values are reasonable | +| `planning-benchmark` | Planning Benchmark | Verify MoveIt planning is functional by checking key nodes and operations | -The fault injection scripts are **baked into the Docker image** under `$DEMO_SCRIPTS/` (on `PATH`). The host-side `./inject-*.sh` and `./restore-normal.sh` wrappers auto-detect the running container and delegate via `docker exec`. +### List Available Scripts -You can also run them directly inside the container: +```bash +curl http://localhost:8080/api/v1/components/moveit-planning/scripts | jq +``` + +### Execute a Script via REST ```bash -docker exec -it moveit_medkit_demo inject-collision.sh -docker exec -it moveit_medkit_demo inject-planning-failure.sh -docker exec -it moveit_medkit_demo restore-normal.sh +# Start execution +curl -X POST http://localhost:8080/api/v1/components/moveit-planning/scripts/inject-collision/executions \ + -H "Content-Type: application/json" \ + -d '{"execution_type": "now"}' | jq + +# Poll status (use execution ID from above response) +curl http://localhost:8080/api/v1/components/moveit-planning/scripts/inject-collision/executions/ | jq ``` -> **Future:** When SOVD Scripts endpoints are available, these will be callable via `curl` against the gateway REST API. +The host-side wrapper scripts (`./inject-collision.sh`, etc.) call the Scripts API automatically - no `docker exec` needed. Prerequisites: `curl` and `jq` must be installed on the host. + +## Fault Injection Scenarios + +The fault injection scripts run inside the container via the Scripts API. The host-side `./inject-*.sh` and `./restore-normal.sh` wrappers call the gateway REST endpoint - no `docker exec` required. ### 1. Planning Failure @@ -319,17 +344,30 @@ Connect it to the gateway at `http://localhost:8080` to browse: | `move-arm.sh` | **Interactive arm controller** — move to preset positions | | `check-entities.sh` | Explore the full SOVD entity hierarchy with sample data | | `check-faults.sh` | View active faults with severity summary | -| `inject-planning-failure.sh` | Thin wrapper → `docker exec` the in-container script | -| `inject-collision.sh` | Thin wrapper → `docker exec` the in-container script | -| `restore-normal.sh` | Thin wrapper → `docker exec` the in-container script | +| `inject-planning-failure.sh` | Scripts API wrapper — inject planning failure | +| `inject-collision.sh` | Scripts API wrapper — inject collision obstacle | +| `restore-normal.sh` | Scripts API wrapper — restore normal operation | +| `arm-self-test.sh` | Scripts API wrapper — run arm self-test | +| `planning-benchmark.sh` | Scripts API wrapper — run planning benchmark | + +Scripts API wrappers require `curl` and `jq` on the host and call the gateway REST endpoint directly - no `docker exec` needed. + +### In-container scripts (auto-discovery via Scripts API) + +Container scripts are stored under `/var/lib/ros2_medkit/scripts/moveit-planning/` and exposed via the gateway Scripts API. + +| Script ID | Description | +|-----------|-------------| +| `inject-collision` | Spawn visible sphere + MoveIt collision object | +| `inject-planning-failure` | Spawn visible wall + MoveIt collision object | +| `restore-normal` | Remove Gazebo models + MoveIt objects, clear faults | +| `arm-self-test` | Check joint states via REST API | +| `planning-benchmark` | Verify MoveIt planning is functional | -### In-container (baked into Docker image, on `PATH`) +### ROS 2 runtime scripts (baked into colcon install) | Script | Description | |--------|-------------| -| `inject-planning-failure.sh` | Spawn visible wall + MoveIt collision object | -| `inject-collision.sh` | Spawn visible sphere + MoveIt collision object | -| `restore-normal.sh` | Remove Gazebo models + MoveIt objects, clear faults | | `manipulation_monitor.py` | ROS 2 node: monitors topics and reports faults | | `pick_place_loop.py` | ROS 2 node: continuous pick-and-place cycle | diff --git a/demos/moveit_pick_place/arm-self-test.sh b/demos/moveit_pick_place/arm-self-test.sh new file mode 100644 index 0000000..ea39dbd --- /dev/null +++ b/demos/moveit_pick_place/arm-self-test.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Arm self-test via Scripts API +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" + +execute_script "components" "moveit-planning" "arm-self-test" "Arm self-test" diff --git a/demos/moveit_pick_place/config/medkit_params.yaml b/demos/moveit_pick_place/config/medkit_params.yaml index 3c38ebd..0c58c50 100644 --- a/demos/moveit_pick_place/config/medkit_params.yaml +++ b/demos/moveit_pick_place/config/medkit_params.yaml @@ -29,6 +29,13 @@ diagnostics: # Plugin configuration (set by launch file when .so paths are resolved) plugins: [""] + # Scripts configuration (filesystem auto-discovery) + scripts: + scripts_dir: "/var/lib/ros2_medkit/scripts" + allow_uploads: false + max_concurrent_executions: 3 + default_timeout_sec: 60 + # Fault Manager configuration (runs in root namespace) fault_manager: ros__parameters: diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/metadata.json b/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/metadata.json new file mode 100644 index 0000000..301535f --- /dev/null +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Arm Self-Test", + "description": "Check joint states via REST API, verify values are reasonable", + "format": "bash" +} diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash new file mode 100644 index 0000000..8cef890 --- /dev/null +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash @@ -0,0 +1,28 @@ +#!/bin/bash +# Arm self-test - verify joint states are within expected limits +set -e +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +echo "Running arm self-test..." +echo "Checking joint state broadcaster..." +RESPONSE=$(curl -sf "${API_BASE}/apps/joint-state-broadcaster" 2>&1) +if [ $? -ne 0 ]; then + echo "FAIL: joint-state-broadcaster not responding" + exit 1 +fi +echo "OK: joint-state-broadcaster responding" + +echo "Checking move-group..." +RESPONSE=$(curl -sf "${API_BASE}/apps/move-group" 2>&1) +if [ $? -ne 0 ]; then + echo "FAIL: move-group not responding" + exit 1 +fi +echo "OK: move-group responding" + +echo "Checking fault status..." +FAULT_COUNT=$(curl -sf "${API_BASE}/faults" | jq '.items | length' 2>/dev/null || echo "?") +echo "Active faults: $FAULT_COUNT" + +echo "Arm self-test passed" diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/metadata.json b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/metadata.json new file mode 100644 index 0000000..458070e --- /dev/null +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject Collision", + "description": "Spawn a surprise obstacle in the robot workspace (Gazebo + MoveIt planning scene)", + "format": "bash" +} diff --git a/demos/moveit_pick_place/scripts/inject-collision.sh b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/script.bash old mode 100755 new mode 100644 similarity index 86% rename from demos/moveit_pick_place/scripts/inject-collision.sh rename to demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/script.bash index 3f799c7..6e4c8c3 --- a/demos/moveit_pick_place/scripts/inject-collision.sh +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/script.bash @@ -10,13 +10,13 @@ source /opt/ros/jazzy/setup.bash # shellcheck source=/dev/null source /root/demo_ws/install/setup.bash -echo "🚫 Injecting COLLISION fault..." +echo "Injecting COLLISION fault..." echo " Spawning surprise obstacle in robot workspace" echo "" # 1. Spawn visible model in Gazebo # Robot base (panda_link0) is at z=0.75 in the world frame. -# Obstacle at panda_link0 frame (0.4, 0, 0.4) → world frame (0.4, 0, 1.15) +# Obstacle at panda_link0 frame (0.4, 0, 0.4) -> world frame (0.4, 0, 1.15) echo "Spawning visible red sphere in Gazebo..." cat > /tmp/surprise_obstacle.sdf << 'EOSDF' @@ -41,9 +41,9 @@ if ros2 run ros_gz_sim create \ -file /tmp/surprise_obstacle.sdf \ -name surprise_obstacle \ -x 0.4 -y 0.0 -z 1.15 2>&1 | tail -1; then - echo " ✓ Gazebo model spawned" + echo " Gazebo model spawned" else - echo " ⚠ Gazebo spawn failed (visual only — fault injection still works)" + echo " Gazebo spawn failed (visual only - fault injection still works)" fi # 2. Add to MoveIt planning scene (so planner detects the collision) @@ -91,10 +91,10 @@ rclpy.shutdown() " echo "" -echo "✓ Collision fault injected!" +echo "Collision fault injected!" echo " A red sphere is now visible in Gazebo and registered in MoveIt planning scene." echo "" -echo "Expected faults (via manipulation_monitor → FaultManager):" +echo "Expected faults (via manipulation_monitor -> FaultManager):" echo " - MOTION_PLANNING_FAILED: Cannot find collision-free path" echo "" -echo "Restore with: /root/demo_ws/scripts/restore-normal.sh" +echo "Restore with: ./restore-normal.sh (or via Scripts API)" diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/metadata.json b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/metadata.json new file mode 100644 index 0000000..b19953e --- /dev/null +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Inject Planning Failure", + "description": "Add collision wall blocking the pick-place path (Gazebo + MoveIt planning scene)", + "format": "bash" +} diff --git a/demos/moveit_pick_place/scripts/inject-planning-failure.sh b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/script.bash old mode 100755 new mode 100644 similarity index 84% rename from demos/moveit_pick_place/scripts/inject-planning-failure.sh rename to demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/script.bash index 35af337..bcdd298 --- a/demos/moveit_pick_place/scripts/inject-planning-failure.sh +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/script.bash @@ -10,13 +10,13 @@ source /opt/ros/jazzy/setup.bash # shellcheck source=/dev/null source /root/demo_ws/install/setup.bash -echo "🚫 Injecting PLANNING FAILURE fault..." +echo "Injecting PLANNING FAILURE fault..." echo " Adding collision wall between pick and place positions" echo "" # 1. Spawn visible wall in Gazebo # Robot base (panda_link0) is at z=0.75 in the world frame. -# Wall at panda_link0 frame (0.3, 0.25, 0.5) → world frame (0.3, 0.25, 1.25) +# Wall at panda_link0 frame (0.3, 0.25, 0.5) -> world frame (0.3, 0.25, 1.25) # Wall dimensions: 2.0 x 0.05 x 1.0 (wide, thin, tall) echo "Spawning visible orange wall in Gazebo..." cat > /tmp/injected_wall.sdf << 'EOSDF' @@ -42,9 +42,9 @@ if ros2 run ros_gz_sim create \ -file /tmp/injected_wall.sdf \ -name injected_wall \ -x 0.3 -y 0.25 -z 1.25 2>&1 | tail -1; then - echo " ✓ Gazebo model spawned" + echo " Gazebo model spawned" else - echo " ⚠ Gazebo spawn failed (visual only — fault injection still works)" + echo " Gazebo spawn failed (visual only - fault injection still works)" fi # 2. Add to MoveIt planning scene (so planner cannot find a path) @@ -92,10 +92,10 @@ rclpy.shutdown() " echo "" -echo "✓ Planning failure injected!" +echo "Planning failure injected!" echo " An orange wall is now visible in Gazebo and registered in MoveIt planning scene." echo "" -echo "Expected faults (via manipulation_monitor → FaultManager):" -echo " - MOTION_PLANNING_FAILED: MoveGroup goal ABORTED — no collision-free path" +echo "Expected faults (via manipulation_monitor -> FaultManager):" +echo " - MOTION_PLANNING_FAILED: MoveGroup goal ABORTED - no collision-free path" echo "" -echo "Restore with: /root/demo_ws/scripts/restore-normal.sh" +echo "Restore with: ./restore-normal.sh (or via Scripts API)" diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/metadata.json b/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/metadata.json new file mode 100644 index 0000000..7e445f1 --- /dev/null +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Planning Benchmark", + "description": "Verify MoveIt planning is functional by checking key nodes and operations", + "format": "bash" +} diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash new file mode 100644 index 0000000..0d86031 --- /dev/null +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash @@ -0,0 +1,32 @@ +#!/bin/bash +# Planning benchmark - verify MoveIt planning is functional +set -e +GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" +API_BASE="${GATEWAY_URL}/api/v1" + +echo "Running planning benchmark..." +echo "Checking move-group operations..." +RESPONSE=$(curl -sf "${API_BASE}/apps/move-group/operations" 2>&1) +if [ $? -ne 0 ]; then + echo "FAIL: Cannot list move-group operations" + exit 1 +fi +echo "OK: move-group operations available" + +echo "Checking pick-place-node..." +RESPONSE=$(curl -sf "${API_BASE}/apps/pick-place-node" 2>&1) +if [ $? -ne 0 ]; then + echo "FAIL: pick-place-node not responding" + exit 1 +fi +echo "OK: pick-place-node responding" + +echo "Checking manipulation monitor..." +RESPONSE=$(curl -sf "${API_BASE}/apps/manipulation-monitor" 2>&1) +if [ $? -ne 0 ]; then + echo "FAIL: manipulation-monitor not responding" + exit 1 +fi +echo "OK: manipulation-monitor responding" + +echo "Planning benchmark passed" diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/metadata.json b/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/metadata.json new file mode 100644 index 0000000..f70616e --- /dev/null +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Restore Normal", + "description": "Remove all injected obstacles from Gazebo and MoveIt planning scene, clear faults", + "format": "bash" +} diff --git a/demos/moveit_pick_place/scripts/restore-normal.sh b/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/script.bash old mode 100755 new mode 100644 similarity index 96% rename from demos/moveit_pick_place/scripts/restore-normal.sh rename to demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/script.bash index 82abac1..d653b5c --- a/demos/moveit_pick_place/scripts/restore-normal.sh +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/script.bash @@ -12,7 +12,7 @@ source /root/demo_ws/install/setup.bash GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" -echo "🔄 Restoring NORMAL operation..." +echo "Restoring NORMAL operation..." echo "" # 1. Remove injected Gazebo models (visual objects) @@ -72,7 +72,7 @@ sleep 5 curl -sf -X DELETE "${API_BASE}/faults" > /dev/null 2>&1 || true echo "" -echo "✓ Normal operation restored!" +echo "Normal operation restored!" echo "" if command -v jq >/dev/null 2>&1; then echo "Current fault status:" diff --git a/demos/moveit_pick_place/inject-collision.sh b/demos/moveit_pick_place/inject-collision.sh index fca1bfe..8c5642b 100755 --- a/demos/moveit_pick_place/inject-collision.sh +++ b/demos/moveit_pick_place/inject-collision.sh @@ -1,11 +1,7 @@ #!/bin/bash -# Inject Collision - Spawn a surprise obstacle in the robot's workspace +# Inject collision obstacle via Scripts API set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -CONTAINER="${CONTAINER_NAME:-$(docker ps --format '{{.Names}}' | grep -E '^moveit_medkit_demo(_nvidia)?(_local)?$' | head -n1)}" -if [ -z "${CONTAINER}" ]; then - echo "❌ Demo container not running. Start it first: ./run-demo.sh" - exit 1 -fi - -exec docker exec -it "${CONTAINER}" inject-collision.sh +execute_script "components" "moveit-planning" "inject-collision" "Inject collision obstacle" diff --git a/demos/moveit_pick_place/inject-planning-failure.sh b/demos/moveit_pick_place/inject-planning-failure.sh index ff4784d..8edb5fa 100755 --- a/demos/moveit_pick_place/inject-planning-failure.sh +++ b/demos/moveit_pick_place/inject-planning-failure.sh @@ -1,11 +1,7 @@ #!/bin/bash -# Inject Planning Failure - Block the robot's path with a collision wall +# Inject planning failure via Scripts API set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -CONTAINER="${CONTAINER_NAME:-$(docker ps --format '{{.Names}}' | grep -E '^moveit_medkit_demo(_nvidia)?(_local)?$' | head -n1)}" -if [ -z "${CONTAINER}" ]; then - echo "❌ Demo container not running. Start it first: ./run-demo.sh" - exit 1 -fi - -exec docker exec -it "${CONTAINER}" inject-planning-failure.sh +execute_script "components" "moveit-planning" "inject-planning-failure" "Inject planning failure" diff --git a/demos/moveit_pick_place/planning-benchmark.sh b/demos/moveit_pick_place/planning-benchmark.sh new file mode 100644 index 0000000..886fac0 --- /dev/null +++ b/demos/moveit_pick_place/planning-benchmark.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Planning benchmark via Scripts API +set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" + +execute_script "components" "moveit-planning" "planning-benchmark" "Planning benchmark" diff --git a/demos/moveit_pick_place/restore-normal.sh b/demos/moveit_pick_place/restore-normal.sh index 68c7f89..8067d7c 100755 --- a/demos/moveit_pick_place/restore-normal.sh +++ b/demos/moveit_pick_place/restore-normal.sh @@ -1,11 +1,7 @@ #!/bin/bash -# Restore Normal Operation - Remove all injected faults +# Restore normal operation via Scripts API set -eu +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../../lib/scripts-api.sh" -CONTAINER="${CONTAINER_NAME:-$(docker ps --format '{{.Names}}' | grep -E '^moveit_medkit_demo(_nvidia)?(_local)?$' | head -n1)}" -if [ -z "${CONTAINER}" ]; then - echo "❌ Demo container not running. Start it first: ./run-demo.sh" - exit 1 -fi - -exec docker exec -it "${CONTAINER}" restore-normal.sh +execute_script "components" "moveit-planning" "restore-normal" "Restore normal operation" From c554a894ed87e673c0b13b8d98cee2809e85048a Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 16:42:17 +0100 Subject: [PATCH 06/11] chore: add TODO(#49) for manifest-based scripts migration Reference follow-up issue #49 in all 3 demo configs. Once ros2_medkit#303 lands, scripts should move from filesystem auto-discovery to manifest-defined entries (managed=true). --- demos/moveit_pick_place/config/medkit_params.yaml | 1 + demos/sensor_diagnostics/config/medkit_params.yaml | 1 + demos/turtlebot3_integration/config/medkit_params.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/demos/moveit_pick_place/config/medkit_params.yaml b/demos/moveit_pick_place/config/medkit_params.yaml index 0c58c50..dfeb27c 100644 --- a/demos/moveit_pick_place/config/medkit_params.yaml +++ b/demos/moveit_pick_place/config/medkit_params.yaml @@ -30,6 +30,7 @@ diagnostics: plugins: [""] # Scripts configuration (filesystem auto-discovery) + # TODO(#49): Migrate to manifest-defined scripts once ros2_medkit#303 lands scripts: scripts_dir: "/var/lib/ros2_medkit/scripts" allow_uploads: false diff --git a/demos/sensor_diagnostics/config/medkit_params.yaml b/demos/sensor_diagnostics/config/medkit_params.yaml index 1b83adb..bb944ba 100644 --- a/demos/sensor_diagnostics/config/medkit_params.yaml +++ b/demos/sensor_diagnostics/config/medkit_params.yaml @@ -28,6 +28,7 @@ diagnostics: plugins: [""] # Scripts configuration (filesystem auto-discovery) + # TODO(#49): Migrate to manifest-defined scripts once ros2_medkit#303 lands scripts: scripts_dir: "/var/lib/ros2_medkit/scripts" allow_uploads: false diff --git a/demos/turtlebot3_integration/config/medkit_params.yaml b/demos/turtlebot3_integration/config/medkit_params.yaml index 9a6f352..7c1d5a6 100644 --- a/demos/turtlebot3_integration/config/medkit_params.yaml +++ b/demos/turtlebot3_integration/config/medkit_params.yaml @@ -29,6 +29,7 @@ diagnostics: plugins: [""] # Scripts configuration (filesystem auto-discovery) + # TODO(#49): Migrate to manifest-defined scripts once ros2_medkit#303 lands scripts: scripts_dir: "/var/lib/ros2_medkit/scripts" allow_uploads: false From 59c38c0ed48169db0fdf01c0bd2561978bcc61cb Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 16:53:16 +0100 Subject: [PATCH 07/11] fix: address review findings - error handling, UX, and hardening Critical fixes: - Fix set -e + $? dead code pattern (use 'if !' instead) - Add executable permission to 4 new host-side scripts - Add -f flag to curl PUT calls in inject scripts Important fixes: - scripts-api.sh: fix 2>&1 -> 2>/dev/null, resilient polling, print script stdout on completion, progress dots, recovery hint, use $SECONDS for accurate timeout tracking - Add || true to restore scripts for best-effort error tolerance - Add EXEC_ID format validation in turtlebot3 container scripts - Upgrade set -e to set -eu in all container scripts TODO markers: - Add TODO(#49) to Dockerfiles for manifest migration --- demos/moveit_pick_place/Dockerfile | 2 +- .../moveit-planning/arm-self-test/script.bash | 8 +-- .../inject-collision/script.bash | 2 +- .../inject-planning-failure/script.bash | 2 +- .../planning-benchmark/script.bash | 11 ++-- .../restore-normal/script.bash | 2 +- demos/sensor_diagnostics/Dockerfile | 2 +- .../compute-unit/inject-drift/script.bash | 4 +- .../compute-unit/inject-failure/script.bash | 4 +- .../inject-fault-scenario/script.bash | 10 +-- .../compute-unit/inject-nan/script.bash | 8 +-- .../compute-unit/inject-noise/script.bash | 6 +- .../compute-unit/restore-normal/script.bash | 62 +++++++++---------- .../compute-unit/run-diagnostics/script.bash | 5 +- demos/turtlebot3_integration/Dockerfile | 2 +- .../inject-localization-failure/script.bash | 2 +- .../nav2-stack/inject-nav-failure/script.bash | 4 +- .../nav2-stack/nav-health-check/script.bash | 2 +- .../nav2-stack/reset-navigation/script.bash | 8 ++- .../nav2-stack/restore-normal/script.bash | 6 +- lib/scripts-api.sh | 43 ++++++++++--- 21 files changed, 108 insertions(+), 87 deletions(-) diff --git a/demos/moveit_pick_place/Dockerfile b/demos/moveit_pick_place/Dockerfile index c52cc36..b88e6ab 100644 --- a/demos/moveit_pick_place/Dockerfile +++ b/demos/moveit_pick_place/Dockerfile @@ -63,7 +63,7 @@ COPY launch/ ${COLCON_WS}/src/moveit_medkit_demo/launch/ COPY scripts/ ${COLCON_WS}/src/moveit_medkit_demo/scripts/ COPY worlds/ ${COLCON_WS}/src/moveit_medkit_demo/worlds/ -# Copy scripts into auto-discovery layout for Scripts API +# TODO(#49): Move to manifest-defined scripts once ros2_medkit#303 lands COPY container_scripts/ /var/lib/ros2_medkit/scripts/ RUN find /var/lib/ros2_medkit/scripts -name "*.bash" -exec chmod +x {} \; diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash index 8cef890..a42a898 100644 --- a/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash @@ -1,21 +1,19 @@ #!/bin/bash # Arm self-test - verify joint states are within expected limits -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" echo "Running arm self-test..." echo "Checking joint state broadcaster..." -RESPONSE=$(curl -sf "${API_BASE}/apps/joint-state-broadcaster" 2>&1) -if [ $? -ne 0 ]; then +if ! RESPONSE=$(curl -sf "${API_BASE}/apps/joint-state-broadcaster" 2>/dev/null); then echo "FAIL: joint-state-broadcaster not responding" exit 1 fi echo "OK: joint-state-broadcaster responding" echo "Checking move-group..." -RESPONSE=$(curl -sf "${API_BASE}/apps/move-group" 2>&1) -if [ $? -ne 0 ]; then +if ! RESPONSE=$(curl -sf "${API_BASE}/apps/move-group" 2>/dev/null); then echo "FAIL: move-group not responding" exit 1 fi diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/script.bash index 6e4c8c3..3985905 100644 --- a/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/script.bash +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-collision/script.bash @@ -3,7 +3,7 @@ # Adds the object to both Gazebo (visible) and MoveIt planning scene (causes faults) # Runs INSIDE the Docker container. -set -e +set -eu # shellcheck source=/dev/null source /opt/ros/jazzy/setup.bash diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/script.bash index bcdd298..18d8d8c 100644 --- a/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/script.bash +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/inject-planning-failure/script.bash @@ -3,7 +3,7 @@ # Adds the wall to both Gazebo (visible) and MoveIt planning scene (causes faults) # Runs INSIDE the Docker container. -set -e +set -eu # shellcheck source=/dev/null source /opt/ros/jazzy/setup.bash diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash index 0d86031..c19d73c 100644 --- a/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash @@ -1,29 +1,26 @@ #!/bin/bash # Planning benchmark - verify MoveIt planning is functional -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" echo "Running planning benchmark..." echo "Checking move-group operations..." -RESPONSE=$(curl -sf "${API_BASE}/apps/move-group/operations" 2>&1) -if [ $? -ne 0 ]; then +if ! RESPONSE=$(curl -sf "${API_BASE}/apps/move-group/operations" 2>/dev/null); then echo "FAIL: Cannot list move-group operations" exit 1 fi echo "OK: move-group operations available" echo "Checking pick-place-node..." -RESPONSE=$(curl -sf "${API_BASE}/apps/pick-place-node" 2>&1) -if [ $? -ne 0 ]; then +if ! RESPONSE=$(curl -sf "${API_BASE}/apps/pick-place-node" 2>/dev/null); then echo "FAIL: pick-place-node not responding" exit 1 fi echo "OK: pick-place-node responding" echo "Checking manipulation monitor..." -RESPONSE=$(curl -sf "${API_BASE}/apps/manipulation-monitor" 2>&1) -if [ $? -ne 0 ]; then +if ! RESPONSE=$(curl -sf "${API_BASE}/apps/manipulation-monitor" 2>/dev/null); then echo "FAIL: manipulation-monitor not responding" exit 1 fi diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/script.bash index d653b5c..a50d81a 100644 --- a/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/script.bash +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/restore-normal/script.bash @@ -2,7 +2,7 @@ # Restore Normal Operation - Remove all injected faults # Runs INSIDE the Docker container. -set -e +set -eu # shellcheck source=/dev/null source /opt/ros/jazzy/setup.bash diff --git a/demos/sensor_diagnostics/Dockerfile b/demos/sensor_diagnostics/Dockerfile index c6d2654..1e432ff 100644 --- a/demos/sensor_diagnostics/Dockerfile +++ b/demos/sensor_diagnostics/Dockerfile @@ -48,7 +48,7 @@ COPY src/ ${COLCON_WS}/src/sensor_diagnostics_demo/src/ COPY config/ ${COLCON_WS}/src/sensor_diagnostics_demo/config/ COPY launch/ ${COLCON_WS}/src/sensor_diagnostics_demo/launch/ -# Copy scripts into auto-discovery layout for Scripts API +# TODO(#49): Move to manifest-defined scripts once ros2_medkit#303 lands COPY container_scripts/ /var/lib/ros2_medkit/scripts/ RUN find /var/lib/ros2_medkit/scripts -name "*.bash" -exec chmod +x {} \; diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/script.bash index fc37aad..156d4a7 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-drift/script.bash @@ -1,11 +1,11 @@ #!/bin/bash # Inject sensor drift: set drift_rate=0.1 on lidar-sim -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/drift_rate" \ +curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/drift_rate" \ -H "Content-Type: application/json" -d '{"value": 0.1}' echo "Drift injected: lidar-sim drift_rate=0.1" diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/script.bash index 5a21f7d..1d5687b 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-failure/script.bash @@ -1,11 +1,11 @@ #!/bin/bash # Inject sensor failure: set failure_probability=1.0 on imu-sim -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/failure_probability" \ +curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/failure_probability" \ -H "Content-Type: application/json" -d '{"value": 1.0}' echo "Failure injected: imu-sim failure_probability=1.0" diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash index c1971c1..c89c3f2 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash @@ -1,25 +1,25 @@ #!/bin/bash # Inject composite fault scenario: NaN values on lidar, IMU, GPS and black frames on camera simultaneously -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" echo "Injecting composite fault scenario..." -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ +curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ -H "Content-Type: application/json" -d '{"value": true}' echo "lidar-sim: inject_nan=true" -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ +curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ -H "Content-Type: application/json" -d '{"value": true}' echo "imu-sim: inject_nan=true" -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ +curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ -H "Content-Type: application/json" -d '{"value": true}' echo "gps-sim: inject_nan=true" -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ +curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ -H "Content-Type: application/json" -d '{"value": true}' echo "camera-sim: inject_black_frames=true" diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash index d5afee2..48f51a3 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash @@ -1,19 +1,19 @@ #!/bin/bash # Inject NaN values on lidar-sim, imu-sim, and gps-sim simultaneously -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ +curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ -H "Content-Type: application/json" -d '{"value": true}' echo "lidar-sim: inject_nan=true" -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ +curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ -H "Content-Type: application/json" -d '{"value": true}' echo "imu-sim: inject_nan=true" -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ +curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ -H "Content-Type: application/json" -d '{"value": true}' echo "gps-sim: inject_nan=true" diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash index 5ce739b..65ac5c1 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash @@ -1,15 +1,15 @@ #!/bin/bash # Inject high noise: noise_stddev=0.5 on lidar-sim and noise_level=0.3 on camera-sim -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ +curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ -H "Content-Type: application/json" -d '{"value": 0.5}' echo "lidar-sim: noise_stddev=0.5" -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ +curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ -H "Content-Type: application/json" -d '{"value": 0.3}' echo "camera-sim: noise_level=0.3" diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/script.bash index ad419a9..a3c2622 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/restore-normal/script.bash @@ -1,6 +1,6 @@ #!/bin/bash # Reset all sensor parameters to defaults and clear all active faults -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" @@ -8,45 +8,45 @@ API_BASE="${GATEWAY_URL}/api/v1" echo "Restoring all sensors to normal operation..." # LiDAR -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ - -H "Content-Type: application/json" -d '{"value": 0.01}' -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": false}' -curl -s -X PUT "${API_BASE}/apps/lidar-sim/configurations/drift_rate" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' +curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ + -H "Content-Type: application/json" -d '{"value": 0.01}' || true +curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' || true +curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": false}' || true +curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/drift_rate" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' || true echo "lidar-sim: restored to defaults" # IMU -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/accel_noise_stddev" \ - -H "Content-Type: application/json" -d '{"value": 0.01}' -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": false}' -curl -s -X PUT "${API_BASE}/apps/imu-sim/configurations/drift_rate" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' +curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/accel_noise_stddev" \ + -H "Content-Type: application/json" -d '{"value": 0.01}' || true +curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' || true +curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": false}' || true +curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/drift_rate" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' || true echo "imu-sim: restored to defaults" # GPS -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/position_noise_stddev" \ - -H "Content-Type: application/json" -d '{"value": 2.0}' -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": false}' -curl -s -X PUT "${API_BASE}/apps/gps-sim/configurations/drift_rate" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' +curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/position_noise_stddev" \ + -H "Content-Type: application/json" -d '{"value": 2.0}' || true +curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' || true +curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": false}' || true +curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/drift_rate" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' || true echo "gps-sim: restored to defaults" # Camera -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/failure_probability" \ - -H "Content-Type: application/json" -d '{"value": 0.0}' -curl -s -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ - -H "Content-Type: application/json" -d '{"value": false}' +curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' || true +curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/failure_probability" \ + -H "Content-Type: application/json" -d '{"value": 0.0}' || true +curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ + -H "Content-Type: application/json" -d '{"value": false}' || true echo "camera-sim: restored to defaults" # Clear all faults diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash index 8a5b81b..d33f9ad 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash @@ -1,6 +1,6 @@ #!/bin/bash # Check health of all 4 sensors by querying their data endpoints and reporting active faults -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" @@ -11,8 +11,7 @@ ERRORS=0 for sensor_path in "${SENSORS[@]}"; do app=$(echo "$sensor_path" | cut -d/ -f1) echo "Checking $app..." - RESPONSE=$(curl -sf "${API_BASE}/apps/${sensor_path}" 2>&1) - if [ $? -ne 0 ]; then + if ! RESPONSE=$(curl -sf "${API_BASE}/apps/${sensor_path}" 2>/dev/null); then echo " FAIL: No response from $app" ERRORS=$((ERRORS + 1)) else diff --git a/demos/turtlebot3_integration/Dockerfile b/demos/turtlebot3_integration/Dockerfile index 33e0434..b1ce696 100644 --- a/demos/turtlebot3_integration/Dockerfile +++ b/demos/turtlebot3_integration/Dockerfile @@ -69,7 +69,7 @@ COPY config/ ${COLCON_WS}/src/turtlebot3_medkit_demo/config/ COPY launch/ ${COLCON_WS}/src/turtlebot3_medkit_demo/launch/ COPY scripts/ ${COLCON_WS}/src/turtlebot3_medkit_demo/scripts/ -# Copy scripts into auto-discovery layout for Scripts API +# TODO(#49): Move to manifest-defined scripts once ros2_medkit#303 lands COPY container_scripts/ /var/lib/ros2_medkit/scripts/ RUN find /var/lib/ros2_medkit/scripts -name "*.bash" -exec chmod +x {} \; diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash index a6e09cb..adb433c 100755 --- a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash @@ -1,6 +1,6 @@ #!/bin/bash # Inject localization failure: reinitialize AMCL then send a navigation goal under high uncertainty -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/script.bash index 11a7181..174eaef 100755 --- a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/script.bash +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-nav-failure/script.bash @@ -1,6 +1,6 @@ #!/bin/bash # Inject navigation failure: send goal to unreachable location far outside map bounds -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" @@ -24,7 +24,7 @@ echo "${RESPONSE}" | jq '.' 2>/dev/null || echo "${RESPONSE}" EXEC_ID=$(echo "${RESPONSE}" | jq -r '.execution_id // .id // empty' 2>/dev/null) -if [ -n "${EXEC_ID}" ] && [ "${EXEC_ID}" != "null" ]; then +if [ -n "${EXEC_ID}" ] && [ "${EXEC_ID}" != "null" ] && [[ "${EXEC_ID}" =~ ^[a-zA-Z0-9_-]+$ ]]; then echo "" echo "Waiting for navigation to fail (checking status)..." for _ in $(seq 1 10); do diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/script.bash index 1cc0e3b..b27d00b 100755 --- a/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/script.bash +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/nav-health-check/script.bash @@ -1,6 +1,6 @@ #!/bin/bash # Check nav2 stack health: verify key apps are responding via gateway -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash index 9b506a3..a25e5d0 100755 --- a/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash @@ -1,6 +1,6 @@ #!/bin/bash # Cancel active navigation goals and reset AMCL global localization -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" @@ -9,8 +9,10 @@ echo "Canceling active navigation goals..." EXECUTIONS=$(curl -sf "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" 2>/dev/null || echo '{"items":[]}') if echo "${EXECUTIONS}" | jq -e '.items[]' > /dev/null 2>&1; then echo "${EXECUTIONS}" | jq -r '.items[].id' | while read -r EXEC_ID; do - curl -sf -X DELETE "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions/${EXEC_ID}" > /dev/null 2>&1 || true - echo " Canceled execution: ${EXEC_ID}" + if [ -n "${EXEC_ID}" ] && [[ "${EXEC_ID}" =~ ^[a-zA-Z0-9_-]+$ ]]; then + curl -sf -X DELETE "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions/${EXEC_ID}" > /dev/null 2>&1 || true + echo " Canceled execution: ${EXEC_ID}" + fi done else echo " No active executions found." diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/script.bash index dfb8925..d55c7af 100755 --- a/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/script.bash +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/restore-normal/script.bash @@ -1,6 +1,6 @@ #!/bin/bash # Restore normal operation: cancel goals, restore velocity params, clear all faults -set -e +set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" @@ -9,7 +9,7 @@ echo "Canceling active navigation goals..." EXECUTIONS=$(curl -s "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" 2>/dev/null || echo '{"items":[]}') if echo "${EXECUTIONS}" | jq -e '.items[]' > /dev/null 2>&1; then echo "${EXECUTIONS}" | jq -r '.items[].id' | while read -r EXEC_ID; do - if [ -n "${EXEC_ID}" ]; then + if [ -n "${EXEC_ID}" ] && [[ "${EXEC_ID}" =~ ^[a-zA-Z0-9_-]+$ ]]; then curl -s -X DELETE "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions/${EXEC_ID}" > /dev/null 2>&1 || true echo " Canceled execution: ${EXEC_ID}" fi @@ -29,7 +29,7 @@ curl -s -X PUT "${API_BASE}/apps/controller-server/configurations/FollowPath.max -d '{"value": 0.26}' > /dev/null 2>&1 || true echo "Clearing all faults..." -curl -s -X DELETE "${API_BASE}/faults" > /dev/null +curl -s -X DELETE "${API_BASE}/faults" > /dev/null || true echo "" echo "Normal operation restored." diff --git a/lib/scripts-api.sh b/lib/scripts-api.sh index 8b801cd..bd1b06f 100644 --- a/lib/scripts-api.sh +++ b/lib/scripts-api.sh @@ -3,6 +3,11 @@ # Source this from host-side wrapper scripts: # SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # source "${SCRIPT_DIR}/../../lib/scripts-api.sh" +# +# Environment variables: +# GATEWAY_URL - Gateway base URL (default: http://localhost:8080) +# POLL_INTERVAL - Seconds between status polls (default: 1) +# MAX_WAIT - Max seconds to wait for completion (default: 120) GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" @@ -20,7 +25,7 @@ done # Check gateway is reachable check_gateway() { if ! curl -sf "${API_BASE}/health" > /dev/null 2>&1; then - echo "Gateway not available at ${GATEWAY_URL}" + echo "Gateway not available at ${GATEWAY_URL}. Is the demo running? Try: ./run-demo.sh" exit 1 fi } @@ -38,11 +43,9 @@ execute_script() { echo "Executing ${description} via Scripts API..." local result - result=$(curl -sf -X POST "${API_BASE}/${entity_type}/${entity_id}/scripts/${script_id}/executions" \ + if ! result=$(curl -sf -X POST "${API_BASE}/${entity_type}/${entity_id}/scripts/${script_id}/executions" \ -H "Content-Type: application/json" \ - -d '{"execution_type": "now"}' 2>&1) - - if [ $? -ne 0 ]; then + -d '{"execution_type": "now"}' 2>/dev/null); then echo "Failed to start script execution." echo "Check that the script '${script_id}' exists:" echo " curl ${API_BASE}/${entity_type}/${entity_id}/scripts | jq" @@ -60,30 +63,52 @@ execute_script() { echo "Execution started: $exec_id" # Poll until done - local elapsed=0 - while [ $elapsed -lt $MAX_WAIT ]; do + local start=$SECONDS + while [ $((SECONDS - start)) -lt "$MAX_WAIT" ]; do local exec_data - exec_data=$(curl -sf "${API_BASE}/${entity_type}/${entity_id}/scripts/${script_id}/executions/${exec_id}") + exec_data=$(curl -sf "${API_BASE}/${entity_type}/${entity_id}/scripts/${script_id}/executions/${exec_id}" 2>/dev/null) || exec_data="" + + if [ -z "$exec_data" ]; then + # Transient failure - keep polling + printf "." >&2 + sleep "$POLL_INTERVAL" + continue + fi + local status status=$(echo "$exec_data" | jq -r '.status') case "$status" in completed) + echo "" + # Show script output if available + local stdout + stdout=$(echo "$exec_data" | jq -r '.parameters.stdout // empty' 2>/dev/null) + if [ -n "$stdout" ]; then + echo "$stdout" + fi echo "Done." return 0 ;; failed|terminated) + echo "" echo "Script ${status}!" echo "$exec_data" | jq -r '.error // empty' 2>/dev/null + local err_stdout + err_stdout=$(echo "$exec_data" | jq -r '.parameters.stdout // empty' 2>/dev/null) + if [ -n "$err_stdout" ]; then + echo "$err_stdout" + fi return 1 ;; *) + printf "." >&2 sleep "$POLL_INTERVAL" - elapsed=$((elapsed + POLL_INTERVAL)) ;; esac done + echo "" echo "Timeout waiting for script execution (${MAX_WAIT}s)" return 1 } From 7af95a841aad92c1c60f47437c7bd8958dafb411 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 16:56:28 +0100 Subject: [PATCH 08/11] docs: address review findings - README consistency and gaps - Standardize exec_id placeholder to across all demos - Add GATEWAY_URL override example to each Scripts API section - Add curl/jq to turtlebot3 Prerequisites - Update sensor_diagnostics scripts table note to list all scripts - Add container_scripts/ to turtlebot3 File Structure section - Fix moveit stale {"data":{"value":...}} -> {"value":...} format --- demos/moveit_pick_place/README.md | 11 +++++++++-- demos/sensor_diagnostics/README.md | 11 +++++++++-- demos/turtlebot3_integration/README.md | 17 ++++++++++++++++- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/demos/moveit_pick_place/README.md b/demos/moveit_pick_place/README.md index 423f7c1..6e79903 100644 --- a/demos/moveit_pick_place/README.md +++ b/demos/moveit_pick_place/README.md @@ -240,7 +240,7 @@ curl http://localhost:8080/api/v1/apps/panda-arm-controller/configurations/gains # Set a parameter value curl -X PUT http://localhost:8080/api/v1/apps/panda-arm-controller/configurations/constraints.goal_time \ -H 'Content-Type: application/json' \ - -d '{"data": {"value": 0.5}}' + -d '{"value": 0.5}' ``` ## Scripts API @@ -272,7 +272,14 @@ curl -X POST http://localhost:8080/api/v1/components/moveit-planning/scripts/inj -d '{"execution_type": "now"}' | jq # Poll status (use execution ID from above response) -curl http://localhost:8080/api/v1/components/moveit-planning/scripts/inject-collision/executions/ | jq +curl http://localhost:8080/api/v1/components/moveit-planning/scripts/inject-collision/executions/ | jq +``` + +### Override Gateway URL + +```bash +# Point scripts at a non-default gateway +GATEWAY_URL=http://192.168.1.10:8080 ./inject-collision.sh ``` The host-side wrapper scripts (`./inject-collision.sh`, etc.) call the Scripts API automatically - no `docker exec` needed. Prerequisites: `curl` and `jq` must be installed on the host. diff --git a/demos/sensor_diagnostics/README.md b/demos/sensor_diagnostics/README.md index a2fac97..c6db136 100644 --- a/demos/sensor_diagnostics/README.md +++ b/demos/sensor_diagnostics/README.md @@ -162,7 +162,14 @@ curl -X POST http://localhost:8080/api/v1/components/compute-unit/scripts/inject ### Check Execution Status ```bash -curl http://localhost:8080/api/v1/components/compute-unit/scripts/inject-nan/executions/{exec_id} | jq +curl http://localhost:8080/api/v1/components/compute-unit/scripts/inject-nan/executions/ | jq +``` + +### Override Gateway URL + +```bash +# Point scripts at a non-default gateway +GATEWAY_URL=http://192.168.1.10:8080 ./inject-nan.sh ``` ### Available Scripts @@ -253,7 +260,7 @@ curl http://localhost:8080/api/v1/faults | jq | `inject-drift.sh` | Enable sensor drift | | `restore-normal.sh` | Clear all faults | -> **Note:** The inject and restore scripts (`inject-*.sh`, `restore-normal.sh`) are also available via the [Scripts API](#scripts-api) - callable as REST endpoints without requiring the host-side scripts. +> **Note:** All diagnostic scripts (`inject-*.sh`, `restore-normal.sh`, `run-diagnostics.sh`, `inject-fault-scenario.sh`) are also available via the [Scripts API](#scripts-api) - callable as REST endpoints without requiring the host-side scripts. ## Sensor Parameters diff --git a/demos/turtlebot3_integration/README.md b/demos/turtlebot3_integration/README.md index 9a8dd6a..079e156 100644 --- a/demos/turtlebot3_integration/README.md +++ b/demos/turtlebot3_integration/README.md @@ -25,6 +25,7 @@ This demo demonstrates: - Docker and docker-compose - X11 display server (Linux with GUI, or XQuartz on macOS) - (Optional) NVIDIA GPU + [nvidia-container-toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html) +- `curl` and `jq` (required for host-side scripts) ## Quick Start @@ -379,7 +380,14 @@ curl -X POST http://localhost:8080/api/v1/components/nav2-stack/scripts/inject-n ### Check Execution Status ```bash -curl http://localhost:8080/api/v1/components/nav2-stack/scripts/inject-nav-failure/executions/{exec_id} | jq +curl http://localhost:8080/api/v1/components/nav2-stack/scripts/inject-nav-failure/executions/ | jq +``` + +### Override Gateway URL + +```bash +# Point scripts at a non-default gateway +GATEWAY_URL=http://192.168.1.10:8080 ./inject-nav-failure.sh ``` ### Available Scripts @@ -508,6 +516,13 @@ demos/turtlebot3_integration/ │ ├── turtlebot3_manifest.yaml # SOVD manifest (entity hierarchy) │ ├── nav2_params.yaml # Nav2 navigation parameters │ └── turtlebot3_world.yaml # Map configuration +├── container_scripts/ +│ └── nav2-stack/ # Scripts API auto-discovery layout +│ ├── nav-health-check/ +│ ├── reset-navigation/ +│ ├── inject-nav-failure/ +│ ├── inject-localization-failure/ +│ └── restore-normal/ ├── launch/ │ └── demo.launch.py # ROS 2 launch file └── scripts/ From c766a90c5752adc7440361e5c69b1b3a3ef52cb3 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 17:49:50 +0100 Subject: [PATCH 09/11] fix: resolve shellcheck warnings (SC2034, SC1091) - Replace unused RESPONSE variable captures with direct curl > /dev/null (arm-self-test, planning-benchmark, run-diagnostics) - Add shellcheck source directive to all host-side wrappers --- demos/moveit_pick_place/arm-self-test.sh | 1 + .../moveit-planning/arm-self-test/script.bash | 4 ++-- .../moveit-planning/planning-benchmark/script.bash | 6 +++--- demos/moveit_pick_place/inject-collision.sh | 1 + demos/moveit_pick_place/inject-planning-failure.sh | 1 + demos/moveit_pick_place/planning-benchmark.sh | 1 + demos/moveit_pick_place/restore-normal.sh | 1 + .../compute-unit/run-diagnostics/script.bash | 2 +- demos/sensor_diagnostics/inject-drift.sh | 1 + demos/sensor_diagnostics/inject-failure.sh | 1 + demos/sensor_diagnostics/inject-fault-scenario.sh | 1 + demos/sensor_diagnostics/inject-nan.sh | 1 + demos/sensor_diagnostics/inject-noise.sh | 1 + demos/sensor_diagnostics/restore-normal.sh | 1 + demos/sensor_diagnostics/run-diagnostics.sh | 1 + demos/turtlebot3_integration/inject-localization-failure.sh | 1 + demos/turtlebot3_integration/inject-nav-failure.sh | 1 + demos/turtlebot3_integration/nav-health-check.sh | 1 + demos/turtlebot3_integration/reset-navigation.sh | 1 + demos/turtlebot3_integration/restore-normal.sh | 1 + 20 files changed, 23 insertions(+), 6 deletions(-) diff --git a/demos/moveit_pick_place/arm-self-test.sh b/demos/moveit_pick_place/arm-self-test.sh index ea39dbd..47eb2c5 100644 --- a/demos/moveit_pick_place/arm-self-test.sh +++ b/demos/moveit_pick_place/arm-self-test.sh @@ -2,6 +2,7 @@ # Arm self-test via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "arm-self-test" "Arm self-test" diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash index a42a898..b95ada2 100644 --- a/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/arm-self-test/script.bash @@ -6,14 +6,14 @@ API_BASE="${GATEWAY_URL}/api/v1" echo "Running arm self-test..." echo "Checking joint state broadcaster..." -if ! RESPONSE=$(curl -sf "${API_BASE}/apps/joint-state-broadcaster" 2>/dev/null); then +if ! curl -sf "${API_BASE}/apps/joint-state-broadcaster" > /dev/null 2>&1; then echo "FAIL: joint-state-broadcaster not responding" exit 1 fi echo "OK: joint-state-broadcaster responding" echo "Checking move-group..." -if ! RESPONSE=$(curl -sf "${API_BASE}/apps/move-group" 2>/dev/null); then +if ! curl -sf "${API_BASE}/apps/move-group" > /dev/null 2>&1; then echo "FAIL: move-group not responding" exit 1 fi diff --git a/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash b/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash index c19d73c..a951352 100644 --- a/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash +++ b/demos/moveit_pick_place/container_scripts/moveit-planning/planning-benchmark/script.bash @@ -6,21 +6,21 @@ API_BASE="${GATEWAY_URL}/api/v1" echo "Running planning benchmark..." echo "Checking move-group operations..." -if ! RESPONSE=$(curl -sf "${API_BASE}/apps/move-group/operations" 2>/dev/null); then +if ! curl -sf "${API_BASE}/apps/move-group/operations" > /dev/null 2>&1; then echo "FAIL: Cannot list move-group operations" exit 1 fi echo "OK: move-group operations available" echo "Checking pick-place-node..." -if ! RESPONSE=$(curl -sf "${API_BASE}/apps/pick-place-node" 2>/dev/null); then +if ! curl -sf "${API_BASE}/apps/pick-place-node" > /dev/null 2>&1; then echo "FAIL: pick-place-node not responding" exit 1 fi echo "OK: pick-place-node responding" echo "Checking manipulation monitor..." -if ! RESPONSE=$(curl -sf "${API_BASE}/apps/manipulation-monitor" 2>/dev/null); then +if ! curl -sf "${API_BASE}/apps/manipulation-monitor" > /dev/null 2>&1; then echo "FAIL: manipulation-monitor not responding" exit 1 fi diff --git a/demos/moveit_pick_place/inject-collision.sh b/demos/moveit_pick_place/inject-collision.sh index 8c5642b..788ea97 100755 --- a/demos/moveit_pick_place/inject-collision.sh +++ b/demos/moveit_pick_place/inject-collision.sh @@ -2,6 +2,7 @@ # Inject collision obstacle via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "inject-collision" "Inject collision obstacle" diff --git a/demos/moveit_pick_place/inject-planning-failure.sh b/demos/moveit_pick_place/inject-planning-failure.sh index 8edb5fa..db2365d 100755 --- a/demos/moveit_pick_place/inject-planning-failure.sh +++ b/demos/moveit_pick_place/inject-planning-failure.sh @@ -2,6 +2,7 @@ # Inject planning failure via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "inject-planning-failure" "Inject planning failure" diff --git a/demos/moveit_pick_place/planning-benchmark.sh b/demos/moveit_pick_place/planning-benchmark.sh index 886fac0..f0f2834 100644 --- a/demos/moveit_pick_place/planning-benchmark.sh +++ b/demos/moveit_pick_place/planning-benchmark.sh @@ -2,6 +2,7 @@ # Planning benchmark via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "planning-benchmark" "Planning benchmark" diff --git a/demos/moveit_pick_place/restore-normal.sh b/demos/moveit_pick_place/restore-normal.sh index 8067d7c..b10abc8 100755 --- a/demos/moveit_pick_place/restore-normal.sh +++ b/demos/moveit_pick_place/restore-normal.sh @@ -2,6 +2,7 @@ # Restore normal operation via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "restore-normal" "Restore normal operation" diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash index d33f9ad..61a39a1 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/run-diagnostics/script.bash @@ -11,7 +11,7 @@ ERRORS=0 for sensor_path in "${SENSORS[@]}"; do app=$(echo "$sensor_path" | cut -d/ -f1) echo "Checking $app..." - if ! RESPONSE=$(curl -sf "${API_BASE}/apps/${sensor_path}" 2>/dev/null); then + if ! curl -sf "${API_BASE}/apps/${sensor_path}" > /dev/null 2>&1; then echo " FAIL: No response from $app" ERRORS=$((ERRORS + 1)) else diff --git a/demos/sensor_diagnostics/inject-drift.sh b/demos/sensor_diagnostics/inject-drift.sh index 013d8ee..10de394 100755 --- a/demos/sensor_diagnostics/inject-drift.sh +++ b/demos/sensor_diagnostics/inject-drift.sh @@ -2,6 +2,7 @@ # Inject sensor drift set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-drift" "Inject sensor drift" diff --git a/demos/sensor_diagnostics/inject-failure.sh b/demos/sensor_diagnostics/inject-failure.sh index 57bd171..a34ec09 100755 --- a/demos/sensor_diagnostics/inject-failure.sh +++ b/demos/sensor_diagnostics/inject-failure.sh @@ -2,6 +2,7 @@ # Inject sensor failure set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-failure" "Inject sensor failure" diff --git a/demos/sensor_diagnostics/inject-fault-scenario.sh b/demos/sensor_diagnostics/inject-fault-scenario.sh index 9e33e4c..e511602 100644 --- a/demos/sensor_diagnostics/inject-fault-scenario.sh +++ b/demos/sensor_diagnostics/inject-fault-scenario.sh @@ -2,6 +2,7 @@ # Inject composite fault scenario set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-fault-scenario" "Inject composite fault scenario" diff --git a/demos/sensor_diagnostics/inject-nan.sh b/demos/sensor_diagnostics/inject-nan.sh index b06d26c..3a31136 100755 --- a/demos/sensor_diagnostics/inject-nan.sh +++ b/demos/sensor_diagnostics/inject-nan.sh @@ -2,6 +2,7 @@ # Inject NaN values set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-nan" "Inject NaN values" diff --git a/demos/sensor_diagnostics/inject-noise.sh b/demos/sensor_diagnostics/inject-noise.sh index ef1ddda..9809c8e 100755 --- a/demos/sensor_diagnostics/inject-noise.sh +++ b/demos/sensor_diagnostics/inject-noise.sh @@ -2,6 +2,7 @@ # Inject high noise set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-noise" "Inject high noise" diff --git a/demos/sensor_diagnostics/restore-normal.sh b/demos/sensor_diagnostics/restore-normal.sh index 7bf413a..8411209 100755 --- a/demos/sensor_diagnostics/restore-normal.sh +++ b/demos/sensor_diagnostics/restore-normal.sh @@ -2,6 +2,7 @@ # Restore normal operation set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "restore-normal" "Restore normal operation" diff --git a/demos/sensor_diagnostics/run-diagnostics.sh b/demos/sensor_diagnostics/run-diagnostics.sh index aac9d34..0c9dbe3 100644 --- a/demos/sensor_diagnostics/run-diagnostics.sh +++ b/demos/sensor_diagnostics/run-diagnostics.sh @@ -2,6 +2,7 @@ # Run sensor diagnostics set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "run-diagnostics" "Run sensor diagnostics" diff --git a/demos/turtlebot3_integration/inject-localization-failure.sh b/demos/turtlebot3_integration/inject-localization-failure.sh index 9cc8a66..be1f34d 100755 --- a/demos/turtlebot3_integration/inject-localization-failure.sh +++ b/demos/turtlebot3_integration/inject-localization-failure.sh @@ -2,6 +2,7 @@ # Inject localization failure via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "inject-localization-failure" "Inject localization failure" diff --git a/demos/turtlebot3_integration/inject-nav-failure.sh b/demos/turtlebot3_integration/inject-nav-failure.sh index 0b3f273..14aee7c 100755 --- a/demos/turtlebot3_integration/inject-nav-failure.sh +++ b/demos/turtlebot3_integration/inject-nav-failure.sh @@ -2,6 +2,7 @@ # Inject navigation failure via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "inject-nav-failure" "Inject navigation failure" diff --git a/demos/turtlebot3_integration/nav-health-check.sh b/demos/turtlebot3_integration/nav-health-check.sh index 2eff436..7607446 100755 --- a/demos/turtlebot3_integration/nav-health-check.sh +++ b/demos/turtlebot3_integration/nav-health-check.sh @@ -2,6 +2,7 @@ # Nav2 health check via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "nav-health-check" "Nav2 health check" diff --git a/demos/turtlebot3_integration/reset-navigation.sh b/demos/turtlebot3_integration/reset-navigation.sh index 034155f..873572a 100755 --- a/demos/turtlebot3_integration/reset-navigation.sh +++ b/demos/turtlebot3_integration/reset-navigation.sh @@ -2,6 +2,7 @@ # Reset navigation via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "reset-navigation" "Reset navigation" diff --git a/demos/turtlebot3_integration/restore-normal.sh b/demos/turtlebot3_integration/restore-normal.sh index f4d2528..ce3d592 100755 --- a/demos/turtlebot3_integration/restore-normal.sh +++ b/demos/turtlebot3_integration/restore-normal.sh @@ -2,6 +2,7 @@ # Restore normal operation via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../../lib/scripts-api.sh source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "restore-normal" "Restore normal operation" From db0e325ec13fc6df0aad02fa7d11f0ac9df10343 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 18:04:21 +0100 Subject: [PATCH 10/11] fix: shellcheck SC1091 - disable for sourced lib path --- demos/moveit_pick_place/arm-self-test.sh | 2 +- demos/moveit_pick_place/inject-collision.sh | 2 +- demos/moveit_pick_place/inject-planning-failure.sh | 2 +- demos/moveit_pick_place/planning-benchmark.sh | 2 +- demos/moveit_pick_place/restore-normal.sh | 2 +- demos/sensor_diagnostics/inject-drift.sh | 2 +- demos/sensor_diagnostics/inject-failure.sh | 2 +- demos/sensor_diagnostics/inject-fault-scenario.sh | 2 +- demos/sensor_diagnostics/inject-nan.sh | 2 +- demos/sensor_diagnostics/inject-noise.sh | 2 +- demos/sensor_diagnostics/restore-normal.sh | 2 +- demos/sensor_diagnostics/run-diagnostics.sh | 2 +- demos/turtlebot3_integration/inject-localization-failure.sh | 2 +- demos/turtlebot3_integration/inject-nav-failure.sh | 2 +- demos/turtlebot3_integration/nav-health-check.sh | 2 +- demos/turtlebot3_integration/reset-navigation.sh | 2 +- demos/turtlebot3_integration/restore-normal.sh | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/demos/moveit_pick_place/arm-self-test.sh b/demos/moveit_pick_place/arm-self-test.sh index 47eb2c5..9d5c12c 100644 --- a/demos/moveit_pick_place/arm-self-test.sh +++ b/demos/moveit_pick_place/arm-self-test.sh @@ -2,7 +2,7 @@ # Arm self-test via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "arm-self-test" "Arm self-test" diff --git a/demos/moveit_pick_place/inject-collision.sh b/demos/moveit_pick_place/inject-collision.sh index 788ea97..33f36f1 100755 --- a/demos/moveit_pick_place/inject-collision.sh +++ b/demos/moveit_pick_place/inject-collision.sh @@ -2,7 +2,7 @@ # Inject collision obstacle via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "inject-collision" "Inject collision obstacle" diff --git a/demos/moveit_pick_place/inject-planning-failure.sh b/demos/moveit_pick_place/inject-planning-failure.sh index db2365d..424badb 100755 --- a/demos/moveit_pick_place/inject-planning-failure.sh +++ b/demos/moveit_pick_place/inject-planning-failure.sh @@ -2,7 +2,7 @@ # Inject planning failure via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "inject-planning-failure" "Inject planning failure" diff --git a/demos/moveit_pick_place/planning-benchmark.sh b/demos/moveit_pick_place/planning-benchmark.sh index f0f2834..0e8f55a 100644 --- a/demos/moveit_pick_place/planning-benchmark.sh +++ b/demos/moveit_pick_place/planning-benchmark.sh @@ -2,7 +2,7 @@ # Planning benchmark via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "planning-benchmark" "Planning benchmark" diff --git a/demos/moveit_pick_place/restore-normal.sh b/demos/moveit_pick_place/restore-normal.sh index b10abc8..ef972e1 100755 --- a/demos/moveit_pick_place/restore-normal.sh +++ b/demos/moveit_pick_place/restore-normal.sh @@ -2,7 +2,7 @@ # Restore normal operation via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "moveit-planning" "restore-normal" "Restore normal operation" diff --git a/demos/sensor_diagnostics/inject-drift.sh b/demos/sensor_diagnostics/inject-drift.sh index 10de394..902ca83 100755 --- a/demos/sensor_diagnostics/inject-drift.sh +++ b/demos/sensor_diagnostics/inject-drift.sh @@ -2,7 +2,7 @@ # Inject sensor drift set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-drift" "Inject sensor drift" diff --git a/demos/sensor_diagnostics/inject-failure.sh b/demos/sensor_diagnostics/inject-failure.sh index a34ec09..971fbc7 100755 --- a/demos/sensor_diagnostics/inject-failure.sh +++ b/demos/sensor_diagnostics/inject-failure.sh @@ -2,7 +2,7 @@ # Inject sensor failure set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-failure" "Inject sensor failure" diff --git a/demos/sensor_diagnostics/inject-fault-scenario.sh b/demos/sensor_diagnostics/inject-fault-scenario.sh index e511602..5b6b7c9 100644 --- a/demos/sensor_diagnostics/inject-fault-scenario.sh +++ b/demos/sensor_diagnostics/inject-fault-scenario.sh @@ -2,7 +2,7 @@ # Inject composite fault scenario set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-fault-scenario" "Inject composite fault scenario" diff --git a/demos/sensor_diagnostics/inject-nan.sh b/demos/sensor_diagnostics/inject-nan.sh index 3a31136..ad8560c 100755 --- a/demos/sensor_diagnostics/inject-nan.sh +++ b/demos/sensor_diagnostics/inject-nan.sh @@ -2,7 +2,7 @@ # Inject NaN values set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-nan" "Inject NaN values" diff --git a/demos/sensor_diagnostics/inject-noise.sh b/demos/sensor_diagnostics/inject-noise.sh index 9809c8e..cff08de 100755 --- a/demos/sensor_diagnostics/inject-noise.sh +++ b/demos/sensor_diagnostics/inject-noise.sh @@ -2,7 +2,7 @@ # Inject high noise set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "inject-noise" "Inject high noise" diff --git a/demos/sensor_diagnostics/restore-normal.sh b/demos/sensor_diagnostics/restore-normal.sh index 8411209..42e2c9d 100755 --- a/demos/sensor_diagnostics/restore-normal.sh +++ b/demos/sensor_diagnostics/restore-normal.sh @@ -2,7 +2,7 @@ # Restore normal operation set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "restore-normal" "Restore normal operation" diff --git a/demos/sensor_diagnostics/run-diagnostics.sh b/demos/sensor_diagnostics/run-diagnostics.sh index 0c9dbe3..55e4246 100644 --- a/demos/sensor_diagnostics/run-diagnostics.sh +++ b/demos/sensor_diagnostics/run-diagnostics.sh @@ -2,7 +2,7 @@ # Run sensor diagnostics set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "compute-unit" "run-diagnostics" "Run sensor diagnostics" diff --git a/demos/turtlebot3_integration/inject-localization-failure.sh b/demos/turtlebot3_integration/inject-localization-failure.sh index be1f34d..965ea2d 100755 --- a/demos/turtlebot3_integration/inject-localization-failure.sh +++ b/demos/turtlebot3_integration/inject-localization-failure.sh @@ -2,7 +2,7 @@ # Inject localization failure via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "inject-localization-failure" "Inject localization failure" diff --git a/demos/turtlebot3_integration/inject-nav-failure.sh b/demos/turtlebot3_integration/inject-nav-failure.sh index 14aee7c..3e9e757 100755 --- a/demos/turtlebot3_integration/inject-nav-failure.sh +++ b/demos/turtlebot3_integration/inject-nav-failure.sh @@ -2,7 +2,7 @@ # Inject navigation failure via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "inject-nav-failure" "Inject navigation failure" diff --git a/demos/turtlebot3_integration/nav-health-check.sh b/demos/turtlebot3_integration/nav-health-check.sh index 7607446..7f36b3a 100755 --- a/demos/turtlebot3_integration/nav-health-check.sh +++ b/demos/turtlebot3_integration/nav-health-check.sh @@ -2,7 +2,7 @@ # Nav2 health check via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "nav-health-check" "Nav2 health check" diff --git a/demos/turtlebot3_integration/reset-navigation.sh b/demos/turtlebot3_integration/reset-navigation.sh index 873572a..fafa00e 100755 --- a/demos/turtlebot3_integration/reset-navigation.sh +++ b/demos/turtlebot3_integration/reset-navigation.sh @@ -2,7 +2,7 @@ # Reset navigation via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "reset-navigation" "Reset navigation" diff --git a/demos/turtlebot3_integration/restore-normal.sh b/demos/turtlebot3_integration/restore-normal.sh index ce3d592..43877e4 100755 --- a/demos/turtlebot3_integration/restore-normal.sh +++ b/demos/turtlebot3_integration/restore-normal.sh @@ -2,7 +2,7 @@ # Restore normal operation via Scripts API set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=../../lib/scripts-api.sh +# shellcheck disable=SC1091 source "${SCRIPT_DIR}/../../lib/scripts-api.sh" execute_script "components" "nav2-stack" "restore-normal" "Restore normal operation" From de39b822ebb9092cca63d555cf27ef87461610ad Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sun, 22 Mar 2026 18:28:42 +0100 Subject: [PATCH 11/11] fix: address PR review feedback (mfaferek93 + Copilot) - Inject scripts: handle partial failures with per-call error tracking instead of aborting on first failure (inject-nan, inject-noise, inject-fault-scenario) - Add -sf flag to turtlebot3 POST calls (inject-localization-failure, reset-navigation) so HTTP errors are caught - Add exec_id format validation to lib/scripts-api.sh - Use proper if/else instead of && || to satisfy shellcheck SC2015 --- .../inject-fault-scenario/script.bash | 41 +++++++++++++------ .../compute-unit/inject-nan/script.bash | 33 +++++++++++---- .../compute-unit/inject-noise/script.bash | 24 ++++++++--- .../inject-localization-failure/script.bash | 4 +- .../nav2-stack/reset-navigation/script.bash | 2 +- lib/scripts-api.sh | 4 ++ 6 files changed, 78 insertions(+), 30 deletions(-) diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash index c89c3f2..7ed2b7f 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-fault-scenario/script.bash @@ -6,22 +6,39 @@ GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" echo "Injecting composite fault scenario..." +ERRORS=0 -curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": true}' -echo "lidar-sim: inject_nan=true" +if curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' > /dev/null 2>&1; then + echo "lidar-sim: inject_nan=true" +else + echo "FAIL: lidar-sim"; ERRORS=$((ERRORS + 1)) +fi -curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": true}' -echo "imu-sim: inject_nan=true" +if curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' > /dev/null 2>&1; then + echo "imu-sim: inject_nan=true" +else + echo "FAIL: imu-sim"; ERRORS=$((ERRORS + 1)) +fi -curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": true}' -echo "gps-sim: inject_nan=true" +if curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' > /dev/null 2>&1; then + echo "gps-sim: inject_nan=true" +else + echo "FAIL: gps-sim"; ERRORS=$((ERRORS + 1)) +fi -curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ - -H "Content-Type: application/json" -d '{"value": true}' -echo "camera-sim: inject_black_frames=true" +if curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/inject_black_frames" \ + -H "Content-Type: application/json" -d '{"value": true}' > /dev/null 2>&1; then + echo "camera-sim: inject_black_frames=true" +else + echo "FAIL: camera-sim"; ERRORS=$((ERRORS + 1)) +fi +if [ $ERRORS -gt 0 ]; then + echo "Composite injection partially failed: $ERRORS error(s)" + exit 1 +fi echo "Composite fault scenario injected on all sensors" exit 0 diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash index 48f51a3..1cabb39 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-nan/script.bash @@ -5,17 +5,32 @@ set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" -curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": true}' -echo "lidar-sim: inject_nan=true" +ERRORS=0 -curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": true}' -echo "imu-sim: inject_nan=true" +if curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' > /dev/null 2>&1; then + echo "lidar-sim: inject_nan=true" +else + echo "FAIL: lidar-sim"; ERRORS=$((ERRORS + 1)) +fi -curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ - -H "Content-Type: application/json" -d '{"value": true}' -echo "gps-sim: inject_nan=true" +if curl -sf -X PUT "${API_BASE}/apps/imu-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' > /dev/null 2>&1; then + echo "imu-sim: inject_nan=true" +else + echo "FAIL: imu-sim"; ERRORS=$((ERRORS + 1)) +fi +if curl -sf -X PUT "${API_BASE}/apps/gps-sim/configurations/inject_nan" \ + -H "Content-Type: application/json" -d '{"value": true}' > /dev/null 2>&1; then + echo "gps-sim: inject_nan=true" +else + echo "FAIL: gps-sim"; ERRORS=$((ERRORS + 1)) +fi + +if [ $ERRORS -gt 0 ]; then + echo "NaN injection partially failed: $ERRORS error(s)" + exit 1 +fi echo "NaN injection enabled on lidar-sim, imu-sim, gps-sim" exit 0 diff --git a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash index 65ac5c1..6980f20 100644 --- a/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash +++ b/demos/sensor_diagnostics/container_scripts/compute-unit/inject-noise/script.bash @@ -5,13 +5,25 @@ set -eu GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" -curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ - -H "Content-Type: application/json" -d '{"value": 0.5}' -echo "lidar-sim: noise_stddev=0.5" +ERRORS=0 -curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ - -H "Content-Type: application/json" -d '{"value": 0.3}' -echo "camera-sim: noise_level=0.3" +if curl -sf -X PUT "${API_BASE}/apps/lidar-sim/configurations/noise_stddev" \ + -H "Content-Type: application/json" -d '{"value": 0.5}' > /dev/null 2>&1; then + echo "lidar-sim: noise_stddev=0.5" +else + echo "FAIL: lidar-sim"; ERRORS=$((ERRORS + 1)) +fi +if curl -sf -X PUT "${API_BASE}/apps/camera-sim/configurations/noise_level" \ + -H "Content-Type: application/json" -d '{"value": 0.3}' > /dev/null 2>&1; then + echo "camera-sim: noise_level=0.3" +else + echo "FAIL: camera-sim"; ERRORS=$((ERRORS + 1)) +fi + +if [ $ERRORS -gt 0 ]; then + echo "Noise injection partially failed: $ERRORS error(s)" + exit 1 +fi echo "High noise injected on lidar-sim and camera-sim" exit 0 diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash index adb433c..c34b396 100755 --- a/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/inject-localization-failure/script.bash @@ -6,7 +6,7 @@ GATEWAY_URL="${GATEWAY_URL:-http://localhost:8080}" API_BASE="${GATEWAY_URL}/api/v1" echo "Reinitializing AMCL global localization (scatters particles)..." -curl -s -X POST "${API_BASE}/apps/amcl/operations/reinitialize_global_localization/executions" \ +curl -sf -X POST "${API_BASE}/apps/amcl/operations/reinitialize_global_localization/executions" \ -H "Content-Type: application/json" \ -d '{}' @@ -15,7 +15,7 @@ echo "Waiting for particles to scatter..." sleep 2 echo "Sending navigation goal with high localization uncertainty..." -curl -s -X POST "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" \ +curl -sf -X POST "${API_BASE}/apps/bt-navigator/operations/navigate_to_pose/executions" \ -H "Content-Type: application/json" \ -d '{ "goal": { diff --git a/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash b/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash index a25e5d0..cedbfae 100755 --- a/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash +++ b/demos/turtlebot3_integration/container_scripts/nav2-stack/reset-navigation/script.bash @@ -20,7 +20,7 @@ fi echo "" echo "Resetting AMCL global localization..." -curl -s -X POST "${API_BASE}/apps/amcl/operations/reinitialize_global_localization/executions" \ +curl -sf -X POST "${API_BASE}/apps/amcl/operations/reinitialize_global_localization/executions" \ -H "Content-Type: application/json" \ -d '{}' diff --git a/lib/scripts-api.sh b/lib/scripts-api.sh index bd1b06f..1e43169 100644 --- a/lib/scripts-api.sh +++ b/lib/scripts-api.sh @@ -59,6 +59,10 @@ execute_script() { echo "$result" | jq '.' 2>/dev/null || echo "$result" exit 1 fi + if [[ ! "$exec_id" =~ ^[a-zA-Z0-9_-]+$ ]]; then + echo "Unexpected execution ID format: $exec_id" + exit 1 + fi echo "Execution started: $exec_id"