Reviewed-on: #21 ## v0.17.0 — 2026-05-17 The "field rescue + DB management" release. Hardened against units that are stuck in a runaway call-home loop, and added an operator-facing path for purging bogus events that those same units dump into the DB before recovery. All work in this release was driven by the BE9558H incident (full incident log + recovery procedure at `docs/runbooks/wedged_unit_recovery.md`). ### Added — wedged-unit recovery toolkit A toolkit for breaking the call-home loop on a misbehaving unit whose firmware is too busy to keep up with normal request/response handshakes. Tested in production against BE9558H (16 May 2026) — a unit with a stuck-triggered Long-axis geophone that had been call-homing the office BW ACH server every 30 seconds for hours. Endpoints layered from "single attempt" to "siege mode" to suit different contention levels: - **`GET /device/events/storage_range`** — SUB 0x06 probe. POLL + one read; ~2s. Returns first/last event keys and an `is_empty` flag. Use to triage whether a unit has stored events without invoking the slow `count_events()` 1E/1F chain (which choked on BE9558H's corrupted event chain). - **`GET /device/events/index`** — SUB 0x08 probe. POLL + one read; ~2s. Returns the lifetime event counter (does NOT decrement on erase — use `storage_range` for "right now" state). - **`POST /device/events/erase`** — full erase sequence `0xA3 → 0x1C → 0x06 → 0xA2` (confirmed 2026-04-11, see the protocol reference). Resets event keys to `0x01110000`. Caller's responsibility to disable ACH first if the underlying trigger condition will re-fill the buffer. - **`POST /device/rescue`** — one TCP session, short connect+recv timeouts: POLL → disable ACH (compliance config write) → erase events → close. Designed for race-loop usage when the device is busy in another session. 503 on connect-refused, 502 on protocol failure, 200 on full sequence success. - **`POST /device/stop_monitoring_blind`** — fire-and-forget Stop Monitoring (SUB 0x97), TCP-only. Dumps `SESSION_RESET + POLL_PROBE + SESSION_RESET + POLL_DATA + 0x97 × repeat` and closes without reading any S3 response. The full POLL preamble is required — write commands without it are silently ignored by the device's protocol parser (false-positive surface area that bit the first version of this endpoint). Use when the device's firmware can't keep up with full request/response but might process inbound bytes at its own pace. - **`POST /device/stop_monitoring_spam`** — server-side hammer loop, duration-bounded. Open TCP → write the same blind payload → close → repeat as fast as possible until `duration_s` elapses. Configurable `connect_timeout` (default 500ms) and `repeat` (frames per session). Reports `sent_ok`, `connect_failed`, `write_failed`, `rate_attempts_per_s`. Clamped to 5min duration. - **`POST /device/stop_monitoring_slow_drip`** — opposite of spam. Open ONE TCP session, drip the wake handshake + stop frames at `interval_s` (default 3s) for `duration_s` (default 120s, max 10min). Each drip is ~23 bytes — well under any UART FIFO size. Opportunistically drains any inbound bytes the device sends back; `bytes_received > 0` in the response strongly suggests the device has started talking and the session is healthy. **This is the endpoint that saved BE9558H.** Spam mode had been overrunning the device's UART FIFO; slow drip stayed under it. - **Six rescue scripts** under `scripts/` — thin bash wrappers around the endpoints, default `SFM_BASE_URL=http://localhost:8200` (direct, not via Terra-View proxy whose 60s timeout would cut off the longer endpoints): - `rescue_device.sh` — race-loop wrapper for `/device/rescue` - `blind_stop.sh` — race-loop wrapper for `/device/stop_monitoring_blind` - `spam_stop.sh` — single-call burst hammer - `slow_drip.sh` — single-call held-session drip - `watch_unit.sh` — passive periodic reachability check (every N min, logs to file), useful for unattended overnight monitoring of a wedged unit - **`docs/runbooks/wedged_unit_recovery.md`** — symptoms, quick-reference recovery procedure, the modem-layer mechanism (Sierra Wireless serial-port mode-flipping is the real failure mode — not the device firmware), and a table of "why simpler approaches don't work" so the next incident skips the dead ends. ### Added — operator event DB management Endpoints powering Terra-View's new `/admin/events` page (v0.12.0). Designed for purging bogus events from a unit that's been forwarding them in bulk (e.g. a stuck-triggered seismograph dumping hundreds of junk events before it's recovered). - **`DELETE /db/events/{event_id}`** — hard-delete one event row. Also unlinks the associated blastware binary (`.AB0*`), `.a5.pkl`, `.sfm.json` sidecar, and `.h5` clean-waveform files via the WaveformStore. Returns the per-file removal status. 404 if the event doesn't exist. - **`POST /db/events/delete_bulk`** — filter-based or id-list-based bulk delete with safety rails: - Filters (`serial`, `from_dt`, `to_dt`, `false_trigger`) combine with AND; same semantics as `GET /db/events`. `ids` is an additional inclusion list. Refuses to run with no filters (would wipe the whole table — raises 422). - `confirm` must be `true` to actually delete. Otherwise returns a dry-run summary (`status: "dry_run"`, `matched: N`, `sample_serials: [...]`). - `max_rows` (default 10,000) caps how many rows can be deleted by-filter in one call. If exceeded, returns `status: "too_many"` with a hint to narrow or raise the cap. Bypassed when only `ids` is supplied. - **`_cleanup_event_files(row)`** helper in `sfm/server.py` — best-effort `unlink()` of all four sidecar paths derived from the row's `blastware_filename`. Logged at WARN if a path exists but unlink fails; the DB row deletion still proceeds. - **`SeismoDb.delete_event(id)` and `SeismoDb.delete_events_bulk(...)`** in `sfm/database.py` — both return the deleted row dict(s) so callers can do file cleanup. `delete_events_bulk` raises `ValueError` if no filters are supplied. ### Changed - **Default protocol recv timeout dropped from 30s → 10s** in `_build_client()`. The unit usually responds in well under a second over cellular; 10s leaves comfortable headroom for retransmits while failing reasonably fast when a unit is wedged. The two endpoints that perform full 5A waveform downloads still pass `timeout=120.0` explicitly so multi-minute event transfers are unaffected. - **`_build_client()` now accepts an optional `connect_timeout`** (TCP-only) so rescue / race-loop endpoints can fail fast on busy modems without affecting the protocol-level recv timeout. ### Fixed - **`GET /device/monitor/status` returned HTTP 500 + uncaught traceback when the device was unresponsive**. The retry-on-`Exception` inner block let the second `client.poll()`'s `ProtocolError` propagate out of the handler. Now wrapped in proper try/except — returns 502 with `{"detail": "Protocol error: No S3 frame received within 10.0s ..."}` on timeout, 502 on connection errors, 500 only for genuinely unexpected exceptions. ### Migration No schema changes. No data migration required. If you've been running a previous version against a wedged unit and accumulated bogus events, the new `/admin/events` page in Terra-View v0.12.0 (or direct `POST /db/events/delete_bulk` with `confirm: true`) is the cleanup tool. Watcher state on the upstream DL2 PC does NOT need separate cleaning — the watcher's `sfm_forwarded.json` keys on file sha256 and won't re-forward the same files. ### Pairing This release pairs with **Terra-View v0.12.0**, which adds the `/admin/events` UI that consumes the new bulk-delete endpoints, the bulk false-trigger flagging on `/unit/{id}`, and the field-deployment workflow that uses the same `series3-watcher` → SFM ingest path as before. --- ## v0.16.1 — 2026-05-14 ### Fixed - **`record_type` always "Waveform" for forwarded events.** `read_blastware_file()` hardcoded `ev.record_type = "Waveform"` regardless of the file's actual type. The watcher-forward pipeline (the main BW ACH ingest path) compounds this by parsing files from a tmp path with a `.bw` suffix, so even a filename-based fallback inside the parser still wouldn't see the original extension. Now: 1. New `derive_record_type_from_filename(filename)` helper in `minimateplus/event_file_io.py` derives the type from the LAST character of the filename's extension (V10.72+ AB0T scheme: `H`=Histogram, `W`=Waveform, `M`=Manual, `E`=Event, `C`=Combo). Falls back to `"Waveform"` for old S338 firmware (3-char extensions ending in `0`) and any unrecognized suffix. 2. `read_blastware_file()` now calls the helper with its `path.name` so direct callers (the `--dry-run` path in `scripts/import_bw.py`, tests, ad-hoc scripts) get the right value automatically. 3. `WaveformStore.save_imported_bw()` overrides `ev.record_type` with the **original** filename's derived type after parsing (the tmp file inside the parser doesn't carry the original extension). This is the path the live watcher-forwarder hits, so the DB column now reflects the actual event type going forward. Events ingested before this fix are stuck with `record_type="Waveform"` in the DB; a one-off backfill (`UPDATE events SET record_type = ... WHERE blastware_filename LIKE '%H'`) would fix them retroactively if desired. Terra-view's event modal also derives client-side from the filename, so the UI already shows the correct type for old events even without the backfill. ---
seismo-relay v0.17.0
A ground-up replacement for Blastware — Instantel's aging Windows-only software for managing MiniMate Plus seismographs.
Built in Python. Runs on Windows, Linux, or macOS. Connects to instruments over direct RS-232 or cellular modem (Sierra Wireless RV50 / RV55).
Status: Active development. Full read + write + erase + monitoring pipeline working end-to-end over TCP/cellular. ACH Auto Call Home server handles inbound unit connections, downloads events, and persists everything to a SQLite database. SFM REST API exposes device control and DB queries. As of v0.14.3 (2026-05-05): SUB 5A bulk waveform protocol is verified byte-perfect against Blastware captures across 2-sec, 3-sec, and 10-sec events. Generated
.G10/.AB0files open cleanly in Blastware with full Event Reports, frequency analysis, and waveform plots. v0.16.0 (2026-05-11) adds BW ASCII report ingestion to/db/import/blastware_file— paired with series3-watcher v1.5.0, every Blastware ACH event lands in SeismoDb with device-authoritative peaks, project metadata, sensor self-check, and ZC/Time-of-Peak data, without depending on the still-undecoded waveform body codec. See CHANGELOG.md for full version history.
What's in here
seismo-relay/
├── seismo_lab.py ← Main GUI (Bridge + Analyzer + Download + Console tabs)
│
├── minimateplus/ ← MiniMate Plus client library
│ ├── transport.py ← SerialTransport, TcpTransport, SocketTransport
│ ├── protocol.py ← DLE frame layer, SUB command dispatch
│ ├── client.py ← High-level client (connect, get_events, delete_all_events, push_config, get_call_home_config, …)
│ ├── framing.py ← Frame builders, DLE codec, S3FrameParser
│ ├── models.py ← DeviceInfo, Event, ComplianceConfig, MonitorLogEntry, CallHomeConfig, …
│ └── blastware_file.py ← Write events to Blastware-compatible .AB0 files
│
├── sfm/ ← SFM REST API server (FastAPI, port 8200)
│ ├── server.py ← Live device endpoints + DB query endpoints + caching
│ ├── database.py ← SeismoDb — SQLite persistence (events, monitor_log, ach_sessions, sessions table)
│ └── sfm_webapp.html ← Embedded web UI with Call Home config tab
│
├── bridges/
│ ├── ach_server.py ← Inbound ACH call-home server (main production server)
│ ├── ach_mitm.py ← Transparent MITM proxy for capturing BW sessions
│ ├── s3-bridge/ ← RS-232 serial bridge (capture tool)
│ ├── tcp_serial_bridge.py ← Local TCP↔serial bridge (bench testing)
│ ├── gui_bridge.py ← Standalone bridge GUI with raw capture checkboxes
│ └── raw_capture.py ← Simple raw capture tool
│
├── parsers/
│ ├── s3_analyzer.py ← Session parser, differ, Claude export
│ ├── gui_analyzer.py ← Standalone analyzer GUI
│ └── frame_db.py ← SQLite frame database
│
└── docs/
└── instantel_protocol_reference.md ← Reverse-engineered protocol spec
Quick start
ACH inbound server (production)
Listens for inbound unit call-homes, downloads all new events and monitor log
entries, and writes everything to bridges/captures/seismo_relay.db.
python bridges/ach_server.py --port 12345 --output bridges/captures/
Point the unit's ACEmanager Remote Host to this machine's IP and Remote Port to 12345.
Options:
--port N Listen port (default 12345)
--output DIR Capture directory (default bridges/captures/)
--allow-ip IP Allowlist an IP (repeat for multiple; default: accept all)
--max-events N Safety cap for first run (default: unlimited)
--clear-after-download Erase device memory after successful download
--verbose Debug logging
SFM REST server
Exposes device control and DB queries as a REST API. Proxied by terra-view.
python sfm/server.py # default: 0.0.0.0:8200
python -m uvicorn sfm.server:app --host 0.0.0.0 --port 8200 --reload
Open http://localhost:8200 for the embedded web UI, or http://localhost:8200/docs
for the interactive API docs.
Seismo Lab GUI
python seismo_lab.py
SFM REST API
Live device endpoints
Each call dials the device, does its work, and closes the connection. TCP
connections are retried once on ProtocolError to handle cold-boot timing.
In-memory caching — frequently-polled endpoints avoid redundant TCP round-trips
via a thread-safe _LiveCache (plain Python dict + threading.Lock):
| Method | URL | Cache Strategy |
|---|---|---|
GET |
/device/info |
Indefinite; invalidated by POST /device/config |
GET |
/device/events |
Count-probe fast path (~2s); full download only when new events detected |
GET |
/device/event/{idx}/waveform |
Permanent per event index |
GET |
/device/monitor/status |
30-second TTL; invalidated by monitor start/stop |
GET |
/device/call_home |
Fresh read from device (not cached) |
POST |
/device/connect |
— |
POST |
/device/config |
Writes compliance config; invalidates info + events cache |
POST |
/device/config/project |
Patches project/client/operator/sensor_location strings |
POST |
/device/monitor/start |
Sends SUB 0x96; immediately evicts status cache |
POST |
/device/monitor/stop |
Sends SUB 0x97; immediately evicts status cache |
POST |
/device/call_home |
Reads, patches specified fields, writes back to device |
Cache bypass — All cached endpoints accept ?force=true to skip the cache and
force a fresh read from the device.
Cache stats — GET /cache/stats returns hit/miss counts and TTL info; DELETE /cache/device
clears the device cache immediately.
Transport query params (supply one set):
Serial: ?port=COM5&baud=38400
TCP: ?host=1.2.3.4&tcp_port=12345
DB read endpoints
Query the SQLite database written by ach_server.py. All read-only except
PATCH /db/events/{id}/false_trigger.
| Method | URL | Description |
|---|---|---|
GET |
/db/units |
All known serials with summary stats |
GET |
/db/events |
Triggered events (filter by serial, date range, false_trigger) |
GET |
/db/monitor_log |
Monitoring intervals |
GET |
/db/sessions |
ACH call-home session history |
PATCH |
/db/events/{id}/false_trigger?value=true |
Flag / unflag false triggers |
minimateplus library
from minimateplus import MiniMateClient
from minimateplus.transport import TcpTransport
# Serial
client = MiniMateClient(port="COM5")
# TCP (cellular modem)
client = MiniMateClient(transport=TcpTransport("1.2.3.4", 12345), timeout=30.0)
with client:
# Read
info = client.connect() # DeviceInfo — serial, firmware, compliance config
count = client.count_events() # Number of stored events
keys = client.list_event_keys() # Fast browse walk — event keys only, no download
events = client.get_events() # Full download: headers + peaks + metadata
monitor = client.get_monitor_status() # Battery, memory, is_monitoring flag
log = client.get_monitor_log_entries() # Monitoring intervals (partial 0x2C records)
ach_cfg = client.get_call_home_config() # Auto Call Home settings (SUB 0x2C)
# Write
client.apply_config(
sample_rate=1024,
recording_mode="Continuous", # Single Shot / Continuous / Histogram / Histogram+Continuous
histogram_interval_sec=15, # 2, 5, 15, 60, 300, 900
trigger_level_geo=0.5,
geo_range="Normal", # Normal (10.000 in/s) / Sensitive (1.25 in/s)
project="Bridge Inspection 2026",
client_name="City of Portland",
operator="B. Harrison",
)
client.set_call_home_config(
auto_call_home_enabled=True,
after_event_recorded=True,
at_specified_times=True,
time1_hour=18, time1_min=30, # 6:30 PM
time2_hour=6, time2_min=0, # 6:00 AM
)
# Control
client.start_monitoring() # SUB 0x96
client.stop_monitoring() # SUB 0x97
client.delete_all_events() # Erase all (SUB 0xA3 → 0x1C → 0x06 → 0xA2)
get_events() runs the full per-event sequence:
1E → 0A → 1E(arm token=0xFE) → 0C → 1F(arm) → POLL×3 → 5A → 1F(browse).
SUB 5A bulk stream walks chunks bounded by the end_offset extracted from
the STRT record at byte 17 of the probe response — no over-reading, no
chunk-count cap. Project / client / operator / sensor location strings come
from the dedicated metadata pages at counter 0x1002 and 0x1004,
read once per session (they reflect the compliance setup at session start,
not per individual event).
Database
ach_server.py writes to bridges/captures/seismo_relay.db (SQLite, WAL mode) using the
SeismoDb persistence layer. Four tables, all unit-keyed by serial number:
| Table | Key | Contents |
|---|---|---|
ach_sessions |
UUID | Per-call-home audit record: serial, timestamp, peer IP, events_downloaded, monitor_entries, duration_seconds |
events |
UUID, UNIQUE(serial, waveform_key) | Triggered events: timestamp, Tran/Vert/Long/VectorSum/Mic PPV, project/client/operator/sensor_location strings, sample_rate, record_type, false_trigger flag |
monitor_log |
UUID, UNIQUE(serial, waveform_key) | Monitoring intervals: serial, waveform_key, start_time, stop_time, duration_seconds, geo_threshold_ips |
events.false_trigger |
Boolean flag | PATCH endpoint to mark/unmark false triggers for review |
Deduplication is by (serial, waveform_key) — repeat call-homes or re-runs never
produce duplicate rows. Post-erase key reuse is handled automatically via the
high-water mark in ach_state.json. Key-based state tracking allows correct
handling of device erasures (external or post-download).
Connecting over cellular (RV50 / RV55)
Field units connect via Sierra Wireless RV50 or RV55 cellular modems.
Required ACEmanager settings
| Setting | Value | Why |
|---|---|---|
| Configure Serial Port | 38400,8N1 |
Must match MiniMate baud rate |
| Flow Control | None |
Hardware FC blocks TX if pins unconnected |
| Quiet Mode | Enable | Critical — disabled injects RING/CONNECT onto serial, corrupting the S3 handshake |
| Data Forwarding Timeout | 1 (= 0.1 s) |
Lower latency |
| TCP Connect Response Delay | 0 |
Non-zero silently drops the first POLL frame |
| TCP Idle Timeout | 2 (minutes) |
Prevents premature disconnect |
| DB9 Serial Echo | Disable |
Echo corrupts the data stream |
Protocol quick-reference
| Term | Value | Meaning |
|---|---|---|
| DLE | 0x10 |
Data Link Escape |
| STX | 0x02 |
Start of frame |
| ETX | 0x03 |
End of frame |
| ACK | 0x41 |
Frame-start marker sent before every BW frame |
| DLE stuffing | 10 10 on wire |
Literal 0x10 in payload |
Response SUB rule: response_SUB = 0xFF - request_SUB (no exceptions)
Full protocol documentation: docs/instantel_protocol_reference.md
Compliance Config Features
The REST API and web UI expose full control over device compliance settings:
- Recording Mode (Single Shot / Continuous / Histogram / Histogram+Continuous)
- Sample Rate (1024 / 2048 / 4096 sps)
- Record Time (float, seconds)
- Histogram Interval (2s, 5s, 15s, 1m, 5m, 15m) — when recording mode includes histogram
- Geo Trigger Levels (float, in/s per channel)
- Geo Maximum Range (Normal 10.000 in/s / Sensitive 1.250 in/s per channel)
- Project / Client / Operator / Sensor Location (ASCII strings)
Auto Call Home config:
- Auto Call Home Enable (bool)
- Dial String (read-only; 40-byte ASCII)
- Trigger on Event (bool)
- Scheduled Call-Ins (two time slots with HH:MM each)
- Retry Settings (count, delay, connection timeout, warm-up time)
Requirements
pip install pyserial fastapi uvicorn
Python 3.10+. Tkinter is included with the standard Python installer on Windows (check "tcl/tk and IDLE" during install).
Virtual COM ports (bridge capture)
Blastware → COM4 (virtual) ↔ s3_bridge.py ↔ COM5 (physical) → MiniMate Plus
Use com0com or VSPD to create the virtual COM pair on Windows.
Key Features
Device support:
- Full read/write/erase pipelines
- Compliance config (recording mode, sample rate, histogram interval, geo sensitivity, project strings)
- Auto Call Home config (read/write ACH settings, dial string, time slots, retries)
- Monitor control (start/stop, status polling, battery/memory)
- Monitor log entries (continuous monitoring intervals without full waveform download)
Data persistence:
- SQLite database (
seismo_relay.db) with 4 tables: ach_sessions, events, monitor_log, plus false_trigger flag - Deduplication by waveform key (handles re-runs and repeat call-homes)
- Post-erase key-reuse detection (tracks high-water mark)
- Session state (
ach_state.json) with downloaded keys and max key
REST API:
- Live device endpoints with in-memory caching (
_LiveCache) - Cache statistics (
/cache/stats) and manual invalidation (/cache/device) - DB query endpoints (units, events, monitor_log, sessions, false_trigger PATCH)
- Call Home config read/write endpoints
- Blastware file download endpoint (
/device/event/{index}/blastware_file)
File output (v0.7+, byte-perfect as of v0.14.3):
- Blastware-compatible
.AB0/.G10file generation (waveform + metadata) - Multi-channel waveform decode from SUB 5A bulk stream
- Second-resolution timestamp encoding in Blastware filename
- Byte-perfect against BW reference captures (verified across 2-sec / 3-sec / 10-sec event durations, both event 0 and event N continuation events)
- STRT-bounded chunk walk + correct event-N probe counter + partial DLE stuffing of
0x10in 5A params (the four fixes that landed in v0.14.0–v0.14.3)
Capture tools:
- Serial-to-TCP bridge with raw BW/S3 capture (s3_bridge.py, defaults to auto-capture)
- GUI bridge with raw capture checkboxes (gui_bridge.py)
- ACH inbound server with bidirectional capture (ach_server.py saves raw_tx + raw_rx)
- Transparent TCP MITM proxy for live BW session capture (ach_mitm.py)
Analysis tools:
- s3_analyzer.py — session parser, frame differ, Claude export
- gui_analyzer.py — standalone analyzer GUI
- frame_db.py — SQLite frame database for capture analysis
seismo_lab.py GUI:
- Bridge tab — Serial/TCP mode selector with raw capture options
- Analyzer tab — BW/S3 capture playback and differencing
- Download tab — Live wire-byte capture during event download
- Console tab — Logging and diagnostics
Roadmap (Future)
High-impact (unblocks product features)
- Waveform body codec reverse-engineering. The 5A bulk-stream body is some kind of compressed/encoded format (not raw int16 LE as previously assumed — see §7.6.1 retraction in
docs/instantel_protocol_reference.md). Structural framing is ~50% decoded on branchclaude/codec-re-cBGNe(tagged-block walker, segment counters); per-byte sample mapping is still open. Until this lands, the in-app waveform viewer renders garbage and BW-import peak values fall back to_peaks_from_samples()saturation noise. Workaround: pair every BW-imported event with its_ASCII.TXTso the device-authoritative peaks land in the DB regardless of codec. - In-app waveform viewer accuracy. Depends on codec decode. Plot.v1 JSON pipeline + viewer skeleton already exist; will start showing real waveforms automatically once
_decode_a5_waveformproduces correct samples. - Terra-view integration — seismo-relay router, unit detail page, VISON-style event listing.
- Vibration summary reports — highest legit PPV per project → Word doc (false-trigger filtering first).
BW ASCII report parser enhancements (built in v0.16.0)
- Histogram-specific structural fields. Current parser handles the shared fields (PPV, ZC Freq, sensor self-check, project) but silently drops histogram-only fields:
Histogram Start/Stop Time,Histogram Start/Stop Date,Number of Intervals,Interval Size, per-channelPeak Time+Peak Date(absolute timestamps rather than the waveform'sTime of Peakrelative seconds). - Histogram interval bin-table parsing. Trailing 792-row table (per-interval Peak/Freq per channel + MicL) in histogram TXTs is unparsed. Probably too big for the sidecar JSON; may want a separate
.histogram.h5companion file. >100 Hzvalue parsing. Histogram TXTs use>100 Hzfor out-of-range ZC freq; current_parse_number()returnsNonefor these (loses information).
Ingestion gaps
- MLG forwarding.
series3-watcherforwards event binaries + their_ASCII.TXTreports, but skips.MLGper-unit monitor log files entirely. Adding anPOST /db/import/mlg_fileendpoint + watcher scan path would populatemonitor_logfor non-ACH-routed units (coverage queries, "was this unit monitoring on date X" lookups). - 0C-record raw bytes persistence in the sidecar. Currently on branch
claude/codec-re-cBGNeas commita187124; cherry-pick if useful as a standalone fix. Preserves the 210-byte 0C record underextensions.raw_records.waveform_record_b64so future field-offset analysis (Peak Acceleration / Time of Peak / etc. — the fields BW computes client-side from samples) can run offline.
Operational
series3-watcherfile archive manager — 90-day-old events moved to<watch_folder>_archive/<year>/<month>/subfolders. Plan drafted inclaude/codec-re-cBGNe's plan-mode session; awaiting a 5-minute test on whether Blastware UI walks subfolders before any code lands (determines layout: in-place subfolders vs sibling archive).- Compliance config encoder — build raw write payloads from a
ComplianceConfigobject. - Modem manager — push RV50/RV55 configs via Sierra Wireless API.
- Call Home dial_string write support (requires DLE escaping for embedded control characters).
- Histogram mode recording support (5A stream analysis for mode 0x03 — separate from histogram ASCII parsing above).
Test coverage
- Verify 30-sec event download — body may exceed
0xFFFFand force the device into a differentend_keyencoding (none of the 2/3/10-sec test cases hit this boundary). - Histogram mode (0x03) write via SFM — confirmed working for Single Shot / Continuous / Histogram+Continuous; Histogram (0x03) needs a live test from a non-Histogram starting state.
Lower-priority cleanups
- Compliance write anchor-9 cleanup — when changing recording_mode via SFM, a spurious
0x10may persist after Histogram→other mode transitions. Doesn't affect device operation but differs from BW's byte-perfect output. - Locate "Sensor Check" byte in compliance config (need capture with Disabled vs Before-monitoring).
- Call Home — map time slots 3/4 offsets; confirm
modem_power_relay_enabled. - RV55 DCD/DTR — newer RV55 firmware doesn't assert DCD by default; units don't resume monitoring after call-home disconnect (
--restart-monitoringflag deferred).