Skip to content

Instantly share code, notes, and snippets.

@dgehriger
Created November 8, 2024 16:49
Show Gist options
  • Save dgehriger/f26e715605efa5d0e4fbb8cec5ef27dc to your computer and use it in GitHub Desktop.
Save dgehriger/f26e715605efa5d0e4fbb8cec5ef27dc to your computer and use it in GitHub Desktop.
Simple service architecture
use crossbeam_channel::{unbounded, Receiver, Sender};
use std::sync::{Arc, Mutex};
fn main() {
// create a multi-producer, multi-consumer channel
let (tx, rx) = unbounded();
let motion_controller = Arc::new(Mutex::new(RodBenderMotionControler::new()));
// connect g_code_executor to motion_controller
let gcode_executor = GCodeExecutor::new(motion_controller.clone(), rx.clone());
// connect jogging_executor to motion_controller
let jogging_executor = JoggingExecutor::new(motion_controller.clone(), rx.clone());
let api_server = ApiServer::new(tx);
// create a thread for G-code executor
// Tokio: use tokio::spawn instead of std::thread::spawn
std::thread::spawn(move || {
gcode_executor.run();
});
// create a thread for jogging executor
std::thread::spawn(move || {
jogging_executor.run();
});
api_server.run()
}
pub enum ApiCommand {
GCode(String),
JogMoveTo(f64),
Abort,
}
pub struct ApiServer {
tx: Sender<ApiCommand>,
}
impl ApiServer {
pub fn new(tx: Sender<ApiCommand>) -> ApiServer {
ApiServer { tx }
}
// Tokio: make function async: pub async fn run(&self)
pub fn run(&self) {
loop {
// read keyboard input and send it to the channel
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
if input.starts_with("G") {
self.tx.send(ApiCommand::GCode(input)).unwrap();
} else if input.starts_with("J") {
println!("Parsing input: {}", input);
let x = input
.trim()
.split_whitespace()
.last()
.unwrap()
.parse()
.unwrap();
println!("Jogging to {}", x);
self.tx.send(ApiCommand::JogMoveTo(x)).unwrap();
} else if input.starts_with("A") {
self.tx.send(ApiCommand::Abort).unwrap();
}
}
}
}
pub struct GCodeExecutor {
motion_controller: Arc<Mutex<dyn MotionController>>,
rx: Receiver<ApiCommand>,
}
impl GCodeExecutor {
pub fn new(
motion_controller: Arc<Mutex<dyn MotionController>>,
rx: Receiver<ApiCommand>,
) -> GCodeExecutor {
GCodeExecutor {
motion_controller,
rx,
}
}
// tokio: make function async: pub async fn run(&self)
pub fn run(&self) {
loop {
// tokio: use self.rx.recv().await instead of self.rx.recv().unwrap()
match self.rx.recv().unwrap() {
ApiCommand::GCode(gcode) => {
println!("Executing G-code: {}", gcode);
}
ApiCommand::Abort => {
println!("Aborting G-code execution");
break;
}
_ => {}
}
}
todo!()
}
}
pub struct JoggingExecutor {
motion_controller: Arc<Mutex<dyn MotionController>>,
rx: Receiver<ApiCommand>,
}
impl JoggingExecutor {
pub fn new(
motion_controller: Arc<Mutex<dyn MotionController>>,
rx: Receiver<ApiCommand>,
) -> JoggingExecutor {
JoggingExecutor {
motion_controller,
rx,
}
}
pub fn run(&self) {
loop {
match self.rx.recv().unwrap() {
ApiCommand::JogMoveTo(x) => {
println!("Jogging to {}", x);
self.motion_controller.lock().unwrap().move_to(x);
}
ApiCommand::Abort => {
println!("Aborting jogging");
break;
}
_ => {
println!("Invalid command");
}
}
}
}
}
pub trait MotionController: Send {
fn move_to(&self, x: f64);
}
pub struct RodBenderMotionControler {}
impl RodBenderMotionControler {
pub fn new() -> RodBenderMotionControler {
RodBenderMotionControler {}
}
}
impl MotionController for RodBenderMotionControler {
fn move_to(&self, x: f64) {
println!("Moving to {}", x);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment