• v1.6.1 9d4ea822fd

    v1.6.1 Stable

    serversdown released this 2026-06-30 01:22:30 -04:00 | 0 commits to main since this release

    [6-25-26] — v1.6.1
    Reliability fixes for the tray app's auto-updater and scan logging.

    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 shared stop_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, 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. (Same fix as thor-watcher 0.4.1.)
    Unsniffable .MLG files are logged once, not every scan. A .MLG file the watcher couldn't extract a unit ID from was never cached, so every scan (~every 5 min) re-sniffed and re-emitted its [unsniffable-recent] log line. With a few hundred such files in a long-lived Blastware autocall folder that meant tens of thousands of duplicate lines per day (a 10-day-old log had grown to ~600K lines). The scan now caches the unsniffable result, so each file is sniffed and logged once per session — and the redundant per-scan header reads are gone.
    Added test_scan_latest.py (unsniffable cached / logged-once + happy-path sniff) and test_series3_tray.py (updater survives a watcher restart; stops on exit).

    Downloads
  • v1.6.0 fb1edfbad4

    serversdown released this 2026-06-25 11:55:15 -04:00 | 2 commits to main since this release

    Added — Mirror (dual-send)

    • Post every heartbeat and event to a second ("mirror") server in addition to the primary. When MIRROR_API_URL and/or MIRROR_SFM_URL are set, each heartbeat POST and each forwarded Blastware event is also sent to the mirror destination — letting a standby box (e.g. the office NAS) stay a continuous replica of prod during a migration so the eventual cutover is a non-event. Default off — blank URLs mean existing 1.5.x installs don't change behaviour after an auto-update.
    • Heartbeat mirror (best-effort). After the primary send_api_payload, the same payload is fired at MIRROR_API_URL when set. It is fully wrapped and bounded by the existing API timeout — the mirror can never delay or fail the primary heartbeat; its outcome is ignored except for a debug log line.
    • Event mirror (reliable, isolated). Events are re-forwarded to MIRROR_SFM_URL through a separate sha256 state file (MIRROR_SFM_STATE_FILE, blank → <log dir>/sfm_forwarded_mirror.json) so the mirror tracks its own delivery independently of the primary forwarder. Before each mirror pass a quick reachability probe (~3 s) checks the mirror server; if it's down the pass is skipped and retried next tick rather than blocking the loop on per-event timeouts. Skipped events stay pending in the mirror state and deliver once the mirror returns — no data loss.
    • Isolation invariant. Nothing on the mirror path can delay or fail the primary: its own state file, its own try/except, the reachability guard + bounded timeouts, and all exceptions swallowed-and-logged. The primary path stays exactly as reliable as before. The mirror rides along with the primary — there is no separate enable flag (heartbeat-mirror active iff the primary heartbeat is on and MIRROR_API_URL is set; event-mirror active iff primary forwarding is on and MIRROR_SFM_URL is set).
    • Mirror fields in the Settings dialog: Mirror API URL + Mirror SFM URL (blank = off).
    • New unit tests in test_event_forwarder.py covering 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 [agent] keys (all default-off — existing 1.5.x deployments don't change behaviour on auto-update):

    Key Default Notes
    MIRROR_API_URL empty Second heartbeat base URL, e.g. http://10.0.0.x:8001 (blank = off)
    MIRROR_SFM_URL empty Second SFM base URL, e.g. http://10.0.0.x:8200 (blank = off)
    MIRROR_SFM_STATE_FILE <log dir>/sfm_forwarded_mirror.json Override location of the mirror's forwarded-sha256 state file

    Operational (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_at is after the snapshot. SFM-side dedup covers any overlap. See docs/mirror-dual-send-design.md.

    Downloads
  • v1.5.0 a457619158

    serversdown released this 2026-05-11 12:37:32 -04:00 | 7 commits to main since this release

    [5-11-26] — v1.5.0

    First release of the SFM event forwarder.

    Added — SFM event forwarder

    • Forward Blastware event binaries (+ paired BW ACH ASCII reports) to an SFM server. When SFM_FORWARD_ENABLED=true and SFM_URL is set, every event binary in the BW ACH watch folder is POSTed as multipart to /db/import/blastware_file along with its <stem>_<ext>_ASCII.TXT partner report (BW ACH convention; manual-export <binary>.TXT is also supported as a fallback). SFM parses the report and indexes the full per-channel stats (PPV, ZC Freq, Time of Peak, Peak Acceleration / Displacement, Peak Vector Sum + time, sensor self-check Pass/Fail, monitor-log timestamps) into a searchable database — no codec decoding required.
    • Idempotent forwarding. Forwarded files are tracked by sha256 in a JSON state file (default <log dir>/sfm_forwarded.json, override via SFM_STATE_FILE). Re-scans don't re-POST and the state survives restarts / auto-updates.
    • Re-pair on late-arriving TXT. When the watcher forwards a binary alone (_ASCII.TXT partner didn't appear within SFM_MISSING_REPORT_GRACE_SECONDS), the state file records had_report: false. On subsequent scans, the watcher re-checks whether the TXT has since arrived. If yes, the event is re-forwarded with the TXT attached — the SFM server's upsert path refreshes the DB row with the report's device-authoritative peak / project values. Without this, slow-disk or AV-interrupted TXT writes would permanently leave that event with broken-codec peaks in the SFM database. Legacy state-file entries (without the had_report field) default to had_report: true so an upgrade doesn't unexpectedly re-forward existing entries.
    • Quiescence + grace-period guards. Files modified within SFM_QUIESCENCE_SECONDS (default 5s) are skipped to avoid forwarding mid-write. If a binary's report partner hasn't appeared after SFM_MISSING_REPORT_GRACE_SECONDS (default 60s), the binary is forwarded alone rather than blocking forever.
    • Per-pass rate cap. SFM_MAX_FORWARDS_PER_PASS (default 500) drips first-deploy backfill instead of hammering the SFM server in one burst. At 60-second SFM_FORWARD_INTERVAL_SECONDS cadence that's ~30K events/hour throughput. Set to 0 for unlimited. Scan walks oldest-first so backfill advances chronologically and successive scans reliably progress.
    • event_forwarder.py --seed-state CLI mode. Walks the watch folder once, sha256s every in-window event, and marks them all as already-forwarded without POSTing anything. Recommended pre-deploy workflow on machines with a large historical archive — flip SFM_FORWARD_ENABLED=true after seeding and only events that appear from then on get forwarded.
    • SFM Forward tab in the Settings dialog with: Forward checkbox, SFM URL + Test button (GETs /health), Forward Interval / Quiescence / Missing-Report Grace / HTTP Timeout / Max Events Per Pass spinboxes, State File entry with Browse... Save-time guard: enabling forwarding without a URL shows a validation error.
    • Histogram-aware log clarity. Histogram events (extensions ending in H) don't get auto-exported reports from BW; the log distinguishes that case ((histogram, no report expected)) from a waveform with unexpectedly missing report (no report ⚠).
    • README "First-time deployment" section documenting the seed-state workflow + the rate cap as belt-and-suspenders for safe rollout on machines with hundreds of thousands of historical events.
    • 31 new unit tests in test_event_forwarder.py covering filename matching, state idempotency, scan logic (quiescence / grace period / max age / already-forwarded / TXT pairing), multipart byte shape, rate cap (oldest-first, cap=0 unlimited, cap=N enforcement), seed-state mode (in-window seeding / max-age skip / end-to-end skip-after-seed / idempotent re-runs), histogram classification, and an end-to-end POST against a stdlib fake server.

    Configuration

    New [agent] keys (all default-off — existing 1.4.x deployments don't change behaviour on auto-update):

    Key Default Notes
    SFM_FORWARD_ENABLED false Master toggle for the forwarder
    SFM_URL empty e.g. http://10.0.0.44:8200
    SFM_FORWARD_INTERVAL_SECONDS 60 Scan-and-forward cadence
    SFM_QUIESCENCE_SECONDS 5 Skip files modified in the last N seconds
    SFM_MISSING_REPORT_GRACE_SECONDS 60 Forward without TXT after this delay
    SFM_HTTP_TIMEOUT 60 Per-request HTTP timeout
    SFM_STATE_FILE <log dir>/sfm_forwarded.json Override location of the forwarded-sha256 state file
    SFM_MAX_FORWARDS_PER_PASS 500 Per-scan cap (0 = unlimited)

    Compatibility

    • Requires SFM server v0.16+ (the /db/import/blastware_file endpoint that accepts paired _ASCII.TXT reports + the BW-report label normalisation — released alongside this watcher version on the seismo-relay side).
    Downloads
  • v1.4.4 1abdc13645

    v1.4.4 Stable

    serversdown released this 2026-03-18 01:34:30 -04:00 | 19 commits to main since this release

    Downloads
  • v1.4.3 f790b21808

    serversdown released this 2026-03-17 21:12:39 -04:00 | 21 commits to main since this release

    [1.4.3] - 2026-03-17

    Added

    • Auto-updater now logs all activity to the watcher log file ([updater] prefix) — silent failures are now visible.
    • Configurable update source: UPDATE_SOURCE = gitea (default), url, or disabled. In url mode the watcher fetches version.txt and the .exe from a custom base URL (e.g. terra-view) instead of the Gitea API — enables updates on isolated networks that cannot reach Gitea. disabled turns off automatic checks while keeping the remote push path (from terra-view) functional.
    • New Updates tab in the Settings dialog to configure UPDATE_SOURCE and UPDATE_URL.

    Fixed

    • Downloaded .exe is now validated before applying: absolute size floor (100 KB), relative size floor (50% of current exe), and MZ magic bytes check. A corrupt or truncated download is now rejected and logged rather than silently overwriting the live exe.
    • Swap .bat now backs up the current exe as <exe>.old before overwriting, providing a manual rollback copy if needed.
    • Swap .bat retry loop is now capped at 5 attempts — was previously infinite if the file remained locked.
    • Swap .bat now cleans up the temp download file on both success and failure.
    Downloads
  • v1.4.2 0bea6ca4ea

    serversdown released this 2026-03-17 16:30:53 -04:00 | 23 commits to main since this release

    Changed

    • Tray icon color now reflects watcher + API health rather than unit ages — green=API OK, amber=API disabled, red=API failing, purple=watcher error.
    • Status menu text updated to show Running — API OK | N unit(s) | scan Xm ago.
    • Units submenu removed from tray — status tracking for individual units is handled by terra-view, not the watcher.
    • Unit list still logged to console and log file for debugging, but no OK/Pending/Missing judgement applied.
    • watcher_status field added to heartbeat payload so terra-view receives accurate watcher health data.

    Install

    • To install on a new machine, please use the installer.exe (from 1.4.1, it will then automatically update to 1.4.2 after detecting the new release)
    • If already running 1.4.1, it SHOULD auto update. (its still tbd if that actually works tho....)
    Downloads
  • v1.4.1 2456fd0ee8

    serversdown released this 2026-03-17 14:36:36 -04:00 | 26 commits to main since this release

    Installation

    New Install

    • download the series3-watcher-setup.exe
    • Run install wizard.

    Existing Install update

    • Should update automatically? (havent tested it yet tho)

    [1.4.1] - 2026-03-17

    Fixed

    • config.ini now saves to AppData\Local\Series3Watcher\ instead of Program Files — fixes permission denied error on first-run wizard save.
    • Config path resolution in both series3_tray.py and series3_watcher.py updated to use sys.frozen + LOCALAPPDATA when running as a PyInstaller .exe.
    • Status menu item now uses a callable so it updates every time the menu opens — was showing stale "Starting..." while tooltip correctly showed current status.
    • Settings dialog now opens in its own thread — fixes unresponsive tabs and text fields while the watcher loop is running.
    • Tray icon reverted to plain colored dot — custom icon graphic was unreadable at 16px tray size. .ico file is still used for the .exe file icon.

    Changed

    • Terra-View URL field in settings wizard now accepts base URL only (e.g. http://192.168.x.x:8000) — /api/series3/heartbeat endpoint appended automatically.
    • Test Connection button now hits /health endpoint instead of posting a fake heartbeat — no database side effects.
    • "terra-view URL" label capitalized to "Terra-View URL".
    • Default log path updated to AppData\Local\Series3Watcher\agent_logs\series3_watcher.log.
    • Installer now creates agent_logs\ folder on install.
    • BUILDING.md added — step-by-step guide for building, releasing, and updating.

    [1.4.0] - 2026-03-12

    Added

    • series3_tray.py — system tray launcher using pystray + Pillow. Color-coded icon (green=OK, amber=Pending, red=Missing, purple=Error, grey=Starting). Right-click menu shows live status, unit count, last scan age, Open Log Folder, and Exit.
    • run_watcher(state, stop_event) in series3_watcher.py for background thread use by the tray. Shared state dict updated on every scan cycle with status, unit list, last scan time, and last error.
    • Interruptible sleep in watcher loop — tray exit is immediate, no waiting out the full scan interval.

    Changed

    • main() now calls run_watcher() — standalone behavior unchanged.
    • requirements.txt updated to document tray dependencies (pystray, Pillow); watcher itself remains stdlib-only.
    Downloads