tooling(micromate): --event selects what to download, incl. 'largest'

The tool downloaded only the first event, which on UM20147 is a 4,796 B
histogram -- so it could not reach the 72,560 B event that is the whole point of
the uint32 chunk-offset change.

--event takes a key in hex, 'first', 'largest' or 'all'.  'largest' is the one
worth running on an unfamiliar unit, since it is what exercises offsets past
64 KB; the output flags any event over 64 KB as carrying into params[1], and
prints the chunk count so a 71-chunk walk is visible as such.

Selected events are downloaded from inside iter_events(), so the chain cursor
sits where THOR's would -- non-selected events are walked past without a
download, which the browse walk already attests is fine.

Also: connect() returning nothing now prints a hint rather than just a bare
timeout.  /dev/ttyACM* numbering shifts whenever another CDC-ACM device is
plugged in, and this cost a debugging session when the tool was aimed at a
TMI-Seismo MCU instead of the Micromate.  `ls -l /dev/serial/by-id/` names each
device and is stable, so the hint says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL
This commit is contained in:
2026-10-01 16:04:21 -04:00
co-authored by Claude Opus 5
parent 9c28b586bb
commit 0cfec2f098
+46 -11
View File
@@ -232,6 +232,18 @@ def _decode(blob: bytes, key: bytes, record: bytes) -> None:
print(f" saved to {out} for offline analysis")
def _select(refs, which: str):
"""Pick which events `--download` fetches."""
which = which.strip().lower()
if which == "first":
return refs[:1]
if which == "all":
return refs
if which == "largest":
return [max(refs, key=lambda r: r.size)]
return [r for r in refs if r.key_hex.lower() == which]
def step(label: str, fn):
"""Run one read, report how long it took and what it returned."""
t0 = time.monotonic()
@@ -259,7 +271,12 @@ def main() -> int:
"and negotiates its own rate.")
ap.add_argument("--timeout", type=float, default=10.0)
ap.add_argument("--download", action="store_true",
help="also download the first stored event (read-only)")
help="also walk the event chain and download (read-only)")
ap.add_argument("--event", default="first", metavar="WHICH",
help="which event --download fetches: a key in hex "
"(e.g. 055d4a83), 'first', 'largest', or 'all'. "
"'largest' is the one worth running on an unfamiliar "
"unit -- it is what exercises offsets past 64 KB.")
ap.add_argument("--capture", metavar="DIR",
help="also write a raw_bw_*/raw_s3_*.bin pair to DIR, so "
"this run can become a test fixture. Worth doing on "
@@ -291,6 +308,12 @@ def main() -> int:
try:
info = step("connect() identity", mm.connect)
if info is None:
print("\n Nothing answered. If the port opened but no frame came back,")
print(" check it is actually a Micromate and not another CDC-ACM device —")
print(" /dev/ttyACM* numbering shifts when anything else is plugged in.")
print(" `ls -l /dev/serial/by-id/` names each device and is stable.")
return 3
if info:
print(f" serial={info.serial} model={info.model} "
f"fw={info.firmware_line} monitoring={info.monitoring}")
@@ -345,24 +368,36 @@ def main() -> int:
print(f" would be filed as {ref.filename}")
if refs:
# Download via iter_events, which reproduces THOR's interleaved
# order -- the one with captures behind it.
print("\n download (first event, THOR's interleaved order):")
wanted = _select(refs, a.event)
if not wanted:
print(f"\n --event {a.event!r} matched nothing")
else:
print(f"\n download ({len(wanted)} of {len(refs)}, "
f"THOR's interleaved order):")
want_keys = {r.key_hex for r in wanted}
# iter_events() walks the chain; download only the selected
# events, at the cursor position THOR would be at.
for ref in mm.iter_events():
if ref.key_hex not in want_keys:
continue
n_chunks = -(-ref.size // 1024)
note = (" <- past 64 KB, carries into params[1]"
if ref.size > 0x10000 else "")
t1 = time.monotonic()
try:
result = mm.get_event(ref) # verify=True
except Exception as e:
print(f" {ref.key_hex}: {type(e).__name__}: {e}")
break
print(f" {ref.key_hex} {ref.size:7} B "
f"FAILED {type(e).__name__}: {e}")
continue
dt = max(time.monotonic() - t1, 1e-6)
n = sum(len(v) for v in getattr(result, "samples", {}).values())
err = mm.decode_error(ref, result)
check = ("PVS agrees to %+.4f%%" % (100 * err)
if err is not None else "PVS check n/a (histogram)")
print(f" {ref.key_hex} {ref.record_type:9} {ref.size:6} B "
f"in {dt:.1f} s -> {n} samples, {check}")
break
check = ("PVS %+.4f%%" % (100 * err) if err is not None
else "PVS n/a (histogram)")
print(f" {ref.key_hex} {ref.record_type:9} "
f"{ref.size:7} B {n_chunks:3} chunks {dt:5.1f} s "
f"{n:6} samples {check}{note}")
except ProtocolError as e:
print(f"\n ABORTED {type(e).__name__}: {e}")