Squirrel State Machine

孔鸿哲
2023-12-01

Maven

当前最新发行版本

<dependency>
	<groupId>org.squirrelframework</groupId>
  	<artifactId>squirrel-foundation</artifactId>
  	<version>0.3.8</version>
</dependency>

最新快照版本

<dependency>
	<groupId>org.squirrelframework</groupId>
  	<artifactId>squirrel-foundation</artifactId>
  	<version>0.3.9-SNAPSHOT</version>
</dependency>

快速开始

public class QuickStartSample {
    // 1. 定义状态机事件
    enum FSMEvent {
        ToA, ToB, ToC, ToD
    }

    // 2. 定义状态机类
    @StateMachineParameters(stateType=String.class, eventType=FSMEvent.class, contextType=Integer.class)
    static class StateMachineSample extends AbstractUntypedStateMachine {
        protected void fromAToB(String from, String to, FSMEvent event, Integer context) {
            System.out.println("Transition from '"+from+"' to '"+to+"' on event '"+event+
                    "' with context '"+context+"'.");
        }

        protected void ontoB(String from, String to, FSMEvent event, Integer context) {
            System.out.println("Entry State \'"+to+"\'.");
        }
    }

    public static void main(String[] args) {
        // 3. 创建状态过渡流程
        UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineSample.class);
        builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");
        builder.externalTransition().from("B").to("C").on(FSMEvent.ToC).callMethod("fromAToB");
        builder.onEntry("B").callMethod("ontoB");

        // 4. 使用状态机
        UntypedStateMachine fsm = builder.newStateMachine("A");
        fsm.fire(FSMEvent.ToB, 10);
        fsm.fire(FSMEvent.ToC);

        System.out.println("Current state is "+fsm.getCurrentState());
    }
}

控制台打印

Transition from 'A' to 'B' on event 'ToB' with context '10'.
Entry State 'B'.
Transition from 'B' to 'C' on event 'ToC' with context 'null'.
Current state is C

这样一个简单的demo就运行起来了。

基础概念

squirrel-foundation不仅支持fluent API和声明性方式来声明状态机,还能让用户能够以直接的方式定义操作方法。

  • StateMachine支持的四种参数类型
    • T:状态机
    • S:状态
    • E:事件
    • C:外部上下文
  • State Machine Builder
StateMachineBuilder<MyStateMachine, MyState, MyEvent, MyContext> builder =
      StateMachineBuilderFactory.create(MyStateMachine.class, MyState.class, MyEvent.class, MyContext.class);
  • Fluent API

State Machine Builder创建完成后,可以用Fluent API去定义状态/转换/动作

builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");

这段代码的含义是:从状态A通过ToB事件转变为状态B,同时触发fromAToB方法。

builder.internalTransition(TransitionPriority.HIGH).within(MyState.A).on(MyEvent.WithinA).perform(new MyAction());

internalTransition可以设置优先级,优先级用于在覆盖原始过渡,状态A在触发"WithinA"事件时调用"myAction",通过myAction实现一定的逻辑,使用internalTransition时在过渡完成后没有状态进入或退出

builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.CTOD).when(
                new Condition<MyContext>() {
                    @Override
                    public boolean isSatisfied(MyContext context) {
                        return context!=null && context.getValue()>80;
                    }

                    @Override
                    public String name() {
                        return "MyCondition";
                    }
                }).callMethod("myInternalTransitionCall");
builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.CTOD).whenMvel(
                "MyCondition:::(context!=null && context.getValue()>80)").callMethod("myInternalTransitionCall");

当isSatisfied为true时触发状态变化。

默认方法调用,通过函数名即可确认调用函数。

 protected void transitFromAToBOnATOB(MyState from, MyState to, MyEvent event, MyContext context)
protected void transitFromAnyToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)

其他的默认方法格式

 transitFrom[fromStateName]To[toStateName]On[eventName]When[conditionName]  
 transitFrom[fromStateName]To[toStateName]On[eventName]  
 transitFromAnyTo[toStateName]On[eventName]  
 transitFrom[fromStateName]ToAnyOn[eventName]  
 transitFrom[fromStateName]To[toStateName]          
 on[eventName] 
  • 注解
    基于注解定义状态机
@States({
        @State(name = "A", entryCallMethod = "entryA", exitCallMethod = "exitA", initialState = true),
        @State(name = "B", entryCallMethod = "entryB", exitCallMethod = "exitB"),
        @State(name = "C", entryCallMethod = "entryC", exitCallMethod = "exitC"),
        @State(name = "D", entryCallMethod = "entryD", exitCallMethod = "exitD")
})
@Transitions({
        @Transit(from = "A", to = "B", on = "ATOB", callMethod = "toB"),
        @Transit(from = "B", to = "C", on = "BTOC", callMethod = "toC"),
        @Transit(from = "C", to = "D", on = "CTOD", callMethod = "toD")
})
@StateMachineParameters(stateType = MyState.class, eventType = MyEvent.class, contextType = MyContext.class)
public class DeclarativeMachine extends AbstractUntypedStateMachine {

    public void entryA(MyState from, MyState to, MyEvent on, MyContext context){

        System.out.println("---entryA");
    }
    public void exitA(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---exitA");
    }
    public void entryB(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---entryB");
    }
    public void exitB(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---exitB");
    }
    public void entryC(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---entryC");
    }
    public void exitC(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---exitC");
    }
    public void entryD(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---entryD");
    }
    public void exitD(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---exitD");
    }
    public void toB(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---toB");
    }
    public void toC(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---toC");
    }
    public void toD(MyState from, MyState to, MyEvent on, MyContext context){
        System.out.println("---toD");
    }
    
}

 @Test
    public void testDeclarativeMachine(){
        UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(DeclarativeMachine.class);
        UntypedStateMachine fsm = builder.newAnyStateMachine(MyState.A);
        fsm.start();
        fsm.fire(MyEvent.ATOB);
        fsm.fire(MyEvent.BTOC);
        fsm.fire(MyEvent.CTOD);
    }

高级特性

  • 定义分层状态
    一个分层状态可能包含嵌套状态,一个子状态可能还有嵌套的子状态,嵌套可能会进行到任何深度。当分层状态处于活动状态时,有且只有一个子状态处于活跃状态。可以通过API和注解定义分层状态。

api

builder.defineSequentialStatesOn(MyState.A, MyState.XINA, MyState.YINA);

annotation

@States({
        @State(name="A", entryCallMethod="entryA", exitCallMethod="exitA"),
        @State(parent="A", name="XINA", entryCallMethod="entryXINA", exitCallMethod="exitXINA", initialState=true),
        @State(parent="A", name="YINA", entryCallMethod="entryYINA", exitCallMethod="exitYINA")
})
  • 定义并行状态
    并行状态封装了一组子状态,当父状态处于活跃时,子状态可以都处于活跃状态。

api

 builder.defineParallelStatesOn(MyState.Root, MyState.RegionState1, MyState.RegionState2);

builder.defineSequentialStatesOn(MyState.RegionState1, MyState.State11, MyState.State12);
builder.externalTransition().from(MyState.State11).to(MyState.State12).on(MyEvent.Event1);

builder.defineSequentialStatesOn(MyState.RegionState2, MyState.State21, MyState.State22);
builder.externalTransition().from(MyState.State21).to(MyState.State22).on(MyEvent.Event2);

annotation

@States({
      @State(name="Root", entryCallMethod="enterRoot", exitCallMethod="exitRoot", compositeType=StateCompositeType.PARALLEL),
      @State(parent="Root", name="RegionState1", entryCallMethod="enterRegionState1", exitCallMethod="exitRegionState1"),
      @State(parent="Root", name="RegionState2", entryCallMethod="enterRegionState2", exitCallMethod="exitRegionState2")
})
  • 定义上下文事件

