1
0
Fork 0

Implement full program logic

This commit is contained in:
rahix 2024-11-03 16:03:48 +01:00
parent ebff378b0a
commit c5c6b7f188
2 changed files with 151 additions and 9 deletions

View file

@ -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
_ => (),
}
}
}

View file

@ -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 {