diff --git a/sfm/server.py b/sfm/server.py index 5d57c49..97dd7b2 100644 --- a/sfm/server.py +++ b/sfm/server.py @@ -873,8 +873,8 @@ def device_event_blastware_file( triggered events; histogram requires recording_mode to be populated from compliance config) - Performs: POLL startup → get_events(full_waveform=False, stop_after_index=index) - → write_blastware_file() → FileResponse. + Performs: POLL startup → get_events(full_waveform=False, extra_chunks=1, + stop_after_index=index) → write_blastware_file() → FileResponse. """ log.info( "GET /device/event/%d/blastware_file port=%s host=%s", @@ -885,17 +885,21 @@ def device_event_blastware_file( def _do(): with _build_client(port, baud, host, tcp_port, timeout=120.0) as client: info = client.connect() - # Use full_waveform=True (stop_after_metadata=False) so the device - # streams until it naturally signals end-of-stream (1-raw-byte signal). - # BW does the same — the ACH download and manual pull both let the device - # determine when to stop. The termination response at that point contains - # the correct 0e 08 footer with monitoring timestamps. - # For Continuous/Single-Shot events, end-of-stream comes after the real - # ADC data (not after 35+ silence chunks as in Histogram+Continuous mode). - # max_chunks=32 is a safety cap; natural end-of-stream stops much earlier. + # Use stop_after_metadata=True (full_waveform=False) with 1 extra + # chunk after "Project:". For any record time, the pre-metadata + # section of the 5A stream naturally carries proportionally more + # ADC data for longer events — so "1 extra chunk" produces the + # correct body length regardless of record time. + # + # full_waveform=True (natural end-of-stream) downloads ALL chunks + # including post-event silence (35+ chunks for a 9-sec event at + # 1024 sps) — this produces 24KB+ files that Blastware rejects. + # Confirmed from file size comparison: BW 1-sec=4400B, BW 3-sec=8114B, + # per-second delta 1857 bytes — matches pre-metadata frame scaling. events = client.get_events( - full_waveform=True, + full_waveform=False, stop_after_index=index, + extra_chunks_after_metadata=1, ) matching = [ev for ev in events if ev.index == index] return matching[0] if matching else None, info