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

View file

@ -15,8 +15,22 @@ pub enum LifeState {
Buried
}
#[derive(Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LifecycleCommandResponse {
Ok,
Refused,
Failed
}
#[derive(Debug, Clone)]
pub struct LifecycleCommand {
pub required_state: LifeState,
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 crate::brain::Brain;
use crate::lifecycle::{LifeState, LifecycleCommand};
use crate::lifecycle::{LifeState, LifecycleCommand, LifecycleReceipt};
fn main() {
let shutdown_requested = Arc::new(AtomicBool::new(false));
@ -26,7 +26,7 @@ fn main() {
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...");
thread::sleep(Duration::from_secs(10));
@ -36,12 +36,12 @@ fn wait_for_death(tx: Sender<LifecycleCommand>, rx: Receiver<String>) {
command_time: Instant::now(),
});
if let Ok(reply) = rx.recv() {
info!("Brain: {}", reply);
if let Ok(receipt) = rx.recv() {
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
info!("God: Commanding Genisys...");
let _ = tx.send(LifecycleCommand {
@ -50,14 +50,14 @@ fn let_there_be_life(tx: &Sender<LifecycleCommand>, rx: &Receiver<String>) {
});
// Wait for Brain's "OK"
if let Ok(reply) = rx.recv() {
info!("Brain: {}", reply);
if let Ok(receipt) = rx.recv() {
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 (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
thread::spawn(move || {