Refactors organ system for component modularity

Introduces a `Parenchyma` trait to represent essential functional tissues within an organ, enabling a component-based architecture.

Transforms the `Heart` into a generic `Organ` capable of hosting multiple `Parenchyma` components, centralizing command processing. Adds a new `LedPump` parenchyma as an example component.

Includes an `impl_identifiable!` macro for streamlined implementation of the `Parenchyma` trait and establishes an ID generation system for organs and their components. Renames `OrganCommand::Beat` to `OrganCommand::HeartBeat` for improved clarity.
This commit is contained in:
Russell Gilbert 2026-03-26 08:55:17 +00:00
parent 2439de214b
commit d5ad70666e
12 changed files with 238 additions and 105 deletions

View file

@ -1 +1 @@
1.0.70 1.0.71

View file

@ -127,7 +127,7 @@ impl Brain {
fn request_heart_beat(&self) { fn request_heart_beat(&self) {
if let Some(socket) = self.organ_sockets.first() { if let Some(socket) = self.organ_sockets.first() {
let envelope = OrganCommandEnvelope { let envelope = OrganCommandEnvelope {
command: OrganCommand::Beat(0), command: OrganCommand::HeartBeat(0),
issued_at: Time::time_stamp_millis(), issued_at: Time::time_stamp_millis(),
}; };

View file

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

View file

@ -0,0 +1,12 @@
#[macro_export]
macro_rules! impl_identifiable {
($target:ident, { $($body:tt)* }) => {
impl Parenchyma for $target {
fn id(&self) -> u32 {
self.id
}
$($body)*
}
};
}

View file

@ -4,6 +4,7 @@ pub mod organs;
pub mod coordinates; pub mod coordinates;
pub mod protocols; pub mod protocols;
pub mod system; pub mod system;
pub mod macros;
use std::fmt::Alignment::Left; use std::fmt::Alignment::Left;
use tracing::{debug, info}; use tracing::{debug, info};

View file

@ -1,4 +1,6 @@
pub mod heart;
pub mod organ; pub mod organ;
pub mod organ_socket; pub mod organ_socket;
pub mod organ_factory; pub mod organ_factory;
pub mod led_pump;
pub mod parenchyma;

View file

@ -1,88 +0,0 @@
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use tracing::{debug, info, instrument};
use crate::lifecycle::LifeState;
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,
life_state: LifeState,
}
impl Heart {
pub(crate) fn new(id: u32, initial_life_state: LifeState, rx: Receiver<OrganCommandEnvelope>, tx: Sender<BrainMessage>) -> Self {
Self {
id,
brain_command_rx: rx,
feedback_to_brain_tx: tx,
last_beat_time: 0,
timestamp: 0,
life_state: initial_life_state,
}
}
#[instrument(skip(self), fields(heart_id = self.id))]
pub fn start(&mut self) {
info!("Heart listener active");
while let Ok(envelope) = self.brain_command_rx.recv() {
self.timestamp = Time::time_stamp_millis();
debug!("Received brain command: {:?}", envelope);
match envelope.command {
OrganCommand::Waken => {
self.wake_up(envelope);
}
OrganCommand::Beat(_) => {
if self.ready_to_beat() {
self.beat(envelope);
}
}
_ => {
self.send_response_brain(envelope, OrganResponse::Ignored)
}
}
}
}
fn ready_to_beat(&self) -> bool {
self.is_ready_state() && self.timestamp >= self.last_beat_time + 500
}
fn is_ready_state(&self) -> bool {
self.life_state == LifeState::Awake
}
fn beat(&mut self, command_envelope: OrganCommandEnvelope) {
self.last_beat_time = self.timestamp;
debug!("Beat time: {}", self.last_beat_time);
self.send_response_brain(command_envelope, OrganResponse::Ok);
}
fn wake_up(&mut self, command_envelope: OrganCommandEnvelope) {
self.life_state = LifeState::Awake;
debug!("Awake");
self.send_response_brain(command_envelope, OrganResponse::Ok);
}
fn send_response_brain(&self, command_envelope: OrganCommandEnvelope, response: OrganResponse) {
let reply = BrainMessage {
organ_command: command_envelope,
responded_at: Time::time_stamp_millis(),
organ_id: self.id,
response,
};
info!(?reply, "Sending response to Brain");
let _ = self.feedback_to_brain_tx.send(reply);
}
}

View file

@ -0,0 +1,28 @@
use crate::impl_identifiable;
use crate::organs::parenchyma::Parenchyma;
use crate::protocols::{OrganCommand, OrganCommandEnvelope, OrganResponse};
pub struct LedPump {
id: u32,
last_beat: u128,
}
impl_identifiable!(LedPump, {
fn do_work(&mut self, _envelope: OrganCommandEnvelope) -> OrganResponse {
// Your logic here
OrganResponse::Ok
}
fn get_supported_commands(&self) -> Vec<OrganCommand> {
vec![OrganCommand::HeartBeat(0)]
}
});
impl LedPump {
pub fn new(id: u32) -> Self {
Self {
id,
last_beat: 0
}
}
}

View file

@ -1,9 +1,128 @@
use crate::coordinates::Point3D; use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use tracing::{debug, info, instrument};
use crate::lifecycle::LifeState; use crate::lifecycle::LifeState;
use crate::organs::parenchyma::Parenchyma;
use crate::protocols::{BrainMessage, OrganCommand, OrganCommandEnvelope, OrganResponse};
use crate::system::time::Time;
pub trait Organ: Send { // pub trait Organ: Send {
/// Returns the immutable U32 ID assigned by the factory. // /// Returns the immutable U32 ID assigned by the factory.
fn id(&self) -> u32; // fn id(&self) -> u32;
//
// fn add_parenchyma(&mut self, parenchyma: Box<dyn Parenchyma>);
// }
pub struct Organ {
id: u32,
brain_command_rx: mpsc::Receiver<OrganCommandEnvelope>,
feedback_to_brain_tx: mpsc::Sender<BrainMessage>,
last_work_done_time: u64,
timestamp: u64,
life_state: LifeState,
parenchymas: Vec<Box<dyn Parenchyma>>,
supported_commands: Vec<OrganCommand>,
} }
impl Organ {
pub(crate) fn new(
id: u32, initial_life_state:
LifeState,
rx: Receiver<OrganCommandEnvelope>,
tx: Sender<BrainMessage>) -> Self {
Self {
id,
brain_command_rx: rx,
feedback_to_brain_tx: tx,
last_work_done_time: 0,
timestamp: 0,
life_state: initial_life_state,
parenchymas: Vec::new(),
supported_commands: Vec::new(),
}
}
#[instrument(skip(self), fields(heart_id = self.id))]
pub fn start(&mut self) {
info!("Heart listener active");
while let Ok(envelope) = self.brain_command_rx.recv() {
self.timestamp = Time::time_stamp_millis();
debug!("Received brain command: {:?}", envelope);
match envelope.command {
OrganCommand::Waken => {
self.wake_up(envelope);
}
OrganCommand::HeartBeat(_) => {
if self.is_ready() {
self.process_command(envelope);
}
}
_ => {
self.send_response_brain(envelope, OrganResponse::Ignored)
}
}
}
}
pub fn add_parenchyma(&mut self, component: Box<dyn Parenchyma>) {
let new_commands = component.get_supported_commands();
for command in new_commands {
if !self.supported_commands.contains(&command) {
self.supported_commands.push(command);
}
}
self.parenchymas.push(component);
}
pub fn is_supported(&self, command:OrganCommand) -> bool {
self.supported_commands.contains(&command)
}
fn is_ready(&self) -> bool {
self.is_ready_state() && self.timestamp >= self.last_work_done_time + 500
}
fn is_ready_state(&self) -> bool {
self.life_state == LifeState::Awake
}
fn process_command (&mut self, command_envelope: OrganCommandEnvelope) {
self.last_work_done_time = self.timestamp;
debug!("Work time: {}", self.last_work_done_time);
for part in &mut self.parenchymas {
part.do_work(command_envelope);
}
self.send_response_brain(command_envelope, OrganResponse::Ok);
}
fn wake_up(&mut self, command_envelope: OrganCommandEnvelope) {
self.life_state = LifeState::Awake;
debug!("Awake");
self.send_response_brain(command_envelope, OrganResponse::Ok);
}
fn send_response_brain(&self, command_envelope: OrganCommandEnvelope, response: OrganResponse) {
let reply = BrainMessage {
organ_command: command_envelope,
responded_at: Time::time_stamp_millis(),
organ_id: self.id,
response,
};
info!(?reply, "Sending response to Brain");
let _ = self.feedback_to_brain_tx.send(reply);
}
}

View file

@ -1,14 +1,19 @@
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::mpsc::{self, Receiver, Sender};
use std::thread; use std::thread;
use crate::lifecycle::LifeState; use crate::lifecycle::LifeState;
use crate::organs::led_pump::LedPump;
use crate::organs::organ_socket::OrganSocket; use crate::organs::organ_socket::OrganSocket;
use crate::organs::heart::Heart; // Assuming Heart is our first organ use crate::organs::organ;
use crate::organs::organ::Organ;
use crate::protocols::{OrganCommandEnvelope, BrainMessage}; use crate::protocols::{OrganCommandEnvelope, BrainMessage};
pub struct OrganFactory { pub struct OrganFactory {
brain_tx: Sender<BrainMessage>, brain_tx: Sender<BrainMessage>,
} }
static NEXT_ORGAN_ID: AtomicU32 = AtomicU32::new(1000000);
impl OrganFactory { impl OrganFactory {
pub(crate) fn new(brain_sender: Sender<BrainMessage>) -> Self { pub(crate) fn new(brain_sender: Sender<BrainMessage>) -> Self {
Self { Self {
@ -18,24 +23,44 @@ impl OrganFactory {
} }
impl OrganFactory { impl OrganFactory {
fn next_organ_id() -> u32 {
NEXT_ORGAN_ID.fetch_add(1000000, Ordering::SeqCst)
}
fn next_parenchyma_id(last_parenchyma_id_for_organ:u32) -> u32 {
last_parenchyma_id_for_organ+1
}
pub fn build_organs(brain_tx: Sender<BrainMessage>) -> Vec<OrganSocket> { pub fn build_organs(brain_tx: Sender<BrainMessage>) -> Vec<OrganSocket> {
let mut sockets = Vec::new(); let mut sockets = Vec::new();
let mut ids = 1..; // let mut ids = 1..;
sockets.push(Self::spawn_heart(ids.next().unwrap(), brain_tx.clone())); sockets.push(Self::spawn_heart(brain_tx.clone()));
tracing::info!(count = sockets.len(), "Organ collection built and threads spawned"); tracing::info!(count = sockets.len(), "Organ collection built and threads spawned");
sockets sockets
} }
fn spawn_heart(id: u32, feedback_to_brain_tx: Sender<BrainMessage>) -> OrganSocket { fn spawn_heart(feedback_to_brain_tx: Sender<BrainMessage>) -> OrganSocket {
let initial_life_state = LifeState::Dead; let initial_life_state = LifeState::Dead;
let (brain_command_to_organ_tx, brain_command_to_organ_rx) = Self::get_organ_channels(); let (brain_command_to_organ_tx, brain_command_to_organ_rx) = Self::get_organ_channels();
let socket = OrganSocket::new(id, initial_life_state, brain_command_to_organ_tx); let organ_id = OrganFactory::next_organ_id();
let socket = OrganSocket::new(organ_id, initial_life_state, brain_command_to_organ_tx);
thread::spawn(move || { thread::spawn(move || {
let mut heart = Heart::new(id, initial_life_state, brain_command_to_organ_rx, feedback_to_brain_tx); let mut heart = Organ::new(
organ_id,
initial_life_state,
brain_command_to_organ_rx,
feedback_to_brain_tx
);
let last_parenchyma_id = organ_id;
let led_pump = LedPump::new(OrganFactory::next_parenchyma_id(last_parenchyma_id));
heart.add_parenchyma(Box::new(led_pump));
heart.start(); heart.start();
}); });

View file

@ -0,0 +1,13 @@
/*
Parenchyma: The essential and distinctive functional tissue of an organ,
as distinguished from its connective tissue, blood vessels,
and nerves (the stroma)
*/
use crate::protocols::{OrganCommand, OrganCommandEnvelope, OrganResponse};
pub trait Parenchyma: Send {
fn id(&self) -> u32;
fn do_work (&mut self, command_envelope: OrganCommandEnvelope) -> OrganResponse;
fn get_supported_commands(&self) -> Vec<OrganCommand>;
}

View file

@ -1,12 +1,13 @@
use std::ffi::CString;
use crate::coordinates::Point3D; use crate::coordinates::Point3D;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum OrganCommand { pub enum OrganCommand {
// Sleep, // Sleep,
Waken, Waken,
// Pause, // Pause,
// Resume, // Resume,
Beat (u32), HeartBeat(u32),
// ChangeSpeed (u32), // ChangeSpeed (u32),
// GoFaster(u32), // GoFaster(u32),
// GoSlower(u32), // GoSlower(u32),
@ -22,8 +23,26 @@ pub enum OrganResponse {
Ignored, Ignored,
Rejected Rejected
} }
#[derive(Debug)]
pub enum TelemetryControlPlaneData {
AppendagePosition {
space: Point3D,
time: u64,
},
BeatIntervalMillis(u32),
}
#[derive(Debug)] #[derive(Debug)]
pub struct OrganMessage {
pub organ_id: u32,
pub organ_component_id: u32,
pub organ_command: Option<OrganCommand>,
pub last_response: Option<OrganResponse>,
pub message:Option<String>,
pub telemetry:Option<TelemetryControlPlaneData>,
}
#[derive(Debug, Clone, Copy)]
pub struct OrganCommandEnvelope { pub struct OrganCommandEnvelope {
pub command: OrganCommand, pub command: OrganCommand,
pub issued_at: u64 pub issued_at: u64
@ -36,3 +55,4 @@ pub struct BrainMessage {
pub organ_id: u32, pub organ_id: u32,
pub response: OrganResponse, pub response: OrganResponse,
} }