use sfsm::*;
use sfsm::message::{MessageError, ReturnMessage, ReceiveMessage};
struct WaitForLaunch {
malfunction: bool,
do_launch: bool,
}
struct Launch {}
struct Abort {}
add_state_machine!(
Rocket,
WaitForLaunch,
[WaitForLaunch, Launch, Abort],
[
WaitForLaunch => Launch,
WaitForLaunch => Abort,
Abort => WaitForLaunch,
]
);
#[derive(Debug)]
struct Malfunction {malfunction: bool}
#[derive(Debug)]
struct StartLaunch {start: bool}
struct Status { velocity: f32, height: f32, }
add_messages!(Rocket,
[
StartLaunch -> WaitForLaunch, Malfunction -> WaitForLaunch, Status <- Launch, ]
);
impl State for WaitForLaunch {
fn entry(&mut self) {
println!("Begin countdown");
}
}
derive_transition_into!(WaitForLaunch, Abort);
impl Transition<Abort> for WaitForLaunch {
fn guard(&self) -> TransitGuard {
return self.malfunction.into();
}
}
impl ReceiveMessage<StartLaunch> for WaitForLaunch {
fn receive_message(&mut self, message: StartLaunch) {
self.do_launch = message.start;
}
}
impl ReceiveMessage<Malfunction> for WaitForLaunch {
fn receive_message(&mut self, message: Malfunction) {
self.malfunction = message.malfunction;
}
}
derive_transition_into!(WaitForLaunch, Launch);
impl Transition<Launch> for WaitForLaunch {
fn guard(&self) -> TransitGuard {
return self.do_launch.into();
}
}
impl State for Abort {
fn entry(&mut self) {
println!("Malfunction found");
}
fn execute(&mut self) {
println!("Fix malfunction");
}
}
impl Into<WaitForLaunch> for Abort {
fn into(self) -> WaitForLaunch {
WaitForLaunch {
malfunction: false,
do_launch: false,
}
}
}
derive_transition!(Abort, WaitForLaunch, TransitGuard::Transit);
impl State for Launch {
fn entry(&mut self) {
println!("Firing up boosters");
}
}
impl ReturnMessage<Status> for Launch {
fn return_message(&mut self) -> Option<Status> {
Some(Status {height: 1000.0f32, velocity: 300.0f32})
}
}
#[sfsm_trace]
fn trace(log: &str) {
println!("{}", log);
}
fn run_launch_sequence_with_message() -> Result<(), SfsmError> {
let mut rocket = Rocket::new();
let wait_for_launch = WaitForLaunch {
malfunction: false,
do_launch: false,
};
rocket.start(wait_for_launch)?;
assert!(IsState::<WaitForLaunch>::is_state(&rocket));
rocket.step()?;
PushMessage::<WaitForLaunch, Malfunction>::push_message(&mut rocket, Malfunction {malfunction: true}).unwrap();
rocket.step()?;
assert!(IsState::<Abort>::is_state(&rocket));
let result = PushMessage::<WaitForLaunch, StartLaunch>::push_message(&mut rocket, StartLaunch {start: true});
assert!(result.is_err());
if let Err(start_result) = result {
if let MessageError::StateIsNotActive(start) = start_result {
assert!(start.start)
}
}
rocket.step()?;
assert!(IsState::<WaitForLaunch>::is_state(&rocket));
PushMessage::<WaitForLaunch, StartLaunch>::push_message(&mut rocket, StartLaunch {start: true}).unwrap();
rocket.step()?;
assert!(IsState::<Launch>::is_state(&rocket));
rocket.step()?;
let status_option = PollMessage::<Launch, Status>::poll_message(&mut rocket).unwrap();
if let Some(status) = status_option {
assert_eq!(status.velocity, 300.0f32);
assert_eq!(status.height, 1000.0f32);
} else {
assert!(false);
}
Ok(())
}
fn main() {
run_launch_sequence_with_message().unwrap();
}
#[cfg(test)]
mod tests {
use crate::run_launch_sequence_with_message;
#[test]
fn launch_sequence_with_message() {
run_launch_sequence_with_message().unwrap();
}
}