针对不同用例定义了三种类型的上下文事件。
启动/终止事件:状态机启动/终止时,将使用声明为启动/终止事件的事件。因此,用户可以区分调用的动作触发器,例如,当状态机正在启动并进入其初始状态时,用户可以区分这些状态进入动作是由启动事件调用的。
完成事件:当所有并行状态都达到最终状态时,将自动触发完成事件。用户可以根据完成事件定义以下过渡。
api

builder.defineFinishEvent(HEvent.Start);
builder.defineTerminateEvent(HEvent.Terminate);
builder.defineStartEvent(HEvent.Finish);

annotation

@ContextEvent(terminateEvent = "RootToA")
public class ParallelStateMachine extends AbstractUntypedStateMachine {
}
  • 使用历史记录保存和还原当前状态

  • 过渡类型
    过渡分为三种类型:

    • Internal Transition (内部过渡)
      不会引起状态的变换,即不会调用entry和exit方法。
    • Local Transition(本地过渡)
      不会退出当前状态,但子状态会重新进入
    • External Transition (外部过渡)
      退出当前状态

api

builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.A2B);
builder.internalTransition().within(MyState.A).on(MyEvent.innerA);
builder.localTransition().from(MyState.A).to(MyState.CinA).on(MyEvent.intoC)

annotation

@Transitions({
     @Transition(from="A", to="B", on="A2B"), 
     @Transition(from="A", on="innerA", type=TransitionType.INTERNAL),
     @Transition(from="A", to="CinA", on="intoC", type=TransitionType.LOCAL),
 })
  • 多态事件调度
    状态机生命周期所触发的事件

|--StateMachineEvent 						/* 所有状态机的事件 */   
     |--StartEvent							/* 当状态机启动时触发  */ 
     |--TerminateEvent						/* 当状态机终止时触发 */ 
     |--TransitionEvent						/* 过渡时触发的事件 */ 
     	  |--TransitionBeginEvent				/* 过渡开始时触发 */ 
          |--TransitionCompleteEvent			/* 过渡完成时触发  */ 
          |--TransitionExceptionEvent			/* 抛出异常时触发  */ 
          |--TransitionDeclinedEvent			/* 过渡拒绝时触发  */ 
          |--TransitionEndEvent				/* 结束过渡时触发(无论何种情况结束)*/
  • 声明式事件监听器
    为了简化状态机的使用,Squirrel 基础提供了一种声明式方法,可通过以下注解添加事件监听器,例如
  static class ExternalModule {
      @OnTransitionEnd
      @ListenerOrder(10) // Since 0.3.1 ListenerOrder can be used to insure listener invoked orderly
      public void transitionEnd() {
          // method annotated with TransitionEnd will be invoked when transition end...
          // the method must be public and return nothing
      }
        
      @OnTransitionBegin
      public void transitionBegin(TestEvent event) {
          // method annotated with TransitionBegin will be invoked when transition begin...
      }
        
      // 'event'(E), 'from'(S), 'to'(S), 'context'(C) and 'stateMachine'(T)可以用于MVEL脚本	
      @OnTransitionBegin(when="event.name().equals(\"toB\")")
      public void transitionBeginConditional() {
          // method will be invoked when transition begin while transition caused by event "toB"
      }
        
      @OnTransitionComplete
      public void transitionComplete(String from, String to, TestEvent event, Integer context) {
          // method annotated with TransitionComplete will be invoked when transition complete...
      }
        
      @OnTransitionDecline
      public void transitionDeclined(String from, TestEvent event, Integer context) {
          // method annotated with TransitionDecline will be invoked when transition declined...
      }
        
      @OnBeforeActionExecuted
  	public void onBeforeActionExecuted(Object sourceState, Object targetState, 
          	Object event, Object context, int[] mOfN, Action<?, ?, ?,?> action) {
          // method annotated with OnAfterActionExecuted will be invoked before action invoked  
      }
        
      @OnAfterActionExecuted
  	public void onAfterActionExecuted(Object sourceState, Object targetState, 
          	Object event, Object context, int[] mOfN, Action<?, ?, ?,?> action) {
      	// method annotated with OnAfterActionExecuted will be invoked after action invoked  
  	}
    
  	@OnActionExecException
  	public void onActionExecException(Action<?, ?, ?,?> action, TransitionException e) {
      	// method annotated with OnActionExecException will be invoked when action thrown exception  
  	}
  }
    
  ExternalModule externalModule = new ExternalModule();
  fsm.addDeclarativeListener(externalModule);
  ...
  fsm.removeDeclarativeListener(externalModule);
  • 过渡扩展方法
    每个过渡事件还对AbstractStateMachine类具有相应的扩展方法,该扩展方法允许在客户状态机实现类中进行扩展。
