Jake Webster opened issue #16: Add example for hierarchical states at sfsm / sfsm

Jake Webster
2022-04-15T18:14:09Z

Took a little time to verify, but thanks to the good design of this library crate, it is possible to compose hierarchical state machines. Recommend mentioning the use case, and possibly adding an example, to help with the crate's visibility.

Based on my use of it so far, there are a couple convenient options:

Use a state machine as a state in another state machine (for simple cases):

// Only relevant parts included.

// Defines the Forward Observer top-level state machine.
add_state_machine!(
ForwardObserver,
Offline,
[Offline, Online],
[
Offline => Online,
Online => Offline,
]
);

// Defines the Online inner state machine.
add_state_machine!(
Online,
Standby,
[Standby, Requesting, Observing, Reporting],
[
Standby => Requesting,
Requesting => Observing,
Observing => Reporting,
Reporting => Standby,
]
);

impl State for Online {
fn entry(&mut self) {}

/// Executes the sub-state machine on each step.
fn execute(&mut self) {
self.step().unwrap();
}

fn exit(&mut self) {}
}

// Initialize the Online state machine on transition.
impl From<Offline> for Online {
/// Constructs, and starts, the [`Online`] state machine on a transition from Offline
fn from(_: Offline) -> Self {
let mut machine = Self::new();
machine.start(Standby {}).unwrap();

machine
}
}

Put a state machine inside a state, as a field (for more complicated cases):

// Only relevant parts included.

// Defines the Forward Observer top-level state machine.
add_state_machine!(
ForwardObserver,
Offline,
[Offline, Online],
[
Offline => Online,
Online => Offline,
]
);

// Defines the Online inner state machine.
add_state_machine!(
OnlineMachine,
Standby,
[Standby, Requesting, Observing, Reporting],
[
Standby => Requesting,
Requesting => Observing,
Observing => Reporting,
Reporting => Standby,
]
);

struct Online {
state_machine: OnlineMachine,
}

impl State for Online {
fn entry(&mut self) {}

/// Executes the sub-state machine on each step.
fn execute(&mut self) {
self.state_machine.step().unwrap();
}

fn exit(&mut self) {}
}

impl From<Offline> for Online {
/// Constructs, and starts, the [`Online`] state machine on a transition from Offline
fn from(_: Offline) -> Self {
let mut online = Self {
state_machine: OnlineMachine::new(),
};
online.state_machine.start(Standby {}).unwrap();

online
}
}