Read the meter once and replay the capture offline - #57
Merged
Merged
Conversation
Every run that talked to the meter did a full transfer of up to 800 records, so producing csv, json, records and bytes meant four transfers. fetch_all already returns readings, raw records and raw packets from a single session — only output::write could emit just one of them. Add --format binary, which writes the raw HID packet stream, and --from-bytes, which replays that file through the same framing and record parsing the live read uses. Packet reassembly and record accumulation are now shared (MessageAssembler, SessionBuilder) instead of duplicated, so a replayed capture cannot drift from a live one; a test asserts the replayed session matches what parsing the records text produces, and the fixture's H record spans several packets so reassembly is covered too. The capture format is magic + version + packet size, then a direction byte per packet at a fixed stride, so a truncated or foreign file is rejected outright rather than decoding into a plausible but short reading list. capture-bgl now touches the meter once and derives the other four files from that capture. It also stages the run in a temp dir and moves it into place at the end, so a failure part-way through no longer leaves fresh files sitting next to stale ones. Also stop --from-records discarding unparseable lines in silence: it warns per line and with a total, since a truncated dump would otherwise yield a short CSV that looks perfectly valid. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #56 — review that one first; this PR's base is
owner/reduce-device-re-reads, so the diff here is only the follow-up work.Closes the
TODO#56 left inbin/capture-bgl, and picks up the two smaller review findings from it.The problem
Every run that talks to the meter is a full transfer of up to 800 records. #56 got the routine capture from four transfers down to two by deriving
csv/jsonfrom a saved records dump, butbytesstill needed its own read — and the data was already in hand:fetch_allreturns readings, raw records and raw packets from a single session. Onlyoutput::writecould emit just one of them.The change
--format binarywrites the raw HID packet stream: magic + version + packet size, then a direction byte per packet at a fixed stride.--from-bytes FILEreplays that capture through the same framing and record parsing the live read uses.Packet reassembly and record accumulation moved into
MessageAssemblerandSessionBuilder, shared by both paths, so a replayed capture cannot drift from a live one. That sharing is the point — a second, parallel decoder would have been the bug waiting to happen.Bad input fails loudly rather than decoding into a plausible but short reading list: wrong magic, wrong version, wrong packet size, a bad direction byte and a truncated tail are each rejected with their own message.
bin/capture-bglnow reads the meter once and derives all four other files from the capture. It also stages the run in a temp dir and moves it into place at the end, so a failure part-way through no longer leaves fresh files next to stale ones from an earlier run.Separately:
--from-recordsno longer discards unparseable lines in silence. It warns per line and with a total, because a truncated dump otherwise yields a short CSV that looks perfectly valid.README documents
--from-bytes,--from-records(previously undocumented) and thebinaryformat, and notes that the binary capture carries the same meter password and serial the records dump does.Testing
cargo test— 56 pass,cargo clippy --all-targetsandcargo fmt --checkclean.Six new tests, the load-bearing one being
replay_matches_text_parsing: it synthesises a capture from the existing fixture and asserts the replayed session matchesparse_records_from_texton the same fixture — device, readings and raw frames. The fixture'sHrecord is longer than one HID packet, so multi-packet reassembly is covered on the way through. Others cover the encode/decode round trip, byte-identical re-encoding after a file round trip, rejection of damaged and foreign files, a NAK-retried frame appearing only once, and an empty capture erroring rather than producing an empty session.CLI paths were exercised by hand for the error cases (empty capture, hex dump passed to
--from-bytes, junk line in a records file).Verified against a physical meter
A Contour Next One was captured twice on the same day with no new readings in between — once on
owner/reduce-device-re-reads, once on this branch — and the outputs compared..csv.jsondevice_time.txtHrecordThe
Hrecord carries two values that are new every session: the meter's per-session password nonce and the capture timestamp. Everything else matches.The
.hexneeds explaining, because a naivediffreports ~3300 changed lines. 3300 of them are packet header lines (--- packet NNNN RX ---): the old capture has two extra packets at the front, so every later packet is renumbered anddiffcannot resync. Normalise the numbering and the real diff is 18 lines:Those 18 lines are: two packets present only in the old capture (
RX 04 05= EOT+ENQ, thenTX 06= ACK), and the twoH-record packets holding the password nonce, the timestamp and its ASTM checksum.Parsing both dumps back into packets and aligning past that two-packet offset gives 0 direction mismatches and 1649 of 1651 packets byte-identical across two independent physical sessions — the only two that differ being the
H-record packets above.The two extra packets are the point rather than a discrepancy.
decode_messagealready documents the meter packingEOT+ENQwhen a session opens on the heels of a previous one; that is exactly what the old capture shows, because its hex dump came from a second device read moments after the records read. This branch's capture is a first session and goes straight to theHrecord.That second read also meant the old
.hexdocumented a different session from the old.txt— different password nonce, 32 seconds apart — so the debug dump never described the data actually shipped alongside it. Both now come from the one session.Finally, replaying the captured
.binback to.hexreproduces the stored.hexbyte for byte, so the conversion is deterministic.🤖 Generated with Claude Code