Rust: Establishes core organ communication framework

Introduces `OrganFactory` to manage the creation and spawning of organs, running each in its own thread. `OrganSocket` provides the Brain's interface for sending timestamped commands to specific organs.

Configures new MPSC channels within the Brain to handle bidirectional communication with its organs, enabling command dispatch and feedback reception. The Heart is integrated as the first example organ within this architecture.
This commit is contained in:
Russell Gilbert 2026-03-13 15:47:48 +00:00
parent 33e7de18cf
commit 7633985535
5 changed files with 137 additions and 6 deletions

View file

@ -1,24 +1,41 @@
use tracing::{debug, info}; use tracing::{debug, info};
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};
use std::thread::JoinHandle;
use crate::lifecycle::{LifeState, LifecycleCommand, LifecycleCommandResponse, LifecycleReceipt}; use crate::lifecycle::{LifeState, LifecycleCommand, LifecycleCommandResponse, LifecycleReceipt};
use crate::lifecycle::LifeState::{Dying, Buried, Genisys}; 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};
pub struct Brain { pub struct Brain {
state:LifeState, state:LifeState,
divine_rx : Receiver<LifecycleCommand>, divine_rx : Receiver<LifecycleCommand>,
divine_tx: Sender<LifecycleReceipt> divine_tx: Sender<LifecycleReceipt>,
organ_rx: Receiver<BrainMessage>,
organ_tx: Sender<BrainMessage>,
organ_sockets: Vec<OrganSocket>,
} }
impl Brain { impl Brain {
pub fn new(divine_rx: Receiver<LifecycleCommand>, divine_tx: Sender<LifecycleReceipt>) -> Self { pub fn new(divine_rx: Receiver<LifecycleCommand>, divine_tx: Sender<LifecycleReceipt>) -> Self {
let (organ_tx, organ_rx) = std::sync::mpsc::channel::<BrainMessage>();
Self { Self {
state: LifeState::Dead, state: LifeState::Dead,
divine_rx, divine_rx,
divine_tx, divine_tx,
organ_rx,
organ_tx,
organ_sockets: Vec::new()
} }
} }
pub fn run(&mut self) { pub fn run(&mut self) {
self.execute_brain_loop();
self.build_organs();
}
fn execute_brain_loop(&mut self) {
loop { loop {
while let Ok(command) = self.divine_rx.try_recv() { while let Ok(command) = self.divine_rx.try_recv() {
self.handle_divine_command(command) self.handle_divine_command(command)
@ -92,5 +109,12 @@ impl Brain {
let _ = self.divine_tx.send(receipt); let _ = self.divine_tx.send(receipt);
} }
fn build_organs(& mut self) {
info!("Building organs...");
let factory = OrganFactory::new(self.organ_tx.clone());
info!("{} organs have been built and wired in.", self.organ_sockets.len());
}
} }

View file

@ -1,2 +1,4 @@
pub mod heart; pub mod heart;
pub mod organ; pub mod organ;
pub mod organ_socket;
pub mod organ_factory;

View file

@ -1,12 +1,29 @@
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use tracing::{info, instrument}; use tracing::{info, instrument};
use crate::protocols::{BrainMessage, OrganCommandEnvelope, OrganResponse}; use crate::protocols::{BrainMessage, OrganCommandEnvelope, OrganResponse};
pub struct Heart { pub struct Heart {
id: u32, id: u32,
rx: mpsc::Receiver<OrganCommandEnvelope>, brain_command_rx: mpsc::Receiver<OrganCommandEnvelope>,
tx: mpsc::Sender<BrainMessage>, feedback_to_brain_tx: mpsc::Sender<BrainMessage>,
}
impl Heart {
pub(crate) fn run_loop(&mut self) {
todo!()
}
}
impl Heart {
pub(crate) fn new(id: u32, rx: Receiver<OrganCommandEnvelope>, tx: Sender<BrainMessage>) -> Self {
Self {
id,
brain_command_rx: rx,
feedback_to_brain_tx: tx,
}
}
} }
impl Heart { impl Heart {
@ -14,7 +31,7 @@ impl Heart {
pub fn start(self) { pub fn start(self) {
info!("Heart listener active"); info!("Heart listener active");
while let Ok(envelope) = self.rx.recv() { while let Ok(envelope) = self.brain_command_rx.recv() {
// 1. Process the command (Logic goes here later) // 1. Process the command (Logic goes here later)
let response = OrganResponse::Ok; let response = OrganResponse::Ok;
@ -33,7 +50,7 @@ impl Heart {
// 4. Trace the reply and send // 4. Trace the reply and send
info!(?reply, "Sending response to Brain"); info!(?reply, "Sending response to Brain");
let _ = self.tx.send(reply); let _ = self.feedback_to_brain_tx.send(reply);
} }
} }
} }

View file

@ -0,0 +1,56 @@
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;
use crate::organs::organ_socket::OrganSocket;
use crate::organs::heart::Heart; // Assuming Heart is our first organ
use crate::protocols::{OrganCommandEnvelope, BrainMessage};
pub struct OrganFactory {
brain_tx: Sender<BrainMessage>,
}
impl OrganFactory {
pub(crate) fn new(brain_sender: Sender<BrainMessage>) -> Self {
Self {
brain_tx: brain_sender,
}
}
}
impl OrganFactory {
pub fn build_organs(
// The Brain's "Inbox" mouth - we'll clone this for each organ
brain_tx: Sender<BrainMessage>
) -> Vec<OrganSocket> {
let mut sockets = Vec::new();
// Let's spawn a Heart as an example (ID: 1)
// In a real factory, you might loop through a config list here
let heart_socket = Self::spawn_heart(1, brain_tx.clone());
sockets.push(heart_socket);
// Add more organs here...
// let lung_socket = Self::spawn_lung(2, brain_tx.clone());
tracing::info!(count = sockets.len(), "Organ collection built and threads spawned");
sockets
}
fn spawn_heart(id: u32, feedback_to_brain_tx: Sender<BrainMessage>) -> OrganSocket {
let (brain_command_to_organ_tx, brain_command_to_organ_rx) =
Self::get_organ_channels();
let socket = OrganSocket::new(id, brain_command_to_organ_tx);
thread::spawn(move || {
let mut heart = Heart::new(id, brain_command_to_organ_rx, feedback_to_brain_tx);
heart.run_loop();
});
socket
}
fn get_organ_channels() -> (Sender<OrganCommandEnvelope>, Receiver<OrganCommandEnvelope>) {
mpsc::channel::<OrganCommandEnvelope>()
}
}

View file

@ -0,0 +1,32 @@
use std::time::{SystemTime, UNIX_EPOCH};
use std::sync::mpsc::Sender;
use crate::protocols::{OrganCommand, OrganCommandEnvelope};
pub struct OrganSocket {
pub id: u32,
tx: Sender<OrganCommandEnvelope>,
}
impl OrganSocket {
pub fn new(id: u32, tx: Sender<OrganCommandEnvelope>) -> Self {
Self { id, tx }
}
pub fn send(&self, command: OrganCommand) {
// Here's the "Stamping" logic.
// The Brain just says "Beat", the Socket adds the "Metadata".
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
let envelope = OrganCommandEnvelope {
command,
issued_at: now,
};
if let Err(e) = self.tx.send(envelope) {
tracing::error!(organ_id = self.id, error = %e, "Socket delivery failed");
}
}
}