Compare commits
5 commits
500b26703e
...
dbb52ccbb2
| Author | SHA1 | Date | |
|---|---|---|---|
| dbb52ccbb2 | |||
| 7633985535 | |||
| 33e7de18cf | |||
| ee01746956 | |||
| 58e1197ff4 |
12 changed files with 263 additions and 25 deletions
|
|
@ -1 +1 @@
|
|||
136
|
||||
146
|
||||
|
|
@ -1,24 +1,41 @@
|
|||
use tracing::info;
|
||||
use tracing::{debug, info};
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use std::thread::JoinHandle;
|
||||
use crate::lifecycle::{LifeState, LifecycleCommand, LifecycleCommandResponse, LifecycleReceipt};
|
||||
use crate::lifecycle::LifeState::{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 {
|
||||
state:LifeState,
|
||||
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 {
|
||||
pub fn new(divine_rx: Receiver<LifecycleCommand>, divine_tx: Sender<LifecycleReceipt>) -> Self {
|
||||
let (organ_tx, organ_rx) = std::sync::mpsc::channel::<BrainMessage>();
|
||||
|
||||
Self {
|
||||
state: LifeState::Dead,
|
||||
divine_rx,
|
||||
divine_tx,
|
||||
organ_rx,
|
||||
organ_tx,
|
||||
organ_sockets: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
self.execute_brain_loop();
|
||||
self.build_organs();
|
||||
}
|
||||
|
||||
fn execute_brain_loop(&mut self) {
|
||||
loop {
|
||||
while let Ok(command) = self.divine_rx.try_recv() {
|
||||
self.handle_divine_command(command)
|
||||
|
|
@ -36,14 +53,22 @@ impl Brain {
|
|||
}
|
||||
|
||||
fn rest() {
|
||||
std::thread::sleep(std::time::Duration::from_millis(1))
|
||||
debug!("Brain is resting.");
|
||||
std::thread::sleep(std::time::Duration::from_millis(200))
|
||||
}
|
||||
|
||||
fn handle_divine_command(&mut self, command: LifecycleCommand) {
|
||||
info!("God has commanded {:?}", command);
|
||||
|
||||
if self.can_transition_lifecycle(&command) {
|
||||
self.set_lifecycle_state(&command);
|
||||
if command.required_state == Dying
|
||||
{
|
||||
self.set_lifecycle_state(&command, Buried);
|
||||
} else {
|
||||
|
||||
self.set_lifecycle_state(&command, command.required_state);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -60,7 +85,7 @@ impl Brain {
|
|||
fn can_transition_lifecycle(&self, command: &LifecycleCommand) -> bool
|
||||
{
|
||||
|
||||
if (command.required_state == Buried) {
|
||||
if command.required_state == Buried || command.required_state == Dying {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -70,17 +95,26 @@ impl Brain {
|
|||
|
||||
false
|
||||
}
|
||||
fn set_lifecycle_state(&mut self, command: &LifecycleCommand) {
|
||||
self.state = command.required_state;
|
||||
fn set_lifecycle_state(&mut self, command: &LifecycleCommand, new_state: LifeState) {
|
||||
self.state = new_state;
|
||||
self.report_to_god(LifecycleReceipt{
|
||||
command: command.clone(),
|
||||
response: LifecycleCommandResponse::Ok,
|
||||
new_state: self.state
|
||||
});
|
||||
}
|
||||
|
||||
fn report_to_god(&self, receipt: LifecycleReceipt) {
|
||||
info!("Reporting to God: Status = {:?}, NewState={:?}", receipt.response, receipt.new_state);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum State {
|
||||
Starting,
|
||||
Running,
|
||||
Stopping,
|
||||
Stopped,
|
||||
}
|
||||
6
src/rustylee/src/coordinates.rs
Normal file
6
src/rustylee/src/coordinates.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Point3D {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ pub enum LifeState {
|
|||
Flight,
|
||||
Panic,
|
||||
Dying,
|
||||
Buried
|
||||
Buried,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
@ -34,3 +34,4 @@ pub struct LifecycleReceipt {
|
|||
pub response: LifecycleCommandResponse,
|
||||
pub new_state: LifeState
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +1,43 @@
|
|||
pub mod lifecycle;
|
||||
pub mod brain;
|
||||
pub mod organs;
|
||||
pub mod coordinates;
|
||||
pub mod protocols;
|
||||
|
||||
use tracing::info;
|
||||
use std::fmt::Alignment::Left;
|
||||
use tracing::{info, debug};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use tracing_subscriber::registry;
|
||||
use crate::brain::Brain;
|
||||
use crate::lifecycle::{LifeState, LifecycleCommand, LifecycleReceipt};
|
||||
|
||||
fn main() {
|
||||
let shutdown_requested = Arc::new(AtomicBool::new(false));
|
||||
|
||||
setup_logging();
|
||||
info!("RustyLee: {}", env!("FULL_VERSION"));
|
||||
|
||||
setup_os_signal_handler(shutdown_requested.clone());
|
||||
let (tx, rx) = spawn_brain();
|
||||
setup_os_signal_handler(tx.clone());
|
||||
let_there_be_life(&tx, &rx);
|
||||
wait_for_death(tx, rx);
|
||||
|
||||
info!("God is watching.");
|
||||
loop{
|
||||
if let Ok(receipt) = rx.recv() {
|
||||
debug!("Brain status: {:?}", receipt.new_state);
|
||||
|
||||
if receipt.new_state == LifeState::Dead || receipt.new_state == LifeState::Buried {
|
||||
break;
|
||||
} else {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
// wait_for_death(tx, rx);
|
||||
|
||||
info!("God: Brain has been buried. Shutting down.");
|
||||
}
|
||||
|
|
@ -69,11 +85,14 @@ fn spawn_brain() -> (Sender<LifecycleCommand>, Receiver<LifecycleReceipt>) {
|
|||
(to_brain_tx, from_brain_rx)
|
||||
}
|
||||
|
||||
fn setup_os_signal_handler(s: Arc<AtomicBool>) {
|
||||
fn setup_os_signal_handler(tx: Sender<LifecycleCommand>) {
|
||||
info!("Setting up SIGTERM/SIGINT handling.");
|
||||
ctrlc::set_handler(move || {
|
||||
info!("[Signal] Shutdown signal caught!");
|
||||
s.store(true, Ordering::SeqCst);
|
||||
let _ = tx.send(LifecycleCommand {
|
||||
required_state: LifeState::Dying,
|
||||
command_time: Instant::now(),
|
||||
});
|
||||
}).expect("Error setting signal handler.");
|
||||
}
|
||||
|
||||
|
|
|
|||
4
src/rustylee/src/organs.rs
Normal file
4
src/rustylee/src/organs.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod heart;
|
||||
pub mod organ;
|
||||
pub mod organ_socket;
|
||||
pub mod organ_factory;
|
||||
48
src/rustylee/src/organs/heart.rs
Normal file
48
src/rustylee/src/organs/heart.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
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};
|
||||
|
||||
pub struct Heart {
|
||||
id: u32,
|
||||
brain_command_rx: mpsc::Receiver<OrganCommandEnvelope>,
|
||||
feedback_to_brain_tx: mpsc::Sender<BrainMessage>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
#[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() {
|
||||
// 1. Process the command (Logic goes here later)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/rustylee/src/organs/organ.rs
Normal file
9
src/rustylee/src/organs/organ.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use crate::coordinates::Point3D;
|
||||
use crate::lifecycle::LifeState;
|
||||
|
||||
|
||||
|
||||
pub trait Organ: Send {
|
||||
/// Returns the immutable U32 ID assigned by the factory.
|
||||
fn id(&self) -> u32;
|
||||
}
|
||||
56
src/rustylee/src/organs/organ_factory.rs
Normal file
56
src/rustylee/src/organs/organ_factory.rs
Normal 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.start();
|
||||
});
|
||||
|
||||
socket
|
||||
}
|
||||
|
||||
fn get_organ_channels() -> (Sender<OrganCommandEnvelope>, Receiver<OrganCommandEnvelope>) {
|
||||
mpsc::channel::<OrganCommandEnvelope>()
|
||||
}
|
||||
}
|
||||
32
src/rustylee/src/organs/organ_socket.rs
Normal file
32
src/rustylee/src/organs/organ_socket.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/rustylee/src/protocols.rs
Normal file
36
src/rustylee/src/protocols.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use crate::coordinates::Point3D;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OrganCommand {
|
||||
Sleep,
|
||||
Waken,
|
||||
Pause,
|
||||
Resume,
|
||||
Beat (u32),
|
||||
ChangeSpeed (u32),
|
||||
GoFaster(u32),
|
||||
GoSlower(u32),
|
||||
MoveTo {
|
||||
relative_to_center: Point3D,
|
||||
speed: u32
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OrganResponse {
|
||||
Ok,
|
||||
Rejected
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrganCommandEnvelope {
|
||||
pub command: OrganCommand,
|
||||
pub issued_at: u64
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BrainMessage {
|
||||
pub organ_command: OrganCommandEnvelope,
|
||||
pub responded_at: u64,
|
||||
pub response: OrganResponse,
|
||||
}
|
||||
Loading…
Reference in a new issue