use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::JoinHandle;
use std::time::Duration;
use crate::board::BoardRow;
use crate::boundary::dispatch::Deps;
use crate::state::SnapshotCell;
use crate::ui_state::{Clock, UiState};
pub struct PilotCtx {
pub deps: Deps,
pub cell: SnapshotCell,
pub clock: Arc<dyn Clock>,
pub ui_path: PathBuf,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Move {
Reap {
row: BoardRow,
claimant: String,
since: String,
},
Spawn { row: BoardRow },
}
impl PilotCtx {
pub fn period(&self) -> Duration {
crate::state::latest_snapshot(&self.cell).cadence.full_sweep
}
pub fn pass(&self) -> bool {
let snapshot = crate::state::latest_snapshot(&self.cell);
if snapshot.fleet.is_empty() {
return false;
}
let ts = self.clock.stamp();
let now: i64 = ts.parse().unwrap_or(0);
let mut ui = UiState::open(self.ui_path.clone());
let board = crate::board::build(&snapshot, &ui, now);
for fleet in &board.fleet {
let Some(workspace) = snapshot.armed_path(&fleet.workspace) else {
continue;
};
if let Some(one) = plan(&snapshot, fleet, &workspace, &board.rows, now) {
return self.fire(&snapshot, &mut ui, &ts, &workspace, &one);
}
}
false
}
}
mod act;
mod plan;
pub use plan::plan;
pub struct Pilot {
stop: Arc<AtomicBool>,
handle: Option<JoinHandle<()>>,
}
impl Pilot {
pub fn spawn(ctx: PilotCtx) -> Self {
let stop = Arc::new(AtomicBool::new(false));
let flag = Arc::clone(&stop);
let handle = std::thread::spawn(move || {
while !flag.load(Ordering::Relaxed) {
ctx.pass();
std::thread::park_timeout(ctx.period());
}
});
Self {
stop,
handle: Some(handle),
}
}
}
impl Drop for Pilot {
fn drop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(handle) = self.handle.take() {
handle.thread().unpark();
let _ = handle.join();
}
}
}
#[cfg(test)]
mod tests;