Phase 6
This commit is contained in:
+68
-48
@@ -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 | 1–2 days | ✅ Complete |
|
||||
| 1 | 3–5 days | ✅ Complete |
|
||||
| 2 | 1–2 weeks | ✅ Complete |
|
||||
| 3 | 1–2 weeks | — |
|
||||
| 3 | 1–2 weeks | ✅ Complete |
|
||||
| 4 | 3–5 days | ✅ Complete |
|
||||
| 5 | 2–3 weeks | — |
|
||||
| 6 | 2–3 weeks | — |
|
||||
| 5 | 2–3 weeks | ✅ Complete |
|
||||
| 6 | 2–3 weeks | ✅ Complete |
|
||||
| 7 | 1–2 weeks | — |
|
||||
| 8 | 1 week | — |
|
||||
| 9 | 1 week | — |
|
||||
|
||||
Reference in New Issue
Block a user