protected void afterTransitionCausedException(Exception e, S fromState, S toState, E event, C context) {

}
    
protected void beforeTransitionBegin(S fromState, E event, C context) {
}
    
protected void afterTransitionCompleted(S fromState, S toState, E event, C context) {
}
    
protected void afterTransitionEnd(S fromState, S toState, E event, C context) {
}
    
protected void afterTransitionDeclined(S fromState, E event, C context) {
}
    
protected void beforeActionInvoked(S fromState, S toState, E event, C context) {
}
  • 加权操作
    用户可以定义操作权重以调整操作执行顺序。状态进入/退出和状态转换期间的动作根据其权重值以升序排列。
    默认的权重是0,以before开头的权重为100,以after开头的权重是-100,注解设置权重的方法是 方法名:权重当方法名:ignore时,不会调用该方法。
public class WeightedActionTest {
    
    @Transitions({
        @Transit(from="A", to="B", on="ToB", callMethod="fromAToB"),
        @Transit(from="A", to="C", on="ToC", callMethod="fromAToC"),
        @Transit(from="A", to="D", on="ToD")
    })
    @States({
        @State(name="D", entryCallMethod="entryD")
    })
    @StateMachineParameters(stateType=String.class, eventType=String.class, contextType=Void.class)
    @ContextInsensitive
    static class UntypedStateMachineBase extends AbstractUntypedStateMachine {
        
        protected StringBuilder logger = new StringBuilder();

        protected void fromAToB(String from, String to, String event) {
            logger.append("fromAToB");
        }
        
        protected void transitFromAToBOnToB(String from, String to, String event) {
            logger.append("transitFromAToBOnToB");
        }
        
        protected void fromAToC(String from, String to, String event) {
            logger.append("fromAToC");
        }
        
        protected void entryD(String from, String to, String event) {
            logger.append("entryD");
        }
        
        @Override
        protected void beforeActionInvoked(Object fromState, Object toState, Object event, Object context) {
            addOptionalDot();
        }
        
        private void addOptionalDot() {
            if (logger.length() > 0) {
                logger.append('.');
            }
        }
        
        public String consumeLog() {
            final String result = logger.toString();
            logger = new StringBuilder();
            return result;
        }
    }
    
    @Transitions({
        @Transit(from="A", to="B", on="ToB", callMethod="beforeFromAToB"),
        @Transit(from="A", to="B", on="ToB", callMethod="afterFromAToB"),
        @Transit(from="A", to="C", on="ToC", callMethod="goAToC1:+150"),
        @Transit(from="A", to="C", on="ToC", callMethod="goAToC2:-150"),
    })
    @States({
        @State(name="D", entryCallMethod="beforeEntryD"),
        @State(name="D", entryCallMethod="goEntryD:-150"),
    })
    static class UntypedStateMachineExt extends UntypedStateMachineBase {

        protected void beforeFromAToB(String from, String to, String event) {
            logger.append("beforeFromAToB");
        }
        
        protected void afterFromAToB(String from, String to, String event) {
            logger.append("afterFromAToB");
        }
        
        protected void goAToC1(String from, String to, String event) {
            logger.append("goAToC1");
        }
        
        protected void goAToC2(String from, String to, String event) {
            logger.append("goAToC2");
        }
        
        protected void beforeEntryD(String from, String to, String event) {
            logger.append("beforeEntryD");
        }
        
        protected void goEntryD(String from, String to, String event) {
            for(int i=0; i<10000; ++i) {
                RandomStringUtils.randomAlphabetic(10);
            }
            logger.append("goEntryD");
        }
        
    }
    
    @States({
        @State(name="D", entryCallMethod="entryD:+200") // override extension method weight
    })
    static class UntypedStateMachineExt2 extends UntypedStateMachineExt {
    }
    
    private UntypedStateMachineExt fsm;
    
    private StateMachineLogger logger;
    
    @After
    public void teardown() {
        if(fsm.getStatus()!=StateMachineStatus.TERMINATED)
            fsm.terminate(null);
    }
    
    @Before
    public void setup() {
        UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt.class);
        fsm = builder.newUntypedStateMachine("A", 
                StateMachineConfiguration.create().enableDebugMode(true),
                new Object[0]);
    }


    /**
     * 测试 before:100 注解  默认方法  after:-100优先级
     */
    @Test
    public void testBeforeExtension() {
        fsm.fire("ToB");
        assertThat(fsm.consumeLog(), is(equalTo("beforeFromAToB.fromAToB.transitFromAToBOnToB.afterFromAToB")));
    }

    /**
     * 测试设置优先级的调用顺序
     */
    @Test
    public void testWeightTransitionAction() {
        fsm.fire("ToC");
        assertThat(fsm.consumeLog(), is(equalTo("goAToC1.fromAToC.goAToC2")));
    }

    /**
     * 
     */
    @Test
    public void testWeightStateAction() {
        fsm.fire("ToD");
        assertThat(fsm.consumeLog(), is(equalTo("beforeEntryD.entryD.goEntryD")));
    }

    /**
     * 测试重写优先级的调用顺序
     */
    @Test
    public void testOverrideWeight() {
        UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt2.class);
        fsm = builder.newUntypedStateMachine("A");
        logger = new StateMachineLogger(fsm);
        logger.startLogging();
        fsm.fire("ToD");
        assertThat(fsm.consumeLog(), is(equalTo("entryD.beforeEntryD.goEntryD")));
    }

    /**
     * 测试ignore来忽略调用方法
     */
    @Test
    public void testIgnoreWeight() {
        UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineExt2.class);
        builder.onEntry("D").callMethod("entryD:ignore"); 
        fsm = builder.newUntypedStateMachine("A");
        logger = new StateMachineLogger(fsm);
        logger.startLogging();
        fsm.fire("ToD");
        assertThat(fsm.consumeLog(), is(equalTo("beforeEntryD.goEntryD")));
    }
}

  • PostProcessor
    用户可以为特定类型的状态机注册后处理器,以便在实例化状态机后添加后处理逻辑,例如
// 1 User defined a state machine interface
  interface MyStateMachine extends StateMachine<MyStateMachine, MyState, MyEvent, MyContext> {
  . . .
  }
	
  // 2 Both MyStateMachineImpl and MyStateMachineImplEx are implemented MyStateMachine
  class MyStateMachineImpl implements MyStateMachine {
      . . . 
  }
  class MyStateMachineImplEx implements MyStateMachine {
      . . .
  }
	
  // 3 User define a state machine post processor
  MyStateMachinePostProcessor implements SquirrelPostProcessor<MyStateMachine> {
      void postProcess(MyStateMachine component) {
          . . . 
      }
  }  
	
  // 4 User register state machine post process
  SquirrelPostProcessorProvider.getInstance().register(MyStateMachine.class, MyStateMachinePostProcessor.class);
  • 状态机导出
    SCXMLVisitor可用于导出SCXML文档中的状态机定义。用户还可以调用StateMachine.exportXMLDefinition(true)导出美化的XML定义。
SCXMLVisitor visitor = SquirrelProvider.getInstance().newInstance(SCXMLVisitor.class);
stateMachine.accept(visitor);
visitor.convertSCXMLFile("MyStateMachine", true);

DotVisitor可以用来生成状态图,可以通过GraphViz查看。

