feat: enhance waveform viewer with record type handling and improved empty state messaging

This commit is contained in:
Brian Harrison
2026-04-03 15:22:26 -04:00
parent e4730376ad
commit f495b91d8a
3 changed files with 33 additions and 14 deletions

View File

@@ -838,7 +838,9 @@ def _extract_record_type(data: bytes) -> Optional[str]:
code = data[1]
if code == 0x10:
return "Waveform"
# TODO: add histogram sub_code once a histogram event is captured with debug=true
# Unknown code — log it so we can identify histogram/noise sub_codes from real captures
log.warning("_extract_record_type: unknown sub_code=0x%02X — returning raw string", code)
return f"Unknown(0x{code:02X})"
return None

View File

@@ -183,21 +183,23 @@ def _build_client(
baud: int,
host: Optional[str],
tcp_port: int,
timeout: float = 30.0,
) -> MiniMateClient:
"""
Return a MiniMateClient configured for either serial or TCP transport.
TCP takes priority if *host* is supplied; otherwise *port* (serial) is used.
Raises HTTPException(422) if neither is provided.
Use timeout=120.0 (or higher) for endpoints that perform a full 5A waveform
download — a 70-second event at 1024 sps takes 2-3 minutes to transfer over
cellular and each individual recv must complete within the timeout window.
"""
if host:
# TCP / modem / ACH path — use a longer timeout to survive cold boots
# (unit takes 5-15s to wake from RS-232 line assertion over cellular)
transport = TcpTransport(host, port=tcp_port)
log.debug("TCP transport: %s:%d", host, tcp_port)
return MiniMateClient(transport=transport, timeout=30.0)
log.debug("TCP transport: %s:%d timeout=%.0fs", host, tcp_port, timeout)
return MiniMateClient(transport=transport, timeout=timeout)
elif port:
# Direct serial path
log.debug("Serial transport: %s baud=%d", port, baud)
return MiniMateClient(port, baud)
else:
@@ -425,7 +427,7 @@ def device_event_waveform(
try:
def _do():
with _build_client(port, baud, host, tcp_port) as client:
with _build_client(port, baud, host, tcp_port, timeout=120.0) as client:
info = client.connect()
# full_waveform=True fetches the complete 5A stream inside the
# 1E→0A→0C→5A→1F loop. Issuing a second 5A after 1F times out.
@@ -458,12 +460,14 @@ def device_event_waveform(
return {
"index": ev.index,
"record_type": ev.record_type,
"timestamp": _serialise_timestamp(ev.timestamp),
"total_samples": ev.total_samples,
"pretrig_samples": ev.pretrig_samples,
"rectime_seconds": ev.rectime_seconds,
"samples_decoded": samples_decoded,
"sample_rate": sample_rate,
"peak_values": _serialise_peak_values(ev.peak_values),
"channels": raw,
}

View File

@@ -374,13 +374,7 @@
const decoded = data.samples_decoded || 0;
const total = data.total_samples || decoded;
const channels = data.channels || {};
// Build time axis (ms)
const times = Array.from({ length: decoded }, (_, i) =>
((i - pretrig) / sr * 1000).toFixed(2)
);
const triggerMs = 0; // t=0 is trigger by construction
const recType = data.record_type || 'Unknown';
// Status bar
const bar = document.getElementById('status-bar');
@@ -392,11 +386,30 @@
} else {
bar.textContent = `Event #${data.index} `;
}
appendMeta('type', recType);
appendMeta('sr', `${sr} sps`);
appendMeta('samples', `${decoded.toLocaleString()} / ${total.toLocaleString()}`);
appendMeta('pretrig', pretrig);
appendMeta('rectime', `${data.rectime_seconds ?? '?'}s`);
// No waveform data — show a clear reason instead of empty charts
if (decoded === 0) {
document.getElementById('empty-state').style.display = 'flex';
document.getElementById('empty-state').querySelector('p').textContent =
recType === 'Waveform'
? 'Waveform decode returned no samples — check server logs'
: `Record type "${recType}" — waveform decode not yet supported for this mode`;
document.getElementById('charts').style.display = 'none';
Object.values(charts).forEach(c => c.destroy());
charts = {};
return;
}
// Build time axis (ms)
const times = Array.from({ length: decoded }, (_, i) =>
((i - pretrig) / sr * 1000).toFixed(2)
);
// Show charts area
document.getElementById('empty-state').style.display = 'none';
const chartsDiv = document.getElementById('charts');