Enhances lifecycle communication with detailed receipts

Replaces simple string messages sent from the Brain with a structured `LifecycleReceipt` object.

This receipt provides comprehensive feedback, including the original command, a specific response status (e.g., `Ok`, `Refused`), and the resulting `LifeState`. This change improves the clarity, type safety, and debuggability of inter-thread communication between the main God thread and the Brain.
This commit is contained in:
Russell Gilbert 2026-03-03 08:47:30 +00:00
parent b8137feea5
commit 500b26703e
4 changed files with 46 additions and 21 deletions

View file

@ -1 +1 @@
135 136

View file

@ -1,16 +1,16 @@
use tracing::info; use tracing::info;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};
use crate::lifecycle::{LifeState, LifecycleCommand}; use crate::lifecycle::{LifeState, LifecycleCommand, LifecycleCommandResponse, LifecycleReceipt};
use crate::lifecycle::LifeState::{Buried, Genisys}; use crate::lifecycle::LifeState::{Buried, Genisys};
pub struct Brain { pub struct Brain {
state:LifeState, state:LifeState,
divine_rx : Receiver<LifecycleCommand>, divine_rx : Receiver<LifecycleCommand>,
divine_tx: Sender<String> divine_tx: Sender<LifecycleReceipt>
} }
impl Brain { impl Brain {
pub fn new(divine_rx: Receiver<LifecycleCommand>, divine_tx: Sender<String>) -> Self { pub fn new(divine_rx: Receiver<LifecycleCommand>, divine_tx: Sender<LifecycleReceipt>) -> Self {
Self { Self {
state: LifeState::Dead, state: LifeState::Dead,
divine_rx, divine_rx,
@ -47,7 +47,14 @@ impl Brain {
return; return;
} }
self.report_to_god("REFUSED") self.refuse_command(command)
}
fn refuse_command(&mut self, command: LifecycleCommand) {
self.report_to_god(LifecycleReceipt{
command: command.clone(),
response: LifecycleCommandResponse::Refused,
new_state: self.state.clone()
})
} }
fn can_transition_lifecycle(&self, command: &LifecycleCommand) -> bool fn can_transition_lifecycle(&self, command: &LifecycleCommand) -> bool
@ -63,13 +70,17 @@ impl Brain {
false false
} }
fn set_lifecycle_state(&mut self, state: &LifecycleCommand) { fn set_lifecycle_state(&mut self, command: &LifecycleCommand) {
self.state = state.required_state; self.state = command.required_state;
self.report_to_god("OK"); self.report_to_god(LifecycleReceipt{
command: command.clone(),
response: LifecycleCommandResponse::Ok,
new_state: self.state
});
} }
fn report_to_god(&self, msg: &str) { fn report_to_god(&self, receipt: LifecycleReceipt) {
info!("Reporting to God {}", msg); info!("Reporting to God: Status = {:?}, NewState={:?}", receipt.response, receipt.new_state);
let _ = self.divine_tx.send(msg.to_string()); let _ = self.divine_tx.send(receipt);
} }
} }

View file

@ -15,8 +15,22 @@ pub enum LifeState {
Buried Buried
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LifecycleCommandResponse {
Ok,
Refused,
Failed
}
#[derive(Debug, Clone)]
pub struct LifecycleCommand { pub struct LifecycleCommand {
pub required_state: LifeState, pub required_state: LifeState,
pub command_time: Instant pub command_time: Instant
} }
#[derive(Debug, Clone)]
pub struct LifecycleReceipt {
pub command: LifecycleCommand,
pub response: LifecycleCommandResponse,
pub new_state: LifeState
}

View file

@ -10,7 +10,7 @@ use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use crate::brain::Brain; use crate::brain::Brain;
use crate::lifecycle::{LifeState, LifecycleCommand}; use crate::lifecycle::{LifeState, LifecycleCommand, LifecycleReceipt};
fn main() { fn main() {
let shutdown_requested = Arc::new(AtomicBool::new(false)); let shutdown_requested = Arc::new(AtomicBool::new(false));
@ -26,7 +26,7 @@ fn main() {
info!("God: Brain has been buried. Shutting down."); info!("God: Brain has been buried. Shutting down.");
} }
fn wait_for_death(tx: Sender<LifecycleCommand>, rx: Receiver<String>) { fn wait_for_death(tx: Sender<LifecycleCommand>, rx: Receiver<LifecycleReceipt>) {
info!("God: Resting for 10 seconds..."); info!("God: Resting for 10 seconds...");
thread::sleep(Duration::from_secs(10)); thread::sleep(Duration::from_secs(10));
@ -36,12 +36,12 @@ fn wait_for_death(tx: Sender<LifecycleCommand>, rx: Receiver<String>) {
command_time: Instant::now(), command_time: Instant::now(),
}); });
if let Ok(reply) = rx.recv() { if let Ok(receipt) = rx.recv() {
info!("Brain: {}", reply); info!("Brain: {:?}", receipt.response);
} }
} }
fn let_there_be_life(tx: &Sender<LifecycleCommand>, rx: &Receiver<String>) { fn let_there_be_life(tx: &Sender<LifecycleCommand>, rx: &Receiver<LifecycleReceipt>) {
// 3. Command: Genisys // 3. Command: Genisys
info!("God: Commanding Genisys..."); info!("God: Commanding Genisys...");
let _ = tx.send(LifecycleCommand { let _ = tx.send(LifecycleCommand {
@ -50,14 +50,14 @@ fn let_there_be_life(tx: &Sender<LifecycleCommand>, rx: &Receiver<String>) {
}); });
// Wait for Brain's "OK" // Wait for Brain's "OK"
if let Ok(reply) = rx.recv() { if let Ok(receipt) = rx.recv() {
info!("Brain: {}", reply); info!("Brain: {:?}", receipt.response);
} }
} }
fn spawn_brain() -> (Sender<LifecycleCommand>, Receiver<String>) { fn spawn_brain() -> (Sender<LifecycleCommand>, Receiver<LifecycleReceipt>) {
let (to_brain_tx, to_brain_rx) = mpsc::channel::<LifecycleCommand>(); let (to_brain_tx, to_brain_rx) = mpsc::channel::<LifecycleCommand>();
let (from_brain_tx, from_brain_rx) = mpsc::channel::<String>(); let (from_brain_tx, from_brain_rx) = mpsc::channel::<LifecycleReceipt>();
// The Brain takes one half of each pair // The Brain takes one half of each pair
thread::spawn(move || { thread::spawn(move || {