Files
Martino Ferrari 915c6fc126 WebUI: resizable plots, digital/mixed modes, buffer fix, zoom brackets, updated docs
- Resizable plot grid: drag handles between plots adjust column/row fr sizes
- Plot configuration toolbar (click title): edit title, select Normal/Mixed/Digital mode
- Digital mode: logic-analyzer banded layout, signals quantized to hi/lo per band
- Mixed mode: banded layout where each signal is independently analog or digital
- Per-signal vscale toolbar embedded inline below plot header (badge click to open)
- Active signal highlighted in foreground with increased line width
- Signal offset markers draggable on Y axis; per-plot vscale state isolation
- Buffer sizing based on signal sampling rate (up to 60s @ configured rate)
- growBuffer: live buffer expansion without data loss on window/rate change
- Zoom bracket lines: nearest out-of-range points included for continuity
- Updated Docs/WebUI.md to reflect current uPlot-based implementation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 16:29:09 +02:00

12 KiB
Raw Permalink Blame History

WebUI Client

The WebUI is a Go binary that acts as a bridge between UDPStreamer and any web browser. It receives UDP packets from UDPStreamer, reassembles fragmented data, and re-publishes decoded signal values over a WebSocket to the browser. The browser renders live plots using uPlot.

MARTe2 RT app
    │ UDP (binary protocol)
    ▼
udpstreamer-webui (Go)
    │ WebSocket (binary frames)
    ▼
Browser (index.html + uPlot)

Building

cd Client/WebUI
go build -o udpstreamer-webui ./...

Requires Go ≥ 1.21. The only external dependency is gorilla/websocket (declared in go.mod; fetched automatically by go build).


Running

./udpstreamer-webui \
    --streamer   127.0.0.1:44500  \   # address:port of the UDPStreamer server
    --listen     :8080             \   # HTTP / WebSocket listen address
    --clientport 44900                 # local UDP port for receiving from streamer

Open http://localhost:8080 in any modern browser.

Flags

Flag Default Description
--streamer 127.0.0.1:44500 UDP address of the UDPStreamer DataSource
--listen :8080 HTTP server bind address
--clientport 44900 Local UDP port for receiving data

Browser UI

Layout

┌───────────────────────────────────────────────────────────────────────────────┐
│ ☰  UDP Scope  │  ⊞ 1×1 ▾  │  A: —  B: —  ΔT: —  │  Window: [5s▾]  Fit  ⬇ CSV  ⚡ Trigger  ⏸ Pause │
├──────────────────┬────────────────────────────────────────────────────────────┤
│ Signals          │ [Plot title]     ○ ○ ○ ← signal badges                    │
│──────────────────│──────────────────────────────────────────────────────────  │
│ Counter  · u32   │  ┌─────────────────────┐  ┌─────────────────────────────┐ │
│ Time     · f64   │  │ Plot 1               │  │ Plot 2                      │ │
│ Sine1    · f32   │  │ (drop signals here)  │  │ (drop signals here)         │ │
│ Sine2    · f32   │  │                      │  │                             │ │
│ Ch1 · [1000] f32 │  └─────────────────────┘  └─────────────────────────────┘ │
└──────────────────┴────────────────────────────────────────────────────────────┘
│ ● Streaming   [📊 Stats]                                               v1.0.0 │
└───────────────────────────────────────────────────────────────────────────────┘

Signal Sidebar

Signals received in the CONFIG packet are listed in the sidebar:

  • Scalar signals — appear as single draggable items (e.g. Sine1 · f32).
  • Temporal arrays — high-frequency burst signals with TimeMode ≠ PacketTime. Displayed as a single draggable item showing element count: Ch1 · [1000] f32.
  • Spatial arraysTimeMode = PacketTime arrays are shown as an expandable group; individual elements (Ch1[0], Ch1[1], …) can be dragged independently.

Click the sidebar toggle button (☰) to collapse/expand the signal list.

Adding Plots

  1. Select a layout from the layout menu (⊞ button in the top bar).
  2. Drag a signal from the sidebar onto a plot panel.
  3. Multiple signals can be overlaid on the same plot.
  4. Click the × inside a signal badge to remove a trace.

Plot Layout

The plot grid supports multiple layouts selectable from the layout menu (⊞):

Layout Description
1×1 Single plot
2×1 Two columns
1×2 Two rows
2×2 Four plots
3×1 Three columns
More layouts available

Resizing plots: When multiple plots are shown, drag the dividers between them to resize. Vertical dividers resize column widths; horizontal dividers resize row heights. Sizes are stored as fractional grid units (fr).

Plot Configuration

Click the plot title to open the configuration toolbar:

  • Title — edit the plot's display name.
  • Mode — select the plot display mode:
    • Normal — standard time-series oscilloscope view (default).
    • Mixed — signals are arranged in horizontal bands (like digital mode), but each signal can independently be displayed as analog (auto-scaled within its band) or digital (quantized to high/low within its band).
    • Digital — logic-analyzer style; each signal occupies a fixed horizontal band and is quantized to high/low based on its data range midpoint as threshold.

Signal Badges and Selection

Each signal assigned to a plot appears as a colored badge in the plot header.

  • Click a badge — selects the signal and opens the V-Scale toolbar for that signal. The selected signal is drawn on top with increased line width.
  • Click again — deselects the signal and closes the V-Scale toolbar.
  • Right-click a badge — opens the style context menu (color, line width, dash style, markers).
  • Click × on a badge — removes the signal from the plot.

V-Scale Toolbar

When a signal is selected (via badge click), an inline toolbar appears below the plot header showing per-signal vertical scale controls:

Control Description
Auto Automatically fits the signal vertically
Range Shows V/div and Pos controls for manual positioning
Manual Fixed V/div with free positioning
V/div Volts (or units) per division
Pos (div) Screen position in divisions (draggable offset marker on Y axis)
Type (Mixed mode only) Toggle between Analog and Digital for this signal
Close the toolbar and deselect the signal

Offset markers (small triangles on the Y axis) show each signal's position and can be dragged to reposition signals without opening the toolbar.

Plot Controls

Control Action
⏸ / ▶ (per plot) Pause / resume that plot; paused plots allow zoom and pan
(per plot) Export all visible traces to CSV
🗑 (per plot) Delete the plot
⏸ Pause (top bar) Pause all plots simultaneously
↺ Auto (top bar) Resume all paused plots and snap back to live view
Window (top bar) Adjust the rolling time window (1 s 60 s)
Fit (top bar) Fit all plots to their current data range
⬇ CSV (top bar) Export all signals from all plots to CSV
Layout button Switch between grid layouts
Sidebar Toggle the signal list panel

Cursor System

Two vertical cursors (A and B) can be placed on plots:

  • Enable with the Cursor button (shown when a plot is paused/zoomed).
  • The top bar readout shows A, B, and ΔT (time between cursors).
  • Cursors follow the mouse within the plot area.

Zoom

  • Drag left or right on a paused plot to zoom into a time range.
  • ← Back button steps back through zoom history.
  • Fit returns to the full data view.
  • When zooming into a region with few or no data points, the nearest data points outside the zoom window are included so that connecting lines are drawn across the view rather than showing blank space.

Trigger System

Click Trigger in the top bar to open the trigger bar:

Control Description
Signal Select the trigger source signal
Edge Rising ↑, Falling ↓, or Both ↕
Threshold Trigger level
Window Capture duration after trigger (100 µs 10 s)
Pre Pre-trigger buffer percentage (0100%)
Mode Normal (re-arms automatically) or Single (fires once)
Rearm Manually re-arm the trigger
Stop Cancel waiting trigger

For array signals, clicking the trigger signal selector prompts for the element index to use as the trigger source.

Status Bar

The status bar at the bottom shows:

  • LED indicator: red (disconnected), orange pulsing (connected, no data), green pulsing (streaming).
  • Status text: connection state and data age.
  • 📊 Stats button: opens the source statistics panel (packet counts, rates, errors).
  • Build version: server build tag.

Data Buffering

Signal buffers are sized based on the signal's configured sampling rate from the CONFIG packet:

capacity = min(600 000, ceil(samplingRate × 60 s × 1.5))

This ensures up to 60 seconds of history is retained for any signal, regardless of sample rate. If a signal's sampling rate is not configured, a default capacity of 100 000 samples is used. Temporal arrays (burst signals) use a fixed capacity of 500 000 samples.

Buffers grow automatically during streaming if a higher effective sample rate is detected. Existing data is preserved during growth.


Architecture (Go side)

Goroutines

main()
 ├── hub.Run()          ← event loop: register/unregister WS clients, batch data at 30 Hz
 ├── udpClient.Run()    ← reconnects on silence; parses UDP packets; feeds hub.dataCh
 └── http.ListenAndServe()
      ├── GET /         ← serves embedded static files
      └── GET /ws       ← upgrades to WebSocket; launches per-client read/write pumps

WebSocket Message Format

Two message types are sent from server to browser.

config message (JSON)

Sent immediately when a browser client connects (if a CONFIG has been received from UDPStreamer) and whenever UDPStreamer sends a new CONFIG packet.

{
  "type": "config",
  "signals": [
    {
      "name": "Sine1",
      "typeCode": 8,
      "quantType": 3,
      "numDimensions": 0,
      "numRows": 1,
      "numCols": 1,
      "rangeMin": -10.0,
      "rangeMax": 10.0,
      "timeMode": 0,
      "samplingRate": 1000.0,
      "timeSignalIdx": 4294967295,
      "unit": "V"
    }
  ]
}

data message (binary)

Sent at ≤ 30 Hz, batching all UDP packets received since the last tick. Uses a compact binary format: a fixed header followed by per-signal blocks.

Each signal block contains:

  • Signal name (length-prefixed)
  • Number of samples
  • Timestamp array (float64 LE, Unix seconds)
  • Value array (float64 LE)

For temporal arrays, each packet contributes N samples (one per array element). For spatial arrays, keys are "Ch1[0]", "Ch1[1]", etc.


Reconnection Behaviour

  • UDP reconnect: The Go client reconnects to UDPStreamer automatically after 5 s of silence. This handles MARTe2 restarts transparently.
  • WebSocket keepalive: The server sends a WebSocket ping every 30 s. The browser auto-responds; if no pong is received within 10 s the connection is closed and the browser reconnects with exponential backoff (starting at 1 s, capped at 30 s).
  • Buffer preservation: Browser-side signal buffers are only reset when the signal layout changes (name, type, or dimensions differ). A reconnect with the same CONFIG keeps existing data visible in the plots.