Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 64 additions & 13 deletions docs/api/rest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,54 @@ Server Capabilities
.. code-block:: json

{
"api_version": "1.0.0",
"gateway_version": "0.1.0",
"name": "ROS 2 Medkit Gateway",
"version": "0.3.0",
"api_base": "/api/v1",
"endpoints": [
{"path": "/areas", "supported_methods": ["GET"]},
{"path": "/components", "supported_methods": ["GET"]},
{"path": "/apps", "supported_methods": ["GET"]}
]
"GET /api/v1/health",
"GET /api/v1/areas",
"GET /api/v1/components",
"GET /api/v1/apps",
"GET /api/v1/functions",
"GET /api/v1/faults",
"..."
],
"capabilities": {
"discovery": true,
"data_access": true,
"operations": true,
"async_actions": true,
"configurations": true,
"faults": true,
"logs": true,
"bulk_data": true,
"cyclic_subscriptions": true,
"updates": false,
"authentication": false,
"tls": false
}
}

``GET /api/v1/version-info``
Get gateway version and status information.

**Example Response:**

.. code-block:: json

{
"items": [
{
"version": "1.0.0",
"base_uri": "/api/v1",
"vendor_info": {
"version": "0.3.0",
"name": "ros2_medkit"
}
}
]
}

``GET /api/v1/health``
Health check endpoint. Returns HTTP 200 if gateway is operational.

Expand All @@ -57,7 +93,7 @@ Areas
{
"id": "powertrain",
"name": "Powertrain",
"self": "/api/v1/areas/powertrain"
"href": "/api/v1/areas/powertrain"
}
]
}
Expand All @@ -71,6 +107,9 @@ Areas
``GET /api/v1/areas/{area_id}/components``
List components in a specific area.

Areas also support the same resource collections as components: ``/data``, ``/operations``,
``/configurations``, and ``/faults``. See the corresponding sections below.

Components
~~~~~~~~~~

Expand All @@ -86,9 +125,7 @@ Components
{
"id": "temp_sensor",
"name": "temp_sensor",
"self": "/api/v1/components/temp_sensor",
"area": "powertrain",
"resource_collections": ["data", "operations", "configurations", "faults"]
"href": "/api/v1/components/temp_sensor"
}
]
}
Expand Down Expand Up @@ -128,6 +165,9 @@ Functions
``GET /api/v1/functions/{function_id}/hosts``
List apps that host this function.

Functions also support ``/data``, ``/operations``, ``/configurations``, and ``/faults``.
See the corresponding sections below.

Data Endpoints
--------------

Expand Down Expand Up @@ -477,6 +517,16 @@ Query and manage faults.
- **204:** Fault cleared
- **404:** Fault not found

``DELETE /api/v1/components/{id}/faults``
Clear all faults for an entity.

Accepts the optional ``?status=`` query parameter (same values as ``GET /faults``).
Without it, clears pending and confirmed faults.

- **204:** Faults cleared (or none to clear)
- **400:** Invalid status parameter
- **503:** Fault manager unavailable

``DELETE /api/v1/faults``
Clear all faults across the system *(ros2_medkit extension, not SOVD)*.

