Software-only show-control readiness baseline
This commit is contained in:
37
docs/acceptance_template.md
Normal file
37
docs/acceptance_template.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Acceptance Template
|
||||
|
||||
Date:
|
||||
|
||||
Technician:
|
||||
|
||||
Firmware build:
|
||||
|
||||
Host build:
|
||||
|
||||
## Core Checks
|
||||
|
||||
- [ ] System starts from cold boot
|
||||
- [ ] All 6 nodes discovered
|
||||
- [ ] All 18 outputs configured
|
||||
- [ ] All outputs confirm 106 LEDs
|
||||
- [ ] Global brightness change is visible immediately
|
||||
- [ ] Blackout works and recovers cleanly
|
||||
- [ ] Preset recall works under load
|
||||
- [ ] Node reconnect works during active show
|
||||
- [ ] 8-hour soak run completed without critical fault
|
||||
|
||||
## Mapping Checks
|
||||
|
||||
- [ ] Walking pixel test completed on every output
|
||||
- [ ] Start pixel confirmed for every output
|
||||
- [ ] Direction confirmed for every output
|
||||
- [ ] Color order confirmed for every output
|
||||
- [ ] Top, middle, bottom assignment confirmed on every node
|
||||
- [ ] No mismatch between configured mapping and observed physical behavior
|
||||
|
||||
## Observations
|
||||
|
||||
- Notes:
|
||||
- Exceptions:
|
||||
- Follow-up actions:
|
||||
|
||||
115
docs/architecture.md
Normal file
115
docs/architecture.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Architecture
|
||||
|
||||
## Goal
|
||||
|
||||
Build a live-capable LED control platform that keeps realtime output deterministic while letting operators change scenes, brightness, tests, and presets without UI jitter leaking into the hot path.
|
||||
|
||||
## Current Priority
|
||||
|
||||
The current delivery order is intentionally software-first:
|
||||
|
||||
1. host-core and shared API
|
||||
2. scene, preset, group, parameter, transition, and simulation model
|
||||
3. web UI as the primary creative surface
|
||||
4. engineering GUI as the technical surface
|
||||
5. external show-control adapters such as grandMA
|
||||
6. hardware validation and real node activation later
|
||||
|
||||
## Layer Split
|
||||
|
||||
1. Control layer
|
||||
- Shared host API first
|
||||
- Creative web UI later
|
||||
- Engineering GUI already implemented in `crates/infinity_host_ui`
|
||||
- Monitoring, mapping, diagnostics, and admin
|
||||
- Never the timing master for LED output
|
||||
2. Realtime engine
|
||||
- Owns the monotonic clock
|
||||
- Computes scene state, transitions, and dirty regions
|
||||
- Produces transport-ready commands or pixel frames
|
||||
3. Transport and node layer
|
||||
- Discovery, heartbeat, config sync, sequencing, and recovery
|
||||
- Control protocol and realtime protocol stay separate
|
||||
- Latest realtime state wins, stale frames may be dropped
|
||||
4. ESP32 firmware
|
||||
- Receives commands
|
||||
- Maintains local buffers
|
||||
- Drives three independent outputs per node
|
||||
- Handles watchdog and reconnect logic locally
|
||||
|
||||
## Runtime Model
|
||||
|
||||
- Logic tick target: 120 Hz
|
||||
- Frame synthesis target: 60 Hz
|
||||
- Network send target: 40-60 Hz, profile dependent
|
||||
- Preview target: 10-15 Hz
|
||||
|
||||
Preview and telemetry are explicitly degradable. Realtime output is not.
|
||||
|
||||
## Shared Surface Model
|
||||
|
||||
Every surface must talk to the same host API:
|
||||
|
||||
- engineering GUI
|
||||
- future creative web UI
|
||||
- CLI inspection
|
||||
- future grandMA adapter
|
||||
|
||||
The host core now also carries a runtime show store and persistence layer for:
|
||||
|
||||
- saved presets
|
||||
- runtime user groups
|
||||
- active scene state
|
||||
- creative snapshots and variants
|
||||
|
||||
The current software-first implementation uses a simulation-backed host API so looks, presets, parameters, and grouping can be developed before real node activation.
|
||||
|
||||
## Modes
|
||||
|
||||
### Distributed Scene Mode
|
||||
|
||||
- Default operating mode
|
||||
- Host sends scene parameters, time basis, seed, palette, and transitions
|
||||
- Nodes render locally for low bandwidth and better resilience
|
||||
|
||||
### Frame Streaming Mode
|
||||
|
||||
- Used for mapping tests, debugging, and effects that cannot run node-local
|
||||
- Host sends explicit output frames
|
||||
- Kept logically separate so it does not contaminate the primary scene pipeline
|
||||
|
||||
## Mapping Model
|
||||
|
||||
The project configuration separates mapping into three layers:
|
||||
|
||||
1. Hardware mapping
|
||||
- Node ID
|
||||
- Top, middle, bottom output
|
||||
- Physical output label
|
||||
- Driver channel reference
|
||||
- LED count, direction, color order, enable flag
|
||||
2. Layout mapping
|
||||
- Optional row and column placement
|
||||
- Optional preview transforms only
|
||||
3. Group mapping
|
||||
- Explicit groups for artistic control and fast operator access
|
||||
|
||||
The current example config intentionally keeps layout mapping empty because the old XML is only a spatial reference and the final node-to-room placement must still be confirmed on real hardware.
|
||||
|
||||
## Validation Gates
|
||||
|
||||
The codebase deliberately blocks activation when these remain unresolved:
|
||||
|
||||
- `UART 6`, `UART 5`, `UART 4` still marked as `pending_validation`
|
||||
- output validation state is not `validated`
|
||||
- LED count deviates from 106
|
||||
- node outputs are missing top, middle, or bottom
|
||||
- driver references are ambiguous or duplicated per node
|
||||
|
||||
## Planned Next Steps
|
||||
|
||||
1. Expand creative authoring on top of the now-versioned host API and web UI
|
||||
2. Keep the engineering GUI focused on mapping, diagnostics, topology, and admin
|
||||
3. Implement transport adapters without coupling them to any single frontend
|
||||
4. Add future external show-control bridges such as grandMA on the same API boundary and generic adapter interface
|
||||
5. Keep hardware activation behind explicit later validation gates
|
||||
46
docs/build_and_deploy.md
Normal file
46
docs/build_and_deploy.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Build and Deploy
|
||||
|
||||
## Host Side
|
||||
|
||||
Required tools:
|
||||
|
||||
- Rust stable toolchain
|
||||
- `cargo`
|
||||
|
||||
Suggested commands:
|
||||
|
||||
```powershell
|
||||
cargo test
|
||||
cargo run -p infinity_host -- snapshot --config config/project.example.toml
|
||||
cargo run -p infinity_host_api -- --config config/project.example.toml --bind 127.0.0.1:9001 --runtime-state data/runtime_state.json
|
||||
cargo run -p infinity_host_ui
|
||||
cargo run -p infinity_host -- validate --config config/project.example.toml --mode structural
|
||||
cargo run -p infinity_host -- plan-boot-scene --config config/project.example.toml --preset-id safe_static_blue
|
||||
```
|
||||
|
||||
The host API server now exposes the common software-first control boundary over HTTP and WebSocket. The creative web UI is served directly from the same process at `http://127.0.0.1:9001/`. Runtime creative data such as saved presets, groups, active scene state, and creative snapshots are persisted to `data/runtime_state.json` by default.
|
||||
|
||||
The native engineering UI and the CLI snapshot continue to run against the same simulation-backed host core so looks, presets, grouping, and parameter flow can be exercised before transport and firmware integration are complete.
|
||||
|
||||
Before any live activation, run:
|
||||
|
||||
```powershell
|
||||
cargo run -p infinity_host -- validate --config config/project.example.toml --mode activation
|
||||
```
|
||||
|
||||
Activation mode is expected to fail until the hardware mapping has been confirmed and the config is updated from `pending_validation` to concrete driver references.
|
||||
|
||||
## Firmware Side
|
||||
|
||||
Required tools:
|
||||
|
||||
- ESP-IDF
|
||||
- Xtensa or RISC-V toolchain matching the actual ESP32 variant
|
||||
|
||||
Suggested layout:
|
||||
|
||||
- `firmware/esp32_node/`
|
||||
- build with `idf.py build`
|
||||
- flash with `idf.py -p <serial-port> flash monitor`
|
||||
|
||||
The firmware skeleton is intentionally conservative. It will not silently select a backend for `UART 6`, `UART 5`, or `UART 4`.
|
||||
87
docs/config_schema.md
Normal file
87
docs/config_schema.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Config Schema
|
||||
|
||||
## Primary File
|
||||
|
||||
The example project file is [config/project.example.toml](../config/project.example.toml).
|
||||
|
||||
## Root Objects
|
||||
|
||||
- `metadata`
|
||||
- `topology`
|
||||
- `transport_profiles`
|
||||
- `safety_profiles`
|
||||
- `presets`
|
||||
|
||||
## `metadata`
|
||||
|
||||
- `project_name`
|
||||
- `schema_version`
|
||||
- `default_transport_profile`
|
||||
- `default_safety_profile`
|
||||
|
||||
## `topology`
|
||||
|
||||
- `expected_node_count`
|
||||
- `outputs_per_node`
|
||||
- `leds_per_output`
|
||||
- `nodes`
|
||||
- `layout_panels`
|
||||
- `groups`
|
||||
|
||||
## `topology.nodes[]`
|
||||
|
||||
- `node_id`
|
||||
- `display_name`
|
||||
- `network.reserved_ip`
|
||||
- `network.telemetry_label`
|
||||
- `outputs`
|
||||
|
||||
## `topology.nodes[].outputs[]`
|
||||
|
||||
Required:
|
||||
|
||||
- `panel_position`
|
||||
- `physical_output_name`
|
||||
- `driver_channel.kind`
|
||||
- `driver_channel.reference`
|
||||
- `led_count`
|
||||
- `direction`
|
||||
- `color_order`
|
||||
- `enabled`
|
||||
- `validation_state`
|
||||
|
||||
Optional:
|
||||
|
||||
- `logical_panel_name`
|
||||
|
||||
## Activation Rules
|
||||
|
||||
Structural validation accepts `pending_validation` so the system can model unresolved wiring.
|
||||
|
||||
Activation validation rejects any output that is still:
|
||||
|
||||
- `driver_channel.kind = "pending_validation"`
|
||||
- `validation_state != "validated"`
|
||||
|
||||
This is intentional and prevents accidental deployment against guessed hardware assumptions.
|
||||
|
||||
## Groups
|
||||
|
||||
`topology.groups[]` keeps grouping explicit and simple:
|
||||
|
||||
- `group_id`
|
||||
- `tags`
|
||||
- `members[] = { node_id, panel_position }`
|
||||
|
||||
## Layout
|
||||
|
||||
`topology.layout_panels[]` is optional and only needed for preview or spatial effects:
|
||||
|
||||
- `node_id`
|
||||
- `panel_position`
|
||||
- `row`
|
||||
- `column`
|
||||
- `rotation_degrees`
|
||||
- `mirror_x`
|
||||
- `mirror_y`
|
||||
|
||||
397
docs/host_api.md
Normal file
397
docs/host_api.md
Normal file
@@ -0,0 +1,397 @@
|
||||
# Host API
|
||||
|
||||
## Purpose
|
||||
|
||||
The host API is the stable external product boundary for:
|
||||
|
||||
- the creative web UI
|
||||
- the native engineering GUI
|
||||
- future remote operator clients
|
||||
- later external show-control adapters such as a grandMA bridge
|
||||
|
||||
The realtime rule remains strict:
|
||||
|
||||
- the API is a control and observation layer
|
||||
- the host core remains the timing authority
|
||||
- no frontend or external adapter is allowed to become the LED clock
|
||||
|
||||
## Runtime Components
|
||||
|
||||
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`
|
||||
|
||||
Server startup:
|
||||
|
||||
```powershell
|
||||
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:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:9001/
|
||||
```
|
||||
|
||||
## Versioning Policy
|
||||
|
||||
The current public contract is `v1`.
|
||||
|
||||
Rules:
|
||||
|
||||
- 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
|
||||
|
||||
## 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": 512,
|
||||
"state": {
|
||||
"system": {
|
||||
"project_name": "Infinity Vis",
|
||||
"schema_version": 1,
|
||||
"topology_label": "6 nodes / 18 outputs / 106 LEDs"
|
||||
},
|
||||
"global": {
|
||||
"blackout": false,
|
||||
"master_brightness": 0.2,
|
||||
"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": 512,
|
||||
"frame_index": 30,
|
||||
"dropped_frames": 0,
|
||||
"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": 512,
|
||||
"panels": [
|
||||
{
|
||||
"node_id": "node-01",
|
||||
"panel_position": "top",
|
||||
"representative_color_hex": "#FF8A5B",
|
||||
"sample_led_hex": [
|
||||
"#FF8A5B",
|
||||
"#F36E43",
|
||||
"#D85A2F"
|
||||
],
|
||||
"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 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.
|
||||
|
||||
### GET `/api/v1/groups`
|
||||
|
||||
Returns only group summaries.
|
||||
|
||||
### POST `/api/v1/command`
|
||||
|
||||
Accepts one versioned command envelope.
|
||||
|
||||
Example request:
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "web-1713352662000",
|
||||
"command": {
|
||||
"type": "save_creative_snapshot",
|
||||
"payload": {
|
||||
"snapshot_id": "variant_floor",
|
||||
"label": "Variant Floor",
|
||||
"overwrite": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"accepted": true,
|
||||
"request_id": "web-1713352662000",
|
||||
"generated_at_millis": 522,
|
||||
"command_type": "save_creative_snapshot",
|
||||
"summary": "creative snapshot saved: variant_floor"
|
||||
}
|
||||
```
|
||||
|
||||
## Stable Error Object
|
||||
|
||||
All API failures return:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"error": {
|
||||
"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 stream emits a typed envelope with a monotonic sequence counter:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_version": "v1",
|
||||
"sequence": 19,
|
||||
"generated_at_millis": 880,
|
||||
"message": {
|
||||
"type": "event",
|
||||
"payload": {
|
||||
"kind": "warning",
|
||||
"code": "unknown_creative_snapshot",
|
||||
"message": "creative snapshot 'does_not_exist' does not exist"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Stable message types:
|
||||
|
||||
- `snapshot`
|
||||
- `preview`
|
||||
- `event`
|
||||
|
||||
Stable event kinds:
|
||||
|
||||
- `info`
|
||||
- `warning`
|
||||
- `error`
|
||||
|
||||
## Guaranteed Commands In `v1`
|
||||
|
||||
Guaranteed control commands:
|
||||
|
||||
- `set_blackout`
|
||||
- `set_master_brightness`
|
||||
- `select_pattern`
|
||||
- `recall_preset`
|
||||
- `select_group`
|
||||
- `set_scene_parameter`
|
||||
- `set_transition_duration_ms`
|
||||
- `set_transition_style`
|
||||
- `trigger_panel_test`
|
||||
|
||||
Guaranteed persistence and creative-library commands:
|
||||
|
||||
- `save_preset`
|
||||
- `save_creative_snapshot`
|
||||
- `recall_creative_snapshot`
|
||||
- `upsert_group`
|
||||
|
||||
## Persistence Behavior
|
||||
|
||||
The simulation-backed host service now persists runtime-facing creative data to `data/runtime_state.json` by default.
|
||||
|
||||
Persisted data includes:
|
||||
|
||||
- active scene
|
||||
- global blackout and brightness state
|
||||
- transition duration and style
|
||||
- runtime user presets
|
||||
- runtime user groups
|
||||
- creative snapshots and variants
|
||||
|
||||
This persistence file is an internal runtime artifact, not the public API contract.
|
||||
|
||||
## 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 flows include:
|
||||
|
||||
- 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
|
||||
|
||||
## Web UI Scope
|
||||
|
||||
The current web UI intentionally focuses on creative use:
|
||||
|
||||
- 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
|
||||
|
||||
Mapping, topology diagnostics, panel-test administration, and low-level node status remain primarily in the native engineering GUI.
|
||||
26
docs/legacy_xml_reference.md
Normal file
26
docs/legacy_xml_reference.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Legacy XML Reference
|
||||
|
||||
Source reviewed:
|
||||
|
||||
- `c:\Users\janni\Nextcloud\Documents\Infinity Vis\sample_data\infinity_mirror_mapping_clean.xml`
|
||||
|
||||
Useful facts extracted from the legacy file:
|
||||
|
||||
- 3 rows by 6 columns logical preview layout
|
||||
- 18 total tiles
|
||||
- 106 LEDs per tile
|
||||
- legacy transport metadata mentions WLED, Art-Net, and per-tile IP addresses
|
||||
- each tile is described as four perimeter segments summing to 106 LEDs
|
||||
|
||||
What this file is **not** used for:
|
||||
|
||||
- it is not the new hardware-mapping schema
|
||||
- it is not proof of the final ESP32 node-to-panel assignment
|
||||
- it does not validate `UART 6`, `UART 5`, or `UART 4`
|
||||
- it should not be imported as a generic slice engine
|
||||
|
||||
Recommended use:
|
||||
|
||||
- use it as a preview and spatial-reference aid only
|
||||
- manually transfer final room layout after the new hardware mapping is physically validated
|
||||
|
||||
50
docs/local_software_only_runbook.md
Normal file
50
docs/local_software_only_runbook.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Local Software-Only Runbook
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Rust `stable` Toolchain mit `cargo`, `rustc`, `rustfmt` und `clippy`
|
||||
- dieses Repo ist lokal aktuell **kein echter Git-Clone**, sondern nur ein Arbeitsbaum ohne `.git`
|
||||
- keine Hardware ist fuer den software-only Betrieb noetig
|
||||
|
||||
Beispiel fuer eine user-lokale Rust-Installation:
|
||||
|
||||
```bash
|
||||
curl -sSf https://sh.rustup.rs -o /tmp/rustup-init.sh
|
||||
sh /tmp/rustup-init.sh -y --profile minimal --default-toolchain stable
|
||||
. "$HOME/.cargo/env"
|
||||
rustup component add rustfmt clippy
|
||||
```
|
||||
|
||||
## Start
|
||||
|
||||
```bash
|
||||
. "$HOME/.cargo/env"
|
||||
cargo run -p infinity_host_api -- --config config/project.example.toml --bind 127.0.0.1:9001 --runtime-state data/runtime_state.json
|
||||
```
|
||||
|
||||
## Lokale URLs
|
||||
|
||||
- Creative Web-UI: `http://127.0.0.1:9001/`
|
||||
- State API: `http://127.0.0.1:9001/api/v1/state`
|
||||
- Preview API: `http://127.0.0.1:9001/api/v1/preview`
|
||||
- Snapshot API: `http://127.0.0.1:9001/api/v1/snapshot`
|
||||
- WebSocket-Stream: `ws://127.0.0.1:9001/api/v1/stream`
|
||||
|
||||
## Minimale Smoke-Checks
|
||||
|
||||
1. Web-UI laedt unter `http://127.0.0.1:9001/`.
|
||||
2. `GET /api/v1/state` antwortet mit `api_version: "v1"`.
|
||||
3. `ws://127.0.0.1:9001/api/v1/stream` verbindet und liefert zuerst `snapshot`, dann `preview`.
|
||||
4. In der Web-UI oder ueber `POST /api/v1/command` funktionieren diese Basisfluesse:
|
||||
- preset recall
|
||||
- preset save / overwrite
|
||||
- creative snapshot save / recall
|
||||
- blackout
|
||||
|
||||
## Runtime-State und Recovery
|
||||
|
||||
- Runtime-Persistenz liegt standardmaessig unter `data/runtime_state.json`.
|
||||
- Beim Schreiben werden aktiver Scene-State, Runtime-Presets, Runtime-Gruppen, Creative Snapshots und globale Steuerwerte persistiert.
|
||||
- Fehlende Dateien sind okay.
|
||||
- Leere, defekte oder schema-inkompatible Persistenzdateien blockieren den Serverstart nicht mehr.
|
||||
- In diesen Recovery-Faellen startet der Host mit Default-State und erzeugt Warning-/Info-Events im Eventfeed statt abzubrechen.
|
||||
67
docs/protocol.md
Normal file
67
docs/protocol.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Protocol
|
||||
|
||||
## Design Rules
|
||||
|
||||
- Separate control traffic from realtime traffic
|
||||
- Version every envelope
|
||||
- Keep realtime messages disposable
|
||||
- Prefer idempotent control commands
|
||||
- Let nodes recover independently after packet loss or reconnect
|
||||
|
||||
## Control Protocol
|
||||
|
||||
Purpose:
|
||||
|
||||
- Discovery
|
||||
- Heartbeat
|
||||
- Config sync
|
||||
- Preset recall
|
||||
- Panic and blackout
|
||||
- Telemetry
|
||||
|
||||
Current envelope model:
|
||||
|
||||
- `protocol_version`
|
||||
- `sequence`
|
||||
- `sent_at_millis`
|
||||
- `message`
|
||||
|
||||
Current control messages:
|
||||
|
||||
- `discovery_hello`
|
||||
- `discovery_ack`
|
||||
- `config_sync`
|
||||
- `heartbeat_request`
|
||||
- `heartbeat`
|
||||
- `preset_recall`
|
||||
- `blackout`
|
||||
|
||||
`config_sync` carries the authoritative per-node hardware assignment so the node can reject invalid activation before the first frame.
|
||||
|
||||
## Realtime Protocol
|
||||
|
||||
Purpose:
|
||||
|
||||
- Scene parameters for distributed rendering
|
||||
- Explicit pixel frames for frame streaming mode
|
||||
- Resync requests
|
||||
|
||||
Current realtime messages:
|
||||
|
||||
- `scene_parameters`
|
||||
- `pixel_frame`
|
||||
- `resync_request`
|
||||
|
||||
## Delivery Semantics
|
||||
|
||||
- Control messages must be small and replay-safe where possible
|
||||
- Realtime messages use latest-state-wins semantics
|
||||
- Nodes may drop stale realtime frames rather than replay them
|
||||
- A reconnecting node must request or receive a clean config sync before resuming output
|
||||
|
||||
## DDP Compatibility
|
||||
|
||||
DDP is treated as an optional compatibility shell for frame streaming mode only.
|
||||
|
||||
The internal model is intentionally broader than DDP because the preferred operating path is distributed scene mode with time-based parameters and node-local rendering.
|
||||
|
||||
113
docs/show_control_primitives.md
Normal file
113
docs/show_control_primitives.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Show-Control Primitives
|
||||
|
||||
## Ziel
|
||||
|
||||
Diese Primitive-Menge ist die kleine, dauerhafte interne Steuersemantik fuer software-only Show-Control. Sie bleibt bewusst generisch:
|
||||
|
||||
- keine UI-spezifischen Sonderfaelle
|
||||
- keine grandMA-spezifische Kopplung
|
||||
- keine zweite Architektur neben Host-Core und API
|
||||
|
||||
Der Implementierungspfad liegt in `crates/infinity_host/src/external_control.rs`.
|
||||
|
||||
## Primitive
|
||||
|
||||
### `blackout`
|
||||
|
||||
- Typ: direkt, mutierend
|
||||
- Semantik: setzt globalen Blackout an oder aus
|
||||
- Idempotenz: ja, bezogen auf den Zielzustand
|
||||
- Fehlercodes: unterliegende Host-Fehler nur bei Persistenzproblemen, typischerweise `persist_failed`
|
||||
- Event-Auswirkung: Info-Event auf Erfolg
|
||||
|
||||
### `recall_preset`
|
||||
|
||||
- Typ: direkt, mutierend
|
||||
- Semantik: recalled ein Preset inklusive seiner Zielgruppe und Transition-Metadaten
|
||||
- Idempotenz: praktisch ja, wenn dasselbe Preset erneut recalled wird
|
||||
- Fehlercodes: `unknown_preset`, `persist_failed`
|
||||
- Event-Auswirkung: Info-Event auf Erfolg, Warning-Event bei unbekanntem Preset
|
||||
|
||||
### `recall_creative_snapshot`
|
||||
|
||||
- Typ: direkt, mutierend
|
||||
- Semantik: recalled einen gespeicherten Creative Snapshot inklusive Scene- und Transition-State
|
||||
- Idempotenz: praktisch ja, wenn derselbe Snapshot erneut recalled wird
|
||||
- Fehlercodes: `unknown_creative_snapshot`, `persist_failed`
|
||||
- Event-Auswirkung: Info-Event auf Erfolg, Warning-Event bei unbekanntem Snapshot
|
||||
|
||||
### `set_master_brightness`
|
||||
|
||||
- Typ: direkt, mutierend
|
||||
- Semantik: setzt globale Helligkeit, intern auf `0.0..1.0` geklemmt
|
||||
- Idempotenz: ja, bezogen auf den geklemmten Zielwert
|
||||
- Fehlercodes: typischerweise nur `persist_failed`
|
||||
- Event-Auswirkung: Info-Event auf Erfolg
|
||||
|
||||
### `set_pattern`
|
||||
|
||||
- Typ: staged
|
||||
- Semantik: staged das Pattern fuer die naechste explizite Transition
|
||||
- Idempotenz: ja, letzter Wert gewinnt
|
||||
- Fehlercodes: `invalid_pattern_id`
|
||||
- Event-Auswirkung: kein Host-Event bis `trigger_transition`
|
||||
|
||||
### `set_group_parameter`
|
||||
|
||||
- Typ: staged
|
||||
- Semantik: staged einen Scene-Parameter fuer die naechste Transition und kann optional gleichzeitig die Zielgruppe fuer diese Transition setzen
|
||||
- Idempotenz: ja, letzter Wert pro Parameter-Key gewinnt
|
||||
- Fehlercodes: `invalid_group_parameter_key`
|
||||
- Event-Auswirkung: kein Host-Event bis `trigger_transition`
|
||||
|
||||
### `upsert_group`
|
||||
|
||||
- Typ: direkt, mutierend
|
||||
- Semantik: legt eine Runtime-Gruppe an oder ueberschreibt sie bewusst mit `overwrite: true`
|
||||
- Idempotenz: ja mit `overwrite: true`, nein mit `overwrite: false`
|
||||
- Fehlercodes: `invalid_group_id`, `invalid_group_members`, `group_exists`, `persist_failed`
|
||||
- Event-Auswirkung: Info-Event auf Erfolg
|
||||
|
||||
### `set_transition_style`
|
||||
|
||||
- Typ: staged
|
||||
- Semantik: staged Transition-Style und optional Duration fuer die naechste explizite Transition
|
||||
- Idempotenz: ja, letzter Wert gewinnt
|
||||
- Fehlercodes: keine zusaetzlichen Primitive-Fehler
|
||||
- Event-Auswirkung: kein Host-Event bis `trigger_transition`
|
||||
|
||||
### `trigger_transition`
|
||||
|
||||
- Typ: ausfuehrend, mutierend
|
||||
- Semantik: materialisiert den aktuell gestagten Transition-Intent in den Host
|
||||
- Ausfuehrungsreihenfolge:
|
||||
1. `select_group` nur wenn ueber staged Parameter ein Gruppenkontext gesetzt wurde
|
||||
2. `set_transition_duration_ms` nur wenn staged
|
||||
3. `set_transition_style` nur wenn staged
|
||||
4. `select_pattern`
|
||||
5. `set_scene_parameter` fuer alle staged Parameter
|
||||
- Idempotenz: nein, erfolgreicher Trigger konsumiert den gestagten Intent
|
||||
- Fehlercodes: `transition_pattern_required` plus unterliegende Host-Fehler wie `unknown_group` oder `persist_failed`
|
||||
- Event-Auswirkung: die unterliegenden Host-Kommandos erzeugen die sichtbaren Info-/Warning-Events
|
||||
|
||||
### `request_snapshot`
|
||||
|
||||
- Typ: read-only
|
||||
- Semantik: liefert den aktuellen Host-Snapshot ohne Host-Mutation
|
||||
- Idempotenz: ja
|
||||
- Fehlercodes: keine Primitive-eigenen
|
||||
- Event-Auswirkung: keine
|
||||
|
||||
## Hinweis zur Adapter-Nutzung
|
||||
|
||||
Die staged Primitive sind fuer externe Show-Control-Adapter gedacht, die absichtlich mehrere kleine Eingaben sammeln und erst mit `trigger_transition` in einen Host-seitigen Uebergang umsetzen. Direkte UI- oder API-Kommandos koennen weiterhin eager bleiben; die stabile interne Adapter-Semantik wird davon nicht aufgeblasen.
|
||||
|
||||
Ein stateless Port darf staged Primitive nicht stillschweigend akzeptieren. Wenn `set_pattern`, `set_group_parameter`, `set_transition_style` oder `trigger_transition` ohne Session-/Adapter-Kontext direkt an einem Port landen, ist der erwartete Fehlercode `show_control_session_required`.
|
||||
|
||||
## Referenz-Client
|
||||
|
||||
Der sehr duenne generische Referenzpfad liegt in `crates/infinity_host/src/external_control.rs`:
|
||||
|
||||
- `ReferenceShowControlClient::stateful(...)` fuer direkte plus staged Flows
|
||||
- `ReferenceShowControlClient::stateless(...)` zum bewussten Nachweis, dass staged Primitive am nackten Port mit `show_control_session_required` abgewiesen werden
|
||||
- `BufferedShowControlAdapter` und `ShowControlSession` als kleine Buffer-/Commit-Implementierung ohne neue Grundarchitektur
|
||||
37
docs/testing.md
Normal file
37
docs/testing.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Testing
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Host-side unit tests currently cover:
|
||||
|
||||
- fixed LED count validation
|
||||
- duplicate driver reference rejection
|
||||
- activation guard against unresolved hardware mapping
|
||||
- protocol envelope version stamping
|
||||
|
||||
## Planned Integration Tests
|
||||
|
||||
1. Host to node config sync
|
||||
2. Host to node preset recall during load
|
||||
3. Reconnect and resync after heartbeat timeout
|
||||
4. Frame streaming fallback without scene-mode support
|
||||
|
||||
## Soak Tests
|
||||
|
||||
Target procedure:
|
||||
|
||||
1. Run a continuous scene loop for 8 hours
|
||||
2. Rotate presets on a schedule
|
||||
3. Simulate packet loss and node reconnects
|
||||
4. Log dropped frames, reconnect count, and jitter
|
||||
|
||||
## Hardware Validation Tests
|
||||
|
||||
Required before live deployment:
|
||||
|
||||
1. Walking pixel test across all 106 LEDs on each of the 18 outputs
|
||||
2. Start pixel verification per output
|
||||
3. Direction verification per output
|
||||
4. Color order verification per output
|
||||
5. Final confirmation of which physical channel maps to top, middle, and bottom on every node
|
||||
|
||||
14
docs/validation_open_points.md
Normal file
14
docs/validation_open_points.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Validation Open Points
|
||||
|
||||
These items must be resolved before the system can move from structural validation to activation validation:
|
||||
|
||||
1. Confirm the real meaning of `UART 6`, `UART 5`, and `UART 4`.
|
||||
2. Confirm the exact LED chipset and required output timing backend.
|
||||
3. Confirm whether every output truly has exactly 106 active LEDs, with no dummy or reserve pixels.
|
||||
4. Confirm color order for each output.
|
||||
5. Confirm direction and start pixel for each output.
|
||||
6. Confirm which physical node controls which installed top, middle, and bottom panel.
|
||||
7. Confirm whether transport runs over dedicated Wi-Fi or wired Ethernet.
|
||||
8. Confirm whether DDP compatibility is optional or mandatory in the first deployment.
|
||||
9. Confirm whether the first production UI is Tauri, egui, or another adapter on top of the host core.
|
||||
|
||||
Reference in New Issue
Block a user