Navigation Collector module
File: backend/navproc_collector.py
Pulls navigation and sensor data from a ship's NATS JetStream KV store and writes it into Redis so the dive detector and the dashboard can read it without a direct NATS connection. The set of keys fetched is controlled by a TOML allow-list rather than discovered dynamically.
Configuration file
The collector reads its key allow-list from
backend/collector_keys.toml at startup:
keys = [
"ROV.CTD.INWATER",
"ROV.MBARI.DEPTH",
"SHIP.GPS.LAT",
"SHIP.GPS.LON",
"ROV.POSITION.LAT",
"ROV.POSITION.LON",
]
The path can be overridden with --keys-config. Parsing uses Python's
stdlib tomllib (3.11+) — no extra dependency required. Missing or
malformed config raises at startup so the collector fails fast rather
than silently mirroring nothing.
load_collector_keys
Reads the TOML file and returns the top-level keys array. Raises
FileNotFoundError if the file is missing and ValueError if keys
is not a list of strings.
Class: ShipWatcher
Async NATS client that connects to the ship's coredata server and fetches the configured allow-list of keys on demand.
Constructor parameters
| Parameter | Type | Description |
|---|---|---|
ship_name |
str |
Ship identifier, e.g. "rcsn" |
domain |
str |
NATS domain, e.g. "rc" |
The NATS server URL is constructed as:
Method: get_keys
Returns every key name present in the ship's JetStream KV bucket. This method is kept for ad-hoc debugging but is no longer called from the main loop — the allow-list replaces it for production polling.
Method: watch_keys
Fetches the current value for each key in keys. Per-key fetches are
wrapped individually, so a missing or malformed key on one entry logs a
warning and the loop continues with the rest of the batch.
Values that parse as int are stored as int; otherwise float;
otherwise the key is skipped (with a debug log) rather than guessing how
to encode a non-numeric value.
Class: ShipCache
Redis writer. Accepts the dict produced by ShipWatcher.watch_keys()
and stores each entry under a normalised key name.
Key normalisation
NATS key names use dot-separated UPPERCASE segments. Redis keys use colon-separated lowercase, prefixed with the bucket name:
| NATS key | Redis key (bucket=rcsn) |
|---|---|
ROV.CTD.INWATER |
rcsn:rov:ctd:inwater |
ROV.MBARI.DEPTH |
rcsn:rov:mbari:depth |
SHIP.GPS.LAT |
rcsn:ship:gps:lat |
ROV.POSITION.LAT |
rcsn:rov:position:lat |
TTL
Every key is written with a 600-second TTL. If the collector stops, keys expire within ten minutes and downstream services see the values as missing.
Method: store_data
Writes all key-value pairs to Redis with the normalised naming scheme and TTL.
Main loop
main() runs an async polling loop:
keys = load_collector_keys(keys_config) # once at startup
await ship_watcher.connect()
while True:
if keys:
data = await ship_watcher.watch_keys(keys)
if data:
ship_cache.store_data(data)
await asyncio.sleep(poll_interval)
The pre-existing get_keys() call has been removed from the hot path —
the allow-list approach saves a NATS round-trip per poll AND ensures
unused keys aren't pulled into Redis.
CLI reference
usage: navproc_collector.py [options]
--ship SHIP Ship name / NATS bucket identifier (e.g. rcsn)
--domain DOMAIN NATS domain (default: rc)
--redis-host HOST Redis hostname (default: localhost)
--redis-port PORT Redis port (default: 6379)
--poll-interval SECS Collection frequency in seconds (default: 5)
--keys-config PATH TOML allow-list (default: backend/collector_keys.toml)
--debug 0|1 Enable verbose logging (default: 0)