Squirrel状态机

squirrel-foundation为Java提供了一种易于使用、类型安全且高度可扩展的状态机实现。

个人根据官方文档翻译的中文版,官网地址:Squirrel State Machine

squirrel-foundation

是什么?

就像松鼠这种小巧、敏捷、聪明、警觉和可爱的动物一样,squirrel-foundation旨在为企业提供轻量级、高度灵活和可扩展、可诊断、易于使用和类型安全的Java状态机实现。

下面是描述ATM机状态变化的状态机图:

image-20230824160701442

示例代码可以在“org.squirrelframework.foundation.fsm.atm”包中找到。

Maven

Squirrel-foundation已经部署到maven中央存储库,因此您只需要向pom.xml添加以下依赖项。

最新Released版本:

1
2
3
4
5
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.8</version>
</dependency>

最新Snapshot版本:

1
2
3
4
5
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.9-SNAPSHOT</version>
</dependency>

快速启动

要快速尝试squirrel状态机功能,请创建一个maven项目,并适当地包含squirrel-foundation依赖项。然后运行下面的示例代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.UntypedStateMachine;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;

public class QuickStartSample {

// 1. Define State Machine Event | 定义状态机事件,状态发生切换时可以触发指定事件
enum FSMEvent {
ToA, ToB, ToC, ToD
}

/**
* 2. Define State Machine Class | 定义状态机
* 参数解释:
* * @StateMachineParameters注解 定义状态机参数
* stateType = String.class 表示状态类型为String
* eventType = FSMEvent.class 表示状态机事件类型为FSMEvent
* contextType = Integer.class 表示状态上下文类型为Integer,用于数据透传
*/
@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. Build State Transitions | 构建状态转换过程
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(StateMachineSample.class);
builder.externalTransition().from("A").to("B").on(FSMEvent.ToB).callMethod("fromAToB");
builder.onEntry("B").callMethod("ontoB");

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

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

}

输出:

1
2
3
4
5
6
7
8
9
10
16:25:32.202 [main] DEBUG org.squirrelframework.foundation.fsm.impl.StateImpl - State "A" entry.
16:25:32.206 [main] DEBUG org.squirrelframework.foundation.fsm.impl.AbstractExecutionService - Actions within 'STATE_ENTRY__A' invoked.
16:25:32.210 [main] DEBUG org.squirrelframework.foundation.fsm.impl.StateImpl - State "A" exit.
16:25:32.211 [main] DEBUG org.squirrelframework.foundation.fsm.impl.StateImpl - State "B" entry.
16:25:32.211 [main] DEBUG org.squirrelframework.foundation.fsm.impl.AbstractExecutionService - Actions within 'STATE_EXIT__A' invoked.
Transition from 'A' to 'B' on event 'ToB' with context '10'.
16:25:32.216 [main] DEBUG org.squirrelframework.foundation.fsm.impl.AbstractExecutionService - Actions within 'TRANSITION__A-[ToB, Always, 1, EXTERNAL]->B' invoked.
Entry State 'B'.
16:25:32.216 [main] DEBUG org.squirrelframework.foundation.fsm.impl.AbstractExecutionService - Actions within 'STATE_ENTRY__B' invoked.
Current state is B

现在您可能对示例代码有很多疑问,请耐心等待。下面的用户指南将回答您的大部分问题。但在进入细节之前,需要对状态机概念有基本的了解。这些材料有助于理解状态机的概念。 [state-machine-diagrams] [qt-state-machine]

用户指南

开始

squirrel-foundation支持流式API和声明式的方式来声明状态机,并使用户能够以简单的方式定义操作方法。

State Machine

状态机

接口接受四个泛型类型参数:

  • T表示实现状态机的类型。
  • S表示实现状态的类型。
  • E表示实现事件的类型。
  • C代表实现的外部上下文的类型。

State Machine Builder

状态机构造器

  • 状态机生成器用于生成状态机定义。StateMachineBuilder可以通过StateMachineBuilderFactory创建。
  • StateMachineBuilder由*TransitionBuilder (InternalTransitionBuilder / LocalTransitionBuilder / ExternalTransitionBuilder)和EntryExitActionBuilder组成,前者用于构建状态之间的转换,而后者用于在进入或退出状态时构建动作。
  • 内部状态在转换创建或状态操作创建期间隐式构建。
  • 由同一状态机构建器创建的所有状态机实例共享相同的数据定义,以优化内存使用。
  • 状态机生成器以惰性方式生成状态机定义。当构建器创建第一个状态机实例时,将生成状态机定义,这是一个耗时的过程。但在生成状态机定义之后,后续的状态机实例创建将会快得多。通常,应该尽可能重用状态机构建器。

为了创建状态机,用户需要首先创建状态机构建器。例如:

1
2
StateMachineBuilder<MyStateMachine, MyState, MyEvent, MyContext> builder =
StateMachineBuilderFactory.create(MyStateMachine.class, MyState.class, MyEvent.class, MyContext.class);

状态机构造器接受的参数是状态机(T)、状态(S)、事件(E)和上下文(C)的类型。

Fluent API

流式API

在创建了状态机构建器之后,我们可以使用Fluent API来定义状态机的状态/转换/动作。

1
builder.externalTransition().from(MyState.A).to(MyState.B).on(MyEvent.GoToB);

在状态“A”到状态“B”之间构建外部转换,并在接收到事件“GoToB”时触发

1
builder.internalTransition(TransitionPriority.HIGH).within(MyState.A).on(MyEvent.WithinA).perform(myAction);

一个优先级设置为高的内部转换,在内部状态’ A ‘中构建事件’ WithinA ‘执行’ myAction ‘行为。内部转换是指在转换完成后,没有退出或进入任何状态。转换优先级用于在状态机扩展时覆盖原始转换。

1
2
3
4
5
6
7
8
9
10
11
12
builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.GoToD).when(
new Condition<MyContext>() {
@Override
public boolean isSatisfied(MyContext context) {
return context!=null && context.getValue()>80;
}

@Override
public String name() {
return "MyCondition";
}
}).callMethod("myInternalTransitionCall");

在事件“GoToD”上从状态“C”构建到状态“D”的条件转换,当外部上下文满足条件限制时,然后调用动作方法“myInternalTransitionCall”。用户还可以使用MVEL(一种功能强大的表达语言)以以下方式描述状态。

1
2
builder.externalTransition().from(MyState.C).to(MyState.D).on(MyEvent.GoToD).whenMvel(
"MyCondition:::(context!=null && context.getValue()>80)").callMethod("myInternalTransitionCall");

注意: 使用字符’:::’分隔条件名称和条件表达式。’context ‘是指向当前context对象的预定义变量。

1
builder.onEntry(MyState.A).perform(Lists.newArrayList(action1, action2))

在上面的示例代码中定义了状态输入操作列表。

Method Call Action

方法调用行为

用户可以在定义转换或状态进入/退出期间定义匿名操作。然而,操作代码将分散在许多地方,这可能使代码难以维护。而且,其他用户不能覆盖这些操作。因此,squirrel-foundation也支持定义状态机方法调用动作,这是随状态机类本身而来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
StateMachineBuilder<...> builder = StateMachineBuilderFactory.create(
MyStateMachine.class, MyState.class, MyEvent.class, MyContext.class);
builder.externalTransition().from(A).to(B).on(toB).callMethod("fromAToB");

// 所有的转换行为方法都跟随状态机类
// All transition action method stays with state machine class
public class MyStateMachine<...> extends AbstractStateMachine<...> {
protected void fromAToB(MyState from, MyState to, MyEvent event, MyContext context) {
// this method will be called during transition from "A" to "B" on event "toB"
// the action method parameters types and order should match
...
}
}

此外,squirrel-foundation还支持以约定优于配置的方式定义方法调用动作。基本上,这意味着如果在状态机中声明的方法满足命名和参数约定,它将被添加到转换操作列表中,并在某个阶段被调用。如:

1
protected void transitFromAToBOnGoToB(MyState from, MyState to, MyEvent event, MyContext context)

命名为transitFrom[SourceStateName]To[TargetStateName]On[EventName],参数化为[MyState, MyState, MyEvent, MyContext]将被添加到转换“A-(GoToB)->B”动作列表中。当接收到事件’ GoToB ‘触发状态’ A ‘转换到状态’ B ‘时,将调用此方法。

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

transitFromAnyTo[TargetStateName]On[EventName]该方法将在事件’ GoToB ‘上从任何状态转移到状态’ B ‘时调用。

1
protected void exitA(MyState from, MyState to, MyEvent event, MyContext context)

该方法将在退出状态’ A ‘时被调用。entry[StateName], beforeExitAny/afterExitAny和beforeEntryAny/afterEntryAny也是如此。

其他支持的命名模式

1
2
3
4
5
6
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]

上面列出的那些方法约定还提供了类似aop的功能,这些功能为任何粒度的松鼠状态机提供了内置的灵活扩展功能。

