igor-krechetov / hsmcpp

C++ based Hierarchical / Finite State Machine library oriented for embedded and RTOS systems.
https://hsmcpp.readthedocs.io
MIT License
62 stars 7 forks source link

History transition removes leaf states on parallel state machines #12

Open gmkado opened 4 months ago

gmkado commented 4 months ago

So far this library has been awesome, I'd really like to get this one feature working but for some reason transitioning back to a history state is mangling some of my parallel state machines.

I have four parallel state machines running under the "Main" state machine:

image

My button state machine is pretty straightforward and looks something like this:

image

When the button is released, it will transition my "Workout" state machine from "WorkoutActiveRunning" to "WorkoutActivePaused" and vice versa. "WorkoutActiveRunning" has some nested states that I'd like to restore when coming back from "WorkoutActivePaused":

image

Here's my workoutActive.scxml with the irrelevant bits removed (note that I'm using the xi:include feature so these states all get preprended with "WorkoutActive" in my generated state machine):

<scxml initial="SetUp" version="1.0" xmlns="http://www.w3.org/2005/07/scxml"    >    
    <state id="Running" initial="Running_Squeezing">        
        <transition event="HW_BUTTON_RELEASED" target="Paused" type="external" />
        <state id="Running_Squeezing" >
            ...
        </state>
        <state id="Running_Resting" >
            ...
        </state>    
        <history type="deep" id="RunningHistory" />    
    </state>
    <state id="Paused">
        <transition event="HW_BUTTON_RELEASED" target="RunningHistory" type="external" />
        <invoke srcexpr="on0301_Paused" />
    </state>
</scxml>

Something gets screwed up when trying to transition to "RunningHistory" from the "Paused" state. I've printed out the active states during the transition:

1) Main, Button, Workout, Led, Haptics, Haptics__Off, Workout__Active, Workout__Active__Paused, Led__Blinking, Led__Blinking_Off, Button__Pressed

---  TRANSITION TO HISTORY (event: HW_BUTTON_RELEASED, state: Workout__Active__RunningHistory, previously active: Workout__Active__Running_Squeezing) ---

2) Main, Button, Workout, Led, Haptics, Workout__Active, Led__Blinking, Workout__Active__Running, 
3) Main, Workout, Led, Workout__Active, Workout__Active__Running, Workout__Active__Running_Squeezing,

1) Is right before the transition back to the history, and all seems good. 2) It re-enters the "WorkoutActiveRunning" state, but for some reason "HapticsOff", "LedBlinking_Off", and "Button_Released" (the new button state) get removed. 3) It re-enters the "WorkoutActiveRunning_Squeezing" but now the "Button", "Haptics" and "Led_Blinking" states are gone

So it seems like it is popping off the leaf states on the parallel state machines for each history state it restores.

My expected final state:

Main, Button, Workout, Led, Haptics, Haptics__Off, Led__Blinking, Led__Blinking_Off, Button__Pressed, Workout__Active, Workout__Active__Running, Workout__Active__Running_Squeezing, 

Hopefully this makes sense. Let me know if it doesn't and I'll try to provide more information.

gmkado commented 4 months ago

I traced this back to around this area:

https://github.com/igor-krechetov/hsmcpp/blob/91b791de43c263a9ce2025267506e9c41ddba555/src/HsmImpl.cpp#L1235-L1239

So looks like when we enter a history state, it pushes "forced" transition events for each transition to get back to the previous states. But it does this on all active states, so my parallel states get forced into invalid states.

Should those lines be checking event.forcedTransitionsInfo.fromState to make sure they are forcing from the expected state?