Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to implement action on event AFTER guard #21

Open
chmorgan opened this issue May 14, 2024 · 3 comments
Open

How to implement action on event AFTER guard #21

chmorgan opened this issue May 14, 2024 · 3 comments

Comments

@chmorgan
Copy link

I'd like to do something like:

  • Action() { save data from event into ctx.context }

then, on the same event:

  • Guard transition to new state based on saved data in ctx.context.

I tried saving the data in the guard() as the first attempt but ctx isn't mutable there, it's only mutable in action and on_entry(). Since action() runs AFTER guard() normally I'm also not able to go that route unless I'm missing something.

Thoughts?

Ideally though I'd want to have an action on an event without any state at all since the purpose of the code is to store away some state that is used in guards and function arguments in other actions(), for example I save the system temperature and in other actions I'd act on the temperature to limit operation of the system, as an example.

@lyuts
Copy link

lyuts commented May 25, 2024

I'd like to do something like:

  • Action() { save data from event into ctx.context }

then, on the same event:

  • Guard transition to new state based on saved data in ctx.context.

As far as I understand, this is not possible with the finny's design. Looks like when a transition is evaluated its guard is checked, and if guard returns true, then an action is executed and state changes to the destination state. It also looks like by design an action must not fail, as there is no way to return an error or any other result from an action.

How do you model the event that notifies about the temperature change? Do you actually have to persist it before letting the FSM to process the temperature change?

@lyuts
Copy link

lyuts commented Jul 27, 2024

I just learned about event queues. I think this may help achieve the behavior you are looking for by introducing an intermediate state for temperature evaluation, and you can guard transition from it. E.g.

use finny::{finny_fsm, FsmFactory, FsmResult};

#[derive(Default)]
pub struct Context { temperature: u32 }

#[derive(Default)]
pub struct Normal;

#[derive(Default)]
pub struct EvaluatingTemperature;

#[derive(Default)]
pub struct TooHot;

#[derive(Clone)]
pub struct NewTempReading(u32);
#[derive(Clone)]
pub struct HighTemperature;

#[derive(Clone)]
pub struct Noop;

#[finny_fsm]
fn my_fsm(mut fsm: FsmBuilder<MyFsm, Context>) -> BuiltFsm {
    fsm.state::<Normal>()
       .on_entry(|_state, _ctx| { println!("Normal"); })
       .on_event::<NewTempReading>()
       .transition_to::<EvaluatingTemperature>()
       .action(|ev, ctx, _state_a, _state_b| {
           ctx.context.temperature = ev.0;
       });
    fsm.state::<EvaluatingTemperature>()
        .on_entry(|_state, ctx| {
            println!("EvaluatingTemperature");
            // if needed this can be moved to a guard too.
            if ctx.temperature > 50 {
               ctx.queue.enqueue(HighTemperature{}).unwrap();
            } else {
               ctx.queue.enqueue(Noop{}).unwrap();
            }
        })
        .on_event::<HighTemperature>()
        .transition_to::<TooHot>();
    fsm.state::<EvaluatingTemperature>()
        .on_event::<Noop>()
        .transition_to::<Normal>();
    fsm.state::<TooHot>()
        .on_entry(|_state, _ctx| { println!("TooHot"); });
    fsm.initial_state::<Normal>();
    fsm.build()
}

// The FSM is built and tested.
fn main() -> FsmResult<()> {
    let mut fsm = MyFsm::new(Context::default())?;
    fsm.start()?;
    fsm.dispatch(NewTempReading(100))?;
    Ok(())
}

@chmorgan
Copy link
Author

@lyuts ahhh yeah, so you'd queue your own message upon the condition, which means you separate the transition occurring on the processing cycle of the checking of the condition. I like it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants