ados_protocol/node_presence.rs
1//! The `gnss` and `lora` node capability flags, shared by the LAN
2//! `/api/status/full` route and the cloud heartbeat so both transports report
3//! the same answer for the same node.
4
5use std::path::Path;
6use std::time::{Duration, SystemTime};
7
8/// Freshness window of the 1 Hz `gnss.json` sidecar (mirrors `contracts.toml`).
9pub const GNSS_FRESH: Duration = Duration::from_secs(5);
10
11fn unit_on(unit: &str, profile: &str) -> bool {
12 let profile = profile.replace('-', "_");
13 crate::units::find(unit).is_some_and(|u| u.on_profile(&profile))
14}
15
16/// The GNSS lane works: `ados-gnss` runs on this profile (wire or underscore
17/// form) and its sidecar is fresh. The service writes it every second only
18/// while receivers are configured, so a file left by a stopped service goes
19/// stale and does not count.
20pub fn gnss_present(profile: &str, run_dir: &Path, now: SystemTime) -> bool {
21 unit_on("ados-gnss", profile)
22 && crate::sidecar::read_fresh_at::<serde_json::Value>(
23 &run_dir.join("gnss.json"),
24 Some(GNSS_FRESH),
25 now,
26 )
27 .is_fresh()
28}
29
30/// The LoRa lane is enabled: `ados-lora` runs on this profile and the
31/// `lora-enabled` marker sits in the config directory (`etc_dir`).
32pub fn lora_present(profile: &str, etc_dir: &Path) -> bool {
33 unit_on("ados-lora", profile) && etc_dir.join("lora-enabled").exists()
34}