fix: ensure proper waveform context by always calling 0A before 0C and use all-zero params for event advancement
This commit is contained in:
35
CLAUDE.md
35
CLAUDE.md
@@ -137,14 +137,23 @@ then sends the termination frame.
|
||||
|
||||
### SUB 1E / 1F — event iteration null sentinel and token position (FIXED, do not re-introduce)
|
||||
|
||||
**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`).
|
||||
**token_params bug (FIXED):** 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.
|
||||
|
||||
**all-zero params required (empirically confirmed):** Even with the correct token position,
|
||||
sending `token=0xFE` causes the device to return null from 1F in multi-event sessions.
|
||||
All callers (`count_events`, `get_events`) must use `advance_event(browse=True)` which
|
||||
sends all-zero params. The 3-31-26 capture that "confirmed" token=0xFE had only one event
|
||||
stored — 1F always returns null at end-of-events, so we never actually observed 1F
|
||||
successfully returning a second key with token=0xFE. Empirical evidence from live device
|
||||
testing with 2+ events is definitive: **always use all-zero params for 1F.**
|
||||
|
||||
**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.
|
||||
when a preceding `read_waveform_header()` (0A) call has established device waveform
|
||||
context for the current key. Call 0A before every event in the loop, not just the first.
|
||||
Calling 1F cold (after only 1E, with no 0A) 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:
|
||||
@@ -162,15 +171,19 @@ echo) — in both cases, all zeros means "no more events."
|
||||
= 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):**
|
||||
**Correct iteration pattern (confirmed empirically with live device, 2+ events):**
|
||||
```
|
||||
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
|
||||
1E(all zeros) → key0, trailing0 ← trailing0 non-zero if event 1 exists
|
||||
0A(key0) ← REQUIRED: establishes device context
|
||||
0C(key0) [+ 5A(key0) for get_events] ← read record data
|
||||
1F(all zeros / browse=True) → key1 ← use all-zero params, NOT token=0xFE
|
||||
0A(key1) ← REQUIRED before each advance
|
||||
0C(key1) [+ 5A(key1) for get_events]
|
||||
1F(all zeros) → null ← done
|
||||
```
|
||||
|
||||
`advance_event(browse=True)` sends all-zero params; `advance_event()` default (browse=False)
|
||||
sends token=0xFE and is NOT used by any caller.
|
||||
`advance_event()` returns `(key4, event_data8)`.
|
||||
Callers (`count_events`, `get_events`) loop while `data8[4:8] != b"\x00\x00\x00\x00"`.
|
||||
|
||||
|
||||
@@ -274,34 +274,31 @@ class MiniMateClient:
|
||||
return []
|
||||
|
||||
events: list[Event] = []
|
||||
idx = 0
|
||||
is_first = True
|
||||
idx = 0
|
||||
|
||||
while data8[4:8] != b"\x00\x00\x00\x00":
|
||||
log.info("get_events: record %d key=%s", idx, key4.hex())
|
||||
ev = Event(index=idx)
|
||||
ev._waveform_key = key4 # stored so download_waveform() can re-use it
|
||||
|
||||
# First event: call 0A to verify it's a full record (0x30 length).
|
||||
# Subsequent keys come from 1F(0xFE) which guarantees full records,
|
||||
# so we skip 0A for those — exactly matching Blastware behaviour.
|
||||
# Always call 0A before 0C to establish device waveform context.
|
||||
# The device requires 0A context for both 0C and the subsequent 1F.
|
||||
# (Earlier code skipped 0A for events after the first — confirmed wrong.)
|
||||
proceed = True
|
||||
if is_first:
|
||||
try:
|
||||
_hdr, rec_len = proto.read_waveform_header(key4)
|
||||
if rec_len < 0x30:
|
||||
log.warning(
|
||||
"get_events: first key=%s is partial (len=0x%02X) — skipping",
|
||||
key4.hex(), rec_len,
|
||||
)
|
||||
proceed = False
|
||||
except ProtocolError as exc:
|
||||
try:
|
||||
_hdr, rec_len = proto.read_waveform_header(key4)
|
||||
if rec_len < 0x30:
|
||||
log.warning(
|
||||
"get_events: 0A failed for key=%s: %s — skipping 0C",
|
||||
key4.hex(), exc,
|
||||
"get_events: key=%s is partial (len=0x%02X) — skipping",
|
||||
key4.hex(), rec_len,
|
||||
)
|
||||
proceed = False
|
||||
is_first = False
|
||||
except ProtocolError as exc:
|
||||
log.warning(
|
||||
"get_events: 0A failed for key=%s: %s — skipping 0C",
|
||||
key4.hex(), exc,
|
||||
)
|
||||
proceed = False
|
||||
|
||||
if proceed:
|
||||
# SUB 0C — full waveform record (peak values, timestamp, "Project:" string)
|
||||
@@ -357,9 +354,18 @@ class MiniMateClient:
|
||||
events.append(ev)
|
||||
idx += 1
|
||||
|
||||
# SUB 1F — advance to the next full waveform record key
|
||||
# SUB 1F — advance to the next record key.
|
||||
# Uses browse mode (all-zero params) — empirically confirmed to work on
|
||||
# this device. token=0xFE (download mode) returns null regardless of
|
||||
# context, even after 0A+0C+5A. The 3-31-26 capture only had one event
|
||||
# so we never saw 1F actually return a second key in download mode;
|
||||
# the token=0xFE assumption was never validated for multi-event iteration.
|
||||
try:
|
||||
key4, data8 = proto.advance_event()
|
||||
key4, data8 = proto.advance_event(browse=True)
|
||||
log.info(
|
||||
"get_events: 1F → key=%s trailing=%s",
|
||||
key4.hex(), data8[4:8].hex(),
|
||||
)
|
||||
except ProtocolError as exc:
|
||||
log.warning("get_events: 1F failed: %s — stopping iteration", exc)
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user