Compare commits

..

No commits in common. "4acf07f389ae75d113c2f9b6dca3a3172dff80d1" and "c201f40d65a49ea664568ffba149f01a79f915eb" have entirely different histories.

7 changed files with 87 additions and 140 deletions

View file

@ -8,6 +8,3 @@ build = "build.rs"
ctrlc = "3.5.2"
tracing = "0.1"
tracing-subscriber = { version = "0.3.22", features = ["env-filter", "fmt"] }
[features]
increment = []

View file

@ -1,17 +1,14 @@
use std::fs;
use std::env;
fn main() {
println!("cargo:rerun-if-changed=src/");
let pkg_version = env!("CARGO_PKG_VERSION");
let content = fs::read_to_string("build_number.txt").unwrap_or_else(|_| "0".to_string());
let mut content = fs::read_to_string("build_number.txt").unwrap_or("0".to_string());
let mut build_num: u32 = content.trim().parse().unwrap_or(0);
if env::var("CARGO_FEATURE_INCREMENT").is_ok() {
build_num += 1;
let _ = fs::write("build_number.txt", build_num.to_string());
}
let full_version = format!("{}.{}", pkg_version, build_num);
println!("cargo:rustc-env=FULL_VERSION={}.{}", pkg_version, build_num);
fs::write("build_number.txt", build_num.to_string()).unwrap();
println!("cargo:rustc-env=FULL_VERSION={}", full_version);
}

View file

@ -1 +1 @@
133
98

View file

@ -1,75 +1,49 @@
pub mod state;
pub mod led_pump;
pub use self::state::State;
use tracing::info;
use std::sync::mpsc::{Receiver, Sender};
use crate::lifecycle::{LifeState, LifecycleCommand};
use crate::lifecycle::LifeState::{Buried, Genisys};
use std::thread::JoinHandle;
use self::led_pump::LedPump;
pub struct Brain {
state:LifeState,
divine_rx : Receiver<LifecycleCommand>,
divine_tx: Sender<String>
pub state: State,
device_threads: Vec<JoinHandle<()>>,
led_pump: LedPump
}
impl Brain {
pub fn new(divine_rx: Receiver<LifecycleCommand>, divine_tx: Sender<String>) -> Self {
pub fn new() -> Self {
Self {
state: LifeState::Dead,
divine_rx,
divine_tx,
state: State::Starting,
device_threads: Vec::new(),
led_pump: LedPump::new()
}
}
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;
}
// TODO: Drain the Organ Feedback Mailbox
// (We will add this once we define the Organ channels)
Self::rest()
pub fn request_shutdown(&mut self) {
if self.state == State::Running {
self.state = State::Stopping;
}
}
fn rest() {
std::thread::sleep(std::time::Duration::from_millis(1))
pub fn update(&mut self) {
match self.state {
State::Starting => {
// For now, we just instantly transition
self.state = State::Running;
}
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;
State::Running => {
self.led_pump.beat();
}
self.report_to_god("REFUSED")
State::Stopping => {
info!("Brain: Waiting for all threads to join...");
while let Some(handle) = self.device_threads.pop() {
let _ = handle.join();
}
fn can_transition_lifecycle(&self, command: &LifecycleCommand) -> bool
{
if (command.required_state == Buried) {
return true
self.state = State::Stopped;
}
State::Stopped => {}
}
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());
}
}

View file

@ -0,0 +1,28 @@
use std::time::{Duration, Instant};
use tracing::debug;
pub struct LedPump {
last_beat: Option<Instant>,
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.");
}
}
}

View file

@ -1,22 +0,0 @@
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
}

View file

@ -1,18 +1,13 @@
pub mod lifecycle;
pub mod brain;
mod brain;
use brain::{Brain, State};
use tracing::info;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, Instant};
use crate::brain::Brain;
use crate::lifecycle::{LifeState, LifecycleCommand};
use std::sync::mpsc::{self, Receiver, Sender};
use std::{thread, time::Duration};
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();
@ -24,45 +19,23 @@ fn main() {
s.store(true, Ordering::SeqCst);
}).expect("Error setting signal handler");
// 1. Setup the Divine Nerves
let (to_brain_tx, to_brain_rx) = mpsc::channel::<LifecycleCommand>();
let (from_brain_tx, from_brain_rx) = mpsc::channel::<String>();
let mut robot_brain = Brain::new();
// 2. Spawn the Brain thread
thread::spawn(move || {
let mut brain = Brain::new(to_brain_rx, from_brain_tx);
brain.run();
});
while robot_brain.state != State::Stopped {
// 3. Command: Genisys
info!("God: Commanding Genisys...");
let _ = to_brain_tx.send(LifecycleCommand {
required_state: LifeState::Genisys,
command_time: Instant::now(),
});
// Wait for Brain's "OK"
if let Ok(reply) = from_brain_rx.recv() {
info!("Brain: {}", reply);
// 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();
}
// 4. Wait two seconds
info!("God: Resting for 2 seconds...");
thread::sleep(Duration::from_secs(2));
// Run one "tick" of brain logic
robot_brain.update();
// 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!("Main Loop: I'm alive! State: {:?}", robot_brain.state);
thread::sleep(Duration::from_millis(500));
}
info!("God: Brain has been buried. Shutting down.");
info!("Main Loop: Brain has Stopped. Exiting cleanly.");
}
fn setup_logging() {