From c843ded45ad35c7613710432ae199b214587e3e0 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Thu, 5 Feb 2026 18:40:46 +0000 Subject: [PATCH 1/6] feat(turtlebot3): add rosbag recording on fault confirmation Enable automatic rosbag capture when faults are confirmed: - Configure fault_manager with rosbag recording enabled - Record 10 seconds before + 2 seconds after fault confirmation - Use MCAP format for cross-platform compatibility - Store recordings in persistent Docker volume Configuration: - medkit_params.yaml: Add fault_manager parameters with snapshot and rosbag configuration - docker-compose.yml: Add medkit_data volume for persistent storage - launch/demo.launch.py: Pass medkit_params to fault_manager node - Dockerfile: Create /var/lib/ros2_medkit/rosbags directory Recorded topics include odometry, pose, scan, velocity commands, transforms, navigation status/feedback, costmaps, and diagnostics. Access rosbags via REST API: GET /faults/{code}/bulk-data/rosbags GET /faults/{code}/snapshots --- demos/turtlebot3_integration/Dockerfile | 3 + demos/turtlebot3_integration/README.md | 40 ++++++++++++- .../config/medkit_params.yaml | 59 +++++++++++++++++++ .../turtlebot3_integration/docker-compose.yml | 6 ++ .../launch/demo.launch.py | 6 +- 5 files changed, 111 insertions(+), 3 deletions(-) diff --git a/demos/turtlebot3_integration/Dockerfile b/demos/turtlebot3_integration/Dockerfile index 52437ba..baf037f 100644 --- a/demos/turtlebot3_integration/Dockerfile +++ b/demos/turtlebot3_integration/Dockerfile @@ -68,6 +68,9 @@ RUN bash -c "source /opt/ros/jazzy/setup.bash && \ rosdep install --from-paths src --ignore-src -r -y && \ colcon build --symlink-install" +# Create storage directories for faults and rosbags +RUN mkdir -p /var/lib/ros2_medkit/rosbags + # Setup environment RUN echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc && \ echo "source ${COLCON_WS}/install/setup.bash" >> ~/.bashrc && \ diff --git a/demos/turtlebot3_integration/README.md b/demos/turtlebot3_integration/README.md index 605e30b..56215b7 100644 --- a/demos/turtlebot3_integration/README.md +++ b/demos/turtlebot3_integration/README.md @@ -15,6 +15,7 @@ This demo demonstrates: - Running Nav2 navigation stack (AMCL, planner, controller) - Running ros2_medkit gateway with **manifest-based discovery** - Fault management via **diagnostic_bridge** (legacy /diagnostics support) +- **Rosbag snapshot capture** when faults are confirmed (MCAP format) - Querying robot data via **REST API** - Entity hierarchy: Areas → Components → Apps → Functions - Controlling the robot via sovd_web_ui @@ -74,11 +75,14 @@ ros2 topic echo /odom ### Stopping the Demo ```bash -./stop-demo.sh # Stop containers -./stop-demo.sh --volumes # Stop and remove volumes +./stop-demo.sh # Stop containers (preserves rosbag data) +./stop-demo.sh --volumes # Stop and remove volumes (deletes rosbag data) ./stop-demo.sh --images # Stop and remove images ``` +**Note:** Rosbag recordings are stored in a Docker volume (`turtlebot3_medkit_data`). +Use `--volumes` to delete this data when stopping. + ### 2. Access the Web UI The Web UI is automatically started by docker-compose and available at . @@ -211,10 +215,42 @@ curl http://localhost:8080/api/v1/faults | jq # Get faults for a specific area curl http://localhost:8080/api/v1/areas/robot/faults | jq +# Get fault details with environment data (includes snapshots) +curl http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED | jq + # Clear a specific fault curl -X DELETE http://localhost:8080/api/v1/apps/diagnostic-bridge/faults/TURTLEBOT3_NODE ``` +### Rosbag Snapshots (Bulk Data) + +When a fault is confirmed, the FaultManager automatically captures: +- **Freeze frame snapshots**: Latest messages from key topics (odometry, pose, scan) +- **Rosbag recording**: 10 seconds before + 2 seconds after fault confirmation + +```bash +# List bulk-data categories for an entity +curl http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED/bulk-data | jq + +# List rosbag files available for download +curl http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED/bulk-data/rosbags | jq + +# Download a rosbag file (returns MCAP format) +curl -O http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED/bulk-data/rosbags/{bulk_data_id} + +# Get fault snapshots (freeze frames) +curl http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED/snapshots | jq +``` + +**Recorded Topics:** +- `/odom`, `/amcl_pose`, `/scan` - Robot state +- `/cmd_vel` - Velocity commands +- `/tf`, `/tf_static` - Transforms +- `/navigate_to_pose/_action/status`, `/navigate_to_pose/_action/feedback` - Navigation state +- `/local_costmap/costmap`, `/global_costmap/costmap` - Costmaps +- `/plan` - Navigation plan +- `/diagnostics` - System diagnostics + ### Operations (Service Calls) ```bash diff --git a/demos/turtlebot3_integration/config/medkit_params.yaml b/demos/turtlebot3_integration/config/medkit_params.yaml index 74a30b9..5b7a1f5 100644 --- a/demos/turtlebot3_integration/config/medkit_params.yaml +++ b/demos/turtlebot3_integration/config/medkit_params.yaml @@ -27,3 +27,62 @@ diagnostics: discovery: runtime: create_synthetic_components: false # Manifest defines components + +# Fault Manager configuration (runs in root namespace) +fault_manager: + ros__parameters: + # Storage configuration + storage_type: "sqlite" + database_path: "/var/lib/ros2_medkit/faults.db" + + # Debounce configuration + confirmation_threshold: 0 # Immediate confirmation + healing_enabled: false + healing_threshold: 3 + auto_confirm_after_sec: 0.0 + + # Snapshot configuration (freeze frames) + snapshots: + enabled: true + background_capture: true # Non-blocking capture + timeout_sec: 2.0 + max_message_size: 131072 # 128KB max per message + + # Topics to capture for all faults + default_topics: + - /odom + - /amcl_pose + - /scan + - /tf + - /navigate_to_pose/_action/status + + # Rosbag recording configuration + rosbag: + enabled: true + duration_sec: 10.0 # Record 10 seconds before fault confirmation + duration_after_sec: 2.0 # Record 2 seconds after confirmation + lazy_start: false # Always recording (ring buffer) + format: "mcap" # MCAP format (recommended for cross-platform) + storage_path: "/var/lib/ros2_medkit/rosbags" + max_bag_size_mb: 100 # Max size per rosbag file + max_total_storage_mb: 1000 # 1GB total storage limit + auto_cleanup: true # Cleanup rosbags on fault clear + + # Topics to record (use 'config' or 'all') + topics: "config" # Use include/exclude lists below + include_topics: + - /odom + - /amcl_pose + - /scan + - /cmd_vel + - /tf + - /tf_static + - /navigate_to_pose/_action/status + - /navigate_to_pose/_action/feedback + - /local_costmap/costmap + - /global_costmap/costmap + - /plan + - /diagnostics + exclude_topics: + - /rosout + - /parameter_events diff --git a/demos/turtlebot3_integration/docker-compose.yml b/demos/turtlebot3_integration/docker-compose.yml index 7b1c78f..62df539 100644 --- a/demos/turtlebot3_integration/docker-compose.yml +++ b/demos/turtlebot3_integration/docker-compose.yml @@ -14,6 +14,7 @@ services: - HEADLESS=${HEADLESS:-false} volumes: - /tmp/.X11-unix:/tmp/.X11-unix:rw + - medkit_data:/var/lib/ros2_medkit # Persistent storage for faults and rosbags ports: - "8080:8080" stdin_open: true @@ -44,6 +45,7 @@ services: - NVIDIA_DRIVER_CAPABILITIES=all volumes: - /tmp/.X11-unix:/tmp/.X11-unix:rw + - medkit_data:/var/lib/ros2_medkit # Persistent storage for faults and rosbags ports: - "8080:8080" deploy: @@ -66,3 +68,7 @@ services: container_name: sovd_web_ui ports: - "3000:80" + +volumes: + medkit_data: + name: turtlebot3_medkit_data diff --git a/demos/turtlebot3_integration/launch/demo.launch.py b/demos/turtlebot3_integration/launch/demo.launch.py index 254320b..1355f4f 100644 --- a/demos/turtlebot3_integration/launch/demo.launch.py +++ b/demos/turtlebot3_integration/launch/demo.launch.py @@ -131,13 +131,17 @@ def generate_launch_description(): ), # Launch ros2_medkit fault_manager in root namespace # Aggregates faults from all nodes via ReportFault service + # Also handles snapshot and rosbag capture when faults are confirmed Node( package="ros2_medkit_fault_manager", executable="fault_manager_node", name="fault_manager", namespace="", output="screen", - parameters=[{"use_sim_time": use_sim_time}], + parameters=[ + medkit_params_file, + {"use_sim_time": use_sim_time}, + ], ), # Launch diagnostic_bridge under /bridge namespace # Converts legacy /diagnostics topic to faults From a808ce4e8fb58bb1b390e89ffc3d30b529f5bac1 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Thu, 5 Feb 2026 19:57:35 +0000 Subject: [PATCH 2/6] feat(sensor-demo): add snapshot and rosbag recording support Add fault_manager configuration to sensor_diagnostics demo with: - SQLite storage for fault persistence - Freeze-frame snapshot capture on fault confirmation - MCAP rosbag recording with ring buffer (10s pre + 2s post fault) - Sensor topics included: /sensors/scan, imu, fix, image_raw Infrastructure changes: - Pass medkit_params to fault_manager node in launch file - Create /var/lib/ros2_medkit/rosbags dir in Dockerfile - Add sqlite3 package to Dockerfile - Add persistent medkit_data volume to docker-compose - Add bulk-data/snapshot demo steps to check-demo.sh --- demos/sensor_diagnostics/Dockerfile | 4 ++ demos/sensor_diagnostics/check-demo.sh | 45 ++++++++++++++-- .../config/medkit_params.yaml | 52 +++++++++++++++++++ demos/sensor_diagnostics/docker-compose.yml | 6 +++ .../sensor_diagnostics/launch/demo.launch.py | 6 ++- 5 files changed, 108 insertions(+), 5 deletions(-) diff --git a/demos/sensor_diagnostics/Dockerfile b/demos/sensor_diagnostics/Dockerfile index 58a12e7..bd235a2 100644 --- a/demos/sensor_diagnostics/Dockerfile +++ b/demos/sensor_diagnostics/Dockerfile @@ -16,6 +16,7 @@ RUN apt-get update && apt-get install -y \ python3-requests \ nlohmann-json3-dev \ libcpp-httplib-dev \ + sqlite3 \ libsqlite3-dev \ git \ curl \ @@ -48,6 +49,9 @@ RUN bash -c "source /opt/ros/jazzy/setup.bash && \ --skip-keys='ament_cmake_clang_format ament_cmake_clang_tidy test_msgs example_interfaces sqlite3' && \ colcon build --symlink-install --cmake-args -DBUILD_TESTING=OFF" +# Create storage directories for faults and rosbags +RUN mkdir -p /var/lib/ros2_medkit/rosbags + # Setup environment RUN echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc && \ echo "source ${COLCON_WS}/install/setup.bash" >> ~/.bashrc diff --git a/demos/sensor_diagnostics/check-demo.sh b/demos/sensor_diagnostics/check-demo.sh index 8da9027..eacad07 100755 --- a/demos/sensor_diagnostics/check-demo.sh +++ b/demos/sensor_diagnostics/check-demo.sh @@ -90,6 +90,42 @@ curl -s "${API_BASE}/apps/lidar-sim/configurations" | jq '.items[] | {name: .nam echo_step "9. Checking Current Faults" curl -s "${API_BASE}/faults" | jq '.' +# If there are faults, demonstrate snapshot / bulk-data endpoints +FAULT_COUNT=$(curl -s "${API_BASE}/faults" | jq '.items | length') +if [ "$FAULT_COUNT" -gt 0 ]; then + FIRST_FAULT=$(curl -s "${API_BASE}/faults" | jq -r '.items[0].code') + FIRST_ENTITY=$(curl -s "${API_BASE}/faults" | jq -r '.items[0].entity_id') + + echo_step "10. Fault Detail with Environment Data (Snapshots)" + echo "Fetching fault ${FIRST_FAULT} on entity ${FIRST_ENTITY}..." + curl -s "${API_BASE}/${FIRST_ENTITY}/faults/${FIRST_FAULT}" | jq '{ + code: .item.code, + status: .item.status, + environment_data: { + extended_data_records: .environment_data.extended_data_records, + snapshot_count: (.environment_data.snapshots | length) + } + }' + + echo_step "11. Bulk-Data Categories (Rosbag Recordings)" + echo "Checking available bulk-data categories..." + curl -s "${API_BASE}/${FIRST_ENTITY}/bulk-data" | jq '.' + + echo_step "12. Bulk-Data Descriptors (Rosbag Files)" + echo "Listing available rosbag recordings..." + curl -s "${API_BASE}/${FIRST_ENTITY}/bulk-data/rosbags" | jq '.items[] | { + id: .id, + name: .name, + size: .size, + mimetype: .mimetype, + "x-medkit": ."x-medkit" + }' +else + echo "" + echo " No active faults. Inject a fault first to see snapshot/bulk-data features:" + echo " ./inject-noise.sh && sleep 5 && bash $0" +fi + echo "" echo_success "API demonstration complete!" echo "" @@ -100,9 +136,10 @@ echo " ./inject-nan.sh # Inject NaN values" echo " ./inject-drift.sh # Inject sensor drift" echo " ./restore-normal.sh # Restore normal operation" echo "" +echo "📸 After injecting a fault, check snapshots and rosbags:" +echo " curl ${API_BASE}/faults | jq # List faults" +echo " curl ${API_BASE}/components/lidar-unit/faults/ | jq # Fault detail + snapshots" +echo " curl ${API_BASE}/components/lidar-unit/bulk-data/rosbags | jq # List rosbag recordings" +echo "" echo "🌐 Web UI: http://localhost:3000" echo "🌐 REST API: http://localhost:8080/api/v1/" -echo "" -echo "📖 More examples:" -echo " curl ${API_BASE}/apps/imu-sim/configurations | jq # IMU parameters" -echo " curl ${API_BASE}/apps/gps-sim/data/fix | jq # GPS data" diff --git a/demos/sensor_diagnostics/config/medkit_params.yaml b/demos/sensor_diagnostics/config/medkit_params.yaml index 4bffa95..17d9a78 100644 --- a/demos/sensor_diagnostics/config/medkit_params.yaml +++ b/demos/sensor_diagnostics/config/medkit_params.yaml @@ -26,3 +26,55 @@ diagnostics: discovery: runtime: create_synthetic_components: false # Manifest defines components + +# Fault Manager configuration (runs in root namespace) +fault_manager: + ros__parameters: + # Storage configuration + storage_type: "sqlite" + database_path: "/var/lib/ros2_medkit/faults.db" + + # Debounce configuration + confirmation_threshold: 0 # Immediate confirmation + healing_enabled: false + healing_threshold: 3 + auto_confirm_after_sec: 0.0 + + # Snapshot configuration (freeze frames) + snapshots: + enabled: true + background_capture: true # Non-blocking capture + timeout_sec: 2.0 + max_message_size: 131072 # 128KB max per message + + # Topics to capture for all faults + default_topics: + - /sensors/scan + - /sensors/imu + - /sensors/fix + - /sensors/image_raw + - /diagnostics + + # Rosbag recording configuration + rosbag: + enabled: true + duration_sec: 10.0 # Record 10 seconds before fault confirmation + duration_after_sec: 2.0 # Record 2 seconds after confirmation + lazy_start: false # Always recording (ring buffer) + format: "mcap" # MCAP format (recommended for cross-platform) + storage_path: "/var/lib/ros2_medkit/rosbags" + max_bag_size_mb: 100 # Max size per rosbag file + max_total_storage_mb: 1000 # 1GB total storage limit + auto_cleanup: true # Cleanup rosbags on fault clear + + # Topics to record (use 'config' or 'all') + topics: "config" # Use include/exclude lists below + include_topics: + - /sensors/scan + - /sensors/imu + - /sensors/fix + - /sensors/image_raw + - /diagnostics + exclude_topics: + - /rosout + - /parameter_events diff --git a/demos/sensor_diagnostics/docker-compose.yml b/demos/sensor_diagnostics/docker-compose.yml index 2bf4f8a..f20f964 100644 --- a/demos/sensor_diagnostics/docker-compose.yml +++ b/demos/sensor_diagnostics/docker-compose.yml @@ -7,6 +7,8 @@ services: container_name: sensor_diagnostics_demo environment: - ROS_DOMAIN_ID=40 + volumes: + - medkit_data:/var/lib/ros2_medkit # Persistent storage for faults and rosbags ports: - "8080:8080" stdin_open: true @@ -46,3 +48,7 @@ services: curl -sf http://localhost:8080/api/v1/health && curl -sf http://localhost:8080/api/v1/apps | jq '.items[] | .id' && echo 'CI validation passed!'" + +volumes: + medkit_data: + name: sensor_diagnostics_medkit_data diff --git a/demos/sensor_diagnostics/launch/demo.launch.py b/demos/sensor_diagnostics/launch/demo.launch.py index 796ffd1..4a35590 100644 --- a/demos/sensor_diagnostics/launch/demo.launch.py +++ b/demos/sensor_diagnostics/launch/demo.launch.py @@ -125,13 +125,17 @@ def generate_launch_description(): # ===== Fault Manager (at root namespace) ===== # Services at /fault_manager/* (e.g., /fault_manager/report_fault) # Both paths report here: diagnostic_bridge (legacy) and anomaly_detector (modern) + # Also handles snapshot and rosbag capture when faults are confirmed Node( package="ros2_medkit_fault_manager", executable="fault_manager_node", name="fault_manager", namespace="", # Root namespace so services are at /fault_manager/* output="screen", - parameters=[{"use_sim_time": use_sim_time}], + parameters=[ + medkit_params_file, + {"use_sim_time": use_sim_time}, + ], ), ] ) From 4b42eef19abf351a487adad92b79cc5a7fcb59f7 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Fri, 6 Feb 2026 17:09:27 +0000 Subject: [PATCH 3/6] fix: address PR review comments - Move mkdir from Dockerfile build to runtime (volume mount masks build-time dirs) - Remove --recurse-submodules from git clone (broken submodule ref) - Fix check-demo.sh: null-safe fault parsing, use entity-scoped API paths - Fix turtlebot3 README: use correct entity-scoped bulk-data endpoints - Add mkdir to all docker-compose commands for both demos --- demos/sensor_diagnostics/Dockerfile | 9 +-- demos/sensor_diagnostics/check-demo.sh | 76 ++++++++++++------- demos/sensor_diagnostics/docker-compose.yml | 6 +- demos/turtlebot3_integration/Dockerfile | 5 +- demos/turtlebot3_integration/README.md | 12 +-- .../turtlebot3_integration/docker-compose.yml | 6 +- 6 files changed, 65 insertions(+), 49 deletions(-) diff --git a/demos/sensor_diagnostics/Dockerfile b/demos/sensor_diagnostics/Dockerfile index bd235a2..adc0d07 100644 --- a/demos/sensor_diagnostics/Dockerfile +++ b/demos/sensor_diagnostics/Dockerfile @@ -25,7 +25,7 @@ RUN apt-get update && apt-get install -y \ # Clone ros2_medkit from GitHub (gateway + dependencies) WORKDIR ${COLCON_WS}/src -RUN git clone --depth 1 --recurse-submodules https://github.com/selfpatch/ros2_medkit.git && \ +RUN git clone --depth 1 https://github.com/selfpatch/ros2_medkit.git && \ mv ros2_medkit/src/ros2_medkit_gateway . && \ mv ros2_medkit/src/ros2_medkit_serialization . && \ mv ros2_medkit/src/ros2_medkit_msgs . && \ @@ -49,9 +49,6 @@ RUN bash -c "source /opt/ros/jazzy/setup.bash && \ --skip-keys='ament_cmake_clang_format ament_cmake_clang_tidy test_msgs example_interfaces sqlite3' && \ colcon build --symlink-install --cmake-args -DBUILD_TESTING=OFF" -# Create storage directories for faults and rosbags -RUN mkdir -p /var/lib/ros2_medkit/rosbags - # Setup environment RUN echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc && \ echo "source ${COLCON_WS}/install/setup.bash" >> ~/.bashrc @@ -59,5 +56,5 @@ RUN echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc && \ # Expose gateway port EXPOSE 8080 -# Default command: launch the demo -CMD ["bash", "-c", "source /opt/ros/jazzy/setup.bash && source /root/demo_ws/install/setup.bash && ros2 launch sensor_diagnostics_demo demo.launch.py"] +# Default command: create storage dirs (volume mount hides build-time mkdir) and launch +CMD ["bash", "-c", "mkdir -p /var/lib/ros2_medkit/rosbags && source /opt/ros/jazzy/setup.bash && source /root/demo_ws/install/setup.bash && ros2 launch sensor_diagnostics_demo demo.launch.py"] diff --git a/demos/sensor_diagnostics/check-demo.sh b/demos/sensor_diagnostics/check-demo.sh index eacad07..48cec64 100755 --- a/demos/sensor_diagnostics/check-demo.sh +++ b/demos/sensor_diagnostics/check-demo.sh @@ -88,38 +88,56 @@ echo "These parameters can be modified at runtime to inject faults..." curl -s "${API_BASE}/apps/lidar-sim/configurations" | jq '.items[] | {name: .name, value: .value, type: .type}' echo_step "9. Checking Current Faults" -curl -s "${API_BASE}/faults" | jq '.' +FAULTS_JSON=$(curl -s "${API_BASE}/faults") +echo "$FAULTS_JSON" | jq '.' # If there are faults, demonstrate snapshot / bulk-data endpoints -FAULT_COUNT=$(curl -s "${API_BASE}/faults" | jq '.items | length') +FAULT_COUNT=$(echo "$FAULTS_JSON" | jq '.items | length') if [ "$FAULT_COUNT" -gt 0 ]; then - FIRST_FAULT=$(curl -s "${API_BASE}/faults" | jq -r '.items[0].code') - FIRST_ENTITY=$(curl -s "${API_BASE}/faults" | jq -r '.items[0].entity_id') - - echo_step "10. Fault Detail with Environment Data (Snapshots)" - echo "Fetching fault ${FIRST_FAULT} on entity ${FIRST_ENTITY}..." - curl -s "${API_BASE}/${FIRST_ENTITY}/faults/${FIRST_FAULT}" | jq '{ - code: .item.code, - status: .item.status, - environment_data: { - extended_data_records: .environment_data.extended_data_records, - snapshot_count: (.environment_data.snapshots | length) - } - }' - - echo_step "11. Bulk-Data Categories (Rosbag Recordings)" - echo "Checking available bulk-data categories..." - curl -s "${API_BASE}/${FIRST_ENTITY}/bulk-data" | jq '.' - - echo_step "12. Bulk-Data Descriptors (Rosbag Files)" - echo "Listing available rosbag recordings..." - curl -s "${API_BASE}/${FIRST_ENTITY}/bulk-data/rosbags" | jq '.items[] | { - id: .id, - name: .name, - size: .size, - mimetype: .mimetype, - "x-medkit": ."x-medkit" - }' + # Find the first fault that has both a non-null entity_id and code + FIRST_FAULT_ENTRY=$(echo "$FAULTS_JSON" | jq -r '.items[] | select(.entity_id != null and .code != null) | "\(.entity_type) \(.entity_id) \(.code)"' | head -n 1) + + if [ -z "$FIRST_FAULT_ENTRY" ]; then + echo "" + echo " Faults exist but none provide both 'entity_id' and 'code'." + echo " Skipping snapshot and bulk-data demonstration." + else + FIRST_ENTITY_TYPE=$(echo "$FIRST_FAULT_ENTRY" | awk '{print $1}') + FIRST_ENTITY=$(echo "$FIRST_FAULT_ENTRY" | awk '{print $2}') + FIRST_FAULT=$(echo "$FIRST_FAULT_ENTRY" | awk '{print $3}') + # Map entity_type to plural resource path (e.g., "app" -> "apps") + case "$FIRST_ENTITY_TYPE" in + app|apps) ENTITY_PATH="apps" ;; + component|components) ENTITY_PATH="components" ;; + area|areas) ENTITY_PATH="areas" ;; + *) ENTITY_PATH="apps" ;; + esac + + echo_step "10. Fault Detail with Environment Data (Snapshots)" + echo "Fetching fault ${FIRST_FAULT} on ${ENTITY_PATH}/${FIRST_ENTITY}..." + curl -s "${API_BASE}/${ENTITY_PATH}/${FIRST_ENTITY}/faults/${FIRST_FAULT}" | jq '{ + code: .item.code, + status: .item.status, + environment_data: { + extended_data_records: .environment_data.extended_data_records, + snapshot_count: (.environment_data.snapshots | length) + } + }' + + echo_step "11. Bulk-Data Categories (Rosbag Recordings)" + echo "Checking available bulk-data categories..." + curl -s "${API_BASE}/${ENTITY_PATH}/${FIRST_ENTITY}/bulk-data" | jq '.' + + echo_step "12. Bulk-Data Descriptors (Rosbag Files)" + echo "Listing available rosbag recordings..." + curl -s "${API_BASE}/${ENTITY_PATH}/${FIRST_ENTITY}/bulk-data/rosbags" | jq '.items[] | { + id: .id, + name: .name, + size: .size, + mimetype: .mimetype, + "x-medkit": ."x-medkit" + }' + fi else echo "" echo " No active faults. Inject a fault first to see snapshot/bulk-data features:" diff --git a/demos/sensor_diagnostics/docker-compose.yml b/demos/sensor_diagnostics/docker-compose.yml index f20f964..4d4de57 100644 --- a/demos/sensor_diagnostics/docker-compose.yml +++ b/demos/sensor_diagnostics/docker-compose.yml @@ -16,7 +16,8 @@ services: # Default command launches the full demo # Override with: docker compose run sensor-demo bash command: > - bash -c "source /opt/ros/jazzy/setup.bash && + bash -c "mkdir -p /var/lib/ros2_medkit/rosbags && + source /opt/ros/jazzy/setup.bash && source /root/demo_ws/install/setup.bash && ros2 launch sensor_diagnostics_demo demo.launch.py" @@ -41,7 +42,8 @@ services: ports: - "8080:8080" command: > - bash -c "source /opt/ros/jazzy/setup.bash && + bash -c "mkdir -p /var/lib/ros2_medkit/rosbags && + source /opt/ros/jazzy/setup.bash && source /root/demo_ws/install/setup.bash && ros2 launch sensor_diagnostics_demo demo.launch.py & sleep 10 && diff --git a/demos/turtlebot3_integration/Dockerfile b/demos/turtlebot3_integration/Dockerfile index baf037f..0713bed 100644 --- a/demos/turtlebot3_integration/Dockerfile +++ b/demos/turtlebot3_integration/Dockerfile @@ -45,7 +45,7 @@ RUN apt-get update && apt-get install -y \ # Clone ros2_medkit from GitHub WORKDIR ${COLCON_WS}/src -RUN git clone --depth 1 --recurse-submodules https://github.com/selfpatch/ros2_medkit.git && \ +RUN git clone --depth 1 https://github.com/selfpatch/ros2_medkit.git && \ mv ros2_medkit/src/ros2_medkit_gateway \ ros2_medkit/src/ros2_medkit_msgs \ ros2_medkit/src/ros2_medkit_serialization \ @@ -68,9 +68,6 @@ RUN bash -c "source /opt/ros/jazzy/setup.bash && \ rosdep install --from-paths src --ignore-src -r -y && \ colcon build --symlink-install" -# Create storage directories for faults and rosbags -RUN mkdir -p /var/lib/ros2_medkit/rosbags - # Setup environment RUN echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc && \ echo "source ${COLCON_WS}/install/setup.bash" >> ~/.bashrc && \ diff --git a/demos/turtlebot3_integration/README.md b/demos/turtlebot3_integration/README.md index 56215b7..2b815fa 100644 --- a/demos/turtlebot3_integration/README.md +++ b/demos/turtlebot3_integration/README.md @@ -230,16 +230,16 @@ When a fault is confirmed, the FaultManager automatically captures: ```bash # List bulk-data categories for an entity -curl http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED/bulk-data | jq +curl http://localhost:8080/api/v1/apps/{entity_id}/bulk-data | jq # List rosbag files available for download -curl http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED/bulk-data/rosbags | jq +curl http://localhost:8080/api/v1/apps/{entity_id}/bulk-data/rosbags | jq # Download a rosbag file (returns MCAP format) -curl -O http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED/bulk-data/rosbags/{bulk_data_id} +curl -O http://localhost:8080/api/v1/apps/{entity_id}/bulk-data/rosbags/{fault_code} -# Get fault snapshots (freeze frames) -curl http://localhost:8080/api/v1/faults/NAVIGATION_GOAL_ABORTED/snapshots | jq +# Get fault detail with snapshots (freeze frames) +curl http://localhost:8080/api/v1/apps/{entity_id}/faults/{fault_code} | jq ``` **Recorded Topics:** @@ -409,7 +409,7 @@ curl http://localhost:8080/api/v1/faults | jq curl http://localhost:8080/api/v1/areas/navigation/faults | jq # Clear specific fault -curl -X DELETE http://localhost:8080/api/v1/faults/{fault_id} +curl -X DELETE http://localhost:8080/api/v1/apps/{entity_id}/faults/{fault_code} # Clear all faults curl -X DELETE http://localhost:8080/api/v1/faults diff --git a/demos/turtlebot3_integration/docker-compose.yml b/demos/turtlebot3_integration/docker-compose.yml index 62df539..c00c4de 100644 --- a/demos/turtlebot3_integration/docker-compose.yml +++ b/demos/turtlebot3_integration/docker-compose.yml @@ -20,7 +20,8 @@ services: stdin_open: true tty: true command: > - bash -c "source /opt/ros/jazzy/setup.bash && + bash -c "mkdir -p /var/lib/ros2_medkit/rosbags && + source /opt/ros/jazzy/setup.bash && source /root/demo_ws/install/setup.bash && export TURTLEBOT3_MODEL=burger && ros2 launch turtlebot3_medkit_demo demo.launch.py headless:=$${HEADLESS}" @@ -58,7 +59,8 @@ services: stdin_open: true tty: true command: > - bash -c "source /opt/ros/jazzy/setup.bash && + bash -c "mkdir -p /var/lib/ros2_medkit/rosbags && + source /opt/ros/jazzy/setup.bash && source /root/demo_ws/install/setup.bash && export TURTLEBOT3_MODEL=burger && ros2 launch turtlebot3_medkit_demo demo.launch.py headless:=$${HEADLESS}" From 20ff9def8ec96abf22b6eb91ae34446e7cb67943 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Fri, 6 Feb 2026 17:22:21 +0000 Subject: [PATCH 4/6] fix(docker): remove cloning of dynmsg in demo docker --- demos/sensor_diagnostics/Dockerfile | 1 - demos/turtlebot3_integration/Dockerfile | 1 - 2 files changed, 2 deletions(-) diff --git a/demos/sensor_diagnostics/Dockerfile b/demos/sensor_diagnostics/Dockerfile index adc0d07..1295195 100644 --- a/demos/sensor_diagnostics/Dockerfile +++ b/demos/sensor_diagnostics/Dockerfile @@ -32,7 +32,6 @@ RUN git clone --depth 1 https://github.com/selfpatch/ros2_medkit.git && \ mv ros2_medkit/src/ros2_medkit_fault_manager . && \ mv ros2_medkit/src/ros2_medkit_fault_reporter . && \ mv ros2_medkit/src/ros2_medkit_diagnostic_bridge . && \ - mv ros2_medkit/src/dynamic_message_introspection/dynmsg . && \ rm -rf ros2_medkit # Copy demo package diff --git a/demos/turtlebot3_integration/Dockerfile b/demos/turtlebot3_integration/Dockerfile index 0713bed..5f8edd6 100644 --- a/demos/turtlebot3_integration/Dockerfile +++ b/demos/turtlebot3_integration/Dockerfile @@ -52,7 +52,6 @@ RUN git clone --depth 1 https://github.com/selfpatch/ros2_medkit.git && \ ros2_medkit/src/ros2_medkit_fault_manager \ ros2_medkit/src/ros2_medkit_fault_reporter \ ros2_medkit/src/ros2_medkit_diagnostic_bridge \ - ros2_medkit/src/dynamic_message_introspection . && \ rm -rf ros2_medkit # Copy demo package from local context (this repo) From e0dce571ebbfee91d5633d42be5f9f965390e325 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Fri, 6 Feb 2026 17:45:09 +0000 Subject: [PATCH 5/6] fix: correct mv syntax in turtlebot3 Dockerfile The mv command was missing destination directory '.' and '&&' separators between individual moves, causing 'mv: invalid option -- r' error because 'rm -rf ros2_medkit' was interpreted as mv arguments. --- demos/turtlebot3_integration/Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/demos/turtlebot3_integration/Dockerfile b/demos/turtlebot3_integration/Dockerfile index 5f8edd6..8988a49 100644 --- a/demos/turtlebot3_integration/Dockerfile +++ b/demos/turtlebot3_integration/Dockerfile @@ -46,12 +46,12 @@ RUN apt-get update && apt-get install -y \ # Clone ros2_medkit from GitHub WORKDIR ${COLCON_WS}/src RUN git clone --depth 1 https://github.com/selfpatch/ros2_medkit.git && \ - mv ros2_medkit/src/ros2_medkit_gateway \ - ros2_medkit/src/ros2_medkit_msgs \ - ros2_medkit/src/ros2_medkit_serialization \ - ros2_medkit/src/ros2_medkit_fault_manager \ - ros2_medkit/src/ros2_medkit_fault_reporter \ - ros2_medkit/src/ros2_medkit_diagnostic_bridge \ + mv ros2_medkit/src/ros2_medkit_gateway . && \ + mv ros2_medkit/src/ros2_medkit_msgs . && \ + mv ros2_medkit/src/ros2_medkit_serialization . && \ + mv ros2_medkit/src/ros2_medkit_fault_manager . && \ + mv ros2_medkit/src/ros2_medkit_fault_reporter . && \ + mv ros2_medkit/src/ros2_medkit_diagnostic_bridge . && \ rm -rf ros2_medkit # Copy demo package from local context (this repo) From e1e6549c92d2f792abfbe6989183fc5d5f215c21 Mon Sep 17 00:00:00 2001 From: Bartosz Burda Date: Sat, 7 Feb 2026 09:02:02 +0000 Subject: [PATCH 6/6] refactor: Remove volume mapping for rosbag storage Volume mapping could cause issues when user has old faults in the fresh demo environment. Removing it to avoid confusion and potential issues with rosbag storage. Users can still access rosbag files through the container if needed. --- demos/sensor_diagnostics/docker-compose.yml | 6 ------ demos/turtlebot3_integration/README.md | 7 +++---- demos/turtlebot3_integration/docker-compose.yml | 6 ------ 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/demos/sensor_diagnostics/docker-compose.yml b/demos/sensor_diagnostics/docker-compose.yml index 4d4de57..541d864 100644 --- a/demos/sensor_diagnostics/docker-compose.yml +++ b/demos/sensor_diagnostics/docker-compose.yml @@ -7,8 +7,6 @@ services: container_name: sensor_diagnostics_demo environment: - ROS_DOMAIN_ID=40 - volumes: - - medkit_data:/var/lib/ros2_medkit # Persistent storage for faults and rosbags ports: - "8080:8080" stdin_open: true @@ -50,7 +48,3 @@ services: curl -sf http://localhost:8080/api/v1/health && curl -sf http://localhost:8080/api/v1/apps | jq '.items[] | .id' && echo 'CI validation passed!'" - -volumes: - medkit_data: - name: sensor_diagnostics_medkit_data diff --git a/demos/turtlebot3_integration/README.md b/demos/turtlebot3_integration/README.md index 2b815fa..65ff80b 100644 --- a/demos/turtlebot3_integration/README.md +++ b/demos/turtlebot3_integration/README.md @@ -75,13 +75,12 @@ ros2 topic echo /odom ### Stopping the Demo ```bash -./stop-demo.sh # Stop containers (preserves rosbag data) -./stop-demo.sh --volumes # Stop and remove volumes (deletes rosbag data) +./stop-demo.sh # Stop containers ./stop-demo.sh --images # Stop and remove images ``` -**Note:** Rosbag recordings are stored in a Docker volume (`turtlebot3_medkit_data`). -Use `--volumes` to delete this data when stopping. +**Note:** Fault data and rosbag recordings are ephemeral — they are stored +inside the container and cleared on restart. ### 2. Access the Web UI diff --git a/demos/turtlebot3_integration/docker-compose.yml b/demos/turtlebot3_integration/docker-compose.yml index c00c4de..e5817f4 100644 --- a/demos/turtlebot3_integration/docker-compose.yml +++ b/demos/turtlebot3_integration/docker-compose.yml @@ -14,7 +14,6 @@ services: - HEADLESS=${HEADLESS:-false} volumes: - /tmp/.X11-unix:/tmp/.X11-unix:rw - - medkit_data:/var/lib/ros2_medkit # Persistent storage for faults and rosbags ports: - "8080:8080" stdin_open: true @@ -46,7 +45,6 @@ services: - NVIDIA_DRIVER_CAPABILITIES=all volumes: - /tmp/.X11-unix:/tmp/.X11-unix:rw - - medkit_data:/var/lib/ros2_medkit # Persistent storage for faults and rosbags ports: - "8080:8080" deploy: @@ -70,7 +68,3 @@ services: container_name: sovd_web_ui ports: - "3000:80" - -volumes: - medkit_data: - name: turtlebot3_medkit_data