Real-time high-voltage monitoring and control for the PRad-II HyCal calorimeter (~1700 channels). A daemon (prad2hvd) connects to CAEN SY1527 mainframes and TDK-Lambda GEN booster supplies, polls continuously, logs faults, and serves live data to any number of browser or Qt clients via WebSocket.
┌──────────────┐ ┌──────────────┐
│ CAEN SY1527 │ │ TDK-Lambda │
│ HV Crates │ │ Boosters │
└──────┬───────┘ └──────┬───────┘
│ TCP/IP │ SCPI/TCP
▼ ▼
┌─────────────────────────────────────┐
│ prad2hvd (daemon) │
│ │
│ HVPoller thread ── poll + classify │
│ BoosterPoller thread ── poll │
│ FaultTracker ── daily log files │
│ WebSocket + HTTP server │
└──────────────┬──────────────────────┘
│ http://host:8765/
│ ws://host:8765/
┌───────┼───────┐
▼ ▼ ▼
Browser Qt GUI Scripts
client client (wscat)
The daemon owns all hardware connections and makes all status decisions (fault, warning, ΔV threshold). Clients are pure displays — they read level, dv_warn, and other fields from the daemon's JSON and render them. No classification logic runs on the client.
# Build (daemon only, no Qt needed)
mkdir build && cd build
cmake ..
make -j$(nproc)
# Start daemon (serves both WebSocket and dashboard HTTP on port 8765)
./bin/prad2hvd
# Open in any browser
# http://localhost:8765/cmake .. -DBUILD_GUI=ON -DCMAKE_PREFIX_PATH=/path/to/Qt5/lib/cmake
make -j$(nproc)
# Run Qt client (loads dashboard from daemon's HTTP server)
./bin/prad2hvmon -H localhost -p 8765
# Save/restore settings via daemon (no GUI needed)
./bin/prad2hvmon read -s snapshot.json
./bin/prad2hvmon write -f snapshot.jsonHeadless process that polls hardware, classifies channel status, logs faults, and broadcasts JSON snapshots over WebSocket.
| Option | Description |
|---|---|
-c <file> |
Crates JSON (default: database/crates.json) |
-m <file> |
Module geometry JSON (default: database/hycal_modules.json) |
-g <file> |
GUI config JSON (default: database/gui_config.json) |
-d <file> |
DAQ map JSON (default: database/daq_map.json) |
-i <file> |
Error-ignore JSON (default: database/error_ignore.json) |
-l <file> |
Voltage limits JSON (default: database/voltage_limits.json) |
-w <file> |
ΔV warning rules JSON (default: database/dv_warn.json) |
-r <dir> |
Resources directory for HTTP serving (default: auto-discover) |
-p <port> |
WebSocket + HTTP port (default: 8765) |
-t <ms> |
Poll interval in ms (default: 2000) |
-n <count> |
Fault log buffer size for live display (default: 200) |
-v <level> |
Console verbosity: 0=silent, 1=faults only, 2=warn+fault (default: 2) |
Stop with Ctrl+C. Fault logs are written to database/fault_log/YYYY-MM-DD.log continuously, whether or not any client is connected.
The daily log file is tab-separated:
2026-03-14 10:23:45.123 FAULT APPEAR channel W232 ON OVC|Over Current 1523.40 1525.00
2026-03-14 10:23:45.456 WARN APPEAR channel G235 ON DVW|dV warning 1402.10 1410.00
2026-03-14 10:23:48.789 FAULT DISAPPEAR channel W232 ON OVC|Over Current 1524.90 1525.00
Columns: timestamp, level (FAULT/WARN), direction (APPEAR/DISAPPEAR), type, name, status, VMon, VSet. The last two columns are empty for board/booster entries. The file always records both levels regardless of -v.
Use tmux to keep the daemon running after logout:
# Start in a named tmux session
tmux new-session -d -s hvd './bin/prad2hvd -v 1'
# Detach and log out — daemon keeps running
# Reattach later to check output
tmux attach -t hvd
# Detach from session (keep daemon running): Ctrl+A then D
# (clonpc19 uses Ctrl+A as the tmux prefix instead of the default Ctrl+B)
# List sessions
tmux ls
# Stop the daemon
tmux send-keys -t hvd C-c
# Force kill if unresponsive
tmux kill-session -t hvdOptional thin client — a QWebEngineView window that loads monitor.html and connects to the daemon via WebSocket. No direct hardware access — all commands go through the daemon.
| Option | Description |
|---|---|
-H <host> |
Daemon hostname (default: localhost) |
-p <port> |
Daemon WebSocket port (default: 8765) |
-r <dir> |
Resources directory (default: auto-discover) |
Ctrl+S saves a timestamped PNG screenshot to database/screenshots/.
Save and restore all writable channel parameters (VSet, ISet, SVMax, etc.) via the daemon — same code path as the GUI Save/Load buttons.
# Save current settings to JSON
./bin/prad2hvmon read -s snapshot.json
# Restore settings from JSON
./bin/prad2hvmon write -f snapshot.json
# Remote daemon
./bin/prad2hvmon read -H clonpc19 -s snapshot.json
./bin/prad2hvmon write -H clonpc19 -f snapshot.json| Option | Description |
|---|---|
-s <file> |
Save output path (read mode; prints to stdout if omitted) |
-f <file> |
Settings file to load (write mode; required) |
-H <host> |
Daemon hostname (default: localhost) |
-p <port> |
Daemon port (default: 8765) |
-t <sec> |
Timeout in seconds (default: 10) |
The daemon must be running. Unchanged parameters are skipped (no unnecessary hardware writes). The write command reports restored / unchanged / skipped / errors.
The daemon serves the dashboard directly — open http://<daemon-host>:8765/ in any browser. No separate file server needed. The same port handles both HTTP (for HTML/JS/CSS) and WebSocket (for live data).
Multiple clients can connect simultaneously. All receive the same live data.
The daemon runs on clonpc19 behind the JLab gateway. To access it from outside:
# SSH tunnel — forward port 8765 through the gateway
ssh -L 8765:clonpc19:8765 -J your_username@hallgw.jlab.org clasrun@clonpc19Then open in your local browser:
http://localhost:8765/
For the Qt GUI client:
./bin/prad2hvmon -H localhost -p 8765The tunnel must stay open while you use the dashboard.
- Channel Table — Sortable, filterable, live-updating. Inline VSet/ISet/SVMax/Name editing in expert mode (click ✏ to edit, Enter/✓ to apply, Escape/✕ to cancel). Bulk ON/OFF and bulk Set V on filtered channels. Summary strip with fault/warning counts.
- Save / Load — Save all writable parameters to JSON, or restore from a previously saved file. Available from the tab bar (GUI) or command line (
prad2hvmon read/write). Both paths go through the daemon — same logic, same format. - Board Status — Per-board temperature, HVMax, firmware, status.
- HyCal Geometry Map — 2D canvas at physical positions. Color by VMon, VSet, |ΔV|, or Status. Click for draggable live popups with controls.
- Booster HV Panel — TDK-Lambda GEN supply cards with live readback, VSet/ISet controls, ON/OFF. Connection managed by daemon.
- Fault Log — Live fault transition log (configurable ring buffer, default 200 entries). Colour-coded by level (FAULT/WARN) and direction (APPEAR/DISAPPEAR). Includes VMon/VSet at the moment of transition for HV channels. Unread-indicator dot on the tab.
- Alarm — Audible two-tone beep every 2s on faults. Mute toggle, auto-re-arm when faults clear.
All in database/. The daemon reads them at startup and serves relevant ones to clients.
| File | Used by | Purpose |
|---|---|---|
crates.json |
Daemon | CAEN crate addresses [{"name":"...","ip":"..."}] |
hycal_modules.json |
Daemon + Client | Module geometry and booster definitions |
gui_config.json |
Client | Display thresholds, color ranges, render interval |
daq_map.json |
Client | DAQ readout addresses (geo tooltips) |
voltage_limits.json |
Daemon | Per-pattern voltage limits (OVL fault) |
error_ignore.json |
Daemon | Per-channel error suppression |
dv_warn.json |
Daemon | Per-pattern ΔV warning thresholds |
{
"default": 2.0,
"rules": [
{ "pattern": "W*", "max_dv": 2.0 },
{ "pattern": "G*", "max_dv": 3.0 },
{ "pattern": "PRIMARY*", "max_dv": 5.0 },
{ "pattern": "*", "max_dv": 2.0 }
]
}First matching pattern wins. Same wildcard scheme as voltage_limits.json and error_ignore.json.
Daemon (pure C++17, no Qt):
- CMake 3.14+, C++17 compiler
libcaenhvwrapper.so(incaen_lib/)- Auto-fetched: nlohmann/json, fmt, websocketpp, standalone Asio
Qt GUI client (optional):
- Qt 5 (Widgets, WebEngineWidgets, WebSockets)
Chao Peng — Argonne National Laboratory