Skip to content

Timer not trigger when resetStateMachine #381

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

Closed
hoswey opened this issue Jun 28, 2017 · 6 comments
Closed

Timer not trigger when resetStateMachine #381

hoswey opened this issue Jun 28, 2017 · 6 comments
Labels
type/bug Is a bug report
Milestone

Comments

@hoswey
Copy link

hoswey commented Jun 28, 2017

I am developing a game using spring statemachine, and want to restore the state machine from the db record;

I have checked the similar question from The right way to reload and restart State Machine with different data set
But it seems the timer transition is not trigger when reset. Below is My code.

There are three state, the transition is timer base.
Ready(1 second) -> A (1 second)-> B.

My Question is:
We expected when restore the state machine with state A, it will transit to the state B after one second, but it kept in the state A all the time.

public enum TestState {
    READY, A, B;
}

public void testRestore() throws Exception {

    StateMachineBuilder.Builder<TestState, Event> builder = StateMachineBuilder.builder();

    // @formatter:off
      builder.configureConfiguration()
              .withVerifier().enabled(true)
              .and()
        .withConfiguration()
        .taskScheduler(threadPoolTaskScheduler)
        .taskExecutor(threadPoolTaskExecutor)
        .beanFactory(applicationContext);

      builder.configureStates()
        .withStates()
        .initial(TestState.READY)
              .states(new HashSet<>(Arrays.asList(TestState.values())));

        builder.configureTransitions()
        .withExternal()
        .source(TestState.READY).target(TestState.A)
        .timerOnce(1000)
        .and()
        .withExternal()
        .source(TestState.A).target(TestState.B)
        .timerOnce(1000);

    // @formatter:on

    StateMachine<TestState, Event> stateMachine = builder.build();
    stateMachine.addStateListener(new StateMachineListenerAdapter<TestState, Event>() {

        @Override
        public void stateChanged(org.springframework.statemachine.state.State<TestState, Event> from,
                org.springframework.statemachine.state.State<TestState, Event> to) {

            log.info("[Testcmd=stateChanged,from={}, to={}]", from.getId(), to.getId());
        }
    });
    stateMachine.stop();
    stateMachine.getStateMachineAccessor()
            .doWithAllRegions(new StateMachineFunction<StateMachineAccess<TestState, Event>>() {
                @Override
                public void apply(StateMachineAccess<TestState, Event> function) {
                    function.resetStateMachine(
                            new DefaultStateMachineContext<TestState, Event>(TestState.A, null, null, null));

                }
            });

    stateMachine.start();

    Thread.sleep(1000);
    log.info("Test, Current State is {}",stateMachine.getState().getId());
    synchronized (this) {
        wait();
    }

}
@jvalkeal
Copy link
Contributor

Ah yeah, I think this is caused because timer(timerOnce) is created when state is entered. When machine is reset, state is not entered thus timer doesn't activate. We need to create some kind of hook to this.

@hoswey
Copy link
Author

hoswey commented Jun 28, 2017

Is there any work around to reset the state and trigger the timer as we want to restore the state machine from the persistent.

@jvalkeal
Copy link
Contributor

I'm not sure. Basically scheduling works by arming/disarming triggers when state is entered/exited.
AbstractState
I haven't tried but in theory you get states StateMachine.getStates(), find a one matching id you resetting with and then casting that state to AbstractState to call getTriggers() and arming those.

@jvalkeal jvalkeal added the type/bug Is a bug report label Jun 28, 2017
@jvalkeal jvalkeal added this to the 1.2.6.RELEASE milestone Jun 29, 2017
@jvalkeal
Copy link
Contributor

I'm trying to sneak this in to a next release as I have a concept working. Just need to figure out some corner cases but it's just basically arming those triggers for active state(s) which are restored.

jvalkeal added a commit to jvalkeal/spring-statemachine that referenced this issue Jul 8, 2017
- As passing in null context don't have any
  meaning other than doing reset with pre-defined
  behaviour which currently just resets back to
  initial state and clears extended state variables.
  Now Also clearing machine id back to null which is
  anyway a default value.
- Fixes spring-projects#381
@jvalkeal
Copy link
Contributor

jvalkeal commented Jul 8, 2017

Merged per 66c1a53

@jvalkeal jvalkeal closed this as completed Jul 8, 2017
@jvalkeal
Copy link
Contributor

jvalkeal commented Jul 8, 2017

Wrong commit message, going to hard reset this branch.

jvalkeal added a commit that referenced this issue Jul 8, 2017
- Previously timer were only armed when state
  were entered, thus failing for timer to work if
  machine were restored. Now changing logic so that
  that state have a lifecycle which is called during
  machine reset.
- Fixes #381
jvalkeal added a commit that referenced this issue Jul 9, 2017
- As passing in null context don't have any
  meaning other than doing reset with pre-defined
  behaviour which currently just resets back to
  initial state and clears extended state variables.
  Now Also clearing machine id back to null which is
  anyway a default value.
- Backport #381
- Relates #307
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/bug Is a bug report
Projects
None yet
Development

No branches or pull requests

2 participants