Files
Martino Ferrari 986f6cd6d8 Phase 6
2026-04-25 22:56:09 +02:00

101 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**uopi** is a web-based HMI (Human-Machine Interface) for monitoring and controlling industrial/scientific systems, primarily EPICS-based. It consists of:
- A **backend** — a single portable binary (targeting old Linux machines) that serves a web UI and REST API, connects to data sources (EPICS, synthetic), and multiplexes live data across multiple clients via WebSocket.
- A **frontend** — a fast, reactive web UI (no WebGPU) with two modes: **edit** (drag-and-drop interface builder) and **view** (live interaction with HMI panels).
## Architecture
### Backend
- Single self-contained binary with embedded frontend assets.
- Data sources are plugin-extensible; built-in: **EPICS** and **Synthetic**.
- Data ingestion uses subscribe/monitor patterns where available; timestamps from the source are authoritative.
- The same channel data is shared (mutualized) across all connected clients requesting the same signal.
- Interfaces are stored server-side and serialized as **XML**.
- REST API for headless/desktop client access alongside the WebSocket push for the web frontend.
### Data Sources
**EPICS**: Use Channel Access or PVAccess. Retrieve all metadata from PV name (type, range, enum values, R/W mode, units). Prefer monitors over polling. Support historical data via EPICS Archive Appliance when available.
**Synthetic**: Compose signals from existing sources (any DS) and math/DSP functions (moving average, bandpass, gain, offset, derivative, custom formula). Support Lua scripting for custom logic.
### Frontend
Two primary modes toggled via toolbar:
- **Edit mode**: resizable signal tree pane (left) + free-form canvas (center) + properties pane (right). Signals are drag-and-dropped from the tree onto the canvas; on drop, compatible widget types are shown. Widget selection shows bounding box with resize handles. Multi-select via Ctrl+click or area select enables group move, delete, and align/distribute. Undo/redo (Ctrl+Z / Ctrl+Shift+Z). Save/load to server; export/import as local XML file.
- **View mode**: collapsible interface list pane (left) + live HMI canvas. Right-click context menu per widget: signal info, copy signal name, export data to CSV. Optional historical time navigation + "live" button.
### Widget Types
- Text view, gauge, vertical/horizontal bar, set-value control, LED (with condition), multi-LED (bitset), button (command/set value).
- Plot widget (multi-signal): timeseries, FFT, waterfall (multidimensional), histogram, bar chart, logic analyzer.
## Technology Stack
- **Backend:** Go 1.22+, single binary, `//go:embed` for frontend assets
- **Frontend:** Preact 10 + TypeScript; bundled by esbuild Go API (no npm/Node.js); plots via uPlot (time-series) and ECharts (others); all vendor JS/CSS checked into `web/vendor/`
- **Config:** TOML file + `UOPI_*` env var overrides (`github.com/BurntSushi/toml`)
- **WebSocket:** `nhooyr.io/websocket` (no CGo)
- **EPICS:** CGo bindings to `libca`; `gopher-lua` for synthetic signal Lua scripts
## Repository Layout
```
cmd/uopi/ # main — wires config, embed, server
internal/config/ # TOML config loading
internal/server/ # HTTP + WebSocket handlers
internal/broker/ # signal fan-out (Phase 1)
internal/datasource/# DataSource interface + EPICS + synthetic (Phases 23)
internal/dsp/ # DSP functions for synthetic (Phase 3)
internal/storage/ # interface XML persistence (Phase 6)
internal/api/ # REST handlers (Phase 1+)
web/ # Preact/TSX source; web/embed.go provides embed.FS to Go
web/src/ # TypeScript/TSX components (Preact)
web/vendor/ # vendored JS/CSS (preact, uplot, echarts) — no npm required
tools/buildfrontend/# esbuild Go API bundler (invoked by go generate)
web/dist/ # built frontend — generated, not committed
dist/ # compiled binary — generated, not committed
```
The embed package lives at `web/embed.go` (not in `cmd/`) because `//go:embed` paths cannot use `..`.
## Development Commands
```bash
# Full build (frontend then backend)
make all
# Backend only (frontend must already be built)
make backend
# Frontend only
make frontend
# Run backend (dev mode, frontend must be running separately)
go run ./cmd/uopi
# All tests
make test
# Single Go test
go test ./internal/broker/... -run TestFanOut
# Go vet
go vet ./...
```
## Key Architectural Notes
- `web/embed.go` declares `//go:embed dist` and exports `var FS embed.FS`; `main.go` does `fs.Sub(web.FS, "dist")` to get a clean root FS for `http.FileServerFS`.
- The `DataSource` interface (to be defined in `internal/datasource/iface.go`) is the extension point for new data sources.
- The signal broker (Phase 1) is the central fan-out: one upstream goroutine per signal, N client channels downstream. Reference-counted subscribe/unsubscribe.
- WebSocket messages are JSON; protocol defined in `docs/TECHNICAL_SPEC.md §3.3`.