6.4 KiB
StreamHub WebSocket API
StreamHub exposes a single WebSocket server (default port 8090, any URL path).
Text frames carry JSON commands/events; binary frames carry data pushes and
trigger captures. The Go reference hub (Client/udpstreamer) implements the
identical protocol; both the browser SPA and the ImGui client work against either.
For the upstream UDPS wire format (source → hub) see Protocol.md.
All numbers in binary frames are little-endian. All timestamps are Unix wall-clock seconds (float64) — see Time base in StreamHub-Developer.md.
1. Commands (client → hub, JSON text frames)
Every command is a JSON object with a type field.
ping
{"type":"ping"}
Reply (unicast): {"type":"pong"}.
addSource
{"type":"addSource","label":"PSU","addr":"192.168.0.10:44500",
"multicastGroup":"239.0.0.1","dataPort":44503}
addr—host:portof the UDPStreamer control port.multicastGroup/dataPort— optional; if present the hub joins the multicast group for DATA and keeps a TCP control connection for CONNECT/CONFIG.- The hub assigns ids
s1,s2, … and broadcasts an updatedsourcesevent.
removeSource
{"type":"removeSource","id":"s1"}
saveSources
{"type":"saveSources"}
Persists the current dynamically-added source list to the hub's SourcesFile
(JSON array of {label,addr,multicastGroup,dataPort}); it is reloaded at startup.
getSources / getConfig / getStats
{"type":"getSources"}
{"type":"getConfig","sourceId":"s1"}
{"type":"getStats"}
Force a broadcast of the corresponding event.
setTrigger
{"type":"setTrigger","signal":"s1:Sine","edge":"rising","threshold":0.0,
"windowSec":0.1,"prePercent":20,"mode":"normal"}
signal— full keysrc:sig, orsrc:sig[i]to trigger on element i of a multi-element PACKET signal.edge—"rising","falling"or"both".windowSec— total capture window (clamped to 1e-4 … 10 s).preSec = windowSec * prePercent / 100,postSec = windowSec − preSec.mode—"normal"(auto-rearm ~200 ms after capture) or"single"(stays TRIGGERED untilrearm).
arm / disarm / rearm / trigStop
{"type":"arm"}
{"type":"disarm"}
{"type":"rearm"}
{"type":"trigStop","stopped":true}
arm— IDLE → ARMED.disarm— any state → IDLE.rearm— TRIGGERED → ARMED (single mode).trigStop— sets the stopped flag suppressing auto-rearm in normal mode; omitstoppedto toggle.
Every transition is broadcast as a triggerState event.
zoom
{"type":"zoom","reqId":17,"t0":1765370000.123,"t1":1765370000.223,
"n":2400,"signals":"s1:Sine,s2:Wave[0]"}
reqId— echoed in the reply; lets the client match request/response.t0/t1— Unix seconds window.n— target points per signal. Absent or<10→ 2400;n ≤ 0→ no decimation (raw ring contents).signals— comma-separated full keys; absent → all signals of all sources.- The reply is unicast to the requesting client only.
setMaxPoints
{"type":"setMaxPoints","maxPoints":50000}
Resizes all ring buffers (applied safely inside the push loop; push cursors are
reset). Broadcasts maxPointsUpdated.
2. Events (hub → client, JSON text frames)
sources
{"type":"sources","sources":[
{"id":"s1","label":"PSU","addr":"192.168.0.10:44500","state":"streaming"}]}
Sent on client connect, after add/remove/getSources, and when a source first delivers its CONFIG.
config
{"type":"config","sourceId":"s1","publishMode":0,"signals":[
{"name":"Sine","typeCode":10,"quantType":0,"numDimensions":0,
"numRows":1,"numCols":1,"rangeMin":-1.0,"rangeMax":1.0,
"timeMode":0,"samplingRate":5000000.0,"timeSignalIdx":-1,"unit":"V"}]}
Field semantics follow the UDPS signal descriptor (Protocol.md).
numElements = numRows × numCols.
stats
Sent at StatsRate Hz (default 1 Hz):
{"type":"stats","sources":{"s1":{
"state":"streaming","totalReceived":1234,"totalLost":0,
"rateHz":100.1,"rateStdHz":0.3,
"fragsPerCycle":3.0,"bytesPerCycle":4200.0,
"cycleAvgMs":10.0,"cycleStdMs":0.1,"cycleMinMs":9.8,"cycleMaxMs":10.4,
"cycleHistMin":9.8,"cycleHistMax":10.4,"cycleHist":[0,1,5,"…(20 bins)"]}}}
triggerState
{"type":"triggerState","state":"triggered","mode":"normal",
"stopped":false,"trigTime":1765370000.1234567}
state ∈ idle | armed | collecting | triggered; trigTime present once a
trigger has fired.
zoom (reply)
{"type":"zoom","reqId":17,"signals":{
"s1:Sine":{"t":[1765370000.1230000,"…"],"v":[0.123456789,"…"]}}}
t is serialised with %.17g (full float64 precision — required for
µs windows at Unix-epoch magnitudes), v with %.9g.
maxPointsUpdated
{"type":"maxPointsUpdated","maxPoints":50000}
3. Binary frames (hub → client)
The first byte of every binary WS frame is a version discriminator.
Version 1 — data push
Sent at PushRate Hz per source. Contains only the samples that are new
since the previous push (per-signal cursors hub-side), LTTB-decimated to at
most MaxPushPoints (default 50) per signal.
[1] version = 1
[1] sourceIdLen (L)
[L] sourceId (UTF-8, no NUL)
[4] numSignals (uint32)
per signal:
[2] keyLen (uint16) (K)
[K] key = signal name; "name[i]" per element for multi-element PACKET signals
[4] pairCount (uint32) (N)
[N×8] t (float64, Unix seconds)
[N×8] v (float64, physical units)
Clients must append samples verbatim — there is no overlap between pushes.
Version 2 — trigger capture
Broadcast once per capture (FSM COLLECTING → TRIGGERED). Contains all
signals over [trigTime − preSec, trigTime + postSec], each LTTB-decimated
to ≤ 20 000 points.
[1] version = 2
[8] trigTime (float64, Unix seconds)
[8] preSec (float64)
[8] postSec (float64)
[4] numSignals (uint32)
per signal:
[2] keyLen (uint16) (K)
[K] fullKey = "src:sig" (UTF-8)
[4] pairCount (uint32) (N)
[N×8] t (float64, Unix seconds)
[N×8] v (float64)
4. Limits
| Limit | Value |
|---|---|
| Concurrent WS clients | 16 |
| UDPS source sessions | 32 |
| Max received WS payload | 64 KiB |
| Max sent WS payload | 4 MiB |