use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::time;
use tracing::{debug, info, warn};
use crate::client::WireBandClient;
use crate::frame;
use crate::symbols::EDGE_WDT_KICK;
fn unix_ts() -> f64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs_f64()
}
pub struct Watchdog {
interval: Duration,
device_path: String,
}
impl Watchdog {
pub fn new(interval: Duration) -> Self {
Self {
interval,
device_path: "/dev/watchdog".to_string(),
}
}
pub fn with_device(mut self, path: impl Into<String>) -> Self {
self.device_path = path.into();
self
}
pub async fn run(self, client: WireBandClient) {
info!(
interval_ms = self.interval.as_millis(),
device = %self.device_path,
"Watchdog started"
);
let mut ticker = time::interval(self.interval);
loop {
ticker.tick().await;
self.kick(&client).await;
}
}
async fn kick(&self, client: &WireBandClient) {
#[cfg(target_os = "linux")]
{
use std::io::Write;
match std::fs::OpenOptions::new().write(true).open(&self.device_path) {
Ok(mut f) => {
if let Err(e) = f.write_all(b"1") {
warn!(device = %self.device_path, err = %e, "Watchdog kick failed");
} else {
debug!(device = %self.device_path, "Hardware watchdog kicked");
}
}
Err(e) => {
debug!(device = %self.device_path, err = %e, "Hardware watchdog not available");
}
}
}
let ts = unix_ts();
let topic = "watchdog/kick".to_string();
let data = serde_json::json!({ "ts": ts });
let encoded = frame::encode(EDGE_WDT_KICK, &topic, &data);
client.buffer_event(topic, EDGE_WDT_KICK, encoded, ts).await;
debug!("Watchdog kick event buffered");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_device_path() {
let w = Watchdog::new(Duration::from_secs(30));
assert_eq!(w.device_path, "/dev/watchdog");
}
#[test]
fn custom_device_path() {
let w = Watchdog::new(Duration::from_secs(10))
.with_device("/dev/watchdog0");
assert_eq!(w.device_path, "/dev/watchdog0");
}
}