要了解更多信息,请参考测试用例“org.squirrelframework.foundation.fsm.ExtensionMethodCallTest”

从0.3.1开始,有另一种方式来定义这些类似aop的扩展方法,就是通过Fluent API。

1
2
3
4
5
6
7
// since 0.3.1
// the same effect as add method transitFromAnyToCOnToC in your state machine | 和transitFromAnyToCOnToC效果类似
builder.transit().fromAny().to("C").on("ToC").callMethod("fromAnyToC");
// the same effect as add method transitFromBToAnyOnToC in your state machine | 和transitFromBToAnyOnToC效果类似
builder.transit().from("B").toAny().on("ToC").callMethod("fromBToAny");
// the same effect as add method transitFromBToAny in your state machine | 和transitFromBToAny效果类似
builder.transit().from("B").toAny().onAny().callMethod("fromBToAny");

或者通过声明性注释,例如:

1
2
3
4
5
// since 0.3.1
@Transitions({
@Transit(from="B", to="E", on="*", callMethod="fromBToEOnAny"),
@Transit(from="*", to="E", on="ToE", callMethod="fromAnyToEOnToE")
})

注意:这些动作方法将附加到 相匹配的并且已存在的转换,但不会创建任何新的转换。

从0.3.4开始,也可以使用以下API一次定义多个转换,例如:

1
2
3
4
5
6
7
8
// transitions(A->B@A2B=>a2b, A->C@A2C=>a2c, A->D@A2D) will be defined at once
builder.transitions().from(State._A).toAmong(State.B, State.C, State.D).
onEach(Event.A2B, Event.A2C, Event.A2D).callMethod("a2b|a2c|_");

// transitions(A->_A@A2ANY=>DecisionMaker, _A->A@ANY2A) will be defined at once
builder.localTransitions().between(State.A).and(State._A).
onMutual(Event.A2ANY, Event.ANY2A).
perform( Lists.newArrayList(new DecisionMaker("SomeLocalState"), null) );

更多信息可以在org.squirrelframework.foundation.fsm.samples.DecisionStateSampleTest;

Declarative Annotation

声明注解

还提供了一种声明性注解的方式来定义和扩展状态机。这里有一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@States({
@State(name="A", entryCallMethod="entryStateA", exitCallMethod="exitStateA"),
@State(name="B", entryCallMethod="entryStateB", exitCallMethod="exitStateB")
})
@Transitions({
@Transit(from="A", to="B", on="GoToB", callMethod="stateAToStateBOnGotoB"),
@Transit(from="A", to="A", on="WithinA", callMethod="stateAToStateAOnWithinA", type=TransitionType.INTERNAL)
})
interface MyStateMachine extends StateMachine<MyStateMachine, MyState, MyEvent, MyContext> {
void entryStateA(MyState from, MyState to, MyEvent event, MyContext context);
void stateAToStateBOnGotoB(MyState from, MyState to, MyEvent event, MyContext context)
void stateAToStateAOnWithinA(MyState from, MyState to, MyEvent event, MyContext context)
void exitStateA(MyState from, MyState to, MyEvent event, MyContext context);
...
}

这个注解既可以在状态机的实现类中定义,也可以在状态机将要实现的任何接口中定义。它还可以与fluent API混合使用,这意味着fluent API中定义的状态机也可以通过这些注释进行扩展。(您可能需要注意的一件事是,在接口中定义的方法必须是公共的,这也意味着方法调用操作实现对调用者也是公共的。)

Converters

转换器

为了在@State@Transit中声明状态和事件,用户需要为其状态(S)和事件(E)类型实现相应的转换器。转换必须实现Converter<T>接口,该接口将状态/事件转换为String。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Converter<T> extends SquirrelComponent {
/**
* Convert object to string.
* @param obj converted object
* @return string description of object
*/
String convertToString(T obj);

/**
* Convert string to object.
* @param name name of the object
* @return converted object
*/
T convertFromString(String name);
}

然后将这些转换器注册到ConverterProvider。如:

1
2
ConverterProvider.INSTANCE.register(MyEvent.class, new MyEventConverter());
ConverterProvider.INSTANCE.register(MyState.class, new MyStateConverter());

注意:如果只使用Fluent API来定义状态机,则不需要实现相应的转换器。而且,如果Event或State类是String或Enumeration类型,在大多数情况下,您不需要显式实现或注册转换器。

New State Machine Instance

新状态机实例

在用户定义状态机行为之后,用户可以通过构建器创建新的状态机实例。注意,一旦从构建器创建了状态机实例,就不能再使用构建器来定义状态机的任何新元素。