DotVisitor visitor = SquirrelProvider.getInstance().newInstance(DotVisitor.class);
stateMachine.accept(visitor);
visitor.convertDotFile("SnakeStateMachine");
  • 状态机导入
    UntypedStateMachineImporter可用于导入由SCXMLVisitor或手写定义导出的状态机SCXML类似定义。UntypedStateMachineImporter将根据定义构建一个UntypedStateMachineBuilder,该定义以后可用于创建状态机实例。
UntypedStateMachineBuilder builder = new UntypedStateMachineImporter().importDefinition(scxmlDef);
  ATMStateMachine stateMachine = builder.newAnyStateMachine(ATMState.Idle);

注意:UntypedStateMachineImporter提供了XML样式来定义状态机,就像状态机构建器API或声明性注释一样。与SCXML类似的定义不等于标准SCXML。

  • 保存/加载状态机数据
    当状态机处于空闲状态时,用户可以保存状态机的数据。用户还可以将上面保存的数据加载到状态已终止或刚刚初始化的另一个状态机中.
    @Test
    public void testSavedData() {
        stateMachine.fire(LEvent.A12A2, 0);
        assertThat(stateMachine.getCurrentState(), equalTo(LState.A2));
        StateMachineData.Reader<TestStateMachine, LState, LEvent, Integer> savedData =
                stateMachine.dumpSavedData();
        assertThat(savedData.linkedStates(), contains(LState.A));
        stateMachine.terminate(null);

        try {
            // use buffering
            OutputStream file = new FileOutputStream("data.sqr");
            OutputStream buffer = new BufferedOutputStream(file);
            OutputStreamWriter osw = new OutputStreamWriter(buffer, "UTF-8");
            osw.write(ObjectSerializableSupport.serialize(savedData));
            osw.flush();
        } catch (IOException ex) {
            Assert.fail();
        }

        setup();

        try {
            // use buffering
            InputStream file = new FileInputStream("data.sqr");
            InputStream buffer = new BufferedInputStream(file);
            InputStreamReader isr = new InputStreamReader(buffer, "UTF-8");
            BufferedReader br = new BufferedReader(isr);
            String fileContent = br.readLine();

            // deserialize the List
            StateMachineData.Reader<TestStateMachine, LState, LEvent, Integer> loadedSavedData =
                    ObjectSerializableSupport.deserialize(fileContent);
            stateMachine.loadSavedData(loadedSavedData);
            stateMachine.fire(LEvent.A22A3, 0);
            assertThat(stateMachine.getCurrentState(), equalTo(LState.A3));
        } catch (Exception ex) {
            ex.printStackTrace();
            Assert.fail();
        }

    }
  • 状态机配置
    创建新的状态机实例时,用户可以通过StateMachineConfiguration来配置其行为 ,例如
UntypedStateMachine fsm = builder.newUntypedStateMachine("a", 
 	 StateMachineConfiguration.create().enableAutoStart(false)
      		.setIdProvider(IdProvider.UUIDProvider.getInstance()), 
      new Object[0]); // since 0.3.0
 fsm.fire(TestEvent.toA);

上面的示例代码用于创建一个以UUID作为其标识符的状态机实例,并禁用自动启动功能。
StateMachineConfigure也可以在状态机构建器上设置,这意味着由该配置创建builder.newStateMachine(S initialStateId)或builder.newStateMachine(S initialStateId, Object… extraParams)将使用此配置的所有状态机实例。

  • 状态机诊断

  • Timed State
    定时触发状态。定时任务将被提交给ScheduledExecutorService。用户可以通过SquirrelSingletonProvider注册您的ScheduledExecutorService实现实例,如果未注册ScheduledExecutorService实例,则SquirrelConfiguration将提供默认实例。之后,可以由状态机构建器定义定时状态。

  ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
  SquirrelSingletonProvider.getInstance().register(ScheduledExecutorService.class, scheduler);
  builder.defineTimedState("A", 50, 100, "FIRST", null);
  builder.internalTransition().within("A").on("FIRST");	
  • 子机状态
    将一个状态机作为另一个状态机的子状态。
public class LinkedStateMachineTest {

    enum LState {
        A, B, C, D, A1, A2, A3
    }

