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:
+101
@@ -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 0x68–0x83). 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
|
||||||
|
|||||||
@@ -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)** | **68–83** | ✅ **new v0.8.0** |
|
| **Write commands (push config to device)** | **68–83** | ✅ 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)
|
||||||
|
|||||||
Reference in New Issue
Block a user