diff --git a/README.md b/README.md index 9813666..539a6b9 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ to complete mobile robot integration: Both demos support: - REST API access via SOVD protocol -- Web UI for visualization ([sovd_web_ui](https://github.com/selfpatch/sovd_web_ui)) +- Web UI for visualization ([ros2_medkit_web_ui](https://github.com/selfpatch/ros2_medkit_web_ui)) - Fault injection and monitoring - Docker deployment for easy setup @@ -189,7 +189,7 @@ CI runs all 3 demos in parallel - each job builds the Docker image, starts the c ## Related Projects - [ros2_medkit](https://github.com/selfpatch/ros2_medkit) — Core diagnostics library with SOVD-compliant gateway -- [sovd_web_ui](https://github.com/selfpatch/sovd_web_ui) — Web-based visualization and control interface +- [ros2_medkit_web_ui](https://github.com/selfpatch/ros2_medkit_web_ui) — Web-based visualization and control interface - [ros2_medkit_mcp](https://github.com/selfpatch/ros2_medkit_mcp) — MCP server for LLM integration - [ros2_medkit documentation](https://selfpatch.github.io/ros2_medkit/) — Full documentation and API reference diff --git a/demos/moveit_pick_place/Dockerfile b/demos/moveit_pick_place/Dockerfile index 6d8afcc..beee585 100644 --- a/demos/moveit_pick_place/Dockerfile +++ b/demos/moveit_pick_place/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get update && apt-get install -y \ libcpp-httplib-dev \ ros-jazzy-rosbag2-storage-mcap \ ros-jazzy-foxglove-bridge \ + libsystemd-dev \ sqlite3 libsqlite3-dev git curl \ && rm -rf /var/lib/apt/lists/* @@ -41,13 +42,18 @@ RUN mkdir -p /var/lib/ros2_medkit/rosbags ARG ROS2_MEDKIT_REF=main WORKDIR ${COLCON_WS}/src RUN git clone --depth 1 --branch ${ROS2_MEDKIT_REF} https://github.com/selfpatch/ros2_medkit.git && \ + mv ros2_medkit/src/ros2_medkit_cmake . && \ 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 \ - ros2_medkit/src/ros2_medkit_cmake . && \ + ros2_medkit/src/ros2_medkit_diagnostic_bridge . && \ + mv ros2_medkit/src/ros2_medkit_plugins/ros2_medkit_graph_provider . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_beacon_common . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_topic_beacon . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_param_beacon . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_linux_introspection . && \ rm -rf ros2_medkit # Copy demo package from local context diff --git a/demos/moveit_pick_place/README.md b/demos/moveit_pick_place/README.md index 1d7f759..aa1b3c9 100644 --- a/demos/moveit_pick_place/README.md +++ b/demos/moveit_pick_place/README.md @@ -40,7 +40,7 @@ That's it! The script will: 1. Build the Docker image (first run: ~15-20 min, ~7 GB) 2. Set up X11 forwarding for Gazebo GUI 3. Launch Panda robot in factory world + MoveIt 2 + ros2_medkit gateway -4. Launch sovd_web_ui at http://localhost:3000 +4. Launch ros2_medkit_web_ui at http://localhost:3000 **REST API:** http://localhost:8080/api/v1/ **Web UI:** http://localhost:3000/ @@ -120,7 +120,7 @@ docker exec -it moveit_medkit_demo bash # Shell into container │ ┌─────────────┼─────────────┐ │ │ │ - sovd_web_ui curl/httpie MCP Server + ros2_medkit_web_ui curl/httpie MCP Server :3000 (LLM tools) ``` @@ -300,7 +300,7 @@ curl http://localhost:8080/api/v1/faults | jq '.items[] | {fault_code, severity_ ## Web UI -The sovd_web_ui container starts automatically at **http://localhost:3000**. +The ros2_medkit_web_ui container starts automatically at **http://localhost:3000**. Connect it to the gateway at `http://localhost:8080` to browse: - Entity tree (Areas → Components → Apps) diff --git a/demos/moveit_pick_place/config/medkit_params.yaml b/demos/moveit_pick_place/config/medkit_params.yaml index f72ef9f..3c38ebd 100644 --- a/demos/moveit_pick_place/config/medkit_params.yaml +++ b/demos/moveit_pick_place/config/medkit_params.yaml @@ -26,6 +26,9 @@ diagnostics: runtime: create_synthetic_components: false # Manifest defines components + # Plugin configuration (set by launch file when .so paths are resolved) + plugins: [""] + # Fault Manager configuration (runs in root namespace) fault_manager: ros__parameters: diff --git a/demos/moveit_pick_place/docker-compose.yml b/demos/moveit_pick_place/docker-compose.yml index 482493b..3e49279 100644 --- a/demos/moveit_pick_place/docker-compose.yml +++ b/demos/moveit_pick_place/docker-compose.yml @@ -79,9 +79,9 @@ services: source /root/demo_ws/install/setup.bash && ros2 launch moveit_medkit_demo demo.launch.py headless:=true" - # SOVD Web UI — pre-built from GHCR - sovd-web-ui: - image: ghcr.io/selfpatch/sovd_web_ui:latest - container_name: sovd_web_ui + # Web UI - pre-built from GHCR + medkit-web-ui: + image: ghcr.io/selfpatch/ros2_medkit_web_ui:latest + container_name: ros2_medkit_web_ui ports: - "3000:80" diff --git a/demos/moveit_pick_place/launch/demo.launch.py b/demos/moveit_pick_place/launch/demo.launch.py index 85d4d5c..988cbd4 100644 --- a/demos/moveit_pick_place/launch/demo.launch.py +++ b/demos/moveit_pick_place/launch/demo.launch.py @@ -11,7 +11,9 @@ import os +from ament_index_python.packages import get_package_prefix from ament_index_python.packages import get_package_share_directory +from ament_index_python.packages import PackageNotFoundError from launch import LaunchDescription from launch.actions import ( DeclareLaunchArgument, @@ -24,6 +26,18 @@ from launch_ros.actions import Node +def _resolve_plugin_path(package_name, lib_name): + """Resolve a gateway plugin .so path, returning empty string if not found.""" + try: + prefix = get_package_prefix(package_name) + path = os.path.join(prefix, 'lib', package_name, f'lib{lib_name}.so') + if os.path.isfile(path): + return path + except PackageNotFoundError: + pass + return '' + + def generate_launch_description(): # Get package share directories demo_pkg_dir = get_package_share_directory("moveit_medkit_demo") @@ -35,6 +49,23 @@ def generate_launch_description(): medkit_params_file = os.path.join(demo_pkg_dir, "config", "medkit_params.yaml") manifest_file = os.path.join(demo_pkg_dir, "config", "panda_manifest.yaml") + # Resolve plugin paths + graph_provider_path = _resolve_plugin_path( + 'ros2_medkit_graph_provider', 'ros2_medkit_graph_provider_plugin') + procfs_plugin_path = _resolve_plugin_path( + 'ros2_medkit_linux_introspection', 'procfs_introspection') + + # Build plugin overrides - only include plugins that were found + plugin_overrides = {} + active_plugins = [] + if graph_provider_path: + active_plugins.append('graph_provider') + plugin_overrides['plugins.graph_provider.path'] = graph_provider_path + if procfs_plugin_path: + active_plugins.append('procfs_introspection') + plugin_overrides['plugins.procfs_introspection.path'] = procfs_plugin_path + plugin_overrides['plugins'] = active_plugins + # Launch configuration variables use_sim_time = LaunchConfiguration("use_sim_time", default="False") headless = LaunchConfiguration("headless", default="False") @@ -128,6 +159,7 @@ def generate_launch_description(): "use_sim_time": use_sim_time, "discovery.manifest_path": manifest_file, }, + plugin_overrides, ], ), # === Foxglove Bridge (WebSocket on port 8765) === diff --git a/demos/moveit_pick_place/launch/demo_gazebo.launch.py b/demos/moveit_pick_place/launch/demo_gazebo.launch.py index 5107d38..72a0127 100644 --- a/demos/moveit_pick_place/launch/demo_gazebo.launch.py +++ b/demos/moveit_pick_place/launch/demo_gazebo.launch.py @@ -21,7 +21,9 @@ import xacro import yaml +from ament_index_python.packages import get_package_prefix from ament_index_python.packages import get_package_share_directory +from ament_index_python.packages import PackageNotFoundError from launch import LaunchDescription from launch.actions import ( DeclareLaunchArgument, @@ -36,6 +38,18 @@ from launch_ros.substitutions import FindPackageShare +def _resolve_plugin_path(package_name, lib_name): + """Resolve a gateway plugin .so path, returning empty string if not found.""" + try: + prefix = get_package_prefix(package_name) + path = os.path.join(prefix, 'lib', package_name, f'lib{lib_name}.so') + if os.path.isfile(path): + return path + except PackageNotFoundError: + pass + return '' + + def generate_launch_description(): # ── Package directories ────────────────────────────────────────── demo_pkg_dir = get_package_share_directory("moveit_medkit_demo") @@ -54,6 +68,23 @@ def generate_launch_description(): demo_pkg_dir, "config", "panda_manifest.yaml" ) + # Resolve plugin paths + graph_provider_path = _resolve_plugin_path( + 'ros2_medkit_graph_provider', 'ros2_medkit_graph_provider_plugin') + procfs_plugin_path = _resolve_plugin_path( + 'ros2_medkit_linux_introspection', 'procfs_introspection') + + # Build plugin overrides - only include plugins that were found + plugin_overrides = {} + active_plugins = [] + if graph_provider_path: + active_plugins.append('graph_provider') + plugin_overrides['plugins.graph_provider.path'] = graph_provider_path + if procfs_plugin_path: + active_plugins.append('procfs_introspection') + plugin_overrides['plugins.procfs_introspection.path'] = procfs_plugin_path + plugin_overrides['plugins'] = active_plugins + # Factory world file path factory_world = os.path.join( demo_pkg_dir, "worlds", "factory.sdf" @@ -472,6 +503,7 @@ def generate_launch_description(): "use_sim_time": True, "discovery.manifest_path": manifest_file, }, + plugin_overrides, ], ), # ═════════════════════════════════════════════════════════ diff --git a/demos/sensor_diagnostics/Dockerfile b/demos/sensor_diagnostics/Dockerfile index 0966fbd..7f2f9be 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 \ + libsystemd-dev \ sqlite3 \ libsqlite3-dev \ git \ @@ -23,16 +24,22 @@ RUN apt-get update && apt-get install -y \ jq \ && rm -rf /var/lib/apt/lists/* -# Clone ros2_medkit from GitHub (gateway + dependencies) +# Clone ros2_medkit from GitHub (gateway + dependencies + plugins) +ARG ROS2_MEDKIT_REF=main WORKDIR ${COLCON_WS}/src -RUN git clone --depth 1 https://github.com/selfpatch/ros2_medkit.git && \ +RUN git clone --depth 1 --branch ${ROS2_MEDKIT_REF} https://github.com/selfpatch/ros2_medkit.git && \ + mv ros2_medkit/src/ros2_medkit_cmake . && \ mv ros2_medkit/src/ros2_medkit_gateway . && \ mv ros2_medkit/src/ros2_medkit_serialization . && \ mv ros2_medkit/src/ros2_medkit_msgs . && \ 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/ros2_medkit_cmake . && \ + mv ros2_medkit/src/ros2_medkit_plugins/ros2_medkit_graph_provider . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_beacon_common . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_topic_beacon . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_param_beacon . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_linux_introspection . && \ rm -rf ros2_medkit # Copy demo package diff --git a/demos/sensor_diagnostics/config/medkit_params.yaml b/demos/sensor_diagnostics/config/medkit_params.yaml index 9bd2eca..744776e 100644 --- a/demos/sensor_diagnostics/config/medkit_params.yaml +++ b/demos/sensor_diagnostics/config/medkit_params.yaml @@ -24,6 +24,9 @@ diagnostics: runtime: create_synthetic_components: false # Manifest defines components + # Plugin configuration (set by launch file when .so paths are resolved) + plugins: [""] + # Fault Manager configuration (runs in root namespace) fault_manager: ros__parameters: diff --git a/demos/sensor_diagnostics/docker-compose.yml b/demos/sensor_diagnostics/docker-compose.yml index cc11a54..5048fc0 100644 --- a/demos/sensor_diagnostics/docker-compose.yml +++ b/demos/sensor_diagnostics/docker-compose.yml @@ -20,9 +20,9 @@ services: ros2 launch sensor_diagnostics_demo demo.launch.py" # Web UI for visualization (optional) - sovd-web-ui: - image: ghcr.io/selfpatch/sovd_web_ui:latest - container_name: sovd_web_ui + medkit-web-ui: + image: ghcr.io/selfpatch/ros2_medkit_web_ui:latest + container_name: ros2_medkit_web_ui ports: - "3000:80" depends_on: diff --git a/demos/sensor_diagnostics/launch/demo.launch.py b/demos/sensor_diagnostics/launch/demo.launch.py index 85a55a4..f34815b 100644 --- a/demos/sensor_diagnostics/launch/demo.launch.py +++ b/demos/sensor_diagnostics/launch/demo.launch.py @@ -20,13 +20,27 @@ import os +from ament_index_python.packages import get_package_prefix from ament_index_python.packages import get_package_share_directory +from ament_index_python.packages import PackageNotFoundError from launch import LaunchDescription from launch.actions import DeclareLaunchArgument from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node +def _resolve_plugin_path(package_name, lib_name): + """Resolve a gateway plugin .so path, returning empty string if not found.""" + try: + prefix = get_package_prefix(package_name) + path = os.path.join(prefix, 'lib', package_name, f'lib{lib_name}.so') + if os.path.isfile(path): + return path + except PackageNotFoundError: + pass + return '' + + def generate_launch_description(): # Get package directory pkg_dir = get_package_share_directory("sensor_diagnostics_demo") @@ -36,6 +50,23 @@ def generate_launch_description(): sensor_params_file = os.path.join(pkg_dir, "config", "sensor_params.yaml") manifest_file = os.path.join(pkg_dir, "config", "sensor_manifest.yaml") + # Resolve plugin paths + graph_provider_path = _resolve_plugin_path( + 'ros2_medkit_graph_provider', 'ros2_medkit_graph_provider_plugin') + procfs_plugin_path = _resolve_plugin_path( + 'ros2_medkit_linux_introspection', 'procfs_introspection') + + # Build plugin overrides - only include plugins that were found + plugin_overrides = {} + active_plugins = [] + if graph_provider_path: + active_plugins.append('graph_provider') + plugin_overrides['plugins.graph_provider.path'] = graph_provider_path + if procfs_plugin_path: + active_plugins.append('procfs_introspection') + plugin_overrides['plugins.procfs_introspection.path'] = procfs_plugin_path + plugin_overrides['plugins'] = active_plugins + # Launch arguments use_sim_time = LaunchConfiguration("use_sim_time", default="false") @@ -120,6 +151,7 @@ def generate_launch_description(): medkit_params_file, {"use_sim_time": use_sim_time}, {"discovery.manifest_path": manifest_file}, + plugin_overrides, ], ), # ===== Fault Manager (at root namespace) ===== diff --git a/demos/turtlebot3_integration/Dockerfile b/demos/turtlebot3_integration/Dockerfile index dd5e752..7831984 100644 --- a/demos/turtlebot3_integration/Dockerfile +++ b/demos/turtlebot3_integration/Dockerfile @@ -37,22 +37,29 @@ RUN apt-get update && apt-get install -y \ python3-requests \ nlohmann-json3-dev \ libcpp-httplib-dev \ + libsystemd-dev \ sqlite3 \ libsqlite3-dev \ git \ curl \ && rm -rf /var/lib/apt/lists/* -# Clone ros2_medkit from GitHub +# Clone ros2_medkit from GitHub (gateway + dependencies + plugins) +ARG ROS2_MEDKIT_REF=main WORKDIR ${COLCON_WS}/src -RUN git clone --depth 1 https://github.com/selfpatch/ros2_medkit.git && \ +RUN git clone --depth 1 --branch ${ROS2_MEDKIT_REF} https://github.com/selfpatch/ros2_medkit.git && \ + mv ros2_medkit/src/ros2_medkit_cmake . && \ 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 . && \ - mv ros2_medkit/src/ros2_medkit_cmake . && \ + mv ros2_medkit/src/ros2_medkit_plugins/ros2_medkit_graph_provider . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_beacon_common . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_topic_beacon . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_param_beacon . && \ + mv ros2_medkit/src/ros2_medkit_discovery_plugins/ros2_medkit_linux_introspection . && \ rm -rf ros2_medkit # Copy demo package from local context (this repo) @@ -66,7 +73,7 @@ WORKDIR ${COLCON_WS} RUN bash -c "source /opt/ros/jazzy/setup.bash && \ rosdep update && \ rosdep install --from-paths src --ignore-src -r -y && \ - colcon build --symlink-install" + colcon build --symlink-install --cmake-args -DBUILD_TESTING=OFF" # Setup environment RUN echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc && \ diff --git a/demos/turtlebot3_integration/README.md b/demos/turtlebot3_integration/README.md index 65ff80b..67e52df 100644 --- a/demos/turtlebot3_integration/README.md +++ b/demos/turtlebot3_integration/README.md @@ -18,7 +18,7 @@ This demo demonstrates: - **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 +- Controlling the robot via ros2_medkit_web_ui ## Prerequisites @@ -40,7 +40,7 @@ That's it! The script will: 1. Build the Docker images (first run takes ~5-10 min, downloads ~4GB) 2. Setup X11 forwarding for Gazebo GUI 3. Launch TurtleBot3 simulation + Nav2 + ros2_medkit gateway in **daemon mode** (background) -4. Launch sovd_web_ui at +4. Launch ros2_medkit_web_ui at **Note:** By default, the demo runs in **daemon mode** with **Gazebo GUI** enabled. This allows you to interact with ROS 2 while the demo is running. @@ -119,7 +119,7 @@ docker compose --profile nvidia logs -f ### Via Web UI -1. Connect to the gateway in sovd_web_ui +1. Connect to the gateway in ros2_medkit_web_ui 2. Find entity with `/cmd_vel` data 3. Enter velocity command JSON (or use form with fields from schema): @@ -449,7 +449,7 @@ curl -X DELETE http://localhost:8080/api/v1/faults │ ┌───────────────────────────────────────┼────────────────────┐ ▼ ▼ ▼ ▼ - sovd_web_ui curl/browser External tools MCP Server + ros2_medkit_web_ui curl/browser External tools MCP Server (localhost:3000) (ros2_medkit_mcp) ``` diff --git a/demos/turtlebot3_integration/config/medkit_params.yaml b/demos/turtlebot3_integration/config/medkit_params.yaml index fdc605f..36d2575 100644 --- a/demos/turtlebot3_integration/config/medkit_params.yaml +++ b/demos/turtlebot3_integration/config/medkit_params.yaml @@ -25,6 +25,9 @@ diagnostics: runtime: create_synthetic_components: false # Manifest defines components + # Plugin configuration (set by launch file when .so paths are resolved) + plugins: [""] + # Fault Manager configuration (runs in root namespace) fault_manager: ros__parameters: diff --git a/demos/turtlebot3_integration/docker-compose.yml b/demos/turtlebot3_integration/docker-compose.yml index c64e6c2..2ec393a 100644 --- a/demos/turtlebot3_integration/docker-compose.yml +++ b/demos/turtlebot3_integration/docker-compose.yml @@ -82,8 +82,8 @@ services: export TURTLEBOT3_MODEL=burger && ros2 launch turtlebot3_medkit_demo demo.launch.py headless:=true" - sovd-web-ui: - image: ghcr.io/selfpatch/sovd_web_ui:latest - container_name: sovd_web_ui + medkit-web-ui: + image: ghcr.io/selfpatch/ros2_medkit_web_ui:latest + container_name: ros2_medkit_web_ui ports: - "3000:80" diff --git a/demos/turtlebot3_integration/launch/demo.launch.py b/demos/turtlebot3_integration/launch/demo.launch.py index 7d32d00..d3c1ddb 100644 --- a/demos/turtlebot3_integration/launch/demo.launch.py +++ b/demos/turtlebot3_integration/launch/demo.launch.py @@ -10,7 +10,9 @@ import os +from ament_index_python.packages import get_package_prefix from ament_index_python.packages import get_package_share_directory +from ament_index_python.packages import PackageNotFoundError from launch import LaunchDescription from launch.actions import ( AppendEnvironmentVariable, @@ -24,6 +26,18 @@ from launch_ros.actions import Node +def _resolve_plugin_path(package_name, lib_name): + """Resolve a gateway plugin .so path, returning empty string if not found.""" + try: + prefix = get_package_prefix(package_name) + path = os.path.join(prefix, 'lib', package_name, f'lib{lib_name}.so') + if os.path.isfile(path): + return path + except PackageNotFoundError: + pass + return '' + + def generate_launch_description(): # Get package directories turtlebot3_gazebo_dir = get_package_share_directory("turtlebot3_gazebo") @@ -37,6 +51,23 @@ def generate_launch_description(): map_file = os.path.join(demo_pkg_dir, "config", "turtlebot3_world.yaml") manifest_file = os.path.join(demo_pkg_dir, "config", "turtlebot3_manifest.yaml") + # Resolve plugin paths + graph_provider_path = _resolve_plugin_path( + 'ros2_medkit_graph_provider', 'ros2_medkit_graph_provider_plugin') + procfs_plugin_path = _resolve_plugin_path( + 'ros2_medkit_linux_introspection', 'procfs_introspection') + + # Build plugin overrides - only include plugins that were found + plugin_overrides = {} + active_plugins = [] + if graph_provider_path: + active_plugins.append('graph_provider') + plugin_overrides['plugins.graph_provider.path'] = graph_provider_path + if procfs_plugin_path: + active_plugins.append('procfs_introspection') + plugin_overrides['plugins.procfs_introspection.path'] = procfs_plugin_path + plugin_overrides['plugins'] = active_plugins + # Gazebo world file world_file = os.path.join(turtlebot3_gazebo_dir, "worlds", "turtlebot3_world.world") @@ -167,6 +198,7 @@ def generate_launch_description(): "use_sim_time": use_sim_time, "discovery.manifest_path": manifest_file, }, + plugin_overrides, ], ), # Launch anomaly detector to monitor navigation and report faults via fault manager