Skip to main content

StateMachine

Struct StateMachine 

Source
pub struct StateMachine {
    pub index: i32,
    pub timer_preset: Duration,
    pub timeout_preset: Duration,
    pub error_code: i32,
    pub message: String,
    pub error_message: String,
    /* private fields */
}
Expand description

State Machine Helper (FB_StateMachine)

A state machine helper with automatic timer management and error tracking. Provides two timers that automatically reset when the state index changes:

  • Timer (timer_done()) - General purpose timer for delays and debouncing
  • Timeout (timed_out()) - For detecting stuck states

This is equivalent to the IEC 61131-3 FB_StateMachine function block.

§Automatic Timer Reset

Both timers automatically reset when index changes. This eliminates a common source of bugs in state machines where timers are not properly reset.

The pattern is:

  1. Set timer_preset (and optionally timeout_preset) in state N
  2. Change index to state N+1 (timers reset and start counting)
  3. In state N+1, check timer_done() or timed_out()

§Example

use autocore_std::StateMachine;
use std::time::Duration;

let mut state = StateMachine::new();

// Simulate a control loop
loop {
    match state.index {
        0 => { // Reset
            state.clear_error();
            state.index = 10;
        }
        10 => { // Idle - wait for start signal
            // For demo, just proceed
            state.timer_preset = Duration::from_millis(100);
            state.index = 20;
        }
        20 => { // Debounce
            if state.timer_done() {
                state.timeout_preset = Duration::from_secs(10);
                state.index = 30;
            }
        }
        30 => { // Wait for operation (simulated)
            // In real code: check operation_complete
            // For demo, check timeout
            if state.timed_out() {
                state.set_error(30, "Operation timeout");
                state.index = 0;
            }
            // Exit demo loop
            break;
        }
        _ => { state.index = 0; }
    }

    state.call(); // Call at end of each scan cycle
}

§Timer Presets Persist

Timer presets persist until you change them. This allows setting a preset once and using it across multiple states:

100 => {
    state.timer_preset = Duration::from_millis(300);
    state.index = 110;
}
110 => {
    // Uses 300ms preset set in state 100
    if some_condition && state.timer_done() {
        state.index = 120;
    }
}
120 => {
    // Still uses 300ms preset (timer reset on state change)
    if state.timer_done() {
        state.index = 10;
    }
}

§Error Handling Pattern

200 => {
    state.timeout_preset = Duration::from_secs(7);
    start_operation();
    state.index = 210;
}
210 => {
    if operation_complete {
        state.index = 1000; // Success
    } else if state.timed_out() {
        state.set_error(210, "Operation timed out");
        state.index = 5000; // Error handler
    }
}
5000 => {
    // Error recovery
    state.index = 0;
}

Fields§

§index: i32

Current state index.

§timer_preset: Duration

Timer preset. timer_done() returns true when time in current state >= this value. Defaults to Duration::MAX (timer never triggers unless you set a preset).

§timeout_preset: Duration

Timeout preset. timed_out() returns true when time in current state >= this value. Defaults to Duration::MAX (timeout never triggers unless you set a preset).

§error_code: i32

Error code. A value of 0 indicates no error. When non-zero, is_error() returns true.

§message: String

Status message for UI display. Content does not indicate an error.

§error_message: String

Error message for UI display. Should only have content when error_code != 0.

Implementations§

Source§

impl StateMachine

Source

pub fn new() -> Self

Creates a new state machine starting at state 0.

Timer presets default to Duration::MAX, meaning timers won’t trigger until you explicitly set a preset.

§Example
use autocore_std::StateMachine;

let state = StateMachine::new();
assert_eq!(state.index, 0);
assert_eq!(state.error_code, 0);
assert!(!state.is_error());
Source

pub fn call(&mut self)

Call once per scan cycle at the END of your state machine logic.

This method:

  • Detects state changes (when index differs from the previous call)
  • Resets internal timers on state change
  • Updates internal tracking for elapsed(), timer_done(), and timed_out()
§Example
use autocore_std::StateMachine;

let mut state = StateMachine::new();

// Your state machine logic here...
match state.index {
    0 => { state.index = 10; }
    _ => {}
}

state.call(); // Always call at the end
Source

pub fn timer_done(&self) -> bool

Returns true when time in current state >= timer_preset.

The timer automatically resets when the state index changes.

§Example
use autocore_std::StateMachine;
use std::time::Duration;

let mut state = StateMachine::new();
state.timer_preset = Duration::from_millis(50);
state.call(); // Start tracking

assert!(!state.timer_done()); // Not enough time elapsed

std::thread::sleep(Duration::from_millis(60));
assert!(state.timer_done()); // Now it's done
Source

pub fn timed_out(&self) -> bool

Returns true when time in current state >= timeout_preset.

Use this for detecting stuck states. The timeout automatically resets when the state index changes.

§Example
use autocore_std::StateMachine;
use std::time::Duration;

let mut state = StateMachine::new();
state.timeout_preset = Duration::from_millis(50);
state.call();

assert!(!state.timed_out());

std::thread::sleep(Duration::from_millis(60));
assert!(state.timed_out());
Source

pub fn elapsed(&self) -> Duration

Returns elapsed time since entering the current state.

Returns Duration::ZERO if call() has never been called.

§Example
use autocore_std::StateMachine;
use std::time::Duration;

let mut state = StateMachine::new();
state.call();

std::thread::sleep(Duration::from_millis(10));
assert!(state.elapsed() >= Duration::from_millis(10));
Source

pub fn is_error(&self) -> bool

Returns true if error_code != 0.

§Example
use autocore_std::StateMachine;

let mut state = StateMachine::new();
assert!(!state.is_error());

state.error_code = 100;
assert!(state.is_error());
Source

pub fn set_error(&mut self, code: i32, message: impl Into<String>)

Set error state with code and message.

This is a convenience method equivalent to setting error_code and error_message directly.

§Example
use autocore_std::StateMachine;

let mut state = StateMachine::new();
state.set_error(110, "Failed to home X axis");

assert_eq!(state.error_code, 110);
assert_eq!(state.error_message, "Failed to home X axis");
assert!(state.is_error());
Source

pub fn clear_error(&mut self)

Clear error state.

Sets error_code to 0 and clears error_message.

§Example
use autocore_std::StateMachine;

let mut state = StateMachine::new();
state.set_error(100, "Some error");
assert!(state.is_error());

state.clear_error();
assert!(!state.is_error());
assert_eq!(state.error_code, 0);
assert!(state.error_message.is_empty());
Source

pub fn state(&self) -> i32

Returns the current state index.

This is equivalent to reading state.index directly but provided for API consistency.

Trait Implementations§

Source§

impl Clone for StateMachine

Source§

fn clone(&self) -> StateMachine

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StateMachine

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for StateMachine

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V