diff --git a/src/logic.rs b/src/logic.rs index fe822b7..535da18 100644 --- a/src/logic.rs +++ b/src/logic.rs @@ -1,5 +1,7 @@ use crate::util::*; +const CYCLE_MS: i32 = 5000; + #[derive(Debug, Default)] pub struct LogicInputs { pub left_limit: bool, @@ -33,9 +35,23 @@ enum MachineState { ExecuteContinuous, ExecuteOnce, Aborted, + Completed, } -#[derive(Debug, Default)] +impl MachineState { + pub fn short_name(&self) -> &'static str { + match self { + MachineState::Stopped => "STOP", + MachineState::Idle => "IDLE", + MachineState::ExecuteContinuous => "RUN CONT", + MachineState::ExecuteOnce => "RUN ONCE", + MachineState::Aborted => "FAULT", + MachineState::Completed => "DONE", + } + } +} + +#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] enum CylinderState { #[default] Unknown, @@ -56,6 +72,8 @@ pub struct Logic { state: MachineState, t_alarm: PulseTimer, + t_state: BaseTimer, + t_cylinder: BaseTimer, } impl Logic { @@ -75,7 +93,7 @@ impl Logic { impl Logic { pub fn run(&mut self, now: profirust::time::Instant) { self.out.signal_fault = matches!(self.state, MachineState::Stopped | MachineState::Aborted); - self.out.signal_ready = matches!(self.state, MachineState::Idle); + self.out.signal_ready = matches!(self.state, MachineState::Idle | MachineState::Completed); self.out.signal_operate = matches!( self.state, MachineState::ExecuteContinuous | MachineState::ExecuteOnce @@ -84,11 +102,12 @@ impl Logic { .t_alarm .run( now, - matches!(self.state, MachineState::Aborted), + matches!(self.state, MachineState::Aborted | MachineState::Completed), 500.millis(), ) .done; + self.t_state.run(now, self.state); self.state = match self.state { MachineState::Stopped if !self.inp.pressure_switch => MachineState::Stopped, _ if !self.inp.pressure_switch => MachineState::Aborted, @@ -101,14 +120,131 @@ impl Logic { } // Go to idle state once pressure is okay - MachineState::Stopped if self.inp.pressure_switch => MachineState::Idle, + MachineState::Stopped + if self.inp.pressure_switch + && !self.inp.switch_run_once + && !self.inp.switch_run_continuous => + { + MachineState::Idle + } - // Start from idle state when one of the modes is requested - MachineState::Idle if self.inp.switch_run_continuous => MachineState::ExecuteContinuous, - MachineState::Idle if self.inp.switch_run_once => MachineState::ExecuteOnce, + // Start from idle state when one of the modes is requested (with 2 seconds delay) + MachineState::Idle + if self.t_state.timer(now, 2.secs()) && self.inp.switch_run_continuous => + { + MachineState::ExecuteContinuous + } + MachineState::Idle if self.t_state.timer(now, 2.secs()) && self.inp.switch_run_once => { + MachineState::ExecuteOnce + } + + // End condition + MachineState::ExecuteContinuous | MachineState::ExecuteOnce + if self.inp.left_limit || self.inp.right_limit => + { + MachineState::Completed + } + + // End on user request + MachineState::ExecuteContinuous if !self.inp.switch_run_continuous => { + MachineState::Idle + } + MachineState::ExecuteOnce + if self.t_state.timer(now, 2.secs()) && self.inp.switch_run_once => + { + MachineState::Idle + } + + // Reset condition + MachineState::Completed + if !self.inp.switch_run_continuous && !self.inp.switch_run_once => + { + MachineState::Idle + } // Stay in current state if when no transition is active state => state, }; + + if !matches!(self.state, MachineState::ExecuteContinuous) { + self.counter = 0; + } + + // Cylinder state update + self.t_cylinder.run(now, self.cylinder_state); + match self.state { + MachineState::ExecuteContinuous => { + self.cylinder_state = match self.cylinder_state { + CylinderState::Unknown => CylinderState::Left, + CylinderState::Left if self.t_cylinder.timer(now, CYCLE_MS.millis()) => { + self.counter += 1; + CylinderState::Right + } + CylinderState::Right if self.t_cylinder.timer(now, CYCLE_MS.millis()) => { + self.counter += 1; + CylinderState::Left + } + s => s, + }; + } + MachineState::ExecuteOnce => { + self.cylinder_state = match self.cylinder_state { + CylinderState::Unknown => CylinderState::Left, + CylinderState::Left if self.t_cylinder.timer(now, 1.secs()) => { + CylinderState::Right + } + s => s, + } + } + _ => { + self.cylinder_state = CylinderState::Unknown; + } + } + + // Only move pneumatic cylinder in execute states + if matches!( + self.state, + MachineState::ExecuteOnce | MachineState::ExecuteContinuous + ) { + self.out.cylinder_left = self.cylinder_state == CylinderState::Left; + self.out.cylinder_right = self.cylinder_state == CylinderState::Right; + } else { + self.out.cylinder_left = false; + self.out.cylinder_right = false; + } + + // Display update + self.out.display[0] = format!("{:^20}", "torque tester"); + let msg = match self.state { + MachineState::Aborted | MachineState::Stopped | MachineState::Completed + if self.inp.switch_run_once => + { + "turn off run once" + } + MachineState::Aborted | MachineState::Stopped | MachineState::Completed + if self.inp.switch_run_continuous => + { + "turn off run cont." + } + MachineState::Stopped if !self.inp.pressure_switch => "no pressure", + MachineState::Idle => "ready to start", + _ => "", + }; + self.out.display[1] = format!("{}: {}", self.state.short_name(), msg); + + match self.state { + MachineState::ExecuteContinuous => { + self.out.display[3] = format!("Cycles: {:6}", self.counter); + } + MachineState::ExecuteOnce => { + if self.cylinder_state == CylinderState::Right { + self.out.display[3] = format!("Time: {}", self.t_cylinder.timer_value(now)); + } else { + self.out.display[3] = format!("Preparing..."); + } + } + // Keep last message visible + _ => (), + } } } diff --git a/src/util.rs b/src/util.rs index ab608f7..446217c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,12 +1,12 @@ use profirust::time; #[derive(Default, Debug)] -struct BaseTimer { +pub struct BaseTimer { change: Option, last: T, } -impl BaseTimer { +impl BaseTimer { pub fn new(start: T) -> Self { Self { change: None, @@ -29,6 +29,12 @@ impl BaseTimer { pub fn timer(&self, now: time::Instant, preset: time::Duration) -> bool { self.change.map(|tt| now >= tt + preset).unwrap_or(true) } + + pub fn timer_value(&self, now: time::Instant) -> time::Duration { + self.change + .map(|tt| now - tt) + .unwrap_or(time::Duration::ZERO) + } } pub struct TimerResult {