a92afd64cd
Saving the Settings dialog calls _restart_watcher, which set+replaced the shared stop_event the updater loop also keyed off — so a settings save silently killed the auto-updater thread (race: survived one save, died on the next). Heartbeats kept flowing but the agent stopped checking for / applying updates until relaunch. Give the updater its own app-lifetime event (_app_stop), set only on Exit / when applying an update and untouched by watcher restarts. Adds test_thor_tray.py (4 lifecycle tests). Bump to v0.4.1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.9 KiB
7.9 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.4.1] - 2026-06-25
Fixed
- Auto-updater no longer dies when settings are saved. Saving the Settings dialog restarts the watcher thread via
_restart_watcher, which sets and then replaces the sharedstop_event. Because the updater loop keyed off that same event, saving settings would terminate the auto-updater thread (a race — it could survive one save and die on the next). The watcher kept running and reporting heartbeats normally, but the agent silently stopped checking for / applying updates until the tray app was relaunched. The updater now keys off its own app-lifetime event (_app_stop) that watcher restarts never touch; it stops only on Exit or when applying an update. - Added
test_thor_tray.pycovering the tray thread lifecycle (updater survives a watcher restart; stops on app exit;_exit/_do_updatesignal the lifetime event).
[0.4.0] - 2026-06-25
Added — Mirror (dual-send)
- Optional best-effort mirror server. Each heartbeat and each forwarded event can now be sent to a second ("mirror") server in addition to the primary, so a standby box (e.g. the office NAS) stays a continuous replica during a migration — making the eventual cutover a non-event. Default off — blank
mirror_api_url/mirror_sfm_urlmeans existing installs are unchanged after auto-update. - Heartbeat mirror (best-effort). After the primary heartbeat POST, the same payload is fired at
mirror_api_urlwhen set. Bounded by the existing API timeout and fully wrapped — it can never delay or fail the primary heartbeat; its result is ignored except for a debug log line. - Event mirror (reliable, isolated). Events are re-forwarded to
mirror_sfm_urlusing a separate sha256 state file (mirror_sfm_state_file, blank →<log_dir>/thor_forwarded_mirror.json), so the mirror tracks its own delivery independently of the primary. A quick reachability probe (~3 s) skips the mirror pass when the mirror server is down, so a dead mirror never stalls the loop on per-event timeouts; skipped events stay pending in the mirror state and deliver when the mirror returns — no data loss. - Isolation invariant (the one rule). Nothing on the mirror path can delay or fail the primary: its own state file, its own try/except, the reachability guard + bounded timeouts, all exceptions swallowed-and-logged. The primary path stays exactly as reliable as before.
- Mirror tab in the Settings dialog with
Mirror API URL+Mirror SFM URLfields (blank = off). The mirror rides along with the primary — there is no separate enable flag (heartbeat-mirror active iff the primary heartbeat is on andmirror_api_urlis set; event-mirror active iff primary forwarding is on andmirror_sfm_urlis set). - New unit tests in
test_event_forwarder.pycovering mirror reachability, the down-mirror skip, separate-state idempotency, and the isolation invariant (a mirror failure leaves the primary's result + state untouched).
Configuration
New config.json keys (all default-empty = off; existing 0.3.x deployments don't change behaviour on auto-update):
| Key | Default | Notes |
|---|---|---|
mirror_api_url |
"" |
Second heartbeat destination — same URL form as api_url (blank = off) |
mirror_sfm_url |
"" |
Second SFM base URL, e.g. http://10.0.0.x:8200 (blank = off) |
mirror_sfm_state_file |
"" |
Override the mirror's forwarded-sha256 state file. Blank → <log_dir>\thor_forwarded_mirror.json |
Changed
- Bumped
VERSIONto0.4.0
Operational note (migration seeding). To make the mirror re-deliver only the gap since a one-time replica snapshot, copy the primary state file to the mirror state file and drop entries whose
forwarded_atis after the snapshot. SFM-side dedup covers any overlap. Seedocs/mirror-dual-send-design.md.
[0.3.1] - 2026-05-20
Hot fix
- Improved update logging
- Improved update settings
[0.3.0] - 2026-05-19
Added
event_forwarder.py— forwards.IDFH(histogram) and.IDFW(waveform) event files plus theirTXT/<basename>.txtsidecars to a seismo-relay SFM server's new/db/import/idf_fileendpoint- Sha256-keyed
thor_forwarded.jsonstate file for idempotency across restarts and re-scans (default path:<log_dir>/thor_forwarded.json) - "SFM Forward" tab in Settings dialog: enable/URL/Test, forward interval, quiescence, missing-report grace, HTTP timeout, max forwards per pass, max event age, state file picker
- Forwarder status line in tray menu:
SFM OK | N fwd, M err | last 30s ago - Tray icon goes amber when the SFM forwarder is failing but the API heartbeat is still healthy
- Re-pair logic: events forwarded without their TXT are re-forwarded once the sidecar appears so the relay can refresh DB rows with device-authoritative PPV/ZCFreq/peak values
event_forwarder.py --seed-stateCLI for skipping historical backfill on a first deploy- Version badge:
Thor Watcher vX.Y.Zshown at the top of the tray menu and in the Settings dialog title bar — operators no longer have to crack open the .exe properties to tell which version is running
Changed
- Bumped
VERSIONto0.3.0 - Settings dialog tab order: Connection / Paths / Scanning / Logging / SFM Forward / Updates
[0.2.0] - 2026-03-20
Added
thor_tray.py— system tray launcher with status icon (green/amber/red/grey), Settings and Open Log Folder menu itemsthor_settings_dialog.py— Tkinter settings dialog with first-run wizard; tabs for Connection, Paths, Scanning, Logging, Updates- Hardened auto-updater: three-layer download validation (100 KB floor, 50% relative size floor, MZ magic bytes), safer swap bat with 5-retry cap and
:failexit,.exe.oldbackup - Configurable update source:
update_source(gitea / url / disabled),update_urlfor custom server - Remote push support:
update_availableflag in API heartbeat response triggers update regardless ofupdate_sourcesetting build.bat— PyInstaller build script; outputs versioned exe for Gitea and plain copy for Inno Setupinstaller.iss— Inno Setup installer script with startup shortcut_update_log()helper writes timestamped[updater]lines to the watcher loglog_tailincluded in heartbeat payload (last 25 lines) for terra-view displayrun_watcher(state, stop_event)pattern inseries4_ingest.pyfor background thread use from tray
Changed
series4_ingest.pyrefactored into tray-friendly background thread module;main()retained for standalone use- Config key
sfm_endpointrenamed toapi_urlfor consistency with series3-watcher - Heartbeat payload now uses
source_id,source_type,versionfields matching terra-view WatcherAgent model - AppData folder:
ThorWatcher(was not previously defined)
[0.1.1] - 2025-12-08
Changed
- Moved configuration from hard-coded values to external
config.jsonfile - Configuration now loads from JSON file with graceful fallback to defaults
- Config file is loaded relative to script location for portability
Added
config.json- External configuration file for all application settingsload_config()function with error handling and default value fallback- Warning messages when config file is missing or malformed
[0.1.0] - 2025-12-04
Added
- Initial release of Series 4 Ingest Agent
- Micromate (Series 4) heartbeat monitoring for Seismo Fleet Manager
- THORDATA folder scanner for MLG files
- Automatic detection of newest MLG file per unit by timestamp
- Age calculation and status classification (OK / LATE / STALE)
- Console heartbeat display with formatted output
- Optional HTTP POST to SFM backend endpoint
- Configurable age thresholds and scan intervals
- Debug logging capability
- Graceful error handling for filesystem and network operations