Add CACHE_STATUS UART poll and dashboard live stream.

Combine cached accel and tap in one low-overhead master command for ~16 ms
host polling. The dashboard uses a single live-stream toggle plus per-slave
accel-stream controls; fix live_stream state so polling is not cleared every
slow client refresh.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-29 20:57:54 +02:00
co-authored by Cursor
parent a8d4d42920
commit f512936d97
21 changed files with 764 additions and 377 deletions
+66 -56
View File
@@ -434,80 +434,90 @@ func runAccelStreamer(link *managedSerial, hub *accelStreamHub, dash *wsHub, ctl
case <-hub.configChanged:
resetTicker()
case <-tick:
wantAccel := hub.anyWantsAccel() && accelStreamPollingActive(dash, ctl)
wantTap := hub.anyWantsTap()
if !wantAccel && !wantTap {
continue
}
now := time.Now().UnixNano()
if hub.anyWantsAccel() && accelStreamPollingActive(dash, ctl) {
resp, err := link.readAccelSnapshotPoll(0)
if errors.Is(err, errUARTBusy) {
cache, err := link.readCacheStatusPoll()
if errors.Is(err, errUARTBusy) {
if wantAccel {
hub.deliver(AccelStreamMessage{
Type: "accel",
T: now,
Success: false,
Error: "uart busy",
})
} else if err != nil {
hub.deliver(AccelStreamMessage{
Type: "accel",
T: now,
Success: false,
Error: err.Error(),
})
} else {
clients := make([]AccelClientSample, 0, len(resp.GetSamples()))
for _, s := range resp.GetSamples() {
clients = append(clients, AccelClientSample{
ClientID: s.GetClientId(),
Valid: s.GetValid(),
X: s.GetX(),
Y: s.GetY(),
Z: s.GetZ(),
AgeMs: s.GetAgeMs(),
})
}
hub.deliver(AccelStreamMessage{
Type: "accel",
T: now,
Success: true,
Clients: clients,
})
}
}
if hub.anyWantsTap() {
nowMs := time.Now().UnixNano()
resp, err := link.readTapSnapshotPoll(0)
if errors.Is(err, errUARTBusy) {
if wantTap {
hub.deliverTap(TapStreamMessage{
Type: "tap",
T: nowMs,
T: now,
Success: false,
Error: "uart busy",
})
} else if err != nil {
hub.deliverTap(TapStreamMessage{
Type: "tap",
T: nowMs,
}
continue
}
if err != nil {
if wantAccel {
hub.deliver(AccelStreamMessage{
Type: "accel",
T: now,
Success: false,
Error: err.Error(),
})
} else {
fresh := make([]TapClientEvent, 0, len(resp.GetEvents()))
for _, e := range resp.GetEvents() {
if !e.GetValid() {
continue
}
fresh = append(fresh, TapClientEvent{
ClientID: e.GetClientId(),
Valid: true,
Kind: tapKindLabelPB(e.GetKind()),
AgeMs: e.GetAgeMs(),
})
}
events := hub.ingestTapEvents(fresh)
if len(events) == 0 {
continue
}
}
if wantTap {
hub.deliverTap(TapStreamMessage{
Type: "tap",
T: nowMs,
T: now,
Success: false,
Error: err.Error(),
})
}
continue
}
if wantAccel {
clients := make([]AccelClientSample, 0, len(cache.GetAccel()))
for _, s := range cache.GetAccel() {
clients = append(clients, AccelClientSample{
ClientID: s.GetClientId(),
Valid: s.GetValid(),
X: s.GetX(),
Y: s.GetY(),
Z: s.GetZ(),
AgeMs: s.GetAgeMs(),
})
}
hub.deliver(AccelStreamMessage{
Type: "accel",
T: now,
Success: true,
Clients: clients,
})
}
if wantTap {
fresh := make([]TapClientEvent, 0, len(cache.GetTaps()))
for _, e := range cache.GetTaps() {
if !e.GetValid() {
continue
}
fresh = append(fresh, TapClientEvent{
ClientID: e.GetClientId(),
Valid: true,
Kind: tapKindLabelPB(e.GetKind()),
AgeMs: e.GetAgeMs(),
})
}
events := hub.ingestTapEvents(fresh)
if len(events) > 0 {
hub.deliverTap(TapStreamMessage{
Type: "tap",
T: now,
Success: true,
Events: events,
})