Skip to content

Feature/replay simulation mode#3

Merged
gitfeber merged 9 commits into
mainfrom
feature/replay-simulation-mode
Jun 7, 2026
Merged

Feature/replay simulation mode#3
gitfeber merged 9 commits into
mainfrom
feature/replay-simulation-mode

Conversation

@gitfeber
Copy link
Copy Markdown
Owner

@gitfeber gitfeber commented Jun 7, 2026

This pull request introduces frontend-only replay and simulation modes, enabling the dashboard to display recorded or synthetic telemetry data without requiring a live hardware connection. It standardizes the log file schema for compatibility with the new replay engine, updates the UI to clearly indicate the active source mode, and ensures the map and track rendering behave correctly across live, replay, and simulation modes. Additional developer tooling and documentation improvements are also included.

Replay & Simulation Mode Support:

  • Added replay and simulation as selectable telemetry sources in the frontend, allowing users to view recorded .jsonl/.json logs or run deterministic simulations, with clear UI indicators and controls for source mode selection. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]

  • Updated the MapPanel component to support a controlled track mode, rendering replay/simulation tracks deterministically and disabling internal track appending when not in live mode. [1] [2] [3] [4]

Log Schema and Backend Updates:

  • Standardized the log file schema (schema version 1) for both Rust (Tauri) and Node.js backends, including fields for schemaVersion, ts, relativeMs, source, type, and state, ensuring compatibility with the replay engine and future extensibility. [1] [2] [3] [4] [5] [6] [7]

Documentation Improvements:

  • Expanded documentation in README.md and CONTEXT.md to clarify terminology and describe the new replay and simulation capabilities, including references to relevant ADRs and technical docs. [1] [2]

Developer Tooling:

  • Added vitest and related scripts to the web app's package.json to enable unit testing. [1] [2]

gitfeber added 9 commits June 7, 2026 17:16
Summary:
- Added replay/source-mode shared types and REPLAY_LOG_SCHEMA_VERSION = 1
  to packages/shared (TelemetrySourceMode, ReplayStatus, ReplayTimingMode,
  ReplaySpeedMode, ReplayLogEvent union, NormalizedReplayEvent,
  ReplayLogMetadata, ReplayDiagnostics, ReplayControllerState,
  SimulationScenario/Options).
- Set up Vitest in apps/web (node env, src/**/*.test.ts) with test/
  test:watch scripts; added vitest dev dependency.
- Implemented the replay parser: JSONL + JSON-manifest forms, legacy
  {time,type,data}, plain TelemetryState lines, comment/empty-line
  skipping, synthetic 20 Hz timeline, non-monotonic preservation, and
  metadata extraction (counts, duration, hasGps/Battery/Radio/Attitude).
- Added parser fixtures and 13 parser tests.
- No UI integration yet.
Summary:
- Added apps/web/src/replay/scheduler.ts: a pure, timer-free scheduler
  core with no React state, timers, DOM, rAF, or wall-clock reads.
- Functions: advanceTo, stepOnce, seekTo, reset, plus eventVirtualTimeMs
  and totalDurationMs helpers.
- Implements all four timing modes:
  - original  — emit events with mapped time <= virtual time
  - fixedRate — index-based cadence (N * 1000/Hz), ignoring original gaps
  - manual    — no auto-advance; stepOnce only
  - max       — capped chunks/frame (DEFAULT_MAX_EVENTS_PER_CHUNK = 1000),
                always finishes on the last event
- seekTo rebuilds from index 0 for deterministic state/track reconstruction.
- Seeks are clamped; stepOnce is idempotent at the end.
- Added 14 scheduler tests using synthetic timestamps (no fake timers).
…rack

Summary:
- Added apps/web/src/replay/reconstruct.ts (pure):
  - applyEvent: full telemetry events replace state (merged onto a fresh
    empty state to normalize + sanitize); partialTelemetry events deep-merge.
  - deepMerge drops NaN/Infinity and out-of-range GPS (lat ∉ [-90,90],
    lon ∉ [-180,180]) without wiping existing fields; unknown fields pass
    through without crashing.
  - foldEvents builds telemetry state + a deduplicated controlled track
    (TrackPoint[]); stationary positions don't append duplicate points.
  - reconstructUpTo rebuilds deterministically from index 0 for seek/restart.
  - Reuses lib/geo validCoordinate and lib/initialTelemetry defaults.
- Added 9 reconstruction tests (full replace, partial merge preservation,
  invalid-value rejection, unknown-field safety, track dedup, determinism,
  seek forward/back, incremental == full rebuild).
Summary:
- Added apps/web/src/replay/simulation.ts (pure, deterministic):
  - mulberry32 PRNG (no dependency), bounded ReplayEvent[] generation.
  - Scenarios: nominalFlight, weakRadioLink, gpsDegradation,
    lowBatteryApproach, each with distinct battery drain, RSSI, and GPS
    behavior plus deterministic lifecycle/warning activity events.
  - generateSimulationEvents + generateSimulationMetadata; no wall-clock
    reads (absoluteTsMs always null, timeline starts at 0).
  - 9 simulation tests (determinism, rate/shape, scenario effects).
- Added apps/web/src/hooks/useReplayController.ts:
  - Stateful engine wrapping the tested scheduler + reconstruction cores.
  - Thin requestAnimationFrame driver; commits React state once per frame.
  - load/start/pause/resume/stop/restart/seek/step, timing-mode/speed/
    fixed-rate setters, tagged [REPLAY]/[SIM] activity log, separate
    ReplayDiagnostics, deterministic seek/restart (track never duplicates).
  - Idempotent controls, duplicate-rAF guard, cleanup on unmount, and a
    sessionStorage reload marker (handoff §12). Never touches serial.
Summary:
- Added apps/web/src/hooks/useTelemetrySource.ts:
  - Wraps live useTelemetry() + useReplayController() behind one
    dashboard-facing surface (same field names the dashboard already uses).
  - Owns activeSourceMode; displayed telemetry is selected from the active
    mode while live/replay/sim telemetry stay internally separate.
  - Live telemetry keeps running in the background during replay/sim;
    exposes liveConnectedInBackground and liveControlsLocked.
  - GUARDS live session-mutating actions (connect/disconnect/reset/logging/
    refreshPorts) to no-op while a non-live mode is active — defense in depth
    so replay/sim can never trigger serial writes or reconnect logic.
  - Merges live + replay/sim activity logs (newest first, capped at 200);
    clearLogs clears both.
  - Switching directly between replay and simulation halts the rAF driver
    and discards the prior load so no stale data shows under the wrong banner.
Summary:
- App.tsx now uses useTelemetrySource(); adds a persistent non-live banner
  (REPLAY/SIMULATION) and an inset color tint around the dashboard.
- MapPanel: added controlled-track support (controlledTrack + trackMode).
  Live mode is unchanged (internal append-on-change track); replay/sim render
  exactly the controller-provided track and never append. Clear Track button
  is hidden in controlled mode.
- Topbar: added Live/Replay/Simulation segmented control + colored source
  badge (green/amber/purple), the "Live connected in background" notice, and
  disabled all live serial controls (port/baud/refresh/connect/disconnect/
  reset/logging) while a non-live mode is active.
- New ReplaySimPanel: file picker with ~25 MB warn / ~100 MB hard-refuse
  guards (raw text read locally, never stored in state), metadata summary,
  transport controls (start/pause/resume/stop/restart/step), seek bar,
  speed/timing-mode/fixed-rate selectors, scenario+seed+duration+rate form
  for simulation, and a diagnostics readout. Shows the GPS/privacy note.
Summary:
- Node writer (apps/server/src/services/loggerService.ts): now records the
  v1 schema { schemaVersion, ts, relativeMs, source:"live", type:"telemetry",
  state } instead of legacy { time, type, data }. Captures sessionStartMs on
  start() so relativeMs = ts - sessionStartMs. Log location and start/stop UX
  unchanged.
- Rust/desktop writer (apps/desktop/src-tauri/src/lib.rs): same v1 schema;
  added LoggerState.session_start_ms (set on start_logging), and
  write_log_if_active now emits schemaVersion/ts/relativeMs/source/state using
  saturating_sub for relativeMs.
- The replay parser already reads both v1 and legacy {time,type,data}, so old
  logs remain replayable and new logs replay without conversion (covered by
  the existing basic-flight.jsonl v1 fixture + legacy-flight.jsonl fixture).
Summary:
- Added docs/replay-mode.md: source modes (Live/Replay/Simulation), replay
  usage + size guards, transport/timing/speed controls, simulation scenarios,
  JSONL v1 + legacy log format, parser tolerance rules, privacy/security
  notes (GPS, no upload, untrusted input), diagnostics, and how to run tests.
- README: added Replay & Simulation to the feature list with links to
  docs/replay-mode.md and ADR 0003.
@gitfeber gitfeber merged commit 7590456 into main Jun 7, 2026
2 checks passed
gitfeber added a commit that referenced this pull request Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant