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) {
|
||||
if let Some(socket) = self.organ_sockets.first() {
|
||||
let envelope = OrganCommandEnvelope {
|
||||
command: OrganCommand::Beat(0),
|
||||
command: OrganCommand::HeartBeat(0),
|
||||
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 protocols;
|
||||
pub mod system;
|
||||
pub mod macros;
|
||||
|
||||
use std::fmt::Alignment::Left;
|
||||
use tracing::{debug, info};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
pub mod heart;
|
||||
pub mod organ;
|
||||
pub mod organ_socket;
|
||||
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::organs::parenchyma::Parenchyma;
|
||||
use crate::protocols::{BrainMessage, OrganCommand, OrganCommandEnvelope, OrganResponse};
|
||||
use crate::system::time::Time;
|
||||
|
||||
|
||||
|
||||
pub trait Organ: Send {
|
||||
/// Returns the immutable U32 ID assigned by the factory.
|
||||
fn id(&self) -> u32;
|
||||
}
|
||||
// pub trait Organ: Send {
|
||||
// /// Returns the immutable U32 ID assigned by the factory.
|
||||
// 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::thread;
|
||||
use crate::lifecycle::LifeState;
|
||||
use crate::organs::led_pump::LedPump;
|
||||
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};
|
||||
|
||||
pub struct OrganFactory {
|
||||
brain_tx: Sender<BrainMessage>,
|
||||
}
|
||||
|
||||
static NEXT_ORGAN_ID: AtomicU32 = AtomicU32::new(1000000);
|
||||
|
||||
impl OrganFactory {
|
||||
pub(crate) fn new(brain_sender: Sender<BrainMessage>) -> Self {
|
||||
Self {
|
||||
|
|
@ -18,24 +23,44 @@ 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> {
|
||||
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");
|
||||
|
||||
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 (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 || {
|
||||
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();
|
||||
});
|
||||
|
||||
|
|
|
|||
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;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum OrganCommand {
|
||||
// Sleep,
|
||||
Waken,
|
||||
// Pause,
|
||||
// Resume,
|
||||
Beat (u32),
|
||||
HeartBeat(u32),
|
||||
// ChangeSpeed (u32),
|
||||
// GoFaster(u32),
|
||||
// GoSlower(u32),
|
||||
|
|
@ -22,8 +23,26 @@ pub enum OrganResponse {
|
|||
Ignored,
|
||||
Rejected
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum TelemetryControlPlaneData {
|
||||
AppendagePosition {
|
||||
space: Point3D,
|
||||
time: u64,
|
||||
},
|
||||
BeatIntervalMillis(u32),
|
||||
}
|
||||
|
||||
#[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 command: OrganCommand,
|
||||
pub issued_at: u64
|
||||
|
|
@ -35,4 +54,5 @@ pub struct BrainMessage {
|
|||
pub responded_at: u64,
|
||||
pub organ_id: u32,
|
||||
pub response: OrganResponse,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue