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
This commit is contained in:
@@ -231,29 +231,40 @@ def test_escaped_bytes_land_in_the_right_field():
|
||||
assert f.checksum_valid
|
||||
|
||||
|
||||
# The real 11.0BD POLL data section, UM20147 over USB 2026-09-30. This
|
||||
# replaces a synthesised frame -- the Thor firmware line was inference-only
|
||||
# until this capture.
|
||||
_POLL_BD_DATA = bytes.fromhex(
|
||||
"30000000000000000000000000005649"
|
||||
"6e7374616e74656c000600c3f04a00e4"
|
||||
"194f0052024d4d2f495345452f530058"
|
||||
"f406001f60c0755760c075"
|
||||
)
|
||||
|
||||
|
||||
def test_thor_firmware_line_survives_destuffing():
|
||||
"""⚠ flags = 0x03 is ETX, so it arrives as `10 03`.
|
||||
|
||||
A parser that does not destuff ends the frame at byte 2 on half the fleet.
|
||||
|
||||
SYNTHESISED frame, but the BEHAVIOUR IS CONFIRMED on real hardware:
|
||||
UM20147 (11.0BD) was read over USB on 2026-09-30 and reported
|
||||
firmware_line="thor", which is only reachable if `10 03` in the flags
|
||||
position destuffed correctly. Raw BD bytes are still not in the repo, so
|
||||
this frame stays synthesised -- built by re-stuffing the captured POLL
|
||||
probe reply with flags flipped to 0x03, so the only difference from a real
|
||||
frame is the one byte under test.
|
||||
The data section is REAL (UM20147, 11.0BD); the frame wrapper is rebuilt,
|
||||
which is sound because the framing is verified 251/251 elsewhere in this
|
||||
file. scratch/mm_frame_parse.py reported payload=64 for this frame, and
|
||||
the assertion below pins that, so the reconstruction is checked rather than
|
||||
assumed.
|
||||
"""
|
||||
real = unstuff(RSP_POLL_PROBE[1:-1])[:-1]
|
||||
payload = bytes([real[0], FLAGS_THOR]) + real[2:]
|
||||
synth = bytes([STX]) + stuff(payload + bytes([checksum(payload)])) + bytes([ETX])
|
||||
payload = bytes([0x00, FLAGS_THOR, 0xA4, 0x00, 0x00]) + _POLL_BD_DATA
|
||||
assert len(payload) == 64, "the parser reported payload=64 for this frame"
|
||||
wire = bytes([STX]) + stuff(payload + bytes([checksum(payload)])) + bytes([ETX])
|
||||
|
||||
assert bytes([DLE, ETX]) in synth, "flags must be escaped on the wire"
|
||||
(f,) = MicromateFrameParser().feed(synth)
|
||||
assert bytes([DLE, ETX]) in wire, "flags 0x03 must be escaped on the wire"
|
||||
(f,) = MicromateFrameParser().feed(wire)
|
||||
assert f.flags == FLAGS_THOR
|
||||
assert f.firmware_line == "thor"
|
||||
assert f.sub == 0xA4
|
||||
assert f.request_sub == 0x5B
|
||||
assert f.checksum_valid
|
||||
assert b"MM/ISEE/S\x00" in f.data, "the shorter BD model string"
|
||||
|
||||
|
||||
def test_an_escaped_checksum_byte_is_read_correctly():
|
||||
|
||||
Reference in New Issue
Block a user