summaryrefslogtreecommitdiff
path: root/web-timeplot/ARCHITECTURE.md
blob: 1cfc3f10b4c507fba3cb4c683f13572d2d3c1ba9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# TimePlot Architecture

## Overview

The restarted TimePlot app is built around five small systems:

1. **Store** — single source of truth for app state
2. **Time controller** — advances real time and plot time
3. **Source registry** — owns active data source lifecycle
4. **Plot view** — renders visible samples and handles hover picking
5. **Panel manager** — builds the DOM shell and user controls

The current implementation is intentionally compact, but each system is already separated enough to grow without turning the app into a monolith again.

## Runtime flow

```text
TimeController.tick()
    ↓
Store.time updated
    ↓
SourceRegistry.update(plotTime)
    ↓
SyntheticWaveSource emits samples
    ↓
PlotBuffer stores bounded history
    ↓
TimeplotView renders visible window
    ↓
PanelManager reflects status + tooltip
```

## Core principles

### 1. Time is explicit

Plot time is not inferred from frame count or rendering. It is advanced by `TimeController`, which makes features like pause, speed changes, replay, and stepping straightforward.

### 2. Data sources are replaceable

`SourceRegistry` talks to the active source through a tiny shared contract:

- `start(startTimeMs)`
- `update(currentPlotTimeMs)`
- `reset(startTimeMs)`
- `updateConfig(partialConfig)`

That keeps future WebSocket, file replay, database, or simulated sources easy to add.

### 3. Rendering stays focused

`TimeplotView` does not own application state or source orchestration. It receives state plus visible points and turns that into pixels. Hover detection also lives close to rendering because it depends on screen-space positions.

### 4. UI panels stay in the DOM

The plot is GPU-rendered with PixiJS. Controls, labels, and config panels stay in regular DOM so they are easy to iterate on, inspect, and restyle.

### 5. Composition happens at the edge

`create-app.js` is the composition root. It wires together store, time, sources, plot, UI, keyboard shortcuts, and the frame loop. That keeps the rest of the modules simple and testable.

## Current state shape

```js
{
  app: { title, renderer },
  time: {
    realNowMs,
    realElapsedMs,
    plotTimeMs,
    speed,
    paused,
  },
  plot: {
    showGrid,
    showPoints,
    windowDurationMs,
    maxPoints,
    valueRange,
    hoveredPoint,
    tooltip,
  },
  source: {
    activeId,
    preset,
    sampleRateHz,
    amplitude,
    noise,
  },
  panels: {
    status,
    source,
    config,
    help,
  },
}
```

## Modules

### `src/core/store.js`

A tiny centralized store. It currently favors clarity over abstraction-heavy patterns.

### `src/core/time-controller.js`

Owns playback semantics:

- pause/resume
- speed control
- plot time reset
- frame-to-frame delta handling

### `src/data/synthetic-wave-source.js`

Generates sample streams from a preset waveform. Right now it supports:

- `telemetry`
- `chirp`
- `burst`

### `src/plot/plot-buffer.js`

Maintains bounded history so rendering and hover picking only operate on a manageable number of samples.

### `src/plot/timeplot-view.js`

Owns Pixi initialization, plotting, grid drawing, and nearest-point hover selection.

### `src/ui/panel-manager.js`

Creates:

- top transport bar
- panel toggle buttons
- status panel
- data source panel
- config panel
- help panel
- floating tooltip

## Why this is a better baseline

The old project had useful ideas but too many concerns were mixed together. The new baseline is better because:

- transport logic is separate from rendering
- data generation is separate from app wiring
- UI is separate from GPU drawing
- state is centralized and observable
- adding a new source or panel no longer requires rewriting the whole app

## Recommended next steps

### Near term

- add persisted settings for panel visibility and playback preferences
- support multiple plot panes from a shared timebase
- add line/series definitions instead of a single hard-coded signal

### Medium term

- add schema-aware input adapters
- add WebSocket and replay-file sources
- add panel docking/layout persistence
- add markers, cursors, and annotations

### Longer term

- multi-stream synchronization
- richer interaction model for HID input mapping
- plug-in style source and panel registration