Die gemeinsame Plattform ist jetzt softwareseitig deutlich vollständiger. Der Host-Core hat mit [show_store.rs](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/crates/infinity_host/src/show_store.rs>) eine echte Runtime-Bibliothek und Persistenz für aktive Szene, Runtime-Presets, Runtime-Gruppen und kreative Varianten bekommen; die Simulation in [simulation.rs](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/crates/infinity_host/src/simulation.rs>) liefert jetzt typisierte Command-Ergebnisse, saubere Fehlercodes und persistiert nach data/runtime_state.json. Dazu kommt das generische External-Show-Control-Interface in [external_control.rs](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/crates/infinity_host/src/external_control.rs>), damit spätere Adapter nur auf definierte Commands und Snapshot-/Preset-/Parameter-Flächen zugreifen.
Die API v1 ist als Produktgrenze geschärft in [dto.rs](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/crates/infinity_host_api/src/dto.rs>) und [server.rs](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/crates/infinity_host_api/src/server.rs>): getrennte Modelle für `state`, `preview`, `snapshot`, `command response`, `event stream` und stabile Fehlerobjekte mit echten Codes statt generischem Fallback. Dazu kamen `GET /api/v1/state` und `GET /api/v1/preview`, neue persistenzbezogene Commands wie `save_preset`, `save_creative_snapshot`, `recall_creative_snapshot`, `set_transition_style` und `upsert_group`, plus serverseitige Durchreichung der echten Fehlercodes. Die kreative Web-UI in [index.html](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/web/v1/index.html>), [app.js](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/web/v1/app.js>) und [styles.css](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/web/v1/styles.css>) nutzt jetzt genau diese API für Preset-Speichern/Überschreiben, Varianten, Transition-Style, filterbaren Eventfeed und klarere Preview-Darstellung, ohne Parallelarchitektur. Die Doku ist auf den neuen Stand gezogen in [docs/host_api.md](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/docs/host_api.md>), [README.md](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/README.md>), [docs/build_and_deploy.md](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/docs/build_and_deploy.md>) und [docs/architecture.md](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/docs/architecture.md>). Verifiziert habe ich `cargo check -q` und `cargo test -q`; dabei laufen die erweiterten Contract- und Persistenztests in [contract.rs](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/crates/infinity_host_api/tests/contract.rs>) sowie neue Core-Tests in [show_store.rs](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/crates/infinity_host/src/show_store.rs>) und [simulation.rs](</C:/Users/janni/Documents/RFP/Infinity_Vis _Rust/crates/infinity_host/src/simulation.rs>). Nicht separat verifiziert habe ich einen echten Browserlauf der Web-UI; die JS-Datei wurde hier nicht mit `node` geprüft, weil `node` in dieser Umgebung nicht installiert ist.
This commit is contained in:
423
docs/host_api.md
423
docs/host_api.md
@@ -2,58 +2,107 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
The host API is the stable external boundary for:
|
||||
The host API is the stable external product boundary for:
|
||||
|
||||
- the creative web UI
|
||||
- the existing engineering GUI
|
||||
- future external show-control adapters such as grandMA
|
||||
- the native engineering GUI
|
||||
- future remote operator clients
|
||||
- later external show-control adapters such as a grandMA bridge
|
||||
|
||||
The core rule stays unchanged:
|
||||
The realtime rule remains strict:
|
||||
|
||||
- the API is a control and observation layer
|
||||
- the realtime engine remains the timing authority
|
||||
- no surface is allowed to become the LED clock
|
||||
- the host core remains the timing authority
|
||||
- no frontend or external adapter is allowed to become the LED clock
|
||||
|
||||
## Current Implementation
|
||||
## Runtime Components
|
||||
|
||||
Runtime pieces:
|
||||
Core and API implementation:
|
||||
|
||||
- `crates/infinity_host/src/control.rs`
|
||||
- `crates/infinity_host/src/scene.rs`
|
||||
- `crates/infinity_host/src/show_store.rs`
|
||||
- `crates/infinity_host/src/simulation.rs`
|
||||
- `crates/infinity_host/src/external_control.rs`
|
||||
- `crates/infinity_host_api/src/dto.rs`
|
||||
- `crates/infinity_host_api/src/server.rs`
|
||||
|
||||
The network-facing server is started with:
|
||||
Server startup:
|
||||
|
||||
```powershell
|
||||
cargo run -p infinity_host_api -- --config config/project.example.toml --bind 127.0.0.1:9001
|
||||
cargo run -p infinity_host_api -- --config config/project.example.toml --bind 127.0.0.1:9001 --runtime-state data/runtime_state.json
|
||||
```
|
||||
|
||||
Creative web UI V1 is served by the same process at:
|
||||
Creative web UI V1 is served by the same process:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:9001/
|
||||
```
|
||||
|
||||
## Versioning
|
||||
## Versioning Policy
|
||||
|
||||
- HTTP and WebSocket routes are versioned under `/api/v1`
|
||||
- responses include `api_version: "v1"`
|
||||
- the external DTOs are intentionally not a direct 1:1 dump of internal core structs
|
||||
The current public contract is `v1`.
|
||||
|
||||
## Endpoint Contract
|
||||
Rules:
|
||||
|
||||
### GET `/api/v1/snapshot`
|
||||
- all public HTTP and WebSocket routes are namespaced under `/api/v1`
|
||||
- every response body carries `api_version: "v1"`
|
||||
- additive fields are allowed inside `v1`
|
||||
- semantic breaking changes require a new version namespace
|
||||
- external consumers must treat undocumented internal-only fields as unstable and ignore them
|
||||
|
||||
Returns the current state and preview in one response.
|
||||
## Stable External Models
|
||||
|
||||
Stable external response families:
|
||||
|
||||
- command response
|
||||
- state snapshot
|
||||
- preview snapshot
|
||||
- combined snapshot
|
||||
- catalog
|
||||
- event stream
|
||||
- typed error object
|
||||
|
||||
Stable external command families:
|
||||
|
||||
- global control
|
||||
- pattern and preset selection
|
||||
- group targeting
|
||||
- scene parameter updates
|
||||
- transition configuration
|
||||
- preset persistence
|
||||
- creative snapshot persistence and recall
|
||||
- panel test trigger
|
||||
|
||||
## Internal Versus External Fields
|
||||
|
||||
External and stable in `v1`:
|
||||
|
||||
- every field defined in `crates/infinity_host_api/src/dto.rs`
|
||||
- route names and payload shapes documented below
|
||||
- error object shape `{ api_version, error: { code, message } }`
|
||||
- event stream envelope shape `{ api_version, sequence, generated_at_millis, message }`
|
||||
|
||||
Internal and not part of the API contract:
|
||||
|
||||
- the exact shape of `HostSnapshot` in `crates/infinity_host/src/control.rs`
|
||||
- simulation-only storage layout in `data/runtime_state.json`
|
||||
- internal event history buffering size
|
||||
- internal scene library structures in `show_store.rs`
|
||||
- engineering-GUI-specific rendering or polling behavior
|
||||
|
||||
## HTTP Endpoints
|
||||
|
||||
### GET `/api/v1/state`
|
||||
|
||||
Returns only the stable state snapshot.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"generated_at_millis": 241,
|
||||
"generated_at_millis": 512,
|
||||
"state": {
|
||||
"system": {
|
||||
"project_name": "Infinity Vis",
|
||||
@@ -63,104 +112,113 @@ Example:
|
||||
"global": {
|
||||
"blackout": false,
|
||||
"master_brightness": 0.2,
|
||||
"selected_pattern": "solid_color",
|
||||
"selected_group": null,
|
||||
"transition_duration_ms": 150
|
||||
"selected_pattern": "gradient",
|
||||
"selected_group": "top_panels",
|
||||
"transition_duration_ms": 320,
|
||||
"transition_style": "chase"
|
||||
},
|
||||
"engine": {
|
||||
"logic_hz": 120,
|
||||
"frame_hz": 60,
|
||||
"preview_hz": 15,
|
||||
"uptime_ms": 241,
|
||||
"frame_index": 14,
|
||||
"uptime_ms": 512,
|
||||
"frame_index": 30,
|
||||
"dropped_frames": 0,
|
||||
"active_transition": null
|
||||
},
|
||||
"active_scene": {
|
||||
"preset_id": null,
|
||||
"pattern_id": "solid_color",
|
||||
"seed": 100,
|
||||
"palette": [
|
||||
"#ffffff"
|
||||
],
|
||||
"parameters": [],
|
||||
"target_group": null,
|
||||
"blackout": false
|
||||
"active_transition": {
|
||||
"style": "chase",
|
||||
"from_pattern_id": "solid_color",
|
||||
"to_pattern_id": "gradient",
|
||||
"duration_ms": 320,
|
||||
"progress": 0.28
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/api/v1/preview`
|
||||
|
||||
Returns only the stable preview snapshot.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"generated_at_millis": 512,
|
||||
"preview": {
|
||||
"generated_at_millis": 241,
|
||||
"generated_at_millis": 512,
|
||||
"panels": [
|
||||
{
|
||||
"node_id": "node-01",
|
||||
"panel_position": "top",
|
||||
"representative_color_hex": "#33CCFF",
|
||||
"representative_color_hex": "#FF8A5B",
|
||||
"sample_led_hex": [
|
||||
"#33CCFF",
|
||||
"#28A3CC",
|
||||
"#1E7A99"
|
||||
"#FF8A5B",
|
||||
"#F36E43",
|
||||
"#D85A2F"
|
||||
],
|
||||
"energy_percent": 28,
|
||||
"source": "scene"
|
||||
"energy_percent": 47,
|
||||
"source": "transition"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/api/v1/snapshot`
|
||||
|
||||
Returns the convenience composition of `state` plus `preview`.
|
||||
|
||||
This route exists for lightweight clients and debugging. Consumers that want strict separation should prefer `GET /api/v1/state` and `GET /api/v1/preview`.
|
||||
|
||||
### GET `/api/v1/catalog`
|
||||
|
||||
Returns the creative catalog:
|
||||
Returns the stable creative library:
|
||||
|
||||
- patterns
|
||||
- presets
|
||||
- groups
|
||||
- creative snapshots
|
||||
|
||||
Example preset summary:
|
||||
|
||||
```json
|
||||
{
|
||||
"preset_id": "ocean_gradient",
|
||||
"pattern_id": "gradient",
|
||||
"target_group": null,
|
||||
"transition_duration_ms": 320,
|
||||
"transition_style": "crossfade",
|
||||
"source": "built_in"
|
||||
}
|
||||
```
|
||||
|
||||
Example creative snapshot summary:
|
||||
|
||||
```json
|
||||
{
|
||||
"snapshot_id": "variant_floor",
|
||||
"label": "Variant Floor",
|
||||
"pattern_id": "noise",
|
||||
"target_group": "bottom_panels",
|
||||
"transition_duration_ms": 220,
|
||||
"transition_style": "chase",
|
||||
"saved_at_unix_ms": 1760000000000
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/api/v1/presets`
|
||||
|
||||
Returns only preset summaries.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"presets": [
|
||||
{
|
||||
"preset_id": "ocean_gradient",
|
||||
"pattern_id": "gradient",
|
||||
"target_group": null,
|
||||
"transition_duration_ms": 320
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/api/v1/groups`
|
||||
|
||||
Returns only group summaries.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"groups": [
|
||||
{
|
||||
"group_id": "top_panels",
|
||||
"member_count": 6,
|
||||
"tags": [
|
||||
"row",
|
||||
"top"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### POST `/api/v1/command`
|
||||
|
||||
Accepts a versioned command envelope.
|
||||
Accepts one versioned command envelope.
|
||||
|
||||
Example request:
|
||||
|
||||
@@ -168,9 +226,11 @@ Example request:
|
||||
{
|
||||
"request_id": "web-1713352662000",
|
||||
"command": {
|
||||
"type": "set_master_brightness",
|
||||
"type": "save_creative_snapshot",
|
||||
"payload": {
|
||||
"value": 0.42
|
||||
"snapshot_id": "variant_floor",
|
||||
"label": "Variant Floor",
|
||||
"overwrite": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,85 +244,44 @@ Example response:
|
||||
"accepted": true,
|
||||
"request_id": "web-1713352662000",
|
||||
"generated_at_millis": 522,
|
||||
"summary": "master brightness set to 42%"
|
||||
"command_type": "save_creative_snapshot",
|
||||
"summary": "creative snapshot saved: variant_floor"
|
||||
}
|
||||
```
|
||||
|
||||
Errors use a stable error object:
|
||||
## Stable Error Object
|
||||
|
||||
All API failures return:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"error": {
|
||||
"code": "invalid_command",
|
||||
"message": "command request was rejected: missing field `enabled`"
|
||||
"code": "unknown_creative_snapshot",
|
||||
"message": "creative snapshot 'does_not_exist' does not exist"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Stable `v1` error families currently include:
|
||||
|
||||
- `invalid_request_json`
|
||||
- `invalid_command`
|
||||
- `unknown_group`
|
||||
- `unknown_preset`
|
||||
- `unknown_creative_snapshot`
|
||||
- `preset_exists`
|
||||
- `snapshot_exists`
|
||||
- `group_exists`
|
||||
- `persist_failed`
|
||||
- `missing_websocket_key`
|
||||
- `not_found`
|
||||
|
||||
## WebSocket Event Stream
|
||||
|
||||
### WS `/api/v1/stream`
|
||||
|
||||
The WebSocket stream emits envelopes with a monotonic sequence and a typed payload.
|
||||
|
||||
Stream message types:
|
||||
|
||||
- `snapshot`
|
||||
- `preview`
|
||||
- `event`
|
||||
|
||||
Example snapshot envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"sequence": 17,
|
||||
"generated_at_millis": 875,
|
||||
"message": {
|
||||
"type": "snapshot",
|
||||
"payload": {
|
||||
"global": {
|
||||
"blackout": false,
|
||||
"master_brightness": 0.35,
|
||||
"selected_pattern": "gradient",
|
||||
"selected_group": "top_panels",
|
||||
"transition_duration_ms": 320
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example preview envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"sequence": 18,
|
||||
"generated_at_millis": 875,
|
||||
"message": {
|
||||
"type": "preview",
|
||||
"payload": {
|
||||
"generated_at_millis": 875,
|
||||
"panels": [
|
||||
{
|
||||
"node_id": "node-01",
|
||||
"panel_position": "top",
|
||||
"representative_color_hex": "#FF8A5B",
|
||||
"sample_led_hex": [
|
||||
"#FF8A5B",
|
||||
"#F36E43",
|
||||
"#D85A2F"
|
||||
],
|
||||
"energy_percent": 47,
|
||||
"source": "transition"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example event envelope:
|
||||
The stream emits a typed envelope with a monotonic sequence counter:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -272,16 +291,29 @@ Example event envelope:
|
||||
"message": {
|
||||
"type": "event",
|
||||
"payload": {
|
||||
"kind": "info",
|
||||
"message": "preset recalled: ocean_gradient"
|
||||
"kind": "warning",
|
||||
"code": "unknown_creative_snapshot",
|
||||
"message": "creative snapshot 'does_not_exist' does not exist"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Supported Commands
|
||||
Stable message types:
|
||||
|
||||
The current API command set covers:
|
||||
- `snapshot`
|
||||
- `preview`
|
||||
- `event`
|
||||
|
||||
Stable event kinds:
|
||||
|
||||
- `info`
|
||||
- `warning`
|
||||
- `error`
|
||||
|
||||
## Guaranteed Commands In `v1`
|
||||
|
||||
Guaranteed control commands:
|
||||
|
||||
- `set_blackout`
|
||||
- `set_master_brightness`
|
||||
@@ -290,53 +322,76 @@ The current API command set covers:
|
||||
- `select_group`
|
||||
- `set_scene_parameter`
|
||||
- `set_transition_duration_ms`
|
||||
- `set_transition_style`
|
||||
- `trigger_panel_test`
|
||||
|
||||
This is intentionally enough for:
|
||||
Guaranteed persistence and creative-library commands:
|
||||
|
||||
- creative look development in the web UI
|
||||
- engineering test triggers in the native GUI
|
||||
- future external show-control translation layers
|
||||
- `save_preset`
|
||||
- `save_creative_snapshot`
|
||||
- `recall_creative_snapshot`
|
||||
- `upsert_group`
|
||||
|
||||
## Web UI V1
|
||||
## Persistence Behavior
|
||||
|
||||
The first creative web UI is intentionally limited to:
|
||||
The simulation-backed host service now persists runtime-facing creative data to `data/runtime_state.json` by default.
|
||||
|
||||
- pattern selection
|
||||
- preset recall
|
||||
- group selection
|
||||
- global brightness
|
||||
- blackout
|
||||
- transition duration
|
||||
- scene parameter controls driven from the API schema
|
||||
- panel preview
|
||||
- snapshot display
|
||||
- event feed
|
||||
Persisted data includes:
|
||||
|
||||
It does not absorb mapping, topology, or hardware-diagnostic workflows. Those stay in the native engineering UI.
|
||||
- active scene
|
||||
- global blackout and brightness state
|
||||
- transition duration and style
|
||||
- runtime user presets
|
||||
- runtime user groups
|
||||
- creative snapshots and variants
|
||||
|
||||
## Contract Tests
|
||||
This persistence file is an internal runtime artifact, not the public API contract.
|
||||
|
||||
The API contract is currently verified in:
|
||||
## External Show Control Adapter Boundary
|
||||
|
||||
The generic internal adapter surface lives in:
|
||||
|
||||
- `crates/infinity_host/src/external_control.rs`
|
||||
|
||||
Key rule:
|
||||
|
||||
- future adapters may only translate external intent into the defined host command surface
|
||||
- they must not reach into simulation internals, UI state, or hardware driver details directly
|
||||
|
||||
## Contract And Integration Coverage
|
||||
|
||||
Current software-side hardening lives in:
|
||||
|
||||
- `crates/infinity_host_api/tests/contract.rs`
|
||||
- `crates/infinity_host/src/show_store.rs` tests
|
||||
- `crates/infinity_host/src/simulation.rs` tests
|
||||
|
||||
Covered paths:
|
||||
Covered flows include:
|
||||
|
||||
- root web shell
|
||||
- `GET /api/v1/snapshot`
|
||||
- `GET /api/v1/catalog`
|
||||
- `GET /api/v1/presets`
|
||||
- `GET /api/v1/groups`
|
||||
- `POST /api/v1/command`
|
||||
- `WS /api/v1/stream`
|
||||
- state, preview, snapshot, catalog, presets, and groups endpoints
|
||||
- command success and typed command failure
|
||||
- WebSocket snapshot, preview, and event messages
|
||||
- group targeting
|
||||
- parameter updates
|
||||
- transition configuration
|
||||
- blackout
|
||||
- preset save
|
||||
- creative snapshot save and recall
|
||||
- persistence across restart
|
||||
- a longer ignored load-oriented sequence for platform hardening
|
||||
|
||||
## Future Direction
|
||||
## Web UI Scope
|
||||
|
||||
Next adapters should be built on this boundary instead of reaching into the host core directly.
|
||||
The current web UI intentionally focuses on creative use:
|
||||
|
||||
That includes:
|
||||
- pattern and preset selection
|
||||
- group targeting
|
||||
- transition configuration
|
||||
- scene parameters
|
||||
- preset save and overwrite
|
||||
- creative snapshot save and recall
|
||||
- preview
|
||||
- raw snapshot display
|
||||
- filterable event feed
|
||||
|
||||
- a richer web authoring surface
|
||||
- remote operator clients
|
||||
- a grandMA bridge that translates external show control into host API commands
|
||||
Mapping, topology diagnostics, panel-test administration, and low-level node status remain primarily in the native engineering GUI.
|
||||
|
||||
Reference in New Issue
Block a user