use deoxy::{actix::*, Pin, Pwm}; use futures::future::Future; use std::str::FromStr; use std::{ io::{stdin, stdout, Write}, ops::Deref, time::Duration, }; use termion::input::TermRead; #[derive(Default)] struct Prompt; impl Prompt { fn prompt(&self, message: S) -> Option where T: FromStr, S: Deref, { let stdout = stdout(); let mut stdout = stdout.lock(); let stdin = stdin(); let mut stdin = stdin.lock(); stdout.write_all(message.to_string().as_bytes()).ok()?; stdout.write_all(b": ").ok()?; stdout.flush().ok()?; let input = stdin.read_line().ok()??; input.parse::().ok() } } enum PromptMessage { Start, GetPeriod { pin: u16 }, GetInitial { pin: u16, period: Duration }, GetNext { motor: Addr }, } impl Actor for Prompt { type Context = Context; fn started(&mut self, context: &mut Self::Context) { context.notify(PromptMessage::Start); } } impl ActixMessage for PromptMessage { type Result = (); } impl Handle for Prompt { type Result = (); fn handle(&mut self, message: PromptMessage, context: &mut Self::Context) -> Self::Result { match message { PromptMessage::Start => { let pin: Option = self.prompt("Pin number"); if let Some(pin) = pin { context.notify(PromptMessage::GetPeriod { pin }); } else { context.notify(PromptMessage::Start); } } PromptMessage::GetPeriod { pin } => { let period: Option = self.prompt("Duty cycle/period (ms)"); if let Some(period) = period { context.notify(PromptMessage::GetInitial { pin, period: Duration::from_millis(period), }); } else { context.notify(PromptMessage::GetPeriod { pin }); } } PromptMessage::GetInitial { pin, period } => { let start: Option = self.prompt("Start position (µs)"); if let Some(start) = start { let motor = Motor::try_new(pin, period, context.address()) .map(|mut motor| { motor.pulse_width = Duration::from_micros(start); motor }) .unwrap(); let _addr = Arbiter::start(move |_| motor); } else { context.notify(PromptMessage::GetInitial { pin, period }); } } PromptMessage::GetNext { motor } => { let s: Option = self.prompt("Set pulse width [number/+/-/^C]"); if let Some(s) = s { let pulse_width = match s.as_str() { "+" => motor.send(MotorMessage::Increase), "-" => motor.send(MotorMessage::Decrease), x => { if let Ok(width) = x.parse::() { motor.send(MotorMessage::Set(Duration::from_micros(width))) } else { motor.send(MotorMessage::Get) } } }; let pulse_width = pulse_width.wait().unwrap(); println!("Current pulse width: {} µs", pulse_width); context.notify(PromptMessage::GetNext { motor }); } } } } } struct Motor { pin: Pin, period: Duration, pulse_width: Duration, prompt: Addr, } impl Motor { fn try_new(pin: u16, period: Duration, prompt: Addr) -> Option { let pin = Pin::try_new(pin).ok()?; Some(Self { pin, period, pulse_width: Duration::default(), prompt, }) } } impl Actor for Motor { type Context = Context; fn started(&mut self, context: &mut Self::Context) { self.prompt.do_send(PromptMessage::GetNext { motor: context.address(), }); } } impl ActixMessage for MotorMessage { type Result = u64; } enum MotorMessage { Increase, Decrease, Set(Duration), Get, } impl Handle for Motor { type Result = u64; fn handle(&mut self, message: MotorMessage, context: &mut Self::Context) -> Self::Result { match message { MotorMessage::Increase => { self.pulse_width += Duration::from_micros(1); } MotorMessage::Decrease => { self.pulse_width -= Duration::from_micros(1); } MotorMessage::Set(duration) => { self.pulse_width = duration; } MotorMessage::Get => {} }; self.pin.set_pwm(self.period, self.pulse_width).unwrap(); // Stop signaling the motor after five seconds context.run_later(Duration::new(5, 0), move |motor, _| { motor .pin .set_pwm(motor.period, Duration::new(0, 0)) .unwrap(); }); self.pulse_width.as_micros() as u64 } } fn main() { let system = System::new("pulse-width"); let prompt = Prompt; let _ = prompt.start(); system.run(); }