fix: fix token position in params and enhance event iteration logic for MiniMateClient

This commit is contained in:
Brian Harrison
2026-04-03 17:30:47 -04:00
parent 3effa1aab5
commit 4fb1bbfe35
3 changed files with 52 additions and 16 deletions

View File

@@ -135,21 +135,40 @@ the setup at record time, not the current device config — this is why we fetch
`stop_after_metadata=True` (default) stops the 5A loop as soon as `b"Project:"` appears,
then sends the termination frame.
### SUB 1E / 1F — event iteration null sentinel (FIXED, do not re-introduce)
### SUB 1E / 1F — event iteration null sentinel and token position (FIXED, do not re-introduce)
**The null sentinel for end-of-events is `event_data8[4:8] == b"\x00\x00\x00\x00"`, NOT
`key4 == b"\x00\x00\x00\x00"`.**
**token_params bug:** The token byte was at `params[6]` (wrong). Both 3-31-26 and 4-3-26
BW TX captures confirm it belongs at **`params[7]`** (raw: `00 00 00 00 00 00 00 fe 00 00`).
With the wrong position the device ignores the token and 1F returns null immediately.
Event 0's waveform key is `00000000` — all-zero key4 is a valid event address.
Checking `key4 == b"\x00\x00\x00\x00"` exits the loop immediately after the 1E call,
seeing event 0's key and incorrectly treating it as "no events."
Confirmed from the 4-3-26 two-event capture (`bridges/captures/4-3-26-multi_event/`):
**0A context requirement:** `advance_event()` (1F) only returns a valid next-event key
when a preceding `read_waveform_header()` (0A) or `read_waveform_record()` (0C) call has
established device waveform context for the current key. Calling 1F cold (after only 1E,
with no 0A/0C) returns the null sentinel regardless of how many events are stored.
**1F response layout:** The next event's key IS at `data_rsp.data[11:15]` (= payload[16:20]).
Confirmed from 4-3-26 browse-mode S3 captures:
```
1E response (event 0): key4=00000000 data8=0000000000011100 ← valid, trailing bytes non-zero
1F response (event 1): key4=0000fe00 data8=0000fe0000011100 ← valid
1F null sentinel: key4=0000fe00 data8=0000fe0000000000 ← done, trailing 4 bytes = 00
1F after 0A(key0=01110000): data[11:15]=0111245a data[15:19]=00001e36 ← valid
1F after 0A(key1=0111245a): data[11:15]=01114290 data[15:19]=00000046 ← valid
1F null sentinel: data[11:15]=00000000 data[15:19]=00000000 ← done
```
**Null sentinel:** `data8[4:8] == b"\x00\x00\x00\x00"` (= `data_rsp.data[15:19]`)
works for BOTH 1E trailing (offset to next event key) and 1F response (null key
echo) — in both cases, all zeros means "no more events."
**1E response layout:** `data_rsp.data[11:15]` = event 0's actual key; `data_rsp.data[15:19]`
= sample-count offset to the next event key (key1 = key0 + this offset). If offset == 0,
there is only one event.
**Correct iteration pattern (confirmed from 4-3-26 capture):**
```
1E(all zeros) → key0, trailing0 ← trailing0 non-zero if event 1 exists
0A(key0) ← establishes device context
1F(token=0xFE at params[7]) → key1, trailing1
0A(key1) ← establishes context for next advance
1F(token=0xFE) → null ← done
```
`advance_event()` returns `(key4, event_data8)`.

View File

@@ -201,9 +201,20 @@ class MiniMateClient:
log.info("count_events: 1E returned null sentinel — device is empty")
return 0
# Iterate via 0A → 1F. Each 0A call establishes waveform context so that
# 1F (EVENT_ADVANCE) returns the actual next-event key. Without the 0A
# call, 1F immediately returns the null sentinel regardless of how many
# events are stored. Confirmed from 4-3-26 two-event BW/S3 capture:
# browse-mode sequence is 0A(keyN) → 1F → 0A(keyN+1) → 1F → … → null.
count = 0
while data8[4:8] != b"\x00\x00\x00\x00":
count += 1
try:
# 0A establishes device context for this key before 1F advances.
proto.read_waveform_header(key4)
except ProtocolError as exc:
log.warning("count_events: 0A failed for key=%s: %s", key4.hex(), exc)
break
try:
key4, data8 = proto.advance_event()
log.warning(

View File

@@ -223,21 +223,27 @@ def token_params(token: int = 0) -> bytes:
Build the 10-byte params block that carries a single token byte.
Used for SUBs 1E (EVENT_HEADER) and 1F (EVENT_ADVANCE).
The token goes at params[6], which maps to payload[12].
The token goes at params[7], which maps to payload[13].
Confirmed from BOTH 3-31-26 and 4-3-26 BW TX captures:
raw params bytes: 00 00 00 00 00 00 00 fe 00 00
token is at index 7 (not 6 — that was wrong).
Confirmed from 3-31-26 capture:
- token=0x00: first-event read / browse mode (no download marking)
- token=0xfe: download mode (causes 1F to skip partial bins and
advance to the next full record)
The device echoes the token at data[8] of the S3 response (payload[13]),
distinct from the next-event key at data[11:15] (payload[16:20]).
Args:
token: single byte to place at params[6] / payload[12].
token: single byte to place at params[7] / payload[13].
Returns:
10-byte params block with token at position [6].
10-byte params block with token at position [7].
"""
p = bytearray(10)
p[6] = token
p[7] = token
return bytes(p)