1
T newStateMachine(S initialStateId, Object... extraParams);

要从状态机生成器创建新的状态机实例,需要传递以下参数。

  1. initialStateId: 启动时,状态机的初始状态。

  2. extraParams: 创建新状态机实例所需的额外参数。设置为“new Object[0]”,不需要额外参数。

    a.如果用户在创建新的状态机实例时传递了额外的参数,请确保StateMachineBuilderFactory在创建状态机生成器时也定义了额外参数的类型。否则,额外的参数将被忽略。

    b.额外的参数可以通过两种方式传递到状态机实例中。一种是通过状态机构造函数,这意味着用户需要为状态机实例定义具有相同参数类型和顺序的构造函数。另一种方法是定义一个名为postConstruct的方法,并且具有相同的参数类型和顺序。

如果不需要向状态机传递额外的参数,用户可以简单地调用T newstatemmachine (S initialStateId)来创建一个新的状态机实例。

来自状态机构建器的新状态机。(在这种情况下,不需要传递额外的参数。)

1
MyStateMachine stateMachine = builder.newStateMachine(MyState.Initial);

Trigger Transition

触发转换

状态机创建后,用户可以触发事件和上下文来触发状态机内部的转换。如:

1
stateMachine.fire(MyEvent.Prepare, new MyContext("Testing"));	

Untyped State Machine

无类型状态机

为了简化状态机的使用,并避免过多的泛型(例如 StateMachine<T, S, E, C>),这些泛型在某些情况下可能会使代码难以阅读,但在转换动作执行时仍然保持重要的类型安全特性,为此实现了UntypedStateMachine。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.UntypedStateMachine;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
import org.squirrelframework.foundation.fsm.annotation.Transit;
import org.squirrelframework.foundation.fsm.annotation.Transitions;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;

public class UntypedStateMachineDemo {

enum TestEvent {
toA, toB, toC, toD
}

@Transitions({
@Transit(from = "A", to = "B", on = "toB", callMethod = "fromAToB"),
@Transit(from = "B", to = "C", on = "toC"),
@Transit(from = "C", to = "D", on = "toD"),
@Transit(from = "D", to = "A", on = "toA", callMethod = "transitFromDToAOntoA")
})
@StateMachineParameters(stateType = String.class, eventType = TestEvent.class, contextType = Integer.class)
static class UntypedStateMachineSample extends AbstractUntypedStateMachine {
// No need to specify constructor anymore since 0.2.9
// protected UntypedStateMachineSample(ImmutableUntypedState initialState,
// Map<Object, ImmutableUntypedState> states) {
// super(initialState, states);
// }

protected void fromAToB(String from, String to, TestEvent event, Integer context) {
// transition action still type safe ...
System.out.println("Transition from '" + from + "' to '" + to + "' on event '" + event +
"' with context '" + context + "'.");
}

protected void transitFromDToAOntoA(String from, String to, TestEvent event, Integer context) {
// transition action still type safe ...
System.out.println("Transition from '" + from + "' to '" + to + "' on event '" + event +
"' with context '" + context + "'.");
}
}

public static void main(String[] args) {
UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(UntypedStateMachineSample.class);
// state machine builder not type safe anymore
// builder.externalTransition().from("D").to("A").on(TestEvent.toA);
// UntypedStateMachine fsm = builder.newStateMachine("A");
UntypedStateMachine fsm = builder.newStateMachine("D");
fsm.fire(TestEvent.toA, 8);
}
}

输出:

1
2
3
4
5
6
7
8
18:31:10.629 [main] DEBUG org.squirrelframework.foundation.fsm.impl.StateImpl - State "D" entry.
18:31:10.633 [main] DEBUG org.squirrelframework.foundation.fsm.impl.AbstractExecutionService - Actions within 'STATE_ENTRY__D' invoked.
18:31:10.635 [main] DEBUG org.squirrelframework.foundation.fsm.impl.StateImpl - State "D" exit.
18:31:10.636 [main] DEBUG org.squirrelframework.foundation.fsm.impl.StateImpl - State "A" entry.
18:31:10.636 [main] DEBUG org.squirrelframework.foundation.fsm.impl.AbstractExecutionService - Actions within 'STATE_EXIT__D' invoked.
Transition from 'D' to 'A' on event 'toA' with context '8'.
18:31:10.638 [main] DEBUG org.squirrelframework.foundation.fsm.impl.AbstractExecutionService - Actions within 'TRANSITION__D-[toA, Always, 1, EXTERNAL]->A' invoked.
18:31:10.638 [main] DEBUG org.squirrelframework.foundation.fsm.impl.AbstractExecutionService - Actions within 'STATE_ENTRY__A' invoked.

