fix(protocol): update chunk counter formula to use max(key4[2:4], 0x0400) for accurate data streaming
This commit is contained in:
@@ -118,28 +118,29 @@ S3→BW (response):
|
|||||||
Both differences confirmed by reproducing Blastware's exact wire bytes from the 1-2-26
|
Both differences confirmed by reproducing Blastware's exact wire bytes from the 1-2-26
|
||||||
BW TX capture. All 10 frames verified.
|
BW TX capture. All 10 frames verified.
|
||||||
|
|
||||||
### SUB 5A — chunk counter formula (FINAL CORRECTION 2026-04-24)
|
### SUB 5A — chunk counter formula (FINAL CORRECTION 2026-04-26)
|
||||||
|
|
||||||
**Chunk counter = `key4[2:4] + (chunk_num - 1) * 0x0400` for ALL chunks.**
|
**Chunk counter = `max(key4[2:4], 0x0400) + (chunk_num - 1) * 0x0400` for ALL chunks.**
|
||||||
|
|
||||||
where `key4[2:4] = (key4[2] << 8) | key4[3]` is the event's circular-buffer base offset.
|
where `key4[2:4] = (key4[2] << 8) | key4[3]` is the event's circular-buffer base offset.
|
||||||
|
|
||||||
The 4-2-26 BW TX capture showed `counter=0x1004` for chunk 1 of event key `01110000`, which
|
The `max(..., 0x0400)` guard is critical for events at the start of the circular buffer
|
||||||
led to `_CHUNK1_COUNTER = 0x1004` being hardcoded as a special case. This was a Blastware
|
(key4[2:4] == 0x0000, e.g. key `01110000`). Without it, chunk 1 gets counter=0x0000, which
|
||||||
artifact, not a protocol requirement. Empirical test 2026-04-06: with `counter=0x1004` for
|
is the same address as the probe frame — the device re-returns the STRT record data instead
|
||||||
chunk 1 the device times out (120 s); with `counter=0x0400` (= `1 * 0x0400`) it responds
|
of waveform payload. With the guard, chunk 1 gets counter=0x0400, which is confirmed correct
|
||||||
immediately and streams all frames correctly.
|
from the empirical live-device test 2026-04-06 (`counter=0x0400 → responds immediately and
|
||||||
|
streams all frames correctly`).
|
||||||
|
|
||||||
The 4-3-26 capture confirms the pattern for a second event (key `0111245a`):
|
The 4-3-26 capture confirms the pattern for a second event (key `0111245a`, key4[2:4]=0x245a):
|
||||||
chunk 1 = `0x245A`, chunk 2 = `0x285A`, chunk 3 = `0x2C5A` (each +0x0400). Blastware's
|
chunk 1 = `0x245A`, chunk 2 = `0x285A`, chunk 3 = `0x2C5A` (each +0x0400).
|
||||||
true formula is `key4[2:4] + (chunk_num - 1) * 0x0400`.
|
`max(0x245a, 0x0400) = 0x245a` → formula works correctly for non-zero base offset too.
|
||||||
|
|
||||||
**2026-04-24 CORRECTION — `n * 0x0400` is WRONG for non-first events.** For event key
|
**History:**
|
||||||
`01110000`, `key4[2:4] == 0x0000` so the old `chunk_num * 0x0400` formula was accidentally
|
- Original: `_CHUNK1_COUNTER = 0x1004` hardcoded (Blastware capture artifact — WRONG).
|
||||||
correct. For keys with `key4[2:4] != 0` (e.g. key `01111884`, offset `0x1884`), the old
|
- 2026-04-06: Corrected to `chunk_num * 0x0400` (worked for key 01110000 only).
|
||||||
formula sends counters pointing into the wrong buffer region — the device returns data from
|
- 2026-04-24: Corrected to `key4[2:4] + (chunk_num-1) * 0x0400` (fixed non-zero offsets,
|
||||||
a completely different stored event and `b"Project:"` never appears in the stream.
|
but accidentally broke key 01110000 — counter=0x0000 sends probe address again).
|
||||||
Use `key4[2:4] + (chunk_num - 1) * 0x0400` exclusively.
|
- 2026-04-26: Final formula: `max(key4[2:4], 0x0400) + (chunk_num-1) * 0x0400`.
|
||||||
|
|
||||||
### SUB 5A — params are 11 bytes for chunk frames, 10 for termination
|
### SUB 5A — params are 11 bytes for chunk frames, 10 for termination
|
||||||
|
|
||||||
|
|||||||
@@ -1231,23 +1231,31 @@ Two critical differences from `build_bw_frame`:
|
|||||||
| Frame | offset_word | counter | params | Purpose |
|
| Frame | offset_word | counter | params | Purpose |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Probe | `0x1004` | `0x0000` | 10 bytes (`bulk_waveform_params(0)`) | Initiate transfer |
|
| Probe | `0x1004` | `0x0000` | 10 bytes (`bulk_waveform_params(0)`) | Initiate transfer |
|
||||||
| Chunk 1 | `0x1004` | `key4[2:4]` | 11 bytes | First data chunk |
|
| Chunk 1 | `0x1004` | `max(key4[2:4], 0x0400)` | 11 bytes | First data chunk |
|
||||||
| Chunk 2 | `0x1004` | `key4[2:4] + 0x0400` | 11 bytes | Second chunk |
|
| Chunk 2 | `0x1004` | `max(key4[2:4], 0x0400) + 0x0400` | 11 bytes | Second chunk |
|
||||||
| Chunk N | `0x1004` | `key4[2:4] + (N-1) * 0x0400` | 11 bytes | Nth chunk |
|
| Chunk N | `0x1004` | `max(key4[2:4], 0x0400) + (N-1) * 0x0400` | 11 bytes | Nth chunk |
|
||||||
| … | … | … | … | … |
|
| … | … | … | … | … |
|
||||||
| Termination | `0x005A` | `key4[2:4] + N * 0x0400` | 10 bytes | End transfer |
|
| Termination | `0x005A` | `max(key4[2:4], 0x0400) + N * 0x0400` | 10 bytes | End transfer |
|
||||||
|
|
||||||
> ⚠️ **2026-04-06 CORRECTED — chunk counter is `key4[2:4] + (N-1) * 0x0400`.**
|
> ⚠️ **2026-04-06 CORRECTED — chunk counter is `key4[2:4] + (N-1) * 0x0400`.**
|
||||||
> The 4-2-26 BW TX capture showed counter=0x1004 for chunk 1 of key `01110000`, leading to
|
> The 4-2-26 BW TX capture showed counter=0x1004 for chunk 1 of key `01110000`, leading to
|
||||||
> an interim "monotonic n * 0x0400" formula. This was accidentally correct because
|
> an interim "monotonic n * 0x0400" formula. This was accidentally correct because
|
||||||
> `key4[2:4] == 0x0000` for that event.
|
> `key4[2:4] == 0x0000` for that event.
|
||||||
>
|
>
|
||||||
> **2026-04-24 FINAL CORRECTION:** The counter is an absolute circular-buffer address.
|
> **2026-04-24 CORRECTION:** The counter is an absolute circular-buffer address.
|
||||||
> BW's true formula is `key4[2:4] + (chunk_num - 1) * 0x0400` where `key4[2:4]` is the
|
> BW's true formula is `key4[2:4] + (chunk_num - 1) * 0x0400` where `key4[2:4]` is the
|
||||||
> event's storage base offset (`(key4[2]<<8) | key4[3]`). For keys where
|
> event's storage base offset (`(key4[2]<<8) | key4[3]`). For keys where
|
||||||
> `key4[2:4] != 0x0000` (e.g. key `01111884`), using `n * 0x0400` sends requests into the
|
> `key4[2:4] != 0x0000` (e.g. key `01111884`), using `n * 0x0400` sends requests into the
|
||||||
> wrong buffer region — the device returns data from a completely different event and
|
> wrong buffer region — the device returns data from a completely different event.
|
||||||
> `b"Project:"` never appears in the stream. Confirmed correct 2026-04-24.
|
>
|
||||||
|
> **2026-04-26 FINAL CORRECTION:** The formula `key4[2:4] + (N-1) * 0x0400` is wrong when
|
||||||
|
> `key4[2:4] == 0x0000` (e.g. event key `01110000`, the very first event after a device erase).
|
||||||
|
> Counter=0x0000 for chunk 1 is the same address as the probe frame — the device re-returns
|
||||||
|
> the STRT record data instead of waveform payload (frame 1 has len=1097, same as probe, and
|
||||||
|
> contains `b"STRT\xff\xfe"`, contributing zero waveform bytes).
|
||||||
|
> Final formula: `max(key4[2:4], 0x0400) + (chunk_num - 1) * 0x0400`.
|
||||||
|
> For key `01110000`: chunk 1 = 0x0400 (confirmed working, empirical test 2026-04-06).
|
||||||
|
> For key `0111245a`: chunk 1 = 0x245a (unchanged, confirmed from 4-3-26 capture).
|
||||||
|
|
||||||
The `stop_after_metadata=True` flag causes the loop to stop as soon as `b"Project:"` is
|
The `stop_after_metadata=True` flag causes the loop to stop as soon as `b"Project:"` is
|
||||||
found in the accumulated A5 frame data, typically after 4–9 chunks. A termination frame
|
found in the accumulated A5 frame data, typically after 4–9 chunks. A termination frame
|
||||||
|
|||||||
+24
-16
@@ -585,13 +585,12 @@ class MiniMateProtocol:
|
|||||||
frames_data: list[S3Frame] = []
|
frames_data: list[S3Frame] = []
|
||||||
counter = 0
|
counter = 0
|
||||||
|
|
||||||
# BW counter formula (confirmed from 4-3-26 capture for key 0111245a):
|
# BW counter formula (confirmed from 4-3-26 capture for key 0111245a,
|
||||||
# counter for chunk n = key4[2:4] + (n - 1) * 0x0400
|
# and empirical live-device test 2026-04-06 for key 01110000):
|
||||||
# key4[2:4] is the event's circular-buffer base offset — without it, chunk
|
# counter for chunk n = max(key4[2:4], 0x0400) + (n - 1) * 0x0400
|
||||||
# requests address the wrong region of the device buffer and the device
|
# key4[2:4] is the event's circular-buffer base offset. The max() guard
|
||||||
# streams data from the wrong event (no "Project:" in any response).
|
# ensures chunk 1 never uses counter=0x0000 (which equals the probe address
|
||||||
# PREVIOUSLY WRONG NOTE: "device does not validate counter; chunk_num*0x0400
|
# and causes the device to re-return STRT record data for the first chunk).
|
||||||
# is correct" — that was only true for key 01110000 where key4[2:4]==0x0000.
|
|
||||||
_key4_offset = (key4[2] << 8) | key4[3]
|
_key4_offset = (key4[2] << 8) | key4[3]
|
||||||
|
|
||||||
# ── Step 1: probe ────────────────────────────────────────────────────
|
# ── Step 1: probe ────────────────────────────────────────────────────
|
||||||
@@ -612,15 +611,24 @@ class MiniMateProtocol:
|
|||||||
log.debug("5A A5[0] page_key=0x%04X %d bytes", rsp.page_key, len(rsp.data))
|
log.debug("5A A5[0] page_key=0x%04X %d bytes", rsp.page_key, len(rsp.data))
|
||||||
|
|
||||||
# ── Step 2: chunk loop ───────────────────────────────────────────────
|
# ── Step 2: chunk loop ───────────────────────────────────────────────
|
||||||
# Correct counter formula: key4[2:4] + (chunk_num - 1) * 0x0400
|
# Counter formula: _chunk_base + (chunk_num - 1) * 0x0400
|
||||||
# This matches Blastware exactly (confirmed from 4-3-26 capture).
|
# where _chunk_base = max(key4[2:4], 0x0400).
|
||||||
# For events where key4[2:4]==0 (e.g. 01110000), this gives the same
|
#
|
||||||
# result as the old chunk_num*0x0400 formula shifted by one step, which
|
# For events with key4[2:4] != 0 (e.g. key 0111245a, offset 0x245a):
|
||||||
# the device also accepted — but for events with a non-zero base offset
|
# _chunk_base = 0x245a → chunk 1=0x245a, chunk 2=0x285a, ...
|
||||||
# (e.g. key 01111884 with key4[2:4]=0x1884) the old formula sends
|
# Confirmed from 4-3-26 capture.
|
||||||
# completely wrong counters and the device streams the wrong buffer region.
|
#
|
||||||
|
# For events with key4[2:4] == 0 (e.g. key 01110000):
|
||||||
|
# _chunk_base = max(0, 0x0400) = 0x0400
|
||||||
|
# → chunk 1=0x0400, chunk 2=0x0800, ... (= old chunk_num*0x0400)
|
||||||
|
# CRITICAL: counter=0x0000 (same as the probe) causes the device to
|
||||||
|
# re-return the STRT record data for chunk 1, making frame 1 look like
|
||||||
|
# a second probe response (confirmed from server log: frame 1 len=1097,
|
||||||
|
# contains STRT\xff\xfe, contributes zero body bytes after DLE-strip).
|
||||||
|
# counter=0x0400 for chunk 1 confirmed working (empirical test 2026-04-06).
|
||||||
|
_chunk_base = max(_key4_offset, _BULK_COUNTER_STEP)
|
||||||
for chunk_num in range(1, max_chunks + 1):
|
for chunk_num in range(1, max_chunks + 1):
|
||||||
counter = _key4_offset + (chunk_num - 1) * _BULK_COUNTER_STEP
|
counter = _chunk_base + (chunk_num - 1) * _BULK_COUNTER_STEP
|
||||||
params = bulk_waveform_params(key4, counter)
|
params = bulk_waveform_params(key4, counter)
|
||||||
log.debug("5A chunk %d counter=0x%04X", chunk_num, counter)
|
log.debug("5A chunk %d counter=0x%04X", chunk_num, counter)
|
||||||
self._send(build_5a_frame(_BULK_CHUNK_OFFSET, params))
|
self._send(build_5a_frame(_BULK_CHUNK_OFFSET, params))
|
||||||
@@ -675,7 +683,7 @@ class MiniMateProtocol:
|
|||||||
chunk_num, extra_chunks_after_metadata)
|
chunk_num, extra_chunks_after_metadata)
|
||||||
for _extra_n in range(extra_chunks_after_metadata):
|
for _extra_n in range(extra_chunks_after_metadata):
|
||||||
chunk_num += 1
|
chunk_num += 1
|
||||||
counter = _key4_offset + (chunk_num - 1) * _BULK_COUNTER_STEP
|
counter = _chunk_base + (chunk_num - 1) * _BULK_COUNTER_STEP
|
||||||
params = bulk_waveform_params(key4, counter)
|
params = bulk_waveform_params(key4, counter)
|
||||||
self._send(build_5a_frame(_BULK_CHUNK_OFFSET, params))
|
self._send(build_5a_frame(_BULK_CHUNK_OFFSET, params))
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user