    enum LEvent {
        A2B, B2C, C2D, D2A, A12A2, A22A3, A32A1
    }

    @States({
            @State(name = "A", entryCallMethod = "enterA", exitCallMethod = "leftA"),
            @State(name = "B", entryCallMethod = "enterB", exitCallMethod = "leftB"),
            @State(name = "C", entryCallMethod = "enterC", exitCallMethod = "leftC"),
            @State(name = "D", entryCallMethod = "enterD", exitCallMethod = "leftD") })
    @Transitions({
            @Transit(from = "A", to = "B", on = "A2B", callMethod = "transitA2B"),
            @Transit(from = "B", to = "C", on = "B2C", callMethod = "transitB2C"),
            @Transit(from = "C", to = "D", on = "C2D", callMethod = "transitC2D"),
            @Transit(from = "D", to = "A", on = "D2A", callMethod = "transitD2A") })
    static class TestStateMachine extends
            AbstractStateMachine<TestStateMachine, LState, LEvent, Integer> {

        private StringBuilder logger;

        protected TestStateMachine(StringBuilder logger) {
            this.logger = logger;
        }

        public void transitA2B(LState from, LState to, LEvent event,
                Integer context) {
            addOptionalDot();
            logger.append("transitA2B");
        }

        public void transitB2C(LState from, LState to, LEvent event,
                Integer context) {
            addOptionalDot();
            logger.append("transitB2C");
        }

        public void transitC2D(LState from, LState to, LEvent event,
                Integer context) {
            addOptionalDot();
            logger.append("transitC2D");
        }

        public void transitD2A(LState from, LState to, LEvent event,
                Integer context) {
            addOptionalDot();
            logger.append("transitD2A");
        }

        public void enterA(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
            logger.append("enterA");
        }

        public void leftA(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
            logger.append("leftA");
        }

        public void enterB(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
            logger.append("enterB");
        }

        public void leftB(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
            logger.append("leftB");
        }

        public void enterC(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
            logger.append("enterC");
        }

        public void leftC(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
            logger.append("leftC");
        }

        public void enterD(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
            logger.append("enterD");
        }

        public void leftD(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
            logger.append("leftD");
        }

        @Override
        public void start(Integer context) {
            logger.append("start1");
            super.start(context);
        }

        @Override
        public void terminate(Integer context) {
            addOptionalDot();
            logger.append("terminate1");
            super.terminate(context);
        }

        private void addOptionalDot() {
            if (logger.length() > 0) {
                logger.append('.');
            }
        }
    }

    @States({
            @State(name = "A1", entryCallMethod = "enterA1", exitCallMethod = "leftA1"),
            @State(name = "A2", entryCallMethod = "enterA2", exitCallMethod = "leftA2"),
            @State(name = "A3", entryCallMethod = "enterA3", exitCallMethod = "leftA3") })
    @Transitions({
            @Transit(from = "A1", to = "A2", on = "A12A2", callMethod = "transitA12A2"),
            @Transit(from = "A2", to = "A3", on = "A22A3", callMethod = "transitA22A3"),
            @Transit(from = "A3", to = "A1", on = "A32A1", callMethod = "transitA32A1") })
    static class LinkedStateMachine extends
            AbstractStateMachine<LinkedStateMachine, LState, LEvent, Integer> {

        private StringBuilder logger;

        protected LinkedStateMachine(StringBuilder logger) {
            this.logger = logger;
        }

        public void transitA12A2(LState from, LState to, LEvent event,
                Integer context) {
            logger.append("transitA12A2");
        }

        public void transitA22A3(LState from, LState to, LEvent event,
                Integer context) {
            logger.append("transitA22A3");
        }

        public void transitA32A1(LState from, LState to, LEvent event,
                Integer context) {
            logger.append("transitA32A1");
        }

        public void enterA1(LState from, LState to, LEvent event,
                Integer context) {
            logger.append("enterA1");
        }

        public void leftA1(LState from, LState to, LEvent event, Integer context) {
            logger.append("leftA1");
        }

        public void enterA2(LState from, LState to, LEvent event,
                Integer context) {
            logger.append("enterA2");
        }

        public void leftA2(LState from, LState to, LEvent event, Integer context) {
            logger.append("leftA2");
        }

        public void enterA3(LState from, LState to, LEvent event,
                Integer context) {
            logger.append("enterA3");
        }

        public void leftA3(LState from, LState to, LEvent event, Integer context) {
            logger.append("leftA3");
        }
        
        @Override
        protected void beforeActionInvoked(LState from, LState to, LEvent event, Integer context) {
            addOptionalDot();
        }

        @Override
        public void start(Integer context) {
            addOptionalDot();
            logger.append("start2");
            super.start(context);
        }

        @Override
        public void terminate(Integer context) {
            addOptionalDot();
            logger.append("terminate2");
            super.terminate(context);
        }

        private void addOptionalDot() {
            if (logger.length() > 0) {
                logger.append('.');
            }
        }
    }

