diff --git a/minimateplus/client.py b/minimateplus/client.py index bb72858..bcae5de 100644 --- a/minimateplus/client.py +++ b/minimateplus/client.py @@ -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 diff --git a/sfm/server.py b/sfm/server.py index c2f7240..fc87f8e 100644 --- a/sfm/server.py +++ b/sfm/server.py @@ -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, } diff --git a/sfm/waveform_viewer.html b/sfm/waveform_viewer.html index 5ec560c..d5d6f5f 100644 --- a/sfm/waveform_viewer.html +++ b/sfm/waveform_viewer.html @@ -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');