FIrs
This commit is contained in:
+188
@@ -0,0 +1,188 @@
|
||||
# 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 [Plotly.js](https://plotly.com/javascript/).
|
||||
|
||||
```
|
||||
MARTe2 RT app
|
||||
│ UDP (binary protocol)
|
||||
▼
|
||||
udpstreamer-webui (Go)
|
||||
│ WebSocket (JSON)
|
||||
▼
|
||||
Browser (index.html + Plotly.js)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
./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
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ MARTe2 UDP Streamer ● Streaming Window: [5 s▾] ⏸ │ ← top bar
|
||||
├──────────┬──────────────────────────────────────────────┤
|
||||
│ Signals │ Plots [1×1][2×1][2×2]… [+Add] │
|
||||
│──────────│──────────────────────────────────────────────│
|
||||
│ Counter │ ┌─────────────────┐ ┌──────────────────┐ │
|
||||
│ Time │ │ Plot 1 │ │ Plot 2 │ │
|
||||
│ Sine1 │ │ (drop signals) │ │ (drop signals) │ │
|
||||
│ Sine2 │ │ │ │ │ │
|
||||
│ Ch1[1000]│ └─────────────────┘ └──────────────────┘ │
|
||||
│ Ch2[1000]│ │
|
||||
└──────────┴──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 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 arrays** — `TimeMode = PacketTime` arrays are shown as an expandable
|
||||
group; individual elements (`Ch1[0]`, `Ch1[1]`, …) can be dragged independently.
|
||||
|
||||
### Adding Plots
|
||||
|
||||
1. Click **+ Add Plot** in the toolbar.
|
||||
2. Drag a signal from the sidebar onto a plot panel.
|
||||
3. Multiple signals can be overlaid on the same plot.
|
||||
4. Click the **×** badge to remove a trace.
|
||||
|
||||
### 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 All** (top bar) | Pause all plots simultaneously |
|
||||
| **Window** (top bar) | Adjust the rolling time window (1 s – 60 s) |
|
||||
| Layout buttons | Switch between 1×1, 2×1, 1×2, 2×2, 3×1, … grid layouts |
|
||||
| Sidebar **←** | Collapse the signal list to maximise plot area |
|
||||
|
||||
### Status LED
|
||||
|
||||
| Colour | Meaning |
|
||||
|--------|---------|
|
||||
| Red (solid) | No WebSocket connection to browser |
|
||||
| Orange (pulsing) | WebSocket connected but no data received in > 1 s |
|
||||
| Green (pulsing) | Data is being received normally |
|
||||
|
||||
---
|
||||
|
||||
## 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 index.html
|
||||
└── GET /ws ← upgrades to WebSocket; launches per-client read/write pumps
|
||||
```
|
||||
|
||||
### WebSocket Message Format
|
||||
|
||||
All messages are JSON. Two types are sent from server to browser:
|
||||
|
||||
#### `config` message
|
||||
|
||||
Sent immediately when a browser client connects (if a CONFIG has been received from
|
||||
UDPStreamer) and whenever UDPStreamer sends a new CONFIG packet.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "config",
|
||||
"signals": [
|
||||
{
|
||||
"name": "Sine1",
|
||||
"typeCode": 8,
|
||||
"quantType": 3,
|
||||
"numDimensions": 0,
|
||||
"numRows": 1,
|
||||
"numCols": 1,
|
||||
"rangeMin": -10.0,
|
||||
"rangeMax": 10.0,
|
||||
"timeMode": 0,
|
||||
"samplingRate": 0.0,
|
||||
"timeSignalIdx": 4294967295,
|
||||
"unit": "V"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### `data` message
|
||||
|
||||
Sent at ≤ 30 Hz, batching all UDP packets received since the last tick.
|
||||
Each signal carries its own time axis to support mixed scalar + temporal-array signals.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "data",
|
||||
"signals": {
|
||||
"Sine1": { "t": [1747123456.001, 1747123456.002], "v": [3.14, 2.71] },
|
||||
"Counter": { "t": [1747123456.001, 1747123456.002], "v": [12340, 12341] },
|
||||
"Ch1": { "t": [1747123456.000, 1747123456.0001, ...], "v": [0.12, 0.13, ...] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `t` — Unix timestamp in seconds (float64) for each sample.
|
||||
- `v` — physical value (after dequantization if applicable).
|
||||
- For **temporal arrays**, `t` and `v` have `N × batchSize` entries
|
||||
(up to 2000 per 30 Hz tick after server-side decimation).
|
||||
- For **spatial arrays** the 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.
|
||||
Reference in New Issue
Block a user