Commit Graph

33 Commits

Author SHA1 Message Date
claude ef2c38e7db v0.10.0 — monitor log entry support (SUB 0x0A partial records)
Add full decode pipeline for 0x2C partial records from the device's event
list, representing continuous monitoring intervals where no threshold was
crossed.  These records appear interleaved with full triggered events in the
browse walk and were previously ignored.

minimateplus/models.py
- Add MonitorLogEntry dataclass: key, start_time, stop_time, serial,
  geo_threshold_ips, raw_header, duration_seconds property

minimateplus/protocol.py
- read_waveform_header() now returns (data_rsp.data, length) — full payload
  including the record-type byte at position 0 — instead of the sliced header.
  Callers that need the old slice use raw_data[11:11+length] as before.

minimateplus/client.py
- Add _decode_0a_partial_header(): auto-detects 9-byte (sub_code=0x10) vs
  10-byte (sub_code=0x03) timestamp format, handles 1-byte inter-timestamp
  gap, extracts serial via BE anchor and geo threshold via Geo: anchor.
- Add get_monitor_log_entries(skip_keys=None): browse walk (1E → 0A → 1F),
  decodes partial records, skips full records and already-seen keys.

minimateplus/__init__.py
- Export MonitorLogEntry

bridges/ach_server.py
- After get_events(), call get_monitor_log_entries(skip_keys=seen_keys) and
  save new entries to monitor_log.json in the session directory.
- Add _monitor_log_entry_to_dict() helper.
- Include monitor log keys in downloaded_keys for state persistence.

CLAUDE.md / CHANGELOG.md
- Document 0x2C partial record layout (timestamp format, ASCII metadata
  region, 1-byte gap edge case) confirmed from 4-11-26 MITM capture.
- Version bump to v0.10.0; update What's next.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 02:59:40 -04:00
claude e712d68505 feat: add erase-all protocol and browse helpers to protocol/client layer
protocol.py:
- SUB_ERASE_ALL_BEGIN = 0xA3, SUB_ERASE_ALL_CONFIRM = 0xA2 (confirmed 4-11-26 MITM)
- SUB_CHANNEL_CONFIG (0x06) data length = 0x24 (36 bytes) in DATA_LENGTHS
- begin_erase_all()              — single frame, token=0xFE, response 0x5C
- confirm_erase_all()            — single frame, token=0xFE, response 0x5D
- read_event_storage_range()     — two-step read (probe+data), token=0xFE
  Response last 8 bytes = first/last stored event key; both 0x01110000 after erase

client.py:
- list_event_keys()              — browse-mode 1E→0A→1F walk, no waveform download;
  returns list of hex key strings; used as fast pre-check before get_events()
- get_events(skip_waveform_for_keys=set())
  — for already-seen keys: only 0A+1F(browse), skips 1E-arm/0C/POLL×3/5A entirely
- delete_all_events()            — orchestrates the confirmed erase sequence:
  0xA3 → 0x1C → 0x06 → 0xA2; logs first/last key from storage range response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 01:14:37 -04:00
claude dda5683572 fix: improve monitoring functionality with session-reset signal and payload adjustments 2026-04-08 18:29:51 -04:00
claude a41e7a9e1a feat: Add monitoring functionality to MiniMate protocol and web interface
- Introduced new SUBs for monitoring status, start, and stop commands in protocol.py.
- Implemented read_monitor_status, start_monitoring, and stop_monitoring methods in MiniMateProtocol class.
- Added new API endpoints for monitoring status retrieval and control in server.py.
- Enhanced the web application with a monitoring panel, including battery and memory status display.
- Created a new Python script to parse SUB 0x1C response frames for monitoring status.
- Documented the monitoring status response format and field locations in markdown and text files.
2026-04-08 14:34:42 -04:00
claude 7005ae766d feat: implement set_project_info functionality and add POC test script 2026-04-07 02:49:17 -04:00
claude bcc044655a feat: updates to 0.8.0 - initial write functions 2026-04-07 02:09:29 -04:00
claude 781d21f132 perf: reduce 5A chunk timeout to 10s and stop iteration at requested event index
Two improvements to eliminate the ~2-min-per-event wait and unnecessary
full-event-list download when only one event is requested:

1. protocol.py: pass timeout=10.0 to _recv_one in the 5A chunk loop.
   Device responds within ~1s per chunk; 10s gives a safe 10x buffer.
   End-of-stream detection (raw_bytes=1) now fires in 10s instead of 120s,
   cutting ~110s of dead wait per event.