Expand Down Expand Up @@ -1197,16 +1247,17 @@ The gateway implements a subset of the SOVD (Service-Oriented Vehicle Diagnostic
- Operations (``/operations``, ``/executions``)
- Configurations (``/configurations``)
- Faults (``/faults``) with ``environment_data`` and SOVD status object
- Logs (``/logs``) with severity filtering and per-entity configuration
- Bulk Data (``/bulk-data``) for binary data downloads (rosbags, logs)
- Software Updates (``/updates``) with async prepare/execute lifecycle
- Cyclic Subscriptions (``/cyclic-subscriptions``) with SSE-based periodic data delivery

**ros2_medkit Extensions:**

- ``/health`` - Health check endpoint
- ``/health`` - Health check with discovery pipeline stats
- ``/version-info`` - Gateway version information
- ``/manifest/status`` - Manifest discovery status
- SSE fault streaming - Real-time fault notifications
- ``DELETE /faults`` - Clear all faults globally
- ``GET /faults/stream`` - SSE real-time fault notifications
- ``x-medkit`` extension fields in responses

See Also
Expand Down
192 changes: 127 additions & 65 deletions docs/config/discovery-options.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Discovery Options Reference
===========================

.. contents::
:local:
:depth: 2

This document describes configuration options for the gateway's discovery system.
The discovery system maps ROS 2 graph entities (nodes, topics, services, actions)
to SOVD entities (areas, components, apps).
Expand Down Expand Up @@ -87,19 +91,89 @@ The ``min_topics_for_component`` parameter (default: 1) sets the minimum number
of topics required before creating a component. This can filter out namespaces
with only a few stray topics.

Merge Pipeline Options (Hybrid Mode)
-------------------------------------
Merge Pipeline (Hybrid Mode)
-----------------------------

In hybrid mode, the gateway uses a layered merge pipeline to combine entities
from multiple sources. Three layers contribute entities independently:

- **ManifestLayer** - entities declared in the YAML manifest (highest priority for identity/hierarchy)
- **RuntimeLayer** - entities discovered from the ROS 2 graph (highest priority for live data/status)
- **PluginLayer** - entities contributed by ``IntrospectionProvider`` plugins

Each layer's contribution is merged per **field-group** with configurable policies.

Field Groups
^^^^^^^^^^^^

.. list-table::
:header-rows: 1
:widths: 20 80

When using ``hybrid`` mode, the merge pipeline controls how entities from
different discovery layers are combined. The ``merge_pipeline`` section
configures gap-fill behavior for runtime-discovered entities.
* - Field Group
- Contents
* - ``identity``
- id, name, translation_id, description, tags
* - ``hierarchy``
- area, component_id, parent references, depends_on, hosts
* - ``live_data``
- topics, services, actions
* - ``status``
- is_online, bound_fqn
* - ``metadata``
- source, x-medkit extensions, custom metadata fields

Gap-Fill Configuration
^^^^^^^^^^^^^^^^^^^^^^
Merge Policies
^^^^^^^^^^^^^^

In hybrid mode, the manifest is the source of truth. Runtime (heuristic) discovery
fills gaps - entities that exist at runtime but aren't in the manifest. Gap-fill
controls restrict what the runtime layer is allowed to create:
Each layer declares a policy per field-group:

.. list-table::
:header-rows: 1
:widths: 20 80

* - Policy
- Behavior
* - ``authoritative``
- This layer's value wins over lower-priority layers.
* - ``enrichment``
- Fill empty fields only; don't override existing values.
* - ``fallback``
- Use only if no other layer provides the value.

**Defaults:**

- Manifest: ``authoritative`` for identity/hierarchy/metadata, ``enrichment`` for live_data, ``fallback`` for status
- Runtime: ``authoritative`` for live_data/status, ``enrichment`` for metadata, ``fallback`` for identity/hierarchy

Override per-layer policies in ``gateway_params.yaml``. Empty string means
"use layer default". Policy values are **case-sensitive** and must be lowercase
(``authoritative``, ``enrichment``, ``fallback``):

.. code-block:: yaml

discovery:
merge_pipeline:
layers:
manifest:
identity: "" # authoritative (default)
hierarchy: "" # authoritative (default)
live_data: "" # enrichment (default)
status: "" # fallback (default)
metadata: "" # authoritative (default)
runtime:
identity: "" # fallback (default)
hierarchy: "" # fallback (default)
live_data: "" # authoritative (default)
status: "" # authoritative (default)
metadata: "" # enrichment (default)

Gap-Fill
^^^^^^^^

In hybrid mode, the runtime layer can create heuristic entities for namespaces
not covered by the manifest. Gap-fill controls what the runtime layer is allowed
to create:

.. code-block:: yaml

Expand All @@ -110,10 +184,10 @@ controls restrict what the runtime layer is allowed to create:
allow_heuristic_components: true
allow_heuristic_apps: true
allow_heuristic_functions: false
namespace_whitelist: []
namespace_blacklist: []
# namespace_blacklist: ["/rosout"]
# namespace_whitelist: []

.. list-table:: Gap-Fill Options
.. list-table::
:header-rows: 1
:widths: 35 15 50

Expand All @@ -122,62 +196,52 @@ controls restrict what the runtime layer is allowed to create:
- Description
* - ``allow_heuristic_areas``
- ``true``
- Allow runtime layer to create Area entities not in the manifest
- Create areas from namespaces not in manifest.
* - ``allow_heuristic_components``
- ``true``
- Allow runtime layer to create Component entities not in the manifest
- Create synthetic components for unmanifested namespaces.
* - ``allow_heuristic_apps``
- ``true``
- Allow runtime layer to create App entities not in the manifest
- Create apps for nodes without manifest ``ros_binding``.
* - ``allow_heuristic_functions``
- ``false``
- Allow runtime layer to create Function entities (rarely useful at runtime)
* - ``namespace_whitelist``
- ``[]``
- If non-empty, only allow gap-fill from these ROS 2 namespaces (Areas and Components only)
- Create heuristic functions (not recommended).
* - ``namespace_blacklist``
- ``[]``
- Exclude gap-fill from these ROS 2 namespaces (Areas and Components only)

When both whitelist and blacklist are empty, all namespaces are eligible for gap-fill.
If whitelist is non-empty, only listed namespaces pass. Blacklist is always applied.

Namespace matching uses path-segment boundaries: ``/nav`` matches ``/nav`` and ``/nav/sub``
but NOT ``/navigation``. Both lists only filter Areas and Components (which carry
``namespace_path``). Apps and Functions are not namespace-filtered.


Merge Policies
^^^^^^^^^^^^^^

Each discovery layer declares a ``MergePolicy`` per field group. When two layers
provide the same entity (matched by ID), policies determine which values win:

.. list-table:: Merge Policies
:header-rows: 1
:widths: 25 75

* - Policy
- Description
* - ``AUTHORITATIVE``
- This layer's value wins over lower-priority layers.
If two AUTHORITATIVE layers conflict, a warning is logged and the
higher-priority (earlier) layer wins.
* - ``ENRICHMENT``
- Fill empty fields from this layer. Non-empty target values are preserved.
Two ENRICHMENT layers merge additively (collections are unioned).
* - ``FALLBACK``
- Use only if no other layer provides the value. Two FALLBACK layers
merge additively (first non-empty fills gaps).

**Built-in layer policies:**

- **ManifestLayer** (priority 1): IDENTITY, HIERARCHY, METADATA are AUTHORITATIVE.
LIVE_DATA is ENRICHMENT (runtime topics/services take precedence).
STATUS is FALLBACK (manifest cannot know online state).
- **RuntimeLayer** (priority 2): LIVE_DATA and STATUS are AUTHORITATIVE.
METADATA is ENRICHMENT. IDENTITY and HIERARCHY are FALLBACK.
- **PluginLayer** (priority 3+): All field groups ENRICHMENT
- Namespaces excluded from gap-fill (e.g., ``["/rosout"]``).
* - ``namespace_whitelist``
- ``[]``
- If non-empty, only these namespaces are eligible for gap-fill.

Health Endpoint
^^^^^^^^^^^^^^^

``GET /api/v1/health`` includes a ``discovery`` section in hybrid mode with
pipeline stats, linking results, and merge warnings:

.. code-block:: json

{
"status": "healthy",
"discovery": {
"mode": "hybrid",
"strategy": "hybrid",
"pipeline": {
"layers": ["manifest", "runtime", "plugin"],
"total_entities": 6,
"enriched_count": 5,
"conflict_count": 0,
"conflicts": [],
"id_collisions": 0,
"filtered_by_gap_fill": 0
},
"linking": {
"linked_count": 5,
"orphan_count": 1,
"binding_conflicts": 0
}
}
}
Comment on lines +219 to +244
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This health endpoint example doesn’t match the current /api/v1/health response. In code, discovery.strategy is the strategy name string (e.g. "hybrid"), and discovery.pipeline is MergeReport::to_json() (keys like layers, total_entities, enriched_count, conflicts, id_collisions, filtered_by_gap_fill)—it does not include an entity_source map. Please update the example to reflect the actual JSON fields returned by HealthHandlers::handle_health() / MergeReport::to_json().

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 80091bc - health endpoint example now matches MergeReport::to_json() output: total_entities, enriched_count, conflict_count, conflicts, id_collisions, filtered_by_gap_fill. Strategy name corrected to "hybrid".


Configuration Example
---------------------
Expand All @@ -201,15 +265,13 @@ Complete YAML configuration for runtime discovery:
topic_only_policy: "create_component"
min_topics_for_component: 2 # Require at least 2 topics

# Note: merge_pipeline settings only apply when mode is "hybrid"
# Merge pipeline (hybrid mode only)
merge_pipeline:
gap_fill:
allow_heuristic_areas: true
allow_heuristic_components: true
allow_heuristic_apps: true
allow_heuristic_functions: false
namespace_whitelist: []
namespace_blacklist: ["/rosout", "/parameter_events"]
namespace_blacklist: ["/rosout"]

Command Line Override
---------------------
Expand Down
Loading