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:
parent
2439de214b
commit
d5ad70666e
12 changed files with 238 additions and 105 deletions
|
|
@ -1 +1 @@
|
||||||
1.0.70
|
1.0.71
|
||||||
|
|
@ -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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
1
src/rustylee/src/macros.rs
Normal file
1
src/rustylee/src/macros.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod identifiable;
|
||||||
12
src/rustylee/src/macros/identifiable.rs
Normal file
12
src/rustylee/src/macros/identifiable.rs
Normal 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)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
28
src/rustylee/src/organs/led_pump.rs
Normal file
28
src/rustylee/src/organs/led_pump.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
13
src/rustylee/src/organs/parenchyma.rs
Normal file
13
src/rustylee/src/organs/parenchyma.rs
Normal 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>;
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue