af8eebd724677e1295fdaaaf16df20f92ee3ba81
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
080fb92ba2 |
test(micromate): real 11.0BD fixtures replace the synthesised ones
UM20147 captured over USB with mm_client_check.py --capture, read out with
scratch/mm_frame_parse.py: 11 frames each direction, 0 bad checksums. The two
BD-specific response data sections are now verbatim fixtures; only the frame
wrapper is rebuilt, which is sound because the framing is verified 251/251
elsewhere in the same file, and the reconstruction is pinned against the
payload length the parser reported.
Every field lands where the forward-offset model says it should:
data[0] = 0x30 = 48 = 59 - 11 (CB declares 0x2C = 44; the +4 holds)
content[1] = 0x0e monitoring
content[2:10] = 30 Sep 2026 14:00:49 matches the wall clock
content[34:36] = 3.50 V
content[36:44] = 15,000,000 total / 14,848,448 free
AND THE WARNING IS NOW DEMONSTRATED ON THE UNIT IT PREDICTED. Series III reads
battery from data[-10:-8], which on this block is e1 c0 -> 577.92 V. That is
the exact figure the A/B section named, and there is now a test asserting it,
so the reason the offsets are forward cannot be quietly refactored away.
Two corrections to the reference:
1. The four extra BD bytes are `0f a0 00 04`, not `0f a0 00 00`. Only the
first two look fixed. Nothing reads them, but the last is not padding.
2. POLL content[3] VARIES BETWEEN UNITS -- 0x50 on UM12947, 0x56 on UM20147.
It is printable in both cases ("P" and "V"), which is a second, independent
reason the POLL block cannot be parsed by scanning for printable runs: a
scan returns "PInstantel" on one unit and "VInstantel" on the other. The
fixed offset content[4] was the right call for a reason I had not seen.
The model string sits at content[26] on both firmware lines; only its tail
differs, so anchoring on "MM/" remains correct.
98 micromate tests pass. Full suite unchanged at 16 pre-existing failures.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL
|
||
|
|
b55e4e946a |
verify(micromate): 11.0BD confirmed on hardware -- every inference held
UM20147 read over USB by mm_client_check.py. The Thor firmware line was entirely inference until now, and two of its three differences were covered only by SYNTHESISED test frames. All three held: - flags = 0x03 identifies the line -> reported firmware_line="thor". That is only reachable if `10 03` in the flags position destuffs before indexing, since 0x03 is ETX -- so the escaped-flags case is confirmed too. - the model string is shorter -> reported "MM/ISEE/S" exactly. Anchoring the search on b"MM/" rather than a fixed span is what made this work. - the 0x1C block is 4 bytes longer with the extras TRAILING, so from-the-start offsets survive -> battery 3.55 V and a clock correct to the second. That last one is the one that mattered. Series III's from-the-end offsets would have given this unit 577.92 V, which is why mm_client_check watches for an impossible voltage: it is a free self-check on exactly the inference most likely to be wrong. Also clean: serial UM20147, 5 setups (factory.MMB -> test2.mmb), active setup test2.mmb, 15,000,000 B total and free, 21 reads / 2,165 B in. "One protocol stack drives the whole fleet regardless of firmware line" -- the headline finding of 2026-09-23 -- is now demonstrated by a working client rather than by matching response SUBs. Test and docstring claims downgraded from inference to confirmed where the hardware settled them, and left as synthesised-frame notes where it did not: the BEHAVIOUR is confirmed but raw BD bytes are still not in the repo. Added --capture DIR to mm_client_check: writes a raw_bw_*/raw_s3_* pair in the layout scratch/mm_frame_parse.py already reads, so a run on an unfamiliar unit becomes a test fixture without setting up a relay. Verified by round-tripping its own output through that parser: 28 frames, 0 bad checksums. Still not covered: a download from a BD unit (UM20147 had no events stored, so the chunk walk remains CB-only), raw BD fixture bytes, a monitoring unit, a nearly-full buffer, and the inbound call-home session. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL |
||
|
|
f09b7dcaaf |
feat(micromate): client layer -- connect, state, setups; read-only
Step 3 of docs/micromate_client_spec.md: micromate/client.py, two models in micromate/models.py, 26 offline tests. Every response constant in the tests is a real captured data section from UM12947. Field offsets were measured rather than taken from the spec, which turned up one general rule and one trap: THE RESPONSE SHAPE. Every response carries an 11-byte prefix and content starts at data[11]. One rule, every command. THE TRAP: data[0] is the content length & 0xFF, with no high byte anywhere in the prefix. It is therefore correct for every response under 256 bytes -- most of them -- and then reports 44 for a 2,092-byte setup block, 30 for a 286-byte monitor-log record, and 0 for a 1,024-byte download chunk. 182 of 251 captured responses agree with a naive read; the 69 that disagree are exactly the ones >= 256 bytes. That is the third length in this protocol read too narrow, after payload[9]-vs-payload[8:10] in the probe response. The client takes content as data[11:] and lets the frame's own length bound it -- nothing needs the declared length, since the frame already knows how long it is. Also measured: - POLL content[3] is 0x50, printable as "P", immediately before "Instantel". A generic printable-run scan therefore returns "PInstantel" -- it caught a test, not a unit. Vendor comes from a fixed offset; the model is found by searching for "MM/", which is structural rather than positional and so survives the Thor line's shorter "MM/ISEE/S". - The setup walk terminates on an EMPTY NAME, not an error: 23 responses, 22 names, factory.MMB first through TEST1.mmb last. - The 0x1C clock has an unidentified byte at content[6]; the hour is at content[7]. The protocol reference's 0x1C section already had this right and names the byte -- its one-line summary in the divergences list reads as six contiguous fields and is the version not to trust. Re-verified against three captures: 19:12:25, 19:13:34 and 01:14:05 against filenames stamped 19:12:14, 19:12:14 and 01:14:03. - Battery and memory are read FORWARD from content start, never backward from the end. This block is 4 bytes longer on the Thor line; the Series III from-the-end offsets give a 11.0BD unit 577.92 V. A test appends the four trailing bytes and asserts the forward offsets survive. connect() is narrower than the spec asked. The spec said to mirror Thor's POLL -> SERIAL -> 0x49 -> POLL "because it is known-good"; measurement showed that is Thor's connection check (3 of 8 sessions) and its fourth frame repeats its first. So connect() sends the three reads that gather something, and 0x01 is not read at all -- Thor never reads it, its layout is unmapped, and firmware_line comes free from any response's flags byte. If a unit ever refuses the next command after a cold connect, put the fourth POLL back and record it. A dead clock battery yields device_time=None rather than failing the whole state read; an unreadable active setup yields active_setup=None rather than failing connect. Both are real device states. Still verified only against 11.0CB and only over USB. The BD offsets follow from the extra bytes being trailing, which is documented but not something this code has seen. Full suite unchanged at 16 pre-existing failures; 445 passed, up 26. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL |