use std::thread;
use std::time::Duration;
use tokio::sync::watch;
use super::api::PrinterSource;
use crate::client::{LanMqttClient, WatchStep};
use crate::config::ResolvedTarget;
use crate::core::status::PrinterStatus;
const RECONNECT_DELAY: Duration = Duration::from_secs(3);
const STALL: Duration = Duration::from_secs(120);
pub struct LiveSource {
tx: watch::Sender<PrinterStatus>,
_keepalive: watch::Receiver<PrinterStatus>,
}
impl LiveSource {
pub fn connect(target: ResolvedTarget, interval: Option<Duration>) -> Self {
Self::spawn(move |tx| {
loop {
let client = LanMqttClient::new(target.clone()).with_timeout(STALL);
let _ = client.monitor(interval, |rs| {
if tx.send(PrinterStatus::from_state(rs.get())).is_err() {
WatchStep::Stop
} else {
WatchStep::Continue
}
});
if tx.receiver_count() == 0 {
break; }
thread::sleep(RECONNECT_DELAY);
}
})
}
fn spawn<R>(run: R) -> Self
where
R: FnOnce(watch::Sender<PrinterStatus>) + Send + 'static,
{
let (tx, rx) = watch::channel(PrinterStatus::default());
let worker = tx.clone();
thread::spawn(move || run(worker));
Self { tx, _keepalive: rx }
}
}
impl PrinterSource for LiveSource {
fn current(&self) -> PrinterStatus {
self.tx.borrow().clone()
}
fn subscribe(&self) -> watch::Receiver<PrinterStatus> {
self.tx.subscribe()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bridges_published_status_to_current_and_subscribers() {
let src = LiveSource::spawn(|tx| {
let _ = tx.send(PrinterStatus {
gcode_state: Some("RUNNING".to_string()),
mc_percent: Some(42),
..Default::default()
});
});
let mut got = None;
for _ in 0..200 {
let s = src.current();
if s.gcode_state.is_some() {
got = Some(s);
break;
}
thread::sleep(Duration::from_millis(10));
}
let s = got.expect("worker thread should publish a status");
assert_eq!(s.gcode_state.as_deref(), Some("RUNNING"));
assert_eq!(s.mc_percent, Some(42));
}
}