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

4.9 KiB
Raw Permalink Blame History

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

# 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.