    TestStateMachine stateMachine;
    
    TestStateMachine stateMachine2;

    StringBuilder logger;
    
    @Before
    public void setup() {
        logger = new StringBuilder();
        StateMachineBuilder<LinkedStateMachine, LState, LEvent, Integer> builderOfLinkedStateMachine = 
                StateMachineBuilderFactory.create(LinkedStateMachine.class, LState.class, LEvent.class,
                        Integer.class, new Class<?>[] { StringBuilder.class });

        StateMachineBuilder<TestStateMachine, LState, LEvent, Integer> builderOfTestStateMachine = 
                StateMachineBuilderFactory.create(TestStateMachine.class, LState.class, LEvent.class,
                        Integer.class, new Class<?>[] { StringBuilder.class });
        
        // 定义链接状态
        builderOfTestStateMachine.defineLinkedState(LState.A, builderOfLinkedStateMachine, LState.A1, logger);
        builderOfTestStateMachine.defineLinkedState(LState.C, builderOfLinkedStateMachine, LState.A2, logger);
        stateMachine = builderOfTestStateMachine.newStateMachine(LState.A, logger);
        stateMachine2 = builderOfTestStateMachine.newStateMachine(LState.A, logger);
    }

    @Test
    public void testInitialLinkedState() {
        doTestInitialLinkedState(stateMachine, logger);
        logger.append("|");
        doTestInitialLinkedState(stateMachine2, logger);
        assertThat(logger.toString(), equalTo("start1.enterA.start2.enterA1|start1.enterA.start2.enterA1"));
    }
    
    private void doTestInitialLinkedState(TestStateMachine stateMachine, StringBuilder logger) {
        stateMachine.start(0);
        assertThat(stateMachine.getCurrentState(), equalTo(LState.A1));
        assertThat(stateMachine.getCurrentRawState().getStateId(), equalTo(LState.A1));
    }

    @Test
    public void testLinkedStateMachineProcessEvent() {
        stateMachine.fire(LEvent.A12A2, 0);
        assertThat(
                logger.toString(),
                equalTo("start1.enterA.start2.enterA1.leftA1.transitA12A2.enterA2"));
    }

    @Test
    public void testTestStateMachineProcessEvent() {
        stateMachine.fire(LEvent.A2B, 0);
        assertThat(
                logger.toString(),
                equalTo("start1.enterA.start2.enterA1.terminate2.leftA1.leftA.transitA2B.enterB"));
    }

    @Test
    public void testInitialLinkedState2() {
        stateMachine.fire(LEvent.A2B, 0);
        System.out.println(logger);

        stateMachine.fire(LEvent.B2C, 0);
        System.out.println(logger);
        System.out.println(stateMachine.getCurrentState());
        stateMachine.fire(LEvent.A22A3, 0);
        System.out.println(logger);
        assertThat(stateMachine.getCurrentState(), equalTo(LState.A3));
        assertThat(stateMachine.getCurrentRawState().getStateId(), equalTo(LState.A3));
    }
}
 类似资料:

相关阅读

相关文章

相关问答