Reviews and fixes RuView's 10 most recent substantive issues, plus closes the wiring gap flagged in the perception-substrate review (docs/adr, gist, release notes from the PR #1579 review). ## Fixed - #1526: UI falsely claimed "LIVE — ESP32 Hardware Connected" whenever the unauthenticated /api/v1/status probe 401'd. Now sends the bearer token on the probe (the fallback direction was already fixed by ADR-295). - #1554: top-level `classification` (GET /api/v1/sensing/latest) was the last UDP packet's single node, not the fused room aggregate, so it flapped at packet rate with 2+ disagreeing nodes. Now derived from `RoomInference` (ADR-297's `fuse_room`) at both call sites. - #1541: per-node MQTT `presence`/`presence_score` read a `classification` JSON key that does not exist on `NodeInfo` (the real field is `node_inference`), silently falling back to the room aggregate for every node — reproducing the exact "lockstep publish" the field report measured. Also fixed the *existing* regression test for this bug, which used the same wrong key in its own fixture and so never caught it. - #1557: pose-fusion's `wsPortMap` only knew port 3000, so a remapped host port fell through to `localhost:8765` (nothing there for a remote viewer). WS port is now derived from `location.port` instead of a 2-entry lookup table (the "never render simulated as live" half was already fixed by ADR-295's `onVerifiedFrame` gate). - #1525: the no-model pose path already clamps keypoint confidence to a 0.1 floor, but the renderer's own threshold is also 0.1 compared with `<=`/`>` — a keypoint at exactly the floor was still invisible. Floor raised to 0.15 to clear the client's gate. - #1556: `--mqtt-ca-file`/`--mqtt-client-cert`/`--mqtt-client-key` were parsed and stored but never applied — TLS always used the system trust store, so a self-signed broker always failed UnknownIssuer. Now builds `rumqttc::TlsConfiguration::Simple` from the real PEM files (no new TLS dependency needed). A file that can't be read logs why and falls back to system trust instead of failing opaquely later. - #1555: the MQTT availability heartbeat asserted "online" for every known node on a fixed 30s timer regardless of whether that node's data was still arriving. Now tracks each node's last-seen broadcast snapshot and only reports "online" within a 10s freshness window, otherwise "offline" — a frozen sensor can no longer look available. (The other half — restarting a publisher that goes permanently silent — needs a reproduction the reporter themselves weren't certain of; left for a follow-up rather than guessing at the trigger.) - #1540: already fixed on main (node-keyed RateLimiter, ADR-297). - #1521/#1522: not fixable in this repo (published HF model artifact); replied with the ADR-298 gate status and the exact byte-level fix for the safetensors header, and corrected the README row that claimed the file loads with the reference loader. - #1542, #1527: replied — #1542 is a real, larger firmware+server feature left open for follow-up; #1527's suggested fixes were already applied, the one residual sample is an inherent first-frame paint gap. ## Certificate spine wiring (closes the gap flagged in the PR #1579 review) `ruview-certify` and `ruview-policy` now depend on `ruview-ood` and provide real `From<ruview_ood::DomainState>` adapters plus a composed entry point, `ruview_policy::authorize_from_certificate`, matching the adapter contract `ruview-policy`'s own doc comment already described but that no code actually implemented. A new cross-crate integration test (`acceptance_test_b_real_integration`) mints a real signed `CapabilityCertificate` and proves a real post-drift `ruview_ood::Unknown` denies a `SafetyCritical` action through the composed pipeline — not two disconnected unit tests hand-setting the same enum value. Also: - Wires `evaluate_linear_head` (ADR-298 model-release gate) into a new CI job so the checker itself can't silently regress; documents that gating an actual model publish is still a manual step (no HF automation here). - Wires `SourceState::export_watermark()` into `start_recording`: a recording captured while the source is synthetic is now stamped in its metadata (not the filename or per-line JSON, to avoid breaking `delete_recording`'s path reconstruction or the training dataset loader's schema). - Updates docs/user-guide.md's "Developer Preview" section to describe what's now genuinely wired vs. still not (no live continuous calibration/OOD loop in the running server yet). ## Validation - cargo test --workspace --no-default-features: 4391 passed, 0 failed - cargo build --release -p wifi-densepose-sensing-server --features mqtt: clean - Server smoke-tested end-to-end against the simulator (real startup, UDP/WS/HTTP listeners, /api/v1/sensing/latest stable across calls) - Real ESP32-S3 hardware was NOT reachable this session (no COM port present, zero UDP frames received after 40s bound to 0.0.0.0:5005) — the multi-node fixes are validated by the new unit/integration tests and full-workspace regression, not by live hardware. Co-Authored-By: claude-flow <ruv@ruv.net> |
||
|---|---|---|
| .. | ||
| components | ||
| config | ||
| icons | ||
| mobile | ||
| observatory | ||
| pose-fusion | ||
| services | ||
| tests | ||
| utils | ||
| .eslintrc.json | ||
| app.js | ||
| index.html | ||
| manifest.json | ||
| observatory.html | ||
| pose-fusion.html | ||
| README.md | ||
| start-ui.sh | ||
| style.css | ||
| sw.js | ||
| sw.test.mjs | ||
| TEST_REPORT.md | ||
| viz.html | ||
WiFi DensePose UI
A modular, modern web interface for the WiFi DensePose human tracking system. Provides real-time monitoring, WiFi sensing visualization, and pose estimation from CSI (Channel State Information).
Architecture
The UI follows a modular architecture with clear separation of concerns:
ui/
├── app.js # Main application entry point
├── index.html # HTML shell with tab structure
├── style.css # Complete CSS design system
├── config/
│ └── api.config.js # API endpoints and configuration
├── services/
│ ├── api.service.js # HTTP API client
│ ├── websocket.service.js # WebSocket connection manager
│ ├── websocket-client.js # Low-level WebSocket client
│ ├── pose.service.js # Pose estimation API wrapper
│ ├── sensing.service.js # WiFi sensing data service (live + simulation fallback)
│ ├── health.service.js # Health monitoring API wrapper
│ ├── stream.service.js # Streaming API wrapper
│ └── data-processor.js # Signal data processing utilities
├── components/
│ ├── TabManager.js # Tab navigation component
│ ├── DashboardTab.js # Dashboard with live system metrics
│ ├── SensingTab.js # WiFi sensing visualization (3D signal field, metrics)
│ ├── LiveDemoTab.js # Live pose detection with setup guide
│ ├── HardwareTab.js # Hardware configuration
│ ├── SettingsPanel.js # Settings panel
│ ├── PoseDetectionCanvas.js # Canvas-based pose skeleton renderer
│ ├── gaussian-splats.js # 3D Gaussian splat signal field renderer (Three.js)
│ ├── body-model.js # 3D body model
│ ├── scene.js # Three.js scene management
│ ├── signal-viz.js # Signal visualization utilities
│ ├── environment.js # Environment/room visualization
│ └── dashboard-hud.js # Dashboard heads-up display
├── utils/
│ ├── backend-detector.js # Auto-detect backend availability
│ ├── mock-server.js # Mock server for testing
│ └── pose-renderer.js # Pose rendering utilities
└── tests/
├── test-runner.html # Test runner UI
├── test-runner.js # Test framework and cases
└── integration-test.html # Integration testing page
Features
WiFi Sensing Tab
- 3D Gaussian-splat signal field visualization (Three.js)
- Real-time RSSI, variance, motion band, breathing band metrics
- Presence/motion classification with confidence scores
- Data source banner: green "LIVE - ESP32", yellow "RECONNECTING...", or red "SIMULATED DATA"
- Sparkline RSSI history graph
- "About This Data" card explaining CSI capabilities per sensor count
Live Demo Tab
- WebSocket-based real-time pose skeleton rendering
- Estimation Mode badge: green "Signal-Derived" or blue "Model Inference"
- Setup Guide panel showing what each ESP32 count provides:
- 1 ESP32: presence, breathing, gross motion
- 2-3 ESP32s: body localization, motion direction
- 4+ ESP32s + trained model: individual limb tracking, full pose
- Debug mode with log export
- Zone selection and force-reconnect controls
- Performance metrics sidebar (frames, uptime, errors)
Dashboard
- Live system health monitoring
- Real-time pose detection statistics
- Zone occupancy tracking
- System metrics (CPU, memory, disk)
- API status indicators
Hardware Configuration
- Interactive antenna array visualization
- Real-time CSI data display
- Configuration panels
- Hardware status monitoring
Data Sources
The sensing service (sensing.service.js) supports three connection states:
| State | Banner Color | Description |
|---|---|---|
| LIVE - ESP32 | Green | Connected to the Rust sensing server receiving real CSI data |
| RECONNECTING | Yellow (pulsing) | WebSocket disconnected, retrying (up to 20 attempts) |
| SIMULATED DATA | Red | Fallback to client-side simulation after 5+ failed reconnects |
Simulated frames include a _simulated: true marker so code can detect synthetic data.
Backends
Rust Sensing Server (primary)
The Rust-based wifi-densepose-sensing-server serves the UI and provides:
GET /health— server healthGET /api/v1/sensing/latest— latest sensing featuresGET /api/v1/vital-signs— vital sign estimates (HR/RR)GET /api/v1/model/info— RVF model container infoWS /ws/sensing— real-time sensing data streamWS /api/v1/stream/pose— real-time pose keypoint stream
Python FastAPI (legacy)
The original Python backend on port 8000 is still supported. The UI auto-detects which backend is available via backend-detector.js.
Quick Start
With Docker (recommended)
cd docker/
# Default: auto-detects ESP32 on UDP 5005, falls back to simulation
docker-compose up
# Force real ESP32 data
CSI_SOURCE=esp32 docker-compose up
# Force simulation (no hardware needed)
CSI_SOURCE=simulated docker-compose up
Open http://localhost:3000/ui/index.html
With local Rust binary
cd v2
cargo build -p wifi-densepose-sensing-server --no-default-features
# Run with simulated data
../../target/debug/sensing-server --source simulated --tick-ms 100 --ui-path ../../ui --http-port 3000
# Run with real ESP32
../../target/debug/sensing-server --source esp32 --tick-ms 100 --ui-path ../../ui --http-port 3000
Open http://localhost:3000/ui/index.html
With Python HTTP server (legacy)
# Start FastAPI backend on port 8000
wifi-densepose start
# Serve the UI on port 3000
cd ui/
python -m http.server 3000
Pose Estimation Modes
| Mode | Badge | Requirements | Accuracy |
|---|---|---|---|
| Signal-Derived | Green | 1+ ESP32, no model needed | Presence, breathing, gross motion |
| Model Inference | Blue | 4+ ESP32s + trained .rvf model |
Full 17-keypoint COCO pose |
To use model inference, start the server with a trained model:
sensing-server --source esp32 --model path/to/model.rvf --ui-path ./ui
Configuration
API Configuration
Edit config/api.config.js:
export const API_CONFIG = {
BASE_URL: window.location.origin,
API_VERSION: '/api/v1',
WS_CONFIG: {
RECONNECT_DELAY: 5000,
MAX_RECONNECT_ATTEMPTS: 20,
PING_INTERVAL: 30000
}
};
Testing
Open tests/test-runner.html to run the test suite:
cd ui/
python -m http.server 3000
# Open http://localhost:3000/tests/test-runner.html
Test categories: API configuration, API service, WebSocket, pose service, health service, UI components, integration.
Styling
Uses a CSS design system with custom properties, dark/light mode, responsive layout, and component-based styling. Key variables in :root of style.css.
License
Part of the WiFi-DensePose system. See the main project LICENSE file.