docs: update CHANGELOG and CLAUDE.md for v0.9.0

CHANGELOG.md:
- New v0.9.0 section covering erase-all protocol, browse helpers,
  delete_all_events(), ach_mitm.py, and ACH server overhaul
- Back-filled v0.8.0 section (write pipeline, monitoring, ACH server)
  that was missing from the previous release notes

CLAUDE.md:
- Bump version to v0.9.0
- Add erase-all protocol section with full wire sequence, SUB 0x06
  storage range response layout, and post-erase key counter reset notes
- Document ACH server state format (ach_state.json v0.9.0 schema with
  downloaded_keys + max_downloaded_key)
- Add RV55 DCD/DTR issue to What's next

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 01:15:11 -04:00
parent 8a1bd34551
commit 77d9c17680
2 changed files with 182 additions and 5 deletions
+101
View File
@@ -4,6 +4,107 @@ All notable changes to seismo-relay are documented here.
--- ---
## v0.9.0 — 2026-04-11
### Added
- **`MiniMateClient.list_event_keys()`** — fast browse-mode walk (1E → 0A → 1F, no waveform
download) that returns the list of event key hex strings currently stored on the device.
Used by the ACH server as a cheap pre-check before deciding whether to call `get_events()`.
- **`get_events(skip_waveform_for_keys=set(...))`** — new optional parameter. For any key in
the set the function performs only 0A + 1F(browse) instead of the full
1E-arm → 0C → POLL×3 → 5A sequence. Eliminates redundant waveform downloads on repeat
call-homes when the device still holds previously downloaded events.
- **`MiniMateClient.delete_all_events()`** — erases all events from device memory using the
confirmed 4-step sequence:
- SUB 0xA3 `begin_erase_all` — initiate erase (token=0xFE) → ack 0x5C
- SUB 0x1C `read_monitor_status` — intermediate status read (Blastware-required)
- SUB 0x06 `read_event_storage_range` — verify storage state (token=0xFE) → 36-byte response
- SUB 0xA2 `confirm_erase_all` — commit erase (token=0xFE) → ack 0x5D
All four steps confirmed from 4-11-26 MITM capture of a live Blastware ACH session.
After a successful call, the device's event counter resets to `0x01110000`.
- **`MiniMateProtocol` erase methods**: `begin_erase_all()`, `confirm_erase_all()`,
`read_event_storage_range()` added to `protocol.py` with documented SUB constants
`SUB_ERASE_ALL_BEGIN = 0xA3` and `SUB_ERASE_ALL_CONFIRM = 0xA2`.
- **`bridges/ach_mitm.py`** — transparent TCP-to-TCP MITM proxy. Listens for inbound unit
connections, connects upstream to a real Blastware ACH server, and saves both directions
to `raw_bw_<ts>.bin` / `raw_s3_<ts>.bin` files matching the existing capture format.
Used to capture the 4-11-26 Blastware ACH session including event deletion.
Usage: `python bridges/ach_mitm.py --bw-host 127.0.0.1 --bw-port 9999 --listen-port 9998`
- **ACH server: key-based state tracking** — `ach_state.json` now stores
`downloaded_keys: [hex_strings]` and `max_downloaded_key: hex_string` per unit instead of
`event_count: N`. This correctly handles the standard workflow where events are deleted
from the device after upload — a count-based approach would see `count=0` on the next
call-home and silently skip new events.
- **ACH server: `--clear-after-download` flag** — after a successful download (at least one
new event saved), erases all events from the device using `delete_all_events()`. Mirrors
the standard Blastware ACH workflow. On success, `downloaded_keys` and
`max_downloaded_key` are reset to empty so the next session starts fresh.
- **ACH server: post-erase key-reuse detection** — after an external erase (Blastware or
manual), device keys restart from `0x01110000`, colliding with previously downloaded keys.
On each browse walk, if `max(device_keys) < max_downloaded_key` (device counter rolled
back), all device keys are treated as new regardless of `seen_keys`. This also catches
erases performed by Blastware between our sessions.
### Protocol / Documentation
- **SUB 0xA3 / SUB 0xA2 — erase-all sequence confirmed** (✅ 4-11-26 MITM capture):
Both frames use `token=0xFE` at `params[7]` and are standard `build_bw_frame` requests
(not write-format). Response SUBs follow the standard formula: 0x5C and 0x5D.
The intermediate 0x1C + 0x06 reads between them are required by Blastware.
- **SUB 0x06 — event storage range read confirmed** (✅ 4-11-26 MITM capture):
Two-step read, data offset = 0x24 (36 bytes). The last 8 bytes of the response contain
the first and last stored event keys (4 bytes each). After a successful erase, both keys
read as `01110000` (device-empty state).
- **Event key counter resets to `0x01110000` after erase** — confirmed by observing key
`01110000` on the device immediately after the MITM erase session.
---
## v0.8.0 — 2026-04-07
### Added
- **Write pipeline end-to-end** — `push_config_raw(event_index_data, compliance_data,
trigger_data, waveform_data)` on `MiniMateClient` orchestrates the full
`68→73 | 71×3→72 | 82→83 | 69→74→72` write sequence.
- **`build_bw_write_frame(sub, data, *, offset, params)`** in `framing.py` — dedicated frame
builder for write commands (SUBs 0x680x83). Doubles only the BW_CMD byte; all other
bytes including offset, params, data, and checksum are written raw. Uses the large-frame
DLE-aware checksum (`sum(b for b in payload[2:] if b != 0x10) + 0x10) & 0xFF`).
- **`MiniMateProtocol` write methods** — `write_event_index()`, `write_compliance()`,
`write_trigger_config()`, `write_waveform_data()`, `write_confirm()`,
`start_monitoring()`, `stop_monitoring()`.
- **`AchSession` inbound server** (`bridges/ach_server.py`) — accepts call-home TCP
connections, runs the full handshake + device-info + event-download sequence, saves
`device_info.json` + `events.json` per session.
### Protocol / Documentation
- **Write frame format confirmed** (✅ 3-11-26 BW TX capture, all 11 frames): only BW_CMD
byte `0x10` is doubled; all other bytes sent raw. Standard `build_bw_frame` DLE-stuffing
is incorrect for write commands.
- **Write ack responses** confirmed as 17-byte zero-data S3 frames.
- **Monitoring SUBs 0x96/0x97** confirmed from 4-8-26 capture.
- **SESSION_RESET signal** (`41 03`) required before POLL for monitoring units.
- **SUB 0x1C monitoring flag** at `section[1]`: `0x00` = idle, `0x10` = monitoring.
Confirmed by byte-diff of all 144 data frames in 4-8-26/2ndtry capture.
---
## v0.7.0 — 2026-04-03 ## v0.7.0 — 2026-04-03
### Added ### Added
+81 -5
View File
@@ -2,7 +2,7 @@
Ground-up Python replacement for **Blastware**, Instantel's Windows-only software for Ground-up Python replacement for **Blastware**, Instantel's Windows-only software for
managing MiniMate Plus seismographs. Connects over direct RS-232 or cellular modem managing MiniMate Plus seismographs. Connects over direct RS-232 or cellular modem
(Sierra Wireless RV50 / RV55). Current version: **v0.8.0**. (Sierra Wireless RV50 / RV55). Current version: **v0.9.0**.
--- ---
@@ -25,9 +25,9 @@ CHANGELOG.md ← version history
--- ---
## Current implementation state (v0.8.0) ## Current implementation state (v0.9.0)
Full read pipeline + write pipeline working end-to-end over TCP/cellular: Full read pipeline + write pipeline + erase pipeline working end-to-end over TCP/cellular:
| Step | SUB | Status | | Step | SUB | Status |
|---|---|---| |---|---|---|
@@ -41,12 +41,15 @@ Full read pipeline + write pipeline working end-to-end over TCP/cellular:
| Waveform record (peaks, timestamp, project) | 0C | ✅ | | Waveform record (peaks, timestamp, project) | 0C | ✅ |
| **Bulk waveform stream (event-time metadata)** | **5A** | ✅ new v0.6.0 | | **Bulk waveform stream (event-time metadata)** | **5A** | ✅ new v0.6.0 |
| Event advance / next key | 1F | ✅ | | Event advance / next key | 1F | ✅ |
| **Write commands (push config to device)** | **6883** | ✅ **new v0.8.0** | | **Write commands (push config to device)** | **6883** | ✅ new v0.8.0 |
| **Erase all events** | **0xA3 → 0x1C → 0x06 → 0xA2** | ✅ **new v0.9.0** |
`get_events()` sequence per event: `1E → 0A → 0C → 5A → 1F` `get_events()` sequence per event: `1E → 0A → 0C → 5A → 1F`
`push_config_raw()` write sequence: `68→73 | 71×3→72 | 82→83 | 69→74→72` `push_config_raw()` write sequence: `68→73 | 71×3→72 | 82→83 | 69→74→72`
`delete_all_events()` erase sequence: `0xA3 → 0x1C → 0x06 → 0xA2`
--- ---
## Protocol fundamentals ## Protocol fundamentals
@@ -720,9 +723,82 @@ Full compliance config encoder is a future task.
--- ---
---
## Erase-all protocol (SUBs 0xA3/0xA2/0x06) — confirmed 2026-04-11
Full sequence confirmed from 4-11-26 MITM capture of a live Blastware ACH session
(`bridges/captures/mitm/ach_mitm_20260411_001912/`).
### Wire sequence
```
BW → device: SUB 0xA3 params=00 00 00 00 00 00 00 FE 00 00 (begin erase)
device → BW: SUB 0x5C (ack)
BW → device: SUB 0x1C probe (offset=0x00)
device → BW: SUB 0xE3 (probe ack)
BW → device: SUB 0x1C data (offset=0x2C)
device → BW: SUB 0xE3 (monitor status response)
BW → device: SUB 0x06 probe (offset=0x00, params same)
device → BW: SUB 0xF9 (probe ack)
BW → device: SUB 0x06 data (offset=0x24)
device → BW: SUB 0xF9 (36-byte storage range response)
BW → device: SUB 0xA2 params=00 00 00 00 00 00 00 FE 00 00 (confirm erase)
device → BW: SUB 0x5D (ack — device memory is now cleared)
```
All frames use standard `build_bw_frame` (not write-format). Response SUBs follow the
standard `0xFF - SUB` formula; no exceptions.
### SUB 0x06 — event storage range response (36 bytes)
The 36-byte response body ends with two 4-byte event keys:
| Offset (from end) | Field | Notes |
|---|---|---|
| `[-8:-4]` | first stored event key | `01110000` when empty |
| `[-4:]` | last stored event key | `01110000` when empty |
Before erase: ends with `<first_key> <last_key>` (e.g. `0111ea60 0111eaa6`).
After erase: both bytes read `01110000` — device's empty/reset sentinel.
### Post-erase key counter reset
After a successful erase, the device resets its event counter. New events start from
key `0x01110000` again — the same key as the very first event ever recorded. This means
key-based deduplication in the ACH server must account for key reuse:
- After our own erase: `ach_state.json` `downloaded_keys` and `max_downloaded_key` are
cleared so the next session starts fresh.
- After an external erase: the ACH server detects it by comparing `max(device_keys)` to
`max_downloaded_key` from state. If the device max has rolled back below the historical
max, all current device keys are treated as new regardless of `seen_keys`.
### ACH server state format (v0.9.0)
`bridges/captures/ach_state.json`:
```json
{
"BE11529": {
"downloaded_keys": ["01110000", "0111245a"],
"max_downloaded_key": "0111245a",
"last_seen": "2026-04-11T01:04:36",
"serial": "BE11529",
"peer": "63.43.212.232:51920"
}
}
```
`max_downloaded_key` is the high-water mark — the largest key ever downloaded from the
unit. It is NOT reset when events are erased from the device (only when our server does
the erase). Used for post-erase detection.
---
## What's next ## What's next
- Compliance config encoder — build raw write payloads from a `ComplianceConfig` object - Compliance config encoder — build raw write payloads from a `ComplianceConfig` object
- Locate "Sensor Check" byte in compliance config (need capture with Disabled vs Before-monitoring) - Locate "Sensor Check" byte in compliance config (need capture with Disabled vs Before-monitoring)
- ACH inbound server — accept call-home connections from field units
- Modem manager — push RV50/RV55 configs via Sierra Wireless API - Modem manager — push RV50/RV55 configs via Sierra Wireless API
- RV55 DCD/DTR issue — newer RV55 firmware doesn't assert DCD by default; units don't
resume monitoring after call-home disconnect (`--restart-monitoring` flag deferred)