Implement full program logic
This commit is contained in:
parent
ebff378b0a
commit
c5c6b7f188
2 changed files with 151 additions and 9 deletions
150
src/logic.rs
150
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<MachineState>,
|
||||
t_cylinder: BaseTimer<CylinderState>,
|
||||
}
|
||||
|
||||
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
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/util.rs
10
src/util.rs
|
|
@ -1,12 +1,12 @@
|
|||
use profirust::time;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct BaseTimer<T> {
|
||||
pub struct BaseTimer<T> {
|
||||
change: Option<time::Instant>,
|
||||
last: T,
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq> BaseTimer<T> {
|
||||
impl<T: PartialEq> BaseTimer<T> {
|
||||
pub fn new(start: T) -> Self {
|
||||
Self {
|
||||
change: None,
|
||||
|
|
@ -29,6 +29,12 @@ impl<T: Copy + PartialEq> BaseTimer<T> {
|
|||
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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue