Compare commits
No commits in common. "4acf07f389ae75d113c2f9b6dca3a3172dff80d1" and "c201f40d65a49ea664568ffba149f01a79f915eb" have entirely different histories.
4acf07f389
...
c201f40d65
7 changed files with 87 additions and 140 deletions
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
133
|
||||
98
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
if self.state == LifeState::Dead && command.required_state == LifeState::Genisys {
|
||||
return true
|
||||
State::Stopped => {}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
28
src/rustylee/src/brain/led_pump.rs
Normal file
28
src/rustylee/src/brain/led_pump.rs
Normal 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue