# uopi A web-based HMI (Human-Machine Interface) for monitoring and controlling EPICS-based scientific and industrial control systems. uopi runs as a **single portable binary** with no runtime dependencies. Point any browser at it — including over an SSH tunnel — and you get a full drag-and-drop HMI builder and live data viewer. --- ## Features | Feature | Details | |---------|---------| | **Live data** | EPICS Channel Access (CA) / PVAccess and user-defined synthetic signals | | **HMI editor** | Drag-and-drop widgets, resize, align/distribute, undo/redo, saved as XML | | **Widget library** | Text view/label, gauge, LED, multi-LED, bar, set-point control, button, image, link, plots | | **Plot types** | Time-series (uPlot), FFT, waterfall, histogram, bar chart, logic analyser | | **Plot panels** | Dedicated chart panels with a recursive split layout (tmux/IDE-style) where plots fill the viewport | | **Panel logic** | In-editor node-graph flow editor (triggers → actions, dialogs) that runs client-side in view mode | | **Control logic** | Server-side always-on flow graphs with cron/alarm triggers and Lua blocks | | **Local variables** | Panel-scoped state variables for set-points, toggles and counters used by logic | | **Historical data** | EPICS Archive Appliance integration via REST; time range picker in UI | | **Synthetic signals** | Compose, filter, and transform signals via a wizard or visual node-graph editor (gain, offset, moving average, lowpass, highpass, derivative, integral, clamp, formula); panel/user/global visibility scopes | | **Access control** | Identity via trusted proxy header; per-user global level (write/read-only/no-access); per-panel ownership + sharing; nested folders; optional logic-editor allowlist | | **Multi-client** | Subscriptions are multiplexed — N clients share one upstream connection per signal | | **Signal discovery** | Filter/search, CSV import, manual add, Channel Finder proxy | | **Observability** | Prometheus-format metrics at `/metrics` | | **Single binary** | Frontend assets embedded at build time — no web server needed | --- ## Quick Start ### Prerequisites - **Go 1.22+** — the only build dependency (the frontend is bundled by a pure-Go esbuild wrapper) - EPICS Base (`libca`) — only required for the `epics` build tag; the stub and synthetic data sources work without it ### Build ```bash # Full build — bundles the frontend then compiles the binary make all ``` The binary is written to `dist/uopi`. ### Run ```bash ./dist/uopi # uses default config (stub data source only) ./dist/uopi --config uopi.toml # explicit config file ``` Then open [http://localhost:8080](http://localhost:8080) in your browser. **SSH tunnel example:** ```bash ssh -L 8080:localhost:8080 user@controlhost # then open http://localhost:8080 locally ``` --- ## Configuration Create `uopi.toml` (all fields are optional — shown values are defaults): ```toml [server] listen = ":8080" storage_dir = "./interfaces" # where interface XML, ACL and logic data are stored # Identity & access control (all optional) trusted_user_header = "" # HTTP header carrying the proxy-authenticated user default_user = "" # identity used when the header is absent (LAN/dev) # logic_editors = [] # restrict who may edit panel/control logic (users or groups) # [[server.blacklist]] # downgrade a user: level = "readonly" or "noaccess" # user = "guest" # level = "readonly" # [[groups]] # named user sets, referenced by panel sharing # name = "operators" # members = ["alice", "bob"] [datasource.epics] enabled = false # requires build with -tags epics ca_addr_list = "" # overrides EPICS_CA_ADDR_LIST if non-empty archive_url = "" # EPICS Archive Appliance base URL, e.g. http://archiver:17665 channel_finder_url = "" # EPICS Channel Finder base URL (optional) [datasource.synthetic] enabled = true ``` All settings can be overridden with environment variables using the `UOPI_` prefix and double underscores for nesting: ```bash UOPI_SERVER_LISTEN=":9090" UOPI_DATASOURCE_EPICS_ENABLED=true ./dist/uopi ``` --- ## Building with EPICS Support The EPICS data source uses CGo bindings to `libca`. First set up the EPICS environment variables, then: ```bash export EPICS_BASE=/path/to/epics/base export CGO_CFLAGS="-I$EPICS_BASE/include -I$EPICS_BASE/include/os/Linux" export CGO_LDFLAGS="-L$EPICS_BASE/lib/linux-x86_64 -lca -lCom" make backend-epics ``` Then enable it in `uopi.toml`: ```toml [datasource.epics] enabled = true ``` --- ## API The REST API is available under `/api/v1`. Key endpoints: | Method | Path | Description | |--------|------|-------------| | `GET` | `/api/v1/datasources` | List registered data sources | | `GET` | `/api/v1/signals?ds=` | List signals for a data source | | `GET` | `/api/v1/signals/search?q=` | Search signals across all sources | | `GET` | `/api/v1/channel-finder?q=` | Proxy to EPICS Channel Finder | | `GET/POST` | `/api/v1/interfaces` | List or create HMI interfaces | | `GET/PUT/DELETE` | `/api/v1/interfaces/{id}` | Read, update, or delete an interface | | `POST` | `/api/v1/interfaces/{id}/clone` | Duplicate an interface | | `POST` | `/api/v1/interfaces/reorder` | Reorder panels / move them between folders | | `GET/PUT` | `/api/v1/interfaces/{id}/acl` | Read or set a panel's sharing rules | | `GET` | `/api/v1/interfaces/{id}/versions` | List saved versions of a panel | | `GET/POST` | `/api/v1/folders` | List or create panel folders | | `PUT/DELETE` | `/api/v1/folders/{id}` | Rename/reparent or delete a folder | | `GET/POST/DELETE` | `/api/v1/synthetic` | Manage synthetic signal definitions | | `GET/POST` | `/api/v1/controllogic` | List or create server-side control-logic graphs | | `GET/PUT/DELETE` | `/api/v1/controllogic/{id}` | Read, update, or delete a control-logic graph | | `GET` | `/api/v1/me` | Caller identity, access level, groups, logic-edit permission | | `GET` | `/api/v1/usergroups` | List configured users and groups (for sharing) | | `GET` | `/metrics` | Prometheus-format server metrics | | `GET` | `/healthz` | Health check | WebSocket live data is at `ws:///ws`. See [`docs/TECHNICAL_SPEC.md`](docs/TECHNICAL_SPEC.md) for the message protocol. --- ## Development ```bash # Build frontend only (output → web/dist/) make frontend # Build backend only (frontend must already be built) make backend # Run the server directly without a binary (uses stub data source) go run ./cmd/uopi # Run all tests make test # Run tests with race detector make race # Run benchmarks make bench # Static analysis make lint # Clean build artefacts make clean ``` --- ## Observability The `/metrics` endpoint exposes Prometheus-format counters and gauges: ``` uopi_uptime_seconds — seconds since start uopi_ws_connections — current open WebSocket connections uopi_signal_subscriptions — current unique upstream signal subscriptions uopi_ws_messages_in_total — total messages received from clients uopi_ws_messages_out_total — total messages sent to clients uopi_write_ops_total — total signal write operations uopi_history_requests_total — total historical data requests ``` Scrape with any Prometheus-compatible tool or view directly in the browser. --- ## Docs | Document | Description | |----------|-------------| | [`docs/FUNCTIONAL_SPEC.md`](docs/FUNCTIONAL_SPEC.md) | User-facing feature specification | | [`docs/TECHNICAL_SPEC.md`](docs/TECHNICAL_SPEC.md) | Architecture, WebSocket protocol, API design | | [`docs/WORK_PLAN.md`](docs/WORK_PLAN.md) | Phased development plan with milestones | --- ## Release Releases are built with [goreleaser](https://goreleaser.com/): ```bash goreleaser release --clean ``` See [`.goreleaser.yaml`](.goreleaser.yaml). Note that release binaries are built **without** EPICS support (`CGO_ENABLED=0`) for maximum portability. Build from source with `-tags epics` for CA connectivity. --- ## License TBD