-
Notifications
You must be signed in to change notification settings - Fork 631
Description
The following is a state machine for a fan that I created using spring spring-statemachine-starter 4.0. When using spring-statemachine-data-mongodb, I always get the error: "Cannot invoke "org.springframework.statemachine.state.State.getId()" because the return value of "org.springframework.statemachine.state.HistoryPseudoState.getState()" is null."
`package com.example.state_demo;
import java.util.EnumSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.configurers.StateConfigurer.History;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.persist.StateMachineRuntimePersister;
import org.springframework.statemachine.state.State;
@configuration
@EnableStateMachineFactory
public class FanStateMachineConfig extends StateMachineConfigurerAdapter<FanStates, FanEvents> {
@Autowired
private StateMachineRuntimePersister<FanStates, FanEvents, String> stateMachineRuntimePersister;
@Override
public void configure(StateMachineStateConfigurer<FanStates, FanEvents> states) throws Exception {
states
.withStates()
.initial(FanStates.POWER_OFF)
.states(EnumSet.of(FanStates.POWER_OFF, FanStates.POWER_ON))
.and()
.withStates()
.parent(FanStates.POWER_ON)
.initial(FanStates.STANDBY)
.states(EnumSet.of(FanStates.STANDBY, FanStates.RUNNING))
.and()
.withStates()
.parent(FanStates.RUNNING)
.initial(FanStates.LEVEL_1)
.states(EnumSet.of(FanStates.LEVEL_1, FanStates.LEVEL_2, FanStates.LEVEL_3))
.history(FanStates.HISTORY,History.DEEP)
;
}
@Override
public void configure(StateMachineTransitionConfigurer<FanStates, FanEvents> transitions) throws Exception {
transitions
.withHistory()
.source(FanStates.HISTORY)
.target(FanStates.LEVEL_1)
.and()
.withExternal()
.source(FanStates.POWER_OFF)
.target(FanStates.POWER_ON)
.event(FanEvents.POWER_ON_EVENT)
.and()
.withExternal()
.source(FanStates.POWER_ON)
.target(FanStates.POWER_OFF)
.event(FanEvents.POWER_OFF_EVENT)
.and()
.withExternal()
.source(FanStates.STANDBY)
.target(FanStates.HISTORY) // 进入RUNNING时使用具体状态
.event(FanEvents.TURN_ON_EVENT)
.and()
.withExternal()
.source(FanStates.RUNNING)
.target(FanStates.STANDBY)
.event(FanEvents.TURN_OFF_EVENT)
.and()
.withExternal()
.source(FanStates.LEVEL_1)
.target(FanStates.LEVEL_2)
.event(FanEvents.SPEED_LEVEL_2)
.and()
.withExternal()
.source(FanStates.LEVEL_1)
.target(FanStates.LEVEL_3)
.event(FanEvents.SPEED_LEVEL_3)
.and()
.withExternal()
.source(FanStates.LEVEL_2)
.target(FanStates.LEVEL_1)
.event(FanEvents.SPEED_LEVEL_1)
.and()
.withExternal()
.source(FanStates.LEVEL_2)
.target(FanStates.LEVEL_3)
.event(FanEvents.SPEED_LEVEL_3)
.and()
.withExternal()
.source(FanStates.LEVEL_3)
.target(FanStates.LEVEL_1)
.event(FanEvents.SPEED_LEVEL_1)
.and()
.withExternal()
.source(FanStates.LEVEL_3)
.target(FanStates.LEVEL_2)
.event(FanEvents.SPEED_LEVEL_2);
;
}
@Override
public void configure(StateMachineConfigurationConfigurer<FanStates, FanEvents> config) throws Exception {
config
.withPersistence()
.runtimePersister(stateMachineRuntimePersister)
.and()
.withConfiguration()
.listener(listener())
;
}
@Bean
public StateMachineListener<FanStates, FanEvents> listener() {
return new StateMachineListenerAdapter<FanStates, FanEvents>() {
@Override
public void stateChanged(State<FanStates, FanEvents> from, State<FanStates, FanEvents> to) {
}
};
}
} `
I've pinpointed that the error occurs in the buildStateMachineContext method of AbstractPersistingStateMachineInterceptor.
if (historyState != null) { historyStates.put(null, ((HistoryPseudoState<S, E>)historyState).getState().getId()); }
The error occurs because the value of historyState.getState() is null. However, my state machine functions properly during testing—it's just that I can't save it. Could there be an error in my configuration? Below is my test code:
`@GetMapping("/test2/{id}")
public String test3(@PathVariable("id") String id) {
var stateMachine = stateMachineFactory.getStateMachine(id);
stateMachine.start();
stateMachine.sendEvent(FanEvents.POWER_ON_EVENT);
stateMachine.sendEvent(FanEvents.TURN_ON_EVENT);
stateMachine.sendEvent(FanEvents.SPEED_LEVEL_3);
stateMachine.sendEvent(FanEvents.POWER_OFF_EVENT);
stateMachine.sendEvent(FanEvents.POWER_ON_EVENT);
stateMachine.sendEvent(FanEvents.TURN_ON_EVENT);
return stateMachine.getState().getIds().toString();
}`