hashmismatch / finny.rs

Finite State Machines for Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Queuing an event from a sub-state into the super-state

Maher4Ever opened this issue · comments

Hello there,

Is there a mechanism in this library that allows a sub-state to queue an event for its super-state? I'm aware of the execute_queue_pre and execute_queue_post flags for queuing an event from one state into another within the same machine, but I'm looking for one that works between a parent and a child state.

No, this is not supported at the moment. One approach that could be used is to have a shared queue of events that are emitted by the children and then later consumed in the main event loop.

The integration points for these would be the context structs (add something like a Arc<Mutex<Vec<FsmEvents>>> to them) and then clone the list in the sub-machine constructor (https://github.com/hashmismatch/fsm.rs/blob/00b8ef8297a7fc60631d5302adfef1298136a8da/fsm_tests/tests/boost_history.rs#L98) ... And then pump those messages into the parent machine from your own code.

Overall, it's a complicated issue that might not play well with Rust's borrow checker, hence why such a dynamic locking solution might be needed. But overall, it would probably be a very useful real-world feature.

@rudib Do you think this issue could be tackled in the new implementation of finny.rs?

I was thinking about this issue when working on it, but the fundamental problem still remains the same: each machine is standalone and each code generation (procedural macro) only works on a single machine at a time. Only the parent machines are aware that submachines even exist, but not the other way around.

Defining an event queue for such messages, sharing it between all the relevant machines within their contexts and pumping the messages at the top level is more or less what the library would do anyway.

@rudib The internals have changed a lot in finny.rs, so could you elaborate more on how you think this should be implemented?

Note: I really like the design of finny compared to the old fsm.rs, especially the fact that all dispatching of events now happens on the main machine. Previously, the application had to know which submachine the system is in to dispatch events to them which defeated the purpose of managing transitions in the state machine.

Extra note: I think it would be valuable to have a bidirectional relationship between super and sub machines, especially for inspection purposes (i.e., printing the name of the super machine in a sub machine).

I've just released 0.2.0 (finally!) which implements a sharable queue. The idea is to instantiate this queue, then store a copy of it as a field in the context struct of any FSM which would like to enqueue the events to it. The name of the implementation of the queue is FsmEventQueueVecShared.

Quick example from my personal project:

Field on the context types of all the machines and sub-machines:

pub queue: FsmEventQueueVecShared<RoomControllerFsm<A>>

The context of sub-machines will need to be explicitly initialized then:

    fsm.sub_machine::<AutoFsm<A>>()
        .with_context(|ctx| {
            AutoFsmContext {
                queue: ctx.queue.clone(),
                logger: ctx.logger.clone()
            }
        });

And when instantiating the main FSM instance, explicitly create all of the frontend implementations:

let queue = FsmEventQueueVecShared::new();

let logger = log.new(o!("ctx" => "fsm_temperature"));
let ctx = RoomControllerContext::new(logger.clone(), dispatcher, queue.clone(), room);
let inspect = InspectSlog::new(Some(logger.clone()));
let timers = TimersStdNoAlloc::new(RoomControllerFsmTimersStorage::default());
let mut fsm = RoomControllerFsm::new_with(ctx, queue, inspect, timers).unwrap();
fsm.start().unwrap();

Especially the last part is not the most obvious as we have to guess a lot of the generated types, which could be improved with a bunch of setup builders. But the shared queue approach should work.