# Functional Specification — uopi ## 1. Purpose uopi is a web-based HMI (Human-Machine Interface) for monitoring and controlling industrial/scientific systems, primarily EPICS-based control systems. It runs as a single server process and is accessed entirely through a web browser, making it suitable for SSH-tunnelled remote access. --- ## 2. Users and Roles User identity is established from a header set by a trusted authenticating reverse proxy (`server.trusted_user_header`), with a configurable `default_user` fallback for unproxied/LAN deployments. There is no login page inside the application. **Global access levels.** Every user is trusted with full **write** access by default. A configuration blacklist can downgrade specific users to **read-only** (view only, no writes) or **no access** (denied). Named **groups** are defined in configuration and referenced by per-panel sharing. **Per-panel access.** Panels are owned by their creator and private by default. Owners share a panel with specific users/groups (read or write) or make it public; the owner's global level always caps the per-panel permission. Panels are organised into nested folders whose permissions inherit down the chain. See §9. **Logic-edit restriction.** Adding/editing panel logic and server-side control logic can optionally be limited to an allowlist of users/groups (`server.logic_editors`); when unset, any writer may edit logic. --- ## 3. System Modes ### 3.1 View Mode (default) The default mode when opening the application. **Interface list pane (left, collapsible, resizable)** - Displays all interfaces saved on the server. - Right-click on an interface: options to open in Edit mode or clone it. - "New interface" button opens Edit mode with a blank canvas. - The pane width can be adjusted by dragging the resize handle on its right edge. **HMI canvas (center)** - Renders the selected interface as a live, interactive panel. - Widgets display real-time data; controls (set-value, buttons) are active. - Panel logic (if any) runs while the panel is open (see §6). - No drag, resize, or layout operations are possible in this mode. - Right-clicking any widget opens a context menu: - **Signal info** — shows DS name, type, unit, range, current value and timestamp. - **Copy signal name** — copies the signal identifier to the clipboard. - **Export data to CSV** — downloads buffered data for the signal(s) used by the widget. > Dedicated multi-signal plotting is provided by **plot panels** — a special interface > kind whose plots fill the viewport (see §3.3) — rather than a separate live "Plot tab". **Top toolbar** - Show/hide interface list pane. - **⏱ History** button: toggle historical time navigation bar. - **⚙ Control logic** button: open the server-side control-logic editor (see §7). Shown only to users permitted to edit logic. - Zoom control (A− / % / A+): adjust the UI scale (see §3.4). - Edit button: switch to Edit mode for the current interface. - Connection status indicator and signed-in user chip. **Historical time navigation bar** (shown when History is active) - Date/time range pickers (start and end). - **Load** button fetches archive data and replaces live data in all widgets. - **Live** button discards archive data and resumes real-time streaming. - Status shown on plot widgets: "Loading history…", "No archive data for this range", "Archive unavailable". ### 3.2 Edit Mode Activated via the "New interface" button or by clicking Edit in the toolbar. **Signal tree pane (left, resizable and collapsible)** - Shows all signals known to each connected data source. - Sources are shown as top-level nodes; signals are nested within. - User can add custom entries: - For EPICS: manually enter a PV name. - For Synthetic: define a new synthetic signal via the Synthetic Wizard (see §5.2). - Filter/search box to narrow the list. - Synthetic signals show an edit (✎) button to reopen the wizard. **Center area — Layout / Logic tabs** - **Layout**: free-form canvas where widgets can be placed at arbitrary pixel positions, over a background grid with snap-to-grid. (For plot panels this is replaced by the split-layout editor; see §3.3.) - **Logic**: a node-graph flow editor for panel behaviour (see §6). Hidden when the user is not permitted to edit logic. **Local variables** - A panel may declare panel-scoped variables (data source `local`) with initial values. - They are written by set-value widgets, buttons and logic actions, and referenced anywhere a signal is. Added from the signal tree's *Local* group / the Logic palette. **Properties pane (right, resizable and collapsible)** - Appears when one or more widgets are selected. - Displays and edits all options for the selected widget (see §4). - Width is adjustable by dragging the resize handle on its left edge. **Top toolbar** - Show/hide signal pane / properties pane. - Undo / Redo (also Ctrl+Z / Ctrl+Shift+Z). - Import / Export interface to/from local XML file. - Save interface to server. - Close (return to View mode). - Zoom control (A− / % / A+). ### 3.3 Plot Panels (Split Layout) A **plot panel** is a special interface kind dedicated to charts. Rather than free-form widget boxes, plots **fill the viewport** and the user divides the space between them with a recursive split layout (tmux/IDE-style tiling). Created via **+ Plot** in the interface list, opening with a single full-viewport empty plot. **Editing (Edit mode):** - Hover a pane and use its split buttons (**⬌** vertical / **⬍** horizontal) to divide it; a new empty plot fills the freed half. Nesting is unlimited. - Drag the divider between two panes to resize them. - Click a pane to select it and configure its plot in the Properties pane (plot sub-type, Y range, time window, legend, per-signal colour). Drag a signal onto a pane to add it. - A pane's **✕** removes it; the layout collapses onto its sibling. **View mode:** the saved split layout fills the screen with live, streaming plots; the per-widget right-click menu (signal info / copy / CSV) and historical time navigation apply as for any time-series plot. Per-plot configuration reuses the standard Plot widget (§4.4), so all plot sub-types and options are available within a pane. ### 3.4 UI Zoom The toolbar in both modes contains a zoom control (**A−** / **nn%** / **A+**) that adjusts the base font size of the entire UI: - 11 zoom steps from 50% to 250% (50, 60, 75, 85, 100, 115, 130, 150, 175, 200, 250%). - The selected zoom level is persisted in `localStorage` and restored on the next page load. - At 100%, the base font size adapts to viewport height via `clamp(13px, 1.5vh, 18px)`, making the UI naturally usable on 4K displays without any manual adjustment. --- ## 4. Widgets ### 4.1 Creating Widgets Drag a signal from the signal tree and drop it onto the canvas. A picker appears showing all widget types compatible with the signal's data type. The user selects one and the widget is placed at the drop location with default size. ### 4.2 Selecting Widgets - Single click: select one widget (deselects others). - Ctrl+click: add/remove a widget from the current selection. - Click-drag on empty canvas area: rubber-band area select. When a widget is selected, a bounding box appears with: - 8 resize handles (corners + midpoints). - A delete button (×) in the top-right corner. - The widget can be moved by dragging its body. ### 4.3 Multi-selection Operations When multiple widgets are selected: - Drag any selected widget to move them all together. - Del key deletes all selected widgets. - An align/distribute toolbar appears above the canvas with: - Align left / center horizontal / right. - Align top / center vertical / bottom. - Distribute evenly (horizontal/vertical). ### 4.4 Widget Catalogue | Widget | Compatible signal types | Description | |--------|-------------------------|-------------| | Text view | any scalar | Displays `name: value unit` | | Gauge | numeric scalar | Circular or arc gauge with configurable range | | Vertical bar | numeric scalar | Vertical level indicator | | Horizontal bar | numeric scalar | Horizontal level indicator | | Set value | numeric, string, or enum; writable | Input field or enum dropdown + Set button | | LED | boolean / numeric | Coloured indicator with configurable condition and label | | Multi-LED | integer (bitset) | One LED per bit with individual labels and conditions | | Button | writable | Sends a fixed value or command on click | | Plot | numeric scalar or array | Multi-signal plot; sub-types below | | Text label | — | Static text annotation | | Image | — | Static image | | Link | — | Button navigating to another interface | **Plot sub-types:** | Sub-type | Signal requirement | |----------|--------------------| | Time series | numeric scalar(s) | | FFT | 1-D numeric array | | Waterfall | 1-D numeric array (repeated) | | Histogram | numeric scalar(s) | | Bar chart | numeric scalar(s) | | Logic analyser | boolean / integer (bitset) | | Waveform | 1-D numeric array (latest sample, x-vs-index) | ### 4.5 Widget Properties (Properties Pane) Common to all: - Label text, font size, text colour. - Position (X, Y) and size (W, H) — editable numerically. - Data source and signal name. Per type: - **Gauge / Bar**: min value, max value, alert thresholds with colours, unit label. - **LED / Multi-LED**: condition expression (e.g. `value > 0`), colours for true/false states, label. - **Plot**: plot sub-type selector, Y-axis range (auto or manual min/max), time window duration, legend position (top / bottom / none), value format string. - **Set value**: no special options — enum mode is detected automatically from signal metadata. - **Link**: target interface name. ### 4.6 Set-value Widget — Enum Mode When the signal's metadata reports enum strings (e.g. EPICS mbbi/mbbo records): - The input field is replaced by a `