This commit is contained in:
Martino Ferrari
2026-04-25 22:56:09 +02:00
parent 8b548ba1c2
commit 986f6cd6d8
85 changed files with 11479 additions and 5050 deletions
+68 -48
View File
@@ -97,26 +97,31 @@
---
## Phase 3 — Synthetic Data Source
## Phase 3 — Synthetic Data Source
**Goal:** users can define computed signals from existing signals.
- [ ] Define synthetic signal definition schema (JSON)
- [ ] Implement DAG evaluator: nodes re-evaluated on upstream change
- [ ] Built-in node types (`internal/dsp/`):
- [x] Define synthetic signal definition schema (JSON)`internal/datasource/synthetic/definition.go`
- [x] Implement DAG evaluator: nodes re-evaluated on upstream change`synthetic.go` Subscribe goroutine
- [x] Built-in node types (`internal/dsp/nodes.go`):
- Arithmetic: gain, offset, add, subtract, multiply, divide
- Statistics: moving average (by count, by time), RMS
- Filters: IIR lowpass/highpass/bandpass (via gonum or biquad)
- Filters: IIR biquad lowpass/highpass/bandpass
- Calculus: finite-difference derivative, cumulative integral
- FFT / inverse FFT (gonum/dft)
- Threshold / clamp
- Custom expression (simple formula parser)
- [ ] Lua node: sandboxed `gopher-lua` state per signal; inputs as globals
- [ ] Load synthetic definitions from `synthetic.json` at startup
- [ ] REST endpoint to CRUD synthetic signal definitions (persists to JSON file)
- [ ] Unit tests for each DSP node type
- Custom expression (formula parser)
- [x] Lua node: sandboxed `gopher-lua` state per signal; inputs as globals
- [x] Load synthetic definitions from `synthetic.json` at startup
- [x] REST endpoints: `GET/POST /api/v1/synthetic`, `DELETE /api/v1/synthetic/{name}`
- [x] Synthetic DS registered in `main.go`; `storePath = cfg.Server.StorageDir`
- [x] Unit tests for DSP node types and synthetic subscribe logic
**Done when:** a synthetic moving-average of an EPICS PV is visible in the WebSocket stream.
**Done when:** a synthetic moving-average of an EPICS PV is visible in the WebSocket stream.
**Notes:**
- Stub DS is always registered; synthetic DS is conditional on `cfg.Synthetic.Enabled`
- Pipeline is re-evaluated on every upstream update; no polling
- REST CRUD routes added to `internal/api/api.go`; Handler now holds optional `*synthetic.Synthetic`
---
@@ -144,53 +149,68 @@
- Uses Svelte 5 runes (`$state`, `$props`, `$derived`) throughout, matching existing code style
- Routing is a simple `mode` state variable in `App.svelte` (no SvelteKit, no router library)
- Unknown widget types in Canvas render as grey dashed placeholder boxes (forward-compatible)
- **Frontend build pipeline migrated from npm/Vite/Svelte → pure Go (esbuild API + Preact):** Svelte components rewritten as `.tsx` Preact components; `tools/buildfrontend/main.go` bundles `web/src/main.tsx` via `github.com/evanw/esbuild`; vendor libraries (Preact, uPlot, ECharts) committed as ESM files in `web/vendor/`; `//go:generate go run ../tools/buildfrontend/main.go` in `web/embed.go`; `make frontend` now runs `go generate ./web/...` — no Node/npm required
---
## Phase 5 — View Mode Widgets
## Phase 5 — View Mode Widgets
**Goal:** all widget types rendered and interactive in view mode.
- [ ] Text view widget
- [ ] Gauge widget (SVG arc, configurable range/thresholds)
- [ ] Vertical / horizontal bar widget
- [ ] LED widget (condition evaluator, configurable colours)
- [ ] Multi-LED widget (bitset, per-bit labels)
- [ ] Set-value widget (input + Set button; sends `write` over WS)
- [ ] Button widget (sends fixed value on click)
- [ ] Plot widget:
- Time-series (uPlot, streaming buffer of N points)
- FFT, waterfall, histogram, bar chart, logic analyser (ECharts)
- Multi-signal support; legend
- [ ] Text label (static)
- [ ] Image widget (base64 or server URL)
- [ ] Link widget (navigates to another interface)
- [ ] Right-click context menu: signal info dialog, copy name, export CSV
- [ ] Svelte component tests for each widget type (mock store)
- [x] Text view widget (`TextView.tsx`) — live value + quality dot + unit
- [x] Gauge widget (`Gauge.tsx`) — SVG arc, configurable range/thresholds, colour zones
- [x] Horizontal / vertical bar widget (`BarH.tsx`, `BarV.tsx`)
- [x] LED widget (`Led.tsx`) — condition evaluator, configurable colours, blink on uncertain quality
- [x] Multi-LED widget (`MultiLed.tsx`) — bitset, per-bit labels and colours
- [x] Set-value widget (`SetValue.tsx`) — input + Set button; sends `write` over WS
- [x] Button widget (`Button.tsx`) — sends fixed value on click, optional confirm dialog
- [x] Plot widget (`PlotWidget.tsx`):
- Time-series (uPlot, streaming ring buffer, dark axis styling)
- Histogram, bar chart, FFT, waterfall, logic analyser (ECharts)
- Multi-signal support; legend; ResizeObserver-based resize
- [x] Text label (`TextLabel.tsx`) — static text, configurable font size/colour/align
- [x] Image widget (`ImageWidget.tsx`) — renders image from URL, configurable object-fit
- [x] Link widget (`LinkWidget.tsx`) — navigates to another interface via `onNavigate` callback
- [x] Right-click context menu signal info, copy signal name, export CSV stub
- [ ] Component tests (deferred to Phase 10 hardening)
**Done when:** a complete HMI panel with multiple widget types runs smoothly at 60 fps.
**Done when:** a complete HMI panel with multiple widget types runs smoothly at 60 fps.
**Notes:**
- Frontend build pipeline migrated to pure Go (esbuild + Preact); no npm/Node required
- `Canvas.tsx` dispatches `onNavigate(interfaceId)``ViewMode.tsx``GET /api/v1/interfaces/{id}`
- Plot widget uses static imports (not `await import()`) to avoid async race with signal subscriptions
---
## Phase 6 — Edit Mode (Core)
## Phase 6 — Edit Mode (Core)
**Goal:** users can build and save an interface from scratch.
- [ ] Konva stage setup in edit mode; `devicePixelRatio` scaling
- [ ] Widget renderer adapter: same widget components rendered on Konva layer
- [ ] Signal tree pane: fetch signal list, tree display, filter/search
- [ ] Drag signal from tree → drop on canvas → widget type picker
- [ ] Place widget at drop coordinates with default size
- [ ] Single selection: bounding box + resize handles via Konva `Transformer`
- [ ] Move widget by dragging body
- [ ] Delete widget (× button or Del key)
- [ ] Properties pane: common options (label, position, size)
- [ ] Per-widget property editors (range, colour, plot type, etc.)
- [ ] Save interface to server (POST/PUT XML)
- [ ] Load interface from server (GET XML, populate canvas)
- [ ] Export / import local XML file
- [x] `internal/storage/store.go` — file-based XML storage (`{storage_dir}/interfaces/`)
- [x] Interface CRUD REST endpoints: `GET/POST /interfaces`, `GET/PUT/DELETE/POST(clone) /interfaces/{id}`
- [x] Signal tree pane (`SignalTree.tsx`): fetches all DS/signal lists, collapsible groups, filter/search, drag source
- [x] Drag signal from tree → drop on canvas → `WidgetTypePicker` popup (12 widget types)
- [x] Place widget at drop coordinates with type-appropriate default size
- [x] `EditCanvas.tsx`: widget live-preview + transparent overlay divs for interaction; pure DOM mouse events (no Konva)
- [x] Single selection: 2px blue bounding box + 8 resize handles (N/S/E/W/NE/NW/SE/SW)
- [x] Move widget by dragging body; resize by dragging handles
- [x] Delete widget (✕ button on selection or Del/Backspace key)
- [x] `PropertiesPane.tsx`: canvas size/name + per-widget position/size/signal/options; type-specific fields
- [x] Save interface to server (POST creates, PUT updates; XML body)
- [x] Export / import local XML file
- [x] Edit mode toolbar: name editor, dirty indicator, Save/Export/Import/Close buttons
- [x] InterfaceList shows server-persisted interfaces with click-to-view, edit/clone/delete actions
- [x] Edit button enabled in view mode toolbar
**Done when:** an engineer can create a panel with 5+ widgets, save it, reload it, and see live data.
**Done when:** an engineer can create a panel with 5+ widgets, save it, reload it, and see live data.
**Notes:**
- Pure DOM drag/resize (no Konva): overlay `div` per widget, `document.addEventListener('mousemove/mouseup')` during drag/resize
- HTML5 drag-and-drop transfers `SignalRef` JSON via `dataTransfer`
- `serializeInterface()` in `xml.ts` serializes `Interface` → XML; `parseInterface()` deserializes back
- Storage IDs are slugified names; uniqueness guaranteed by millisecond timestamp suffix
- No Konva dependency — Konva deferred to Phase 7 if multi-select area-select is needed
---
@@ -273,10 +293,10 @@ These are rough single-developer estimates. Parallel work across backend and fro
| 0 | 12 days | ✅ Complete |
| 1 | 35 days | ✅ Complete |
| 2 | 12 weeks | ✅ Complete |
| 3 | 12 weeks | |
| 3 | 12 weeks | ✅ Complete |
| 4 | 35 days | ✅ Complete |
| 5 | 23 weeks | |
| 6 | 23 weeks | |
| 5 | 23 weeks | ✅ Complete |
| 6 | 23 weeks | ✅ Complete |
| 7 | 12 weeks | — |
| 8 | 1 week | — |
| 9 | 1 week | — |