diff --git a/src/rustylee/build_number.txt b/src/rustylee/build_number.txt index 405e057..af21323 100644 --- a/src/rustylee/build_number.txt +++ b/src/rustylee/build_number.txt @@ -1 +1 @@ -127 \ No newline at end of file +133 \ No newline at end of file diff --git a/src/rustylee/src/brain.rs b/src/rustylee/src/brain.rs index 503dc22..e7ad1ea 100644 --- a/src/rustylee/src/brain.rs +++ b/src/rustylee/src/brain.rs @@ -1,51 +1,75 @@ -pub mod state; -pub mod led_pump; - -pub use self::state::State; use tracing::info; -use std::thread::JoinHandle; -use self::led_pump::LedPump; +use std::sync::mpsc::{Receiver, Sender}; +use crate::lifecycle::{LifeState, LifecycleCommand}; +use crate::lifecycle::LifeState::{Buried, Genisys}; pub struct Brain { - pub state: State, - device_threads: Vec>, - led_pump: LedPump + state:LifeState, + divine_rx : Receiver, + divine_tx: Sender } impl Brain { - pub fn new() -> Self { + pub fn new(divine_rx: Receiver, divine_tx: Sender) -> Self { Self { - state: State::Starting, - device_threads: Vec::new(), - led_pump: LedPump::new() + state: LifeState::Dead, + divine_rx, + divine_tx, } } + pub fn run(&mut self) { + loop { + while let Ok(command) = self.divine_rx.try_recv() { + self.handle_divine_command(command) + } + if self.state == LifeState::Buried { + break; + } - pub fn request_shutdown(&mut self) { - if self.state == State::Running { - self.state = State::Stopping; + // TODO: Drain the Organ Feedback Mailbox + // (We will add this once we define the Organ channels) + + Self::rest() } } - pub fn update(&mut self) { - match self.state { - State::Starting => { - // For now, we just instantly transition - self.state = State::Running; - } - State::Running => { - self.led_pump.beat(); - } - State::Stopping => { - info!("Brain: Waiting for all threads to join..."); - while let Some(handle) = self.device_threads.pop() { - let _ = handle.join(); - } - self.state = State::Stopped; - } - State::Stopped => {} - } + fn rest() { + std::thread::sleep(std::time::Duration::from_millis(1)) } -} \ No newline at end of file + + fn handle_divine_command(&mut self, command: LifecycleCommand) { + info!("God has commanded {:?}", command); + + if self.can_transition_lifecycle(&command) { + self.set_lifecycle_state(&command); + return; + } + + self.report_to_god("REFUSED") + } + + fn can_transition_lifecycle(&self, command: &LifecycleCommand) -> bool + { + + if (command.required_state == Buried) { + return true + } + + if self.state == LifeState::Dead && command.required_state == LifeState::Genisys { + return true + } + + false + } + fn set_lifecycle_state(&mut self, state: &LifecycleCommand) { + self.state = state.required_state; + self.report_to_god("OK"); + } + fn report_to_god(&self, msg: &str) { + info!("Reporting to God {}", msg); + let _ = self.divine_tx.send(msg.to_string()); + } +} + diff --git a/src/rustylee/src/brain/led_pump.rs b/src/rustylee/src/brain/led_pump.rs deleted file mode 100644 index 9360466..0000000 --- a/src/rustylee/src/brain/led_pump.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::time::{Duration, Instant}; -use tracing::debug; -pub struct LedPump { - last_beat: Option, - interval: Duration, -} - -impl LedPump { - pub fn new() -> LedPump { - Self{ - last_beat: None, - interval: Duration::from_millis(500), - } - } - - pub fn beat(&mut self) { - let now = Instant::now(); - - - let should_beat = self.last_beat.map_or( - true, |last_beat| now.duration_since(last_beat) > self.interval); - - if should_beat { - self.last_beat = Some(now); - debug!("LED Pump beat."); - } - } -} \ No newline at end of file diff --git a/src/rustylee/src/lifecycle.rs b/src/rustylee/src/lifecycle.rs new file mode 100644 index 0000000..3d35ff2 --- /dev/null +++ b/src/rustylee/src/lifecycle.rs @@ -0,0 +1,22 @@ +use std::time::Instant; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LifeState { + Dead, + Genisys, + Sleeping, + Wakening, + Awake, + DeepThought, + ActionFocused, + Flight, + Panic, + Dying, + Buried +} + +#[derive(Debug)] +pub struct LifecycleCommand { + pub required_state: LifeState, + pub command_time: Instant +} \ No newline at end of file diff --git a/src/rustylee/src/main.rs b/src/rustylee/src/main.rs index 2e86efd..4e799a8 100644 --- a/src/rustylee/src/main.rs +++ b/src/rustylee/src/main.rs @@ -1,13 +1,18 @@ -mod brain; -use brain::{Brain, State}; +pub mod lifecycle; +pub mod brain; + use tracing::info; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use std::sync::mpsc::{self, Receiver, Sender}; -use std::{thread, time::Duration}; +use std::sync::mpsc; +use std::thread; +use std::time::{Duration, Instant}; + +use crate::brain::Brain; +use crate::lifecycle::{LifeState, LifecycleCommand}; fn main() { - let semantic_version = env!("CARGO_PKG_VERSION").to_string(); + let _semantic_version = env!("CARGO_PKG_VERSION").to_string(); let shutdown_requested = Arc::new(AtomicBool::new(false)); let s = shutdown_requested.clone(); @@ -19,23 +24,45 @@ fn main() { s.store(true, Ordering::SeqCst); }).expect("Error setting signal handler"); - let mut robot_brain = Brain::new(); + // 1. Setup the Divine Nerves + let (to_brain_tx, to_brain_rx) = mpsc::channel::(); + let (from_brain_tx, from_brain_rx) = mpsc::channel::(); - while robot_brain.state != State::Stopped { + // 2. Spawn the Brain thread + thread::spawn(move || { + let mut brain = Brain::new(to_brain_rx, from_brain_tx); + brain.run(); + }); - // If the signal handler tripped the flag, tell the brain to stop - if shutdown_requested.load(Ordering::SeqCst) && robot_brain.state == State::Running { - robot_brain.request_shutdown(); - } + // 3. Command: Genisys + info!("God: Commanding Genisys..."); + let _ = to_brain_tx.send(LifecycleCommand { + required_state: LifeState::Genisys, + command_time: Instant::now(), + }); - // Run one "tick" of brain logic - robot_brain.update(); - - info!("Main Loop: I'm alive! State: {:?}", robot_brain.state); - thread::sleep(Duration::from_millis(500)); + // Wait for Brain's "OK" + if let Ok(reply) = from_brain_rx.recv() { + info!("Brain: {}", reply); } - info!("Main Loop: Brain has Stopped. Exiting cleanly."); + // 4. Wait two seconds + info!("God: Resting for 2 seconds..."); + thread::sleep(Duration::from_secs(2)); + + // 5. Command: Buried + info!("God: Commanding Burial..."); + let _ = to_brain_tx.send(LifecycleCommand { + required_state: LifeState::Buried, + command_time: Instant::now(), + }); + + // Wait for Brain's final "OK" + if let Ok(reply) = from_brain_rx.recv() { + info!("Brain: {}", reply); + } + + info!("God: Brain has been buried. Shutting down."); } fn setup_logging() {