# Argonaut 3 — UI Design Specification **Version:** 2.0 **Date:** 9 July 2026 **Status:** ⚠ **SAFETY-CRITICAL FINDING — W1 does not see the vehicle.** With the flight controller unreachable, W1 displayed GREEN / "Systems nominal". Every widget reflects the **RPi5 autonomy stack**, not the vehicle. In AUV mode W1 is the pre-dive GO/NO-GO gate (`health_role: gate`) — as built it would pass a dive with a dead FC. See §W1 and DIR-7 / DIR-12. **OPEN.** **This session:** `rov_api` `/mode` endpoints (GET/POST) built, deployed, tested (`9109439`). **W8 Mode Indicator built and validated live — render + data** (`87000e6`) — the first widget beyond W0/W1 confirmed with real data. Chronic "widgets show no data" fault root-caused: `cockpit_bridge` had no `respawn`, so a boot-time race left the data-lake feed permanently dead (`c099c00`); fixed and verified across a cold stack restart. **Prior status (v1.9):** FastAPI backend (`rov_api`) core deployed and verified on RPi5:8081. W3 backend (`/abort`) now LIVE and confirmed end-to-end (button path → failsafe_monitor FSM). Mission start/stop endpoints available. W3 widget label decision "RETURN TO SAFE" stands; widget code update + `FASTAPI_HOST` config still pending. W6 committed as `w6_altitude.json`. Stage 2b endpoints (return budget, backup, mission upload) deferred. Production data path confirmed as cockpit_bridge WebSocket. --- ## Version History | Version | Date | Changes | |---|---|---| | 2.0 | 9 Jul 2026 | **⚠ SAFETY-CRITICAL: W1 does not see the vehicle.** With the FC unreachable, W1 showed GREEN / "Systems nominal". `failsafe_monitor` does not gate on FC connectivity; absence of data is being read as good data. In AUV mode W1 is the pre-dive GO/NO-GO gate — as built it would pass a dive with a dead flight controller. W1 spec rewritten with required fixes; status line promoted from debug affordance to operational element carrying cause text. See DIR-7, DIR-12. **OPEN.** **W8 Mode Indicator added** — read-only active-profile display, colour-coded by base mode; validated live (render + data), the first widget beyond W0/W1 so confirmed (`87000e6`). Mode *switching* explicitly excluded from W8 (belongs to the setup wizard, `POST /mode`); ArduSub arm/vehicle mode excluded (Cockpit shows it natively). **Widget testing status corrected** — "renders" and "works" are now explicitly distinguished throughout: validated with live data = W0, W1, W8; render-only = W2–W6; not built = W7. Prior wording conflated the two. **`rov_api` `/mode` endpoints** (GET/POST) built, deployed, tested incl. rejection path (`9109439`). **Chronic "widgets show no data" root-caused:** `cockpit_bridge` was launched but had no `respawn`; a boot-time race left it permanently dead, killing the whole data-lake feed. Fixed (`c099c00`), verified across a cold stack restart. This — not the widgets — was why W1 "sometimes" worked. | | 1.9 | July 2026 | FastAPI backend (`rov_api`) core deployed on RPi5:8081 (commit 66a18ef). W3 backend `/abort` now LIVE — verified end-to-end (POST /abort → Bool /rov/mission/abort → failsafe_monitor flag_manual_abort → FSM NORMAL→HOLD_AND_RECOVER). Mission endpoints `/mission/start` and `/mission/stop` available and tested. `/health` returns live mission/recording/failsafe. W6 committed to Gitea as `w6_altitude.json` (underscore convention); probe widget removed. W3 widget code update (label + FASTAPI_HOST) still pending. Stage 2b deferred: `/return_budget` (W5), `/backup/*` (W7), `/mission/upload`. FastAPI runs as argonaut-api.service (manual-start). | | 1.8 | July 2026 | W6 Altitude Widget built and deployed (`w6_altitude.json`). NO SONAR state confirmed — correct baseline with no Ping2. cockpit_bridge updated to 8 variables: `rov-recording` and `rov-altitude` added. W3 button label decided: "RETURN TO SAFE" (assessed vehicle return-to-safe per DIR-5, not emergency hard-stop; widget code update pending). Widget testing status clarified: W0 confirmed live, W1–W5 visual confirmed only, W7 not built. Production data path: cockpit_bridge WebSocket is confirmed production path — NAMED_VALUE bridge node not required. | | 1.7 | June 2026 | W7 (Backup Due Notice) added to widget scope — operator-facing reminder that a device backup is due, with delay options. Depends on RPi5:8081 FastAPI backend. See W7 spec and DIR-11. | | 1.6 | June 2026 | Cockpit v1.18.2 DIY widget API confirmed via probe widget. Full `window.cockpit` surface (17 keys) enumerated. Confirmed: `getDataLakeVariableData(id)` returns live value. W1–W5 can now be built. | | 1.5 | June 2026 | Production data path confirmed as `cockpit_bridge` ROS2 node (port 9001, in rov_mission, commit 12b5161) — all 6 vars live in data lake as `external/rov-*`. W0 working with scroll fix. | | 1.4 | May 2026 | Data injection architecture fully documented with confirmed working path (Generic WebSocket) and all failed approaches. Widget variable names corrected. | | 1.3 | May 2026 | Cockpit native app confirmed required. JSON import format confirmed. CSS rules established. All 6 widgets complete and committed to Gitea. | | 1.2 | May 2026 | Confirmed DIY widget approach, Data Lake Inspector as W0, build status added. | | 1.1 | May 2026 | Added Mission Setup button widget. | | 1.0 | May 2026 | Initial UI design specification. | --- ## 1. Approach — Cockpit DIY Widgets (Confirmed) Widgets are Cockpit **DIY widgets** — JSON files containing `html`, `css`, `js`, and `inheritCockpitStyles` fields, imported via the gear icon on a placed DIY widget panel. > **Cockpit native desktop app v1.18.2 is required.** > The BlueOS browser extension version is "Lite" and does not support DIY widgets. > DIY widget support was added in v1.9.0. > Generic WebSocket data injection was added in v1.18.0. > Download: https://github.com/bluerobotics/cockpit/releases ### Import Format Each widget is a `.json` file with this exact structure: ```json { "html": "...", "css": "...", "js": "...", "inheritCockpitStyles": false } ``` ### CSS Rules — Hard Constraints (Do Not Break) These were discovered through testing. Violating any of them crashes or corrupts the Cockpit renderer: | Rule | Reason | |---|---| | No `body {}` or `:root {}` blocks | Bleed into Cockpit's own styles — causes renderer crash | | No `@keyframes` animations | Cause renderer instability | | No unicode characters in JS strings | Use plain ASCII only (-- not —, ... not …) | | No `position: fixed` elements | Use `display: block/none` toggling instead | | No `async/await` | Use `.then()/.catch()` for all fetch calls | | No global tag selectors (e.g. `div {}`) | Use ID and class selectors only, scoped to widget root | ### What DIY Widgets Can Do | Capability | Mechanism | |---|---| | Read all variable metadata | `window.cockpit.getAllDataLakeVariablesInfo()` — returns array of {id, name, type, description} ONLY, not values | | **Read a single live value** | **`window.cockpit.getDataLakeVariableData(variableId)` — CONFIRMED working in v1.18.2. Returns live numeric/string value. Use this in all widgets.** | | Read single variable metadata | `window.cockpit.getDataLakeVariableInfo(variableId)` | | Set a variable | `window.cockpit.setDataLakeVariableData(variableId, value)` — available (not tested) | | Call external REST endpoints | `fetch()` with `.then()/.catch()` | | Open a new browser tab | `window.open('URL', '_blank')` | **NOT available in DIY widget scope (confirmed absent by probe):** - `getDataLakeValue()` — undefined in v1.18.2 - `listenToDatalakeVariable()` — undefined (postMessage iframe External API only) **Full window.cockpit surface (17 keys, confirmed v1.18.2):** `dataLakeVariableData [object]`, `getDataLakeVariableData [function]`, `listenDataLakeVariable [function]`, `unlistenDataLakeVariable [function]`, `getAllDataLakeVariablesInfo [function]`, `getDataLakeVariableInfo [function]`, `setDataLakeVariableData [function]`, `createDataLakeVariable [function]`, `updateDataLakeVariableInfo [function]`, `deleteDataLakeVariable [function]`, `getDataLakeVariableLastUpdateTimestamp [function]`, `availableCockpitActions [object]`, `registerNewAction [function]`, `deleteAction [function]`, `registerActionCallback [function]`, `unregisterActionCallback [function]`, `executeActionCallback [function]` --- ## 2. Data Injection Architecture — CONFIRMED FACTS ### Production Path (Confirmed) — cockpit_bridge WebSocket ``` cockpit_bridge (ROS2 node on RPi5, rov_mission package, port 9001) WebSocket server :9001 <-- Cockpit connects as client (Settings -> Generic WebSocket Connections) --> Sends 'variable-name=value' text messages at 2Hz --> Data lake key: external/{variable-name} --> DIY widget reads: getDataLakeVariableData('external/{variable-name}') ``` **Message format:** Plain text, one variable per message: ``` variable-name=value ``` **Data lake key format:** `external/{variable-name}` > CRITICAL: Cockpit normalises variable names when creating data lake IDs. > Underscores become hyphens in the data lake ID. > ALWAYS USE HYPHENS in WebSocket variable names. **Cockpit one-time configuration:** ``` Cockpit -> Menu -> Settings -> Generic WebSocket Connections Enter: ws://192.168.1.101:9001 Click: ADD CONNECTION ``` This configuration persists between Cockpit restarts. ### Failed Injection Approaches (Do Not Retry) | Approach | Why it failed | |---|---| | HTTP POST to mavlink2rest /mavlink | Stored in REST only — not broadcast to Cockpit | | UDP to MAVLink ports from external scripts | Does not route to mavlink2rest WebSocket | | NAMED_VALUE with sysid=1, compid=1 | ArduSub detects heartbeat conflict — do not impersonate | ### Data Lake Variables (8 total, published by cockpit_bridge) | Variable | Source topic | Notes | |---|---|---| | `external/rov-failsafe` | `/rov/failsafe` | 0=GREEN, 1=AMBER, 2=RED; -1=no data | | `external/rov-depth` | `/rov/depth` | Metres (3 dp) | | `external/rov-voltage` | `/mavros/battery` | Battery volts (2 dp) | | `external/rov-heading` | `/mavros/mavros/compass_hdg` | Compass degrees (1 dp) | | `external/rov-ms` | `/rov/mission/status` | 0=IDLE…4=ABORTED; -1=no data | | `external/rov-mp` | `/rov/mission/status` | Progress 0-100% | | `external/rov-recording` | `/rov/recording/active` | 1=recording, 0=not recording | | `external/rov-altitude` | `/ping2/altitude` | Ping2 metres; -1.0=NO SONAR | --- ## 3. Widget Palette — Overview Argonaut 3 uses Cockpit as the operator hub. **No more than 8 widgets are in scope.** Cockpit is NOT a container for everything — only the specific operator-facing information and controls needed for field operations. | # | Widget | Purpose | Status | |---|---|---|---| | W0 | Data Lake Inspector | Dev diagnostic — shows all 706 data lake variables live | **Confirmed working** | | W1 | System Health Indicator | DP-style GREEN/AMBER/RED traffic light showing mission capability | **Visual confirmed** | | W2 | Mission Status | Mission state, progress, current task | **Visual confirmed** | | W3 | Return To Safe Button | Operator-initiated vehicle return-to-safe (DIR-5) | **UI confirmed; backend LIVE; label update pending** | | W4 | Mission Setup Button | Opens mission configuration page in new tab | **Visual confirmed** | | W5 | Return Budget | Battery % and dynamic return budget with headroom | **Visual confirmed** | | W6 | Altitude | Ping2 downward sonar altitude with colour-banded bar | **NO SONAR confirmed** | | W7 | Backup Due Notice | Operator-aware backup reminder with delay control | **Not built** | | W8 | Mode Indicator | Read-only active mode profile name, colour-coded by base mode | **Validated live (render + data)** | > **Widget testing status (corrected 9 Jul 2026).** There is a hard distinction between a widget > that **renders** and a widget that **works**. "Renders" means it draws in Cockpit. "Works" means > it has been validated end-to-end with live data. Do not conflate them. > > - **Validated with live data:** W0, W1, **W8**. > - **Render-confirmed only, never validated with data:** W2, W3, W4, W5, W6. > - **Not built:** W7. > > W6 shows a correct NO SONAR state, which is its *no-data* state — that is not data validation. > Full testing requires a live mission, FastAPI backend, and/or Ping2 hardware. > **All widgets read the RPi5 autonomy stack, not the vehicle.** For vehicle state (FC present, > armed/disarmed, board running) the operator currently must use BlueOS/Cockpit-native surfaces. > This is a defect, not a design — see W1 below and DIR-12. In AUV mode there is no operator and > no second tool, so the gate must become self-sufficient. > **Widget consolidation:** Grant may consolidate W0–W7 into a single unified widget > as development progresses and the full operational picture is clearer. Not yet designed. --- ## 4. Widget Specs --- ### W0 — Data Lake Inspector **File:** `w0_data_lake_inspector.json` **Status:** **Confirmed working** — 706/706 vars live. **Purpose:** Dev diagnostic tool. Renders a scrollable table of all Cockpit data lake variables with live values. Used to confirm data is reaching the data lake from cockpit_bridge, MAVROS, and other sources. **Data source:** `window.cockpit.getAllDataLakeVariablesInfo()` for enumeration, `getDataLakeVariableData(id)` for values. **Notes:** Production use is optional — this is a dev/debug tool, not an operational widget. --- ### W1 — System Health Indicator **File:** `w1_system_health_indicator.json` **Status:** **Validated with live data** (GREEN/RED observed). ⚠ **But the assessment behind it is incomplete and unsafe — see below.** **Purpose:** Primary mission-capability indicator. DP-style traffic light showing whether the vehicle is ready to conduct a mission (GREEN), degraded but proceeding (AMBER), or in a critical state (RED). **Data lake variable:** `external/rov-failsafe` (0=GREEN, 1=AMBER, 2=RED; -1=no data) **Mode-dependent role (`external/rov-health-role`):** - **ROV (`advisory`, 0)** — display only; the pilot decides. - **AUV (`gate`, 1)** — **pre-dive GO/NO-GO gate.** RED blocks mission start. Last human-visible decision point before the vehicle goes dark. **Behaviour:** - GREEN: "Systems nominal" — all parameters within limits - AMBER: Named degraded parameter — mission continues with reduced confidence - RED: Critical failure — return-to-safe active - Initial / no data: grey / loading state > ⚠ **OPEN, SAFETY-CRITICAL (9 Jul 2026).** On 9 July, with the Pixhawk unreachable and BlueOS > reporting "No board running", **W1 displayed GREEN / "Systems nominal"**. > > **Cause:** `failsafe_monitor` does not gate on flight-controller connectivity. It assesses the > autonomy stack's own view of health. With no FC data arriving it has no *bad* data, so it reports > nominal. Absence of data is being treated as good data. > > **Consequence:** in AUV mode (`health_role: gate`) W1 would pass a pre-dive gate with a dead > flight controller — on a vehicle that will have no operator once submerged. > > **Required (not built):** > 1. `failsafe_monitor` MUST subscribe to `/mavros/state` and assess FC connection + heartbeat age, > driving AMBER/RED on comms loss. (This was in the node's *original* design intent — > `comms_timeout_sec`, `comms_lost` — but is not effective in the implemented node.) > 2. A monitor with no input MUST NOT report GREEN. "No data" and "good data" must be distinct > states, distinctly displayed. > 3. W1's status line MUST carry cause text — `"Vehicle Disconnected"`, `"Heartbeat Lost"`, > `"No Board Running"`, `"Systems nominal"` — not a bare colour. The status line was previously > treated as a debug affordance; it is promoted to an operational element. > 4. Consider a pre-dive FD-headroom check on BlueOS `ardupilot_manager` (handover §15a) as part of > the same gate — same question, different failure mode. > > See **DIR-7** (health MUSTs) and **DIR-12** (vehicle health ≠ autonomy health). **Test required:** Confirm GREEN/AMBER/RED transitions by triggering failsafe monitor state changes with live data. **Additionally: confirm RED on FC disconnect** once (1) above is built. --- ### W2 — Mission Status **File:** `w2_mission_status.json` **Status:** **Visual confirmed** — IDLE state and progress bar display correctly. Untested with live mission. **Purpose:** Show current mission state (IDLE/RUNNING/PAUSED/COMPLETE/ABORTED) and progress as a percentage progress bar. **Data lake variables:** - `external/rov-ms` (0=IDLE, 1=RUNNING, 2=PAUSED, 3=COMPLETE, 4=ABORTED; -1=no data) - `external/rov-mp` (0-100 percent) **Test required:** Start a mission with waypoints, observe state transitions and progress bar advancing. --- ### W3 — Return To Safe Button **File:** `w3_abort_button.json` **Status:** **UI confirmed** — confirm dialog, 10s auto-cancel countdown, clean network-error handling. Label in widget code still reads "ABORT MISSION" — **update to "RETURN TO SAFE" pending.** **Purpose:** Operator-initiated vehicle return-to-safe action (DIR-5). This is a **vehicle-layer action** — it moves the vehicle. It is NOT the mission start/stop button. **Button label:** "RETURN TO SAFE" (decided July 2026 per DIR-5) - Not "ABORT MISSION" — that implies emergency hard-stop - Not "STOP MISSION" — that is a mission-layer action (DIR-4), not vehicle layer - "RETURN TO SAFE" correctly states what the vehicle does: follow breadcrumb path back to the closest designated safe place **Mechanism:** Confirm dialog (10s auto-dismiss) → `fetch()` POST to FastAPI `/abort` **Config:** `FASTAPI_HOST` constant in JS must be set to `http://192.168.1.101:8081` **Dependency:** FastAPI backend at RPi5:8081 — **LIVE** (commit 66a18ef). `/abort` verified end-to-end server-side: POST → Bool `/rov/mission/abort` → failsafe_monitor FSM NORMAL→HOLD_AND_RECOVER. **Remaining:** update widget code label ("ABORT MISSION" → "RETURN TO SAFE") and set `FASTAPI_HOST`, then test the full button→backend path from Cockpit. --- ### W4 — Mission Setup Button **File:** `w4_mission_setup_button.json` **Status:** **Visual confirmed** — cyan button renders, "Configure before diving" status shown. Opens 404 until setup page exists. **Purpose:** Open mission setup page in a new browser tab without leaving Cockpit. **Behaviour:** - IDLE: Full brightness, "Configure before diving" label — button enabled - RUNNING/PAUSED: Dimmed — button disabled (mission already in progress) **Config:** `SETUP_URL` constant in JS must be set to the FastAPI setup page URL **Dependency:** FastAPI backend serving the setup page — not yet built. **Test required:** Open setup page successfully with live FastAPI backend. --- ### W5 — Return Budget **File:** `w5_battery_return_budget.json` **Status:** **Visual confirmed** — battery % shows pending. Return budget pending until `external/rov-return-budget` is published. **Purpose:** Show battery percentage alongside dynamic return budget and headroom warning. **Data lake variables:** - `external/rov-voltage` — battery voltage from cockpit_bridge - `external/rov-return-budget` — return budget % (not yet published by cockpit_bridge) **Headroom thresholds:** - Headroom < 15%: AMBER warning - Headroom < 5%: RED critical **Dependency:** FastAPI backend for return budget calculation; cockpit_bridge must be updated to publish `external/rov-return-budget`. **Test required:** Full test with live voltage and return budget data. --- ### W6 — Altitude **File:** `w6_altitude.json` **Status:** **Built and deployed** (July 2026). **NO SONAR** state confirmed — correct baseline with no Ping2 connected. Full test hardware-gated on Ping2 sonar purchase. **Purpose:** Display downward Ping2 sonar altitude above hull or seafloor with colour-coded status and vertical bar gauge. **Data lake variable:** `external/rov-altitude` (metres float; -1.0 = NO SONAR sentinel) **Source topic:** `/ping2/altitude` (sensor_msgs/Range) — published by Ping2 driver node (hardware-gated; driver not yet built) **Colour thresholds:** - GREEN (GOOD): > 1.5m - AMBER (CAUTION): 0.5–1.5m - RED (CRITICAL): < 0.5m - GREY (NO SONAR): sentinel value -1.0 (no Ping2 connected or no echo) **Bar scale:** Linear, 0m (bottom) to 5m (top). Values above 5m fill bar to 100%. **No hardware default:** Shows "NO SONAR" when altitude value is ≤ -1.0 (sentinel). The sentinel is published by `cockpit_bridge` when no `/ping2/altitude` messages have been received — meaning no Ping2 driver is running. **Notes:** - `sensor_msgs/Range` field `range_max` (100m for Ping2) is treated as "no return" and also maps to the -1.0 sentinel in cockpit_bridge. - Widget layout uses a vertical bar to the left of the numeric readout. The bar background shows faint colour zone tints at the threshold boundaries even when reading is live. **Pending:** Ping2 ROS2 driver node (wraps ping-python library). Interface defined: topic `/ping2/altitude`, type `sensor_msgs/Range`, field `range` in metres. Write driver when Ping2 hardware arrives — cockpit_bridge and widget are ready. --- ### W7 — Backup Due Notice (ops-facing) **File:** `w7_backup_due_notice.json` — **NOT YET BUILT** **Status:** Scoped, not built. Depends on RPi5:8081 FastAPI backend (shared with W3/W4). **Purpose:** Surface to the operator that a device backup is due, and let the operator delay it, rather than allowing an unattended backup to start during operations. See DIR-11 for intent. **Why this exists:** The automated Pi backup system (built 30 Jun 2026) runs each Pi's self-backup via root cron — RPi4 at 00:00, RPi5 at 00:30 — unattended, which is correct for dev. In the field this is the wrong default: an SD image is a multi-GB `dd` stream that must NOT begin mid-dive. The ops model inverts the dev model — the operator is made aware a backup is due and chooses when it runs, with the option to delay. **Behaviour:** - Hidden/dormant when no backup is due. - When a backup is due, show a small unobtrusive notice: which device(s), how overdue. - Offer delay options (e.g. delay 1h / 4h / until end of session). Delay is operator-chosen, never automatic. - "Run now" option only enabled when vehicle is not in a mission (ties to `external/rov-ms`). **Data sources (planned):** - `FastAPI /backup/status` — backup-due state and overdue age per device - `external/rov-ms` — mission state (to gate "run now") **Delay mechanism (planned):** operator selection POSTs to `FastAPI /backup/delay` **Dependency:** FastAPI backend on RPi5:8081 — not yet built. **Open / TBC with Grant:** exact delay options; whether "run now" is exposed in field; whether notice appears for all operators or pilot-only. --- ### W8 — Mode Indicator **File:** `w8_mode_indicator.json` **Status:** **Validated live** — render + data confirmed 9 Jul 2026 (commit `87000e6`). **Purpose:** Read-only indicator of the **active operating-mode profile**. Answers one question: "what mode am I in?" Nothing else. **Data lake variables:** - `external/rov-mode` (string) — active profile name: `ROV`, `AUV`, or a saved hybrid name - `external/rov-mode-base` (number) — 0 = ROV, 1 = AUV; anything else treated as hybrid **Behaviour:** - Displays the profile name large and prominent, under a small `MODE` header - Colour-coded by base mode: **cyan `#38bdf8`** = ROV, **green `#00e5a0`** = AUV, **amber `#f59e0b`** = hybrid / unrecognised base - No-data state: `--` in grey `#484f58` with a `NO DATA` / `NO API` status line - 2 Hz poll, matching `cockpit_bridge` broadcast rate **Explicitly NOT in scope:** - **Read-only.** W8 never writes to the data lake and never calls `rov_api`. **Mode switching lives in the setup wizard** (`POST /mode`), not here. An indicator indicates. - **Does not show ArduSub arm state or vehicle mode.** Cockpit's native UI already surfaces ARMED/DISARMED and flight mode; duplicating it here would add clutter, not information. > **Note (DIR-12):** W8 reads a resolved profile that originates from `mode_profiles.yaml` on the > RPi5. It reflects **configuration**, not vehicle state — it would display `AUV` with the Pixhawk > disconnected. This is correct for what W8 is, but it is another reason W1 must carry the vehicle > health picture. --- ## 5. Widget File Locations **Gitea repo:** `rov-autonomy/widgets/` **Laptop local:** `C:\Users\grant\Desktop\Autonomous ROV System\rov-autonomy\widgets\` > **Widget JSON → Gitea:** W6 widget JSON (`w6_altitude.json`) and any updated W0–W5 > files need to be re-imported from the laptop and pushed to Gitea. Do NOT overwrite > from chat history — the laptop files are the source of truth. --- ## 6. Production Data Path — Confirmed The production data path is **cockpit_bridge WebSocket** (confirmed working, commit 12b5161 onwards). The NAMED_VALUE bridge node previously considered is not required — the WebSocket path has no 10-char name limit, simpler implementation, and is already deployed. **NAMED_VALUE is not needed for production.** Do not build a NAMED_VALUE bridge node. The one case where native ArduSub NAMED_VALUE messages matter is data that ArduSub itself publishes (e.g. attitude data) — these flow automatically through mavlink2rest to Cockpit's data lake without any bridge. Custom ROS2 data goes exclusively through cockpit_bridge. --- ## 7. FastAPI Backend (`rov_api`) Runs as `argonaut-api.service` on RPi5:8081 (manual-start in dev). **Core deployed and verified end-to-end (commit 66a18ef).** Architecture: uvicorn/FastAPI in the main thread, embedded rclpy node (`RovApiNode`) in a daemon background thread. CORS enabled for Cockpit. ### Endpoints | Endpoint | Method | Action | Status | |---|---|---|---| | `/health` | GET | Returns mission state, recording active, failsafe state | DONE — verified | | `/abort` | POST | W3 RETURN TO SAFE — publishes Bool to `/rov/mission/abort` (vehicle-layer, DIR-5) | DONE — verified end-to-end | | `/mission/start` | POST | MissionCommand START (recording no-go gate enforced by mission_executor) | DONE — verified | | `/mission/stop` | POST | MissionCommand ABORT | DONE — verified | | `/mission/upload` | POST | Accepts YAML file, saves to RPi5 | DEFERRED — Stage 2b | | `/return_budget` | GET | Returns current calculated return budget % (feeds W5) | DEFERRED — Stage 2b | | `/backup/status` | GET | Per-device backup-due state and overdue age (feeds W7) | DEFERRED — Stage 2b (Option A) | | `/backup/delay` | POST | Records operator-chosen backup delay window (feeds W7) | DEFERRED — Stage 2b (Option A) | **W3 abort path detail:** `/abort` publishes `Bool(true)` to `/rov/mission/abort`. The failsafe_monitor latches this as `flag_manual_abort` → FSM transitions NORMAL → HOLD_AND_RECOVER. This is the DIR-5 vehicle-layer return-to-safe, NOT the mission ABORT service. Verified live. **W3 widget wiring (pending):** update `w3_abort_button.json` label to "RETURN TO SAFE" and set the `FASTAPI_HOST` constant to `http://192.168.1.101:8081`. **Stage 2b design notes:** - `/return_budget`: return budget calculation approach under discussion ("shortest path to safety" concept). Without a DVL, position is unreliable — approach TBD with Grant. - `/backup/*`: Option A chosen — server exposes read-only JSON backup status, RPi5 FastAPI polls outbound (most secure: no credentials on the field-exposed RPi5, read-only, outbound only). --- ## 8. Development Sequence | Order | Task | Status | |---|---|---| | 1 | Confirm Cockpit native app required | DONE | | 2 | Confirm JSON import format | DONE | | 3 | Establish CSS rules for DIY widgets | DONE | | 4 | W0 Data Lake Inspector | DONE — confirmed working (706 vars live) | | 5 | W1 System Health Indicator | DONE — visual confirmed; full test pending | | 6 | W2 Mission Status | DONE — visual confirmed; full test pending | | 7 | W3 Return To Safe Button | DONE (UI); backend LIVE; label + FASTAPI_HOST update pending | | 8 | W4 Mission Setup Button | DONE — visual confirmed; needs setup page (Stage 2b/Phase 6) | | 9 | W5 Return Budget | DONE — visual confirmed; needs /return_budget (Stage 2b) | | 10 | Commit W0–W5 to Gitea | DONE — `rov-autonomy/widgets/` | | 11 | cockpit_bridge node (WebSocket production path) | DONE — 8 variables, commits 12b5161, 5d75a9d, 0640353 | | 12 | Probe widget (API discovery tool) | DONE and DELETED — dev tool only, not part of W0–W7 | | 13 | W6 Altitude Widget | DONE — `w6_altitude.json`, committed (commit 785ec56) | | 14 | FastAPI backend core (RPi5:8081) | DONE — commit 66a18ef, verified end-to-end | | 15 | W3 label update to "RETURN TO SAFE" + FASTAPI_HOST | PENDING — widget code update required | | 16 | W3 backend `/abort` POST confirmed end-to-end | DONE — FSM NORMAL→HOLD_AND_RECOVER verified | | 17 | W5 `/return_budget` + `rov-return-budget` publishing | DEFERRED — Stage 2b (approach TBD) | | 18 | W7 Backup Due Notice + FastAPI `/backup/*` | DEFERRED — Stage 2b (Option A) | | 19 | W6 full end-to-end test | HARDWARE-GATED — requires Ping2 sonar (Phase 3 purchase) | | 20 | W2 full test with live mission | READY — backend available; needs loaded plan + `POST /mission/start` | | 21 | Mission Setup View (full page, W4 target) | Phase 6 / Stage 2b | | 22 | Viewer Page | Phase 6 | --- *End of document — Argonaut 3 UI Design v1.9 — July 2026* *Next revision triggered by: FastAPI backend built, W3 label updated, W7 built, Ping2 purchased.*