RustyLee: Adds organ heartbeat and system time module

Introduces a dedicated `system::time` module to provide consistent millisecond timestamps throughout the application.

Refactors the `Heart` organ to process `Beat` commands, incorporating a cooldown mechanism to regulate its rhythm.

Updates the `Brain` to periodically request a heartbeat from its organs when in an active lifecycle state, utilizing the new time utilities. Ensures organs are initialized before the brain begins its operational loop.
This commit is contained in:
Russell Gilbert 2026-03-13 18:29:36 +00:00
parent dbb52ccbb2
commit 793c8eda8d
8 changed files with 92 additions and 30 deletions

View file

@ -1 +1 @@
146
150

View file

@ -6,6 +6,7 @@ use crate::lifecycle::LifeState::{Dying, Buried, Genisys};
use crate::organs::organ_factory::OrganFactory;
use crate::organs::organ_socket::OrganSocket;
use crate::protocols::{BrainMessage, OrganCommand, OrganCommandEnvelope};
use crate::system::time::Time;
pub struct Brain {
state:LifeState,
@ -31,8 +32,8 @@ impl Brain {
}
pub fn run(&mut self) {
self.execute_brain_loop();
self.build_organs();
self.execute_brain_loop();
}
fn execute_brain_loop(&mut self) {
@ -41,12 +42,13 @@ impl Brain {
self.handle_divine_command(command)
}
if self.state == LifeState::Buried {
if self.state == Buried {
break;
}
// TODO: Drain the Organ Feedback Mailbox
// (We will add this once we define the Organ channels)
if self.is_ready() {
self.request_heart_beat();
}
Self::rest()
}
@ -95,6 +97,7 @@ impl Brain {
false
}
fn set_lifecycle_state(&mut self, command: &LifecycleCommand, new_state: LifeState) {
self.state = new_state;
self.report_to_god(LifecycleReceipt{
@ -112,9 +115,26 @@ impl Brain {
fn build_organs(& mut self) {
info!("Building organs...");
let factory = OrganFactory::new(self.organ_tx.clone());
self.organ_sockets = OrganFactory::build_organs(self.organ_tx.clone());
info!("{} organs have been built and wired in.", self.organ_sockets.len());
}
fn request_heart_beat(&self) {
if let Some(socket) = self.organ_sockets.first() {
let envelope = OrganCommandEnvelope {
command: OrganCommand::Beat(0),
issued_at: Time::time_stamp_millis(),
};
_ = socket.tx.send(envelope);
}
}
fn is_ready(&self) -> bool {
self.state != LifeState::Dead &&
self.state != Genisys &&
self.state != Buried
}
}

View file

@ -3,9 +3,10 @@ pub mod brain;
pub mod organs;
pub mod coordinates;
pub mod protocols;
pub mod system;
use std::fmt::Alignment::Left;
use tracing::{info, debug};
use tracing::{debug, info};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::sync::mpsc;

View file

@ -1,13 +1,15 @@
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::time::{SystemTime, UNIX_EPOCH};
use tracing::{info, instrument};
use crate::protocols::{BrainMessage, OrganCommandEnvelope, OrganResponse};
use tracing::{debug, info, instrument};
use crate::protocols::{BrainMessage, OrganCommand, OrganCommandEnvelope, OrganResponse};
use crate::system::time::Time;
pub struct Heart {
id: u32,
brain_command_rx: mpsc::Receiver<OrganCommandEnvelope>,
feedback_to_brain_tx: mpsc::Sender<BrainMessage>,
last_beat_time: u64,
timestamp: u64,
}
impl Heart {
@ -16,6 +18,8 @@ impl Heart {
id,
brain_command_rx: rx,
feedback_to_brain_tx: tx,
last_beat_time: 0,
timestamp: 0,
}
}
@ -24,25 +28,46 @@ impl Heart {
info!("Heart listener active");
while let Ok(envelope) = self.brain_command_rx.recv() {
// 1. Process the command (Logic goes here later)
self.timestamp = Time::time_stamp_millis();
debug!("Received brain command: {:?}", envelope);
match envelope.command {
OrganCommand::Beat(_) => {
if (self.ready_to_beat()) {
self.beat();
}
}
_ => {
}
}
let response = OrganResponse::Ok;
// 2. Capture the "now" timestamp
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
// 3. Package the reply
let reply = BrainMessage {
organ_command: envelope, // Move the original envelope back
responded_at: now,
response,
};
// 4. Trace the reply and send
info!(?reply, "Sending response to Brain");
let _ = self.feedback_to_brain_tx.send(reply);
self.send_response_brain(envelope, response);
}
}
fn ready_to_beat(&self) -> bool {
self.timestamp >= self.last_beat_time + 500
}
fn beat(&mut self) {
self.last_beat_time = self.timestamp;
info!("Beat time: {}", self.last_beat_time);
}
fn send_response_brain(&self, envelope: OrganCommandEnvelope, response: OrganResponse) {
let reply = BrainMessage {
organ_command: envelope,
responded_at: self.timestamp,
response,
};
info!(?reply, "Sending response to Brain");
let _ = self.feedback_to_brain_tx.send(reply);
}
}

View file

@ -4,7 +4,7 @@ use crate::protocols::{OrganCommand, OrganCommandEnvelope};
pub struct OrganSocket {
pub id: u32,
tx: Sender<OrganCommandEnvelope>,
pub tx: Sender<OrganCommandEnvelope>,
}
impl OrganSocket {

View file

@ -0,0 +1 @@
pub mod time;

View file

@ -0,0 +1,15 @@
use std::time::{SystemTime, UNIX_EPOCH};
pub struct Time {
}
impl Time {
pub fn time_stamp_millis() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}
}