# Thor Watcher **Version:** 0.3.0 Micromate (Series 4) watcher agent for Terra-View fleet management. Runs as a Windows system tray application, scans THORDATA for Micromate unit activity, sends heartbeat data to Terra-View, and (optionally) forwards `.IDFH`/`.IDFW` event files to a seismo-relay SFM server. --- ## Overview Thor Watcher monitors `C:\THORDATA\\\*.MLG` files, determines each unit's last activity from the MLG filename timestamp, and periodically posts a heartbeat payload to the Terra-View backend. It runs silently in the system tray and auto-starts on login. --- ## Installation 1. Run `thor-watcher-setup.exe` 2. On first launch the setup wizard will appear — enter your THORDATA path and Terra-View URL 3. The app starts in the system tray and launches automatically on login --- ## Building from Source Requires Python 3.10+ and pip on PATH. ```bat build.bat ``` Produces: - `dist\thor-watcher-0.3.0.exe` — upload to Gitea release - `dist\thor-watcher.exe` — use with Inno Setup Then run Inno Setup Compiler on `installer.iss` to produce `thor-watcher-setup.exe`. --- ## Configuration Config is stored at: ``` %LOCALAPPDATA%\ThorWatcher\config.json ``` Managed through the Settings dialog (right-click tray icon → Settings). A `config.example.json` is included as reference. ### Config Keys | Key | Type | Default | Description | |-----|------|---------|-------------| | `thordata_path` | string | `C:\THORDATA` | Root THORDATA directory | | `scan_interval` | integer | `60` | Seconds between scans | | `api_url` | string | `""` | Terra-View heartbeat URL (e.g. `http://10.0.0.40:8000/api/series4/heartbeat`) | | `api_timeout` | integer | `5` | HTTP request timeout in seconds | | `api_interval` | integer | `300` | Seconds between API heartbeat POSTs | | `source_id` | string | hostname | Identifier for this machine in Terra-View | | `source_type` | string | `series4_watcher` | Agent type (do not change) | | `local_timezone` | string | `America/New_York` | Timezone of the field machine — used to convert MLG timestamps to UTC | | `enable_logging` | boolean | `true` | Write log file | | `log_file` | string | `%LOCALAPPDATA%\ThorWatcher\agent_logs\thor_watcher.log` | Log file path | | `log_retention_days` | integer | `30` | Days before log is auto-cleared | | `update_source` | string | `gitea` | Auto-update source: `gitea`, `url`, or `disabled` | | `update_url` | string | `""` | Base URL for `url` mode (e.g. Terra-View server) | | `sfm_forward_enabled` | boolean | `false` | Forward `.IDFH`/`.IDFW` event files to a seismo-relay SFM server | | `sfm_url` | string | `""` | Base URL of the seismo-relay SFM server (e.g. `http://10.0.0.44:8200`) | | `sfm_forward_interval` | integer | `60` | Seconds between forwarder passes | | `sfm_quiescence_seconds` | integer | `5` | Skip files modified within the last N seconds (avoid in-flight files) | | `sfm_missing_report_grace_seconds` | integer | `60` | Forward a binary without its `.txt` sidecar if it hasn't appeared after N seconds | | `sfm_http_timeout` | integer | `60` | HTTP timeout per forward POST | | `sfm_state_file` | string | `""` | Path to the sha256-keyed state file. Blank → `\thor_forwarded.json` | | `sfm_max_forwards_per_pass` | integer | `500` | Cap per pass to drip-feed large backfills | | `sfm_max_event_age_days` | integer | `365` | Skip event files older than this many days | --- ## Event Forwarding When `sfm_forward_enabled` is true and `sfm_url` is set, Thor Watcher walks the THORDATA tree each `sfm_forward_interval` seconds, finds `.IDFH` (histogram) and `.IDFW` (waveform) event binaries plus their `TXT/.txt` ASCII sidecars, and POSTs them to seismo-relay's `/db/import/idf_file` endpoint. - **Idempotent.** Every forwarded file is recorded by sha256 in `thor_forwarded.json`. Re-scans never re-POST. - **Default off.** Operators must explicitly enable from the Settings → SFM Forward tab. - **Re-pair logic.** If a binary is forwarded before its TXT sidecar appears (after the grace period), it's flagged `had_report=false` and re-forwarded once the TXT arrives so the SFM database row can be refreshed with device-authoritative PPV/ZCFreq/peak values. - **TXT export must be enabled in Thor.** Thor's TXT sidecars are not produced automatically — operators should enable TXT export so the relay can extract rich metadata. Forwards without a TXT are still useful (binary gets indexed; rich fields stay NULL). - **Backfill seeding.** To skip a large historical archive on first deploy, run `python event_forwarder.py --seed-state --thordata C:\THORDATA --state ` before flipping the switch. --- ## Tray Icon Colors | Color | Meaning | |-------|---------| | Green | Running, API reporting OK (and SFM forwarder healthy when enabled) | | Amber | Running, API disabled OR SFM forwarder failing while API is healthy | | Red | Running, API failing | | Purple | Error — check logs | | Grey | Starting up | --- ## Auto-Updater Thor Watcher checks for updates every ~5 minutes. When a new release is found it downloads and validates the exe, then relaunches via a swap bat — no manual intervention needed. **Update sources:** - `gitea` — checks the Gitea release page (default) - `url` — fetches `version.txt` and `thor-watcher.exe` from a custom server (e.g. Terra-View) - `disabled` — no automatic checks; remote push from Terra-View still works **Download validation:** 100 KB minimum size, 50% relative size floor vs current exe, MZ magic bytes check. Remote update push from Terra-View Watcher Manager works regardless of `update_source` setting. --- ## Heartbeat Payload Posted to `api_url` on each API interval: ```json { "source_id": "THOR-PC", "source_type": "series4_watcher", "version": "0.3.0", "generated_at": "2026-03-20T14:30:00Z", "log_tail": ["...last 25 log lines..."], "units": [ { "unit_id": "UM11719", "last_call": "2026-03-20T13:18:00Z", "age_minutes": 72, "mlg_path": "C:\\THORDATA\\Project A\\UM11719\\UM11719_20260320131800.MLG", "project_hint": "Project A" } ] } ``` --- ## THORDATA Directory Structure ``` C:\THORDATA\ ├── Project A\ │ ├── UM11719\ │ │ ├── UM11719_20260320131800.MLG │ │ └── UM11719_20260319095430.MLG │ └── UM12345\ │ └── UM12345_20260318091530.MLG └── Project B\ └── UM98765\ └── UM98765_20260301082215.MLG ``` --- ## Troubleshooting **Tray icon is amber:** API URL is not configured or disabled — open Settings and enter Terra-View URL. **Tray icon is red:** API is failing — check Terra-View is reachable, URL is correct, and the network is up. **Units showing wrong time in Terra-View:** Check `local_timezone` in Settings matches the field machine's timezone. **No units found:** Verify `thordata_path` is correct and MLG files exist following the `UM####_YYYYMMDDHHMMSS.MLG` naming convention. **Auto-updater not working:** Check the log file for `[updater]` lines. On first deploy, verify the Gitea release has a `thor-watcher-X.X.X.exe` asset (not a setup exe). --- ## Version History See [CHANGELOG.md](CHANGELOG.md) for detailed version history. --- *Proprietary — Terra-Mechanics Inc. Internal use only.*