2. client.py: add stop_after_index parameter to get_events(). When set,
   iteration stops immediately after the target event is collected — no
   further 0A/1E/0C/5A/1F cycles for events the caller doesn't need.

3. server.py: pass stop_after_index=index to both /device/event/{idx}
   and /device/event/{idx}/waveform endpoints so a single-event request
   only downloads that one event.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:02:01 -04:00
claude e3a5c6f07d 5A: treat partial end-of-stream byte as graceful stream termination
When the device finishes streaming waveform data, it sends a single
partial byte (raw_bytes=1) rather than a complete A5 frame, then goes
silent for the full 120s timeout.

Observed: 35 chunks succeed, chunk 36 times out with raw_bytes=1 —
identical for both events in the test run.

Fix: on TimeoutError, if bytes_fed > 0 and we already have collected
frames, treat it as natural end-of-stream and break to the termination
step rather than propagating the exception.  True transport failures
(raw_bytes=0, no prior frames) still raise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 17:41:29 -04:00
claude 1397f8486f diag: per-chunk WARNING logging in 5A bulk stream loop
Wraps each recv_one call in a try/except TimeoutError, logging:
- On timeout: chunk_num, counter, raw bytes_fed (distinguishes "device
  silent" from "device sent unparseable bytes")
- On success: chunk_num, page_key, data_len, contains_Project flag

Parser is explicitly reset before each chunk recv so bytes_fed is
accurate per-chunk rather than cumulative. Helps identify exactly which
chunk fails and whether the device is responding at all.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 17:32:49 -04:00
claude 5b3e8af1e3 fix: remove special case chunk counter, all chunks use chunk_num * 0x0400 2026-04-06 17:03:09 -04:00
claude ad1c9e48b0 fix: update MiniMateClient and protocol to ensure correct handling of 1F calls and improve event download sequence 2026-04-06 15:58:03 -04:00
claude d0d5a18d5c fix: update event handling in MiniMateClient and protocol to ensure correct sequence for waveform downloads 2026-04-06 13:19:51 -04:00
claude d87e02fab2 fix: update event handling in MiniMateClient and protocol to address 5A timeout issue 2026-04-06 12:41:27 -04:00
claude 5d43acd827 fix: clarify bulk counter step handling and improve comments for waveform chunk processing 2026-04-06 01:35:09 -04:00
claude 6adf8b6078 Fix: advance_event(browse=True) now sends token_params(0) — all zeros — matching the confirmed BW browse sequence. 2026-04-03 18:15:42 -04:00
claude 95f2becf21 feat: update event iteration logic to use null sentinel for end-of-events detection 2026-04-03 16:29:10 -04:00
claude 3b04d4683b fix: update compliance config read logic to handle buffered responses on slow links 2026-04-02 17:18:48 -04:00
claude 0363425d83 fix: add addition recv after loop to make up for modem TCP/IP lag 2026-04-02 17:13:12 -04:00
claude 66967e036c fix: match BW's 5A frame probe to parse event-time metadata. 2026-04-02 16:57:12 -04:00
claude 9bf20803c2 feat: fetch event-time metadata from SUB 5A bulk waveform stream
Add read_bulk_waveform_stream() to MiniMateProtocol and wire it into
get_events() so each event gets authoritative client/operator/sensor_location
from the A5 frames recorded at event-time, not the current compliance config.

- framing.py: bulk_waveform_params() and bulk_waveform_term_params() helpers
  (probe/chunk params and termination params for SUB 5A, confirmed from
  1-2-26 BW TX capture)
- protocol.py: read_bulk_waveform_stream(key4, stop_after_metadata=True) —
  probe + chunk loop (counter += 0x0400), early stop when b"Project:" found
  (A5[7] of 9), then sends termination at offset=0x005A
- client.py: _decode_a5_metadata_into() needle-searches concatenated A5 frame
  data for Project/Client/User Name/Seis Loc/Extended Notes; get_events() now
  calls SUB 5A after each SUB 0C, overwriting project_info with event-time values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 16:08:21 -04:00
claude 638e60532c protocol: tighten compliance config dedup to exact byte content
Old key was (page_key, chunk_len) which would incorrectly drop a second
config section that has the same length as the first (e.g. current-config
vs event-time-config when settings haven't changed).

New key is the full chunk bytes — only truly byte-identical chunks are
dropped.  Different data that happens to share page_key and length now
comes through correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 03:36:25 -04:00
claude ce44852383 protocol: add read_event_index() for SUB 08/F7
Two-step probe+fetch for SUB 08 (EVENT_INDEX), returning the raw 88-byte
(0x58) index block.  SUB_EVENT_INDEX and DATA_LENGTHS[0x08]=0x58 were
already registered — this just wires the method that calls them.

Docstring notes the partially-decoded layout (event count at +3 as uint32 BE,
timestamps at +7) pending live device confirmation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 01:59:48 -04:00
claude 114cbb4679 protocol: skip duplicate E5 chunks in read_compliance_config
BE11529 sometimes returns frame D with page_key=0x0000 (44 bytes),
identical to the frame B response, inflating cfg to ~1115 bytes and
mis-aligning all field offsets.  Track (page_key, chunk_size) pairs
and drop any repeat before appending to the running config buffer.
2026-04-01 15:39:56 -04:00
claude eee1e36a1b protocol: send 3-step data requests for full compliance config (SUB 1A)
Reverse-engineered the full Blastware 4-frame sequence for SUB 1A:
  A (probe):      offset=0x0000, params[7]=0x64
  B (data req 1): offset=0x0400, params[2]=0x00, params[7]=0x64  → bytes 0..1023
  C (data req 2): offset=0x0400, params[2]=0x04, params[7]=0x64  → bytes 1024..2047
  D (data req 3): offset=0x002A, params[2]=0x08, params[7]=0x64  → bytes 2048..2089

We were only sending A+D and getting 44 bytes (the last chunk).
Now sends B, C, D in sequence; each E5 response has 11-byte echo header
stripped, and chunks are concatenated.  Devices that return all data in
one frame (BE18189 style) are handled — timeouts on B/C are skipped
gracefully and data from D still accumulates.

Total expected: 0x0400 + 0x0400 + 0x002A = 0x082A = 2090 bytes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 15:08:50 -04:00
claude a5069c302d protocol: accumulate multiple E5 frames for chunked compliance config
BE11529 sends the compliance config (SUB 1A / E5 response) as a stream
of small frames (~44 bytes each) rather than one large frame like BE18189.

Changes:
- read_compliance_config(): loop calling _recv_one() until 0x082A bytes
  accumulated or 2s inter-frame gap; first frame strips 11-byte echo header,
  subsequent frames logged in full for structure analysis
- _recv_one(): add reset_parser=False option to preserve parser state and
  _pending_frames buffer between consecutive reads from one device response;
  also stash any extra frames parsed in a single TCP chunk so they are not lost

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 14:54:16 -04:00
claude 6e0b83efa5 fix: update compliance config reading to handle variable payload sizes and improve logging 2026-04-01 14:31:03 -04:00
claude d4b1b834a7 fix: refine SUB 1A compliance config read parameters and logging 2026-04-01 14:20:12 -04:00
claude 32b9d3050c feat: implement SUB 1A (compliance config) read
Adds full support for reading device compliance configuration (2090-byte E5
response) containing record time, trigger/alarm levels, and project strings.

protocol.py:
- Implement read_compliance_config() two-step read (SUB 1A → E5)
- Fixed length 0x082A (2090 bytes)

models.py:
- Add ComplianceConfig dataclass with fields: record_time, sample_rate,
  trigger_level_geo, alarm_level_geo, max_range_geo, project strings
- Add compliance_config field to DeviceInfo

client.py:
- Implement _decode_compliance_config_into() to extract:
  * Record time float at offset +0x28 
  * Trigger/alarm levels per-channel (heuristic parsing) 🔶
  * Project/setup strings from E5 payload
  * Placeholder for sample_rate (location TBD )
- Update connect() to read SUB 1A after SUB 01, cache in device_info
- Add ComplianceConfig to imports

sfm/server.py:
- Add _serialise_compliance_config() JSON encoder
- Include compliance_config in /device/info response
- Updated _serialise_device_info() to output compliance config

Both record_time (at fixed offset 0x28) and project strings are  CONFIRMED
from protocol reference §7.6. Trigger/alarm extraction uses heuristics
pending more detailed field mapping from captured data.

Sample rate remains undiscovered in the E5 payload — likely in the
mystery flags at offset +0x12 or requires a "fast mode" capture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 12:08:43 -04:00
claude 9f52745bb4 feat: add full event download pipeline 2026-03-31 20:48:03 -04:00
claude 8074bf0fee bump timeout to 30s to deal with modem slowness. 2026-03-31 12:12:36 -04:00
claude 3f142ce1c0 fix: stop raising on S3 checksum mismatches 2026-03-31 00:15:07 -04:00
claude 8e985154a7 bumps timeout up 2026-03-30 23:46:34 -04:00
claude f8f590b19b sfm first build 2026-03-30 23:23:29 -04:00