WebUI: per-signal vscale toolbar, active-signal highlighting, zoom fix
- 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>
This commit is contained in:
+27
-13
@@ -82,9 +82,14 @@ func (c *wsClient) readPump() {
|
||||
case "addSource":
|
||||
label, _ := env["label"].(string)
|
||||
addr, _ := env["addr"].(string)
|
||||
mcastGroup, _ := env["multicastGroup"].(string)
|
||||
dataPortF, _ := env["dataPort"].(float64)
|
||||
if addr != "" {
|
||||
select {
|
||||
case c.hub.commandCh <- hubCmd{op: "wsAddSource", label: label, addr: addr}:
|
||||
case c.hub.commandCh <- hubCmd{
|
||||
op: "wsAddSource", label: label, addr: addr,
|
||||
multicastGroup: mcastGroup, dataPort: int(dataPortF),
|
||||
}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -139,11 +144,13 @@ type taggedSample struct {
|
||||
type hubCmd struct {
|
||||
op string // "addSource","removeSource","setSourceState","updateConfig",
|
||||
// "wsAddSource","wsRemoveSource","wsSaveSources"
|
||||
sourceID string
|
||||
label string
|
||||
addr string
|
||||
state string
|
||||
sigs []SignalInfo
|
||||
sourceID string
|
||||
label string
|
||||
addr string
|
||||
state string
|
||||
sigs []SignalInfo
|
||||
multicastGroup string
|
||||
dataPort int
|
||||
}
|
||||
|
||||
// Hub is the central broker between UDP clients and WebSocket clients.
|
||||
@@ -474,7 +481,9 @@ func (h *Hub) Run() {
|
||||
|
||||
case "wsAddSource":
|
||||
if h.sm != nil {
|
||||
go func(label, addr string) { h.sm.Add(label, addr, "", 0) }(cmd.label, cmd.addr)
|
||||
go func(label, addr, mcastGroup string, dataPort int) {
|
||||
h.sm.Add(label, addr, mcastGroup, dataPort)
|
||||
}(cmd.label, cmd.addr, cmd.multicastGroup, cmd.dataPort)
|
||||
}
|
||||
|
||||
case "wsRemoveSource":
|
||||
@@ -718,9 +727,10 @@ func (h *Hub) buildDataMessageForSource(src *sourceHubState, batch []DataSample)
|
||||
decimT, decimV := lttbDecimate(allT, allV, maxPushPoints)
|
||||
out[pfx+sig.Name] = sigData{T: decimT, V: decimV}
|
||||
|
||||
case n > 1 && sig.TimeMode == TimeModeFullArray:
|
||||
// The time signal has the same N elements as the data signal.
|
||||
case sig.TimeMode == TimeModeFullArray:
|
||||
// Each element pair (timeSig[k], dataSig[k]) is one (t, v) sample.
|
||||
// This handles both standard N-element FullArray signals and
|
||||
// Accumulate-mode scalars (n=1) auto-assigned FullArray time mode.
|
||||
hasTimeSig := sig.TimeSignalIdx != NoTimeSignal && int(sig.TimeSignalIdx) < len(sigs)
|
||||
var timeSigName string
|
||||
timerToSec := 1e-6
|
||||
@@ -918,7 +928,9 @@ func (h *Hub) buildBinaryDataMessageForSource(src *sourceHubState, batch []DataS
|
||||
decimT, decimV := lttbDecimate(allT, allV, maxPushPoints)
|
||||
pairs[sig.Name] = pairBuf{t: decimT, v: decimV}
|
||||
|
||||
case n > 1 && sig.TimeMode == TimeModeFullArray:
|
||||
case sig.TimeMode == TimeModeFullArray:
|
||||
// Handles both standard N-element FullArray signals and
|
||||
// Accumulate-mode scalars (n=1) with auto-assigned FullArray time mode.
|
||||
hasTimeSig := sig.TimeSignalIdx != NoTimeSignal && int(sig.TimeSignalIdx) < len(sigs)
|
||||
var timeSigName string
|
||||
timerToSec := 1e-6
|
||||
@@ -957,9 +969,11 @@ func (h *Hub) buildBinaryDataMessageForSource(src *sourceHubState, batch []DataS
|
||||
allV = append(allV, vals[k])
|
||||
}
|
||||
}
|
||||
ringT, ringV := lttbDecimate(allT, allV, maxRingPoints)
|
||||
if rb := h.getRing(pfx + sig.Name); rb != nil {
|
||||
rb.write(ringT, ringV)
|
||||
if writeRing {
|
||||
ringT, ringV := lttbDecimate(allT, allV, maxRingPoints)
|
||||
if rb := h.getRing(pfx + sig.Name); rb != nil {
|
||||
rb.write(ringT, ringV)
|
||||
}
|
||||
}
|
||||
decimT, decimV := lttbDecimate(allT, allV, maxPushPoints)
|
||||
pairs[sig.Name] = pairBuf{t: decimT, v: decimV}
|
||||
|
||||
Reference in New Issue
Block a user