要构建一个UntypedStateMachine,用户需要先通过StateMachineBuilderFactory创建一个UntypedStateMachineBuilder。StateMachineBuilderFactory只接受一个参数,即创建UntypedStateMachineBuilder的状态机类的类型。

@StateMachineParameters用于声明状态机通用参数类型。

AbstractUntypedStateMachine是任何无类型状态机的基类。

Context Insensitive State Machine

上下文不敏感状态机

有时状态转换不关心上下文,这意味着转换大多只由事件决定。对于这种情况,用户可以使用上下文不敏感状态机来简化方法调用参数。

声明上下文不敏感状态机非常简单。用户只需要在状态机实现类上添加@ContextInsensitive注解。然后就可以忽略转换方法参数列表中的上下文参数。如:

1
2
3
4
5
6
7
8
9
10
@ContextInsensitive
public class ATMStateMachine extends AbstractStateMachine<ATMStateMachine, ATMState, String, Void> {
// no need to add context parameter here anymore | 参数中不再需要上下文了
public void transitFromIdleToLoadingOnConnected(ATMState from, ATMState to, String event) {
...
}
public void entryLoading(ATMState from, ATMState to, String event) {
...
}
}

Transition Exception Handling

转换异常处理

当状态转换过程中发生异常时,执行的动作列表将被中止,状态机将进入错误状态,这意味着状态机实例无法再处理事件。如果用户继续向状态机实例触发事件,将抛出一个IllegalStateException

在转换阶段发生的所有异常(包括 操作执行 和 外部监听器调用)将被包装到TransitionException(未检查的异常)中。目前,默认的异常处理策略简单粗暴,只是继续抛出异常,请参阅AbstractStateMachine.afterTransitionCausedException方法。

1
protected void afterTransitionCausedException(...) { throw e; }

如果想状态机从该异常中恢复,用户可以扩展afterTransitionCausedException方法,并在该方法中添加相应的恢复逻辑。不要忘记在结束时将状态机状态设置为正常状态。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected void afterTransitionCausedException(Object fromState, Object toState, Object event, Object context) {
Throwable targeException = getLastException().getTargetException();
// recover from IllegalArgumentException thrown out from state 'A' to 'B' caused by event 'ToB'
if(targeException instanceof IllegalArgumentException &&
fromState.equals("A") && toState.equals("B") && event.equals("ToB")) {
// do some error clean up job here | 执行异常清理工作
// ... | 异常恢复逻辑
// after recovered from this exception, reset the state machine status back to normal
setStatus(StateMachineStatus.IDLE); // 异常逻辑恢复之后设置状态机为正常状态
} else if(...) {
// recover from other exception ...
} else {
super.afterTransitionCausedException(fromState, toState, event, context);
}
}

Advanced Feature

Define Hierarchical State

定义分层状态

分层状态可以包含嵌套状态。子状态本身可以有嵌套的子状态,并且嵌套可以进行到任何深度。当层次状态处于活动状态时,它的一个子状态且只有一个子状态处于活动状态。层次状态可以通过API或注释来定义。

1
void defineSequentialStatesOn(S parentStateId, S... childStateIds);

*builder.defineSequentialStatesOn(State.A, State.BinA, State.CinA) *在父状态“A”下定义了两个子状态“BinA”和“CinA”,第一个定义的子状态也将是分层状态“A”的初始状态。同样的层次状态也可以通过注释来定义,例如:

1
2
3
4
5
@States({
@State(name="A", entryMethodCall="entryA", exitMethodCall="exitA"),
@State(parent="A", name="BinA", entryMethodCall="entryBinA", exitMethodCall="exitBinA", initialState=true),
@State(parent="A", name="CinA", entryMethodCall="entryCinA", exitMethodCall="exitCinA")
})

Define Parallel State

定义并行状态

并行状态封装了一组子状态,这些子状态在父元素处于活动状态时同时处于活动状态。并行状态可以通过API或注释来定义。如:

image-20230830105006436
1
2
3
4
5
6
7
8
// defines two region states "RegionState1" and "RegionState2" under parent parallel state "Root"
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);

1
2
3
4
5
@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")
})

得到平行状态的当前所有子状态:

1
stateMachine.getSubStatesOn(MyState.Root); // return list of current sub states of parallel state

当所有并行状态达到最终状态时,将触发Finish上下文事件。