orin-status: publish net_wifi_ssid from iw while WiFi is up, re-read every 10 s
/ crosscheck (push) Successful in 1s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
2026-10-13 16:39:01 +09:00
parent f535a8f466
commit a1722736fd
2 changed files with 26 additions and 3 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ needed.
| `recorder` | `yukart-recorder.service` | Raw CAN + GNSS to disk, in sessions, under a storage bound; OBD-II values from the bus beside them | | `recorder` | `yukart-recorder.service` | Raw CAN + GNSS to disk, in sessions, under a storage bound; OBD-II values from the bus beside them |
| `bus-bench` | not installed: a test tool | Raw CAN and decoded-signal load over MQTT, measured: loss, latency, broker CPU ([report](docs/impl/reports/BUS-BENCH.md)) | | `bus-bench` | not installed: a test tool | Raw CAN and decoded-signal load over MQTT, measured: loss, latency, broker CPU ([report](docs/impl/reports/BUS-BENCH.md)) |
| `obd-poller` | `yukart-obd-poller.service` | OBD-II mode 01 polling, the single CAN writer: rpm, speed, throttle, load, MAP, MAF ~10 Hz; temperatures and fuel 1 Hz (`yukart/vehicle/*`) | | `obd-poller` | `yukart-obd-poller.service` | OBD-II mode 01 polling, the single CAN writer: rpm, speed, throttle, load, MAP, MAF ~10 Hz; temperatures and fuel 1 Hz (`yukart/vehicle/*`) |
| `orin-status` | `yukart-orin-status.service` | The Orin's health on the bus, once a second: temperatures, CPU/GPU load, memory, power, fan, WiFi/LTE/tablet links, UPS battery (`yukart/host/*`) | | `orin-status` | `yukart-orin-status.service` | The Orin's health on the bus, once a second: temperatures, CPU/GPU load, memory, power, fan, WiFi (with SSID)/LTE/tablet links, UPS battery (`yukart/host/*`) |
```bash ```bash
scripts/deploy-recorder.sh # build, glibc check, install on the car Orin scripts/deploy-recorder.sh # build, glibc check, install on the car Orin
+25 -2
View File
@@ -1,6 +1,7 @@
//! Network links: WiFi, LTE and the USB link to the tablet. Up/down and rates //! Network links: WiFi, LTE and the USB link to the tablet. Up/down and rates
//! from sysfs counters; WiFi signal from /proc/net/wireless; the modem's own view //! from sysfs counters; WiFi signal from /proc/net/wireless, its SSID from `iw`;
//! from ModemManager, polled on a thread because `mmcli` can take seconds. //! the modem's own view from ModemManager, polled on a thread because `mmcli` can
//! take seconds.
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@@ -52,6 +53,18 @@ fn wifi_dbm(iface: &str) -> Option<f64> {
line.split_whitespace().nth(3)?.trim_end_matches('.').parse().ok() line.split_whitespace().nth(3)?.trim_end_matches('.').parse().ok()
} }
/// SSID of the associated network, from `iw dev <iface> link` ("\tSSID: name"); None when
/// not associated. iw prints non-printable bytes as \xNN escapes, which are kept as they are.
fn wifi_ssid(iface: &str) -> Option<String> {
let out = Command::new("iw").args(["dev", iface, "link"]).output().ok()?;
let text = String::from_utf8_lossy(&out.stdout);
let ssid = text.lines().find_map(|l| l.trim_start().strip_prefix("SSID: "))?;
Some(ssid.to_string())
}
/// How often the SSID is re-read while WiFi stays up; a change of network without a drop is rare.
const SSID_EVERY: Duration = Duration::from_secs(10);
#[derive(Default, Clone)] #[derive(Default, Clone)]
struct Modem { struct Modem {
state: String, state: String,
@@ -80,6 +93,7 @@ fn poll_modem() -> Modem {
pub struct Net { pub struct Net {
wifi: Option<String>, wifi: Option<String>,
wifi_rate: Rate, wifi_rate: Rate,
ssid: Option<(Instant, Option<String>)>,
lte_rate: Rate, lte_rate: Rate,
tablet_rate: Rate, tablet_rate: Rate,
modem: Arc<Mutex<Option<Modem>>>, modem: Arc<Mutex<Option<Modem>>>,
@@ -97,6 +111,7 @@ impl Net {
Net { Net {
wifi: wifi_iface(), wifi: wifi_iface(),
wifi_rate: Rate { prev: None }, wifi_rate: Rate { prev: None },
ssid: None,
lte_rate: Rate { prev: None }, lte_rate: Rate { prev: None },
tablet_rate: Rate { prev: None }, tablet_rate: Rate { prev: None },
modem, modem,
@@ -110,6 +125,14 @@ impl Net {
if let Some(w) = self.wifi.clone() { if let Some(w) = self.wifi.clone() {
let up = attr(&w, "operstate").as_deref() == Some("up"); let up = attr(&w, "operstate").as_deref() == Some("up");
out.push(("net_wifi_up", json!(up))); out.push(("net_wifi_up", json!(up)));
if !up {
self.ssid = None;
} else if self.ssid.as_ref().is_none_or(|(at, _)| at.elapsed() >= SSID_EVERY) {
self.ssid = Some((Instant::now(), wifi_ssid(&w)));
}
if let Some((_, Some(ssid))) = &self.ssid {
out.push(("net_wifi_ssid", json!(ssid)));
}
if let Some(dbm) = wifi_dbm(&w).filter(|_| up) { if let Some(dbm) = wifi_dbm(&w).filter(|_| up) {
out.push(("net_wifi_signal_dbm", json!(dbm))); out.push(("net_wifi_signal_dbm", json!(dbm)));
} }