- Per-signal, per-plot vertical scale state (sigVScale keyed by plotId:signalKey)
so the same signal in two plots has fully independent vscale config
- Active signal redrawn on top of all series with 2× line width for clear
visual identification; badge click toggles selection and opens/closes the
embedded vscale toolbar (click same badge again to deselect)
- Vscale configurator moved from floating popup to a slim toolbar strip
anchored inside the plot card, with an × close button
- Trigger dropdown shows one entry per array signal with [0…N-1] label;
opening it shows an index-picker dialog to choose the element
- Zoom resampling: when server returns no data for a zoomed range, fall
back to the local circular buffer instead of returning empty arrays
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DataSource (C++):
- New optional config: MulticastGroup (e.g. "239.0.0.1") and DataPort
(default Port+1); when set, enables multicast mode; absent = unicast
- Control plane: BasicTCPSocket listener on Port; CONNECT received over
TCP triggers HandleTCPConnect() which sends CONFIG via the same TCP
connection
- Data plane: BasicUDPSocket aimed at MulticastGroup:DataPort; all DATA
fragments sent via dataSocket.Write() so any joined client receives them
- useMulticast is the single branch point; every unicast code path is
unchanged when MulticastGroup is absent
- New methods: HandleTCPConnect(), IsMulticast()
- 5 new unit tests (ports 44710-44729); all 38 tests passing
WebUI hub (Go):
- SourceConfig gains MulticastGroup and DataPort fields (JSON, optional)
- UDPClient gains multicastGroup/dataPort; Run() routes to new
runMulticastSession() when multicastGroup is non-empty
- runMulticastSession(): TCP dial for CONNECT/CONFIG, net.ListenMulticastUDP
for DATA, background goroutine watches TCP for DISCONNECT/close
- All existing sm.Add() call sites updated (unicast callers pass "", 0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>