状态机简介
状态存储关于过去的信息,就是说:它反映从系统开始到现在时刻的输入变化。转移指示状态变更,并且用必须满足来确使转移发生的条件来描述它。动作是在给定时刻要进行的活动的描述。有多种类型的动作:
- 进入动作(entry action):在进入状态时进行
- 退出动作:在退出状态时进行
- 输入动作:依赖于当前状态和输入条件进行
- 转移动作:在进行特定转移时进行
FSM(有限状态机)可以使用上面图 1 那样的状态图(或状态转移图)来表示。此外可以使用多种类型的状态转移表。下面展示最常见的表示:当前状态(B)和条件(Y)的组合指示出下一个状态(C)。完整的动作信息可以只使用脚注来增加。包括完整动作信息的
FSM 定义可以使用状态表。
State
Chart XML (SCXML): State Machine Notation for Control Abstraction
- SCXML Parser
- 核心 API,用于 SCXML 文档的解析模块,该 SCXML 文档中的各个元素解析组装成对应的 Java 对象实现。
- DataModel
- 核心 API,用于实现 SCXML 状态机中的数据模型定义,将 SCXML 文档中的 datamodel 元素对应的子元素封装成 Java 对象,供状态机引挚在后续操作中使用。
- Context and Evaluators
- 核心 API,用于实现对 SCXML 状态机中上下文环境的保存和更改,以及对 SCXML 文档中表达式语言(例如 <log expr=”${data.value}” />)的解析操作。
- Executor
- 引挚执行器的实例化模块,核心 API,通过此模块完成一个完整引挚执行器的组装和实例化,并提供引挚启动,停止服务器基础功能模块。
- Triggering Event
- 核心 API,SCXML 引挚中事件的定义实现和执行模块。此模块完成一个外部事件的封装,事件池的组织以及具体的事件作用流程控制。
- Custom Actions
- 高级 API,SCXML 状态机引挚的自定义事件支持。除了 SCXML 标准自带的 <var >、<assign> 和 <log> 等标准事件外,程序人员可以进行自定义事件的开发工作,例如系统平台的构件添加、构件删除操作等,需要程序人员的扩展开发工作。以下代码为 Custom Actions 的使用范例。
应用场景:
应用案例说明:
计时器的例子:
创建Maven工程:
<dependencies> <dependency> <groupId>commons-scxml</groupId> <artifactId>commons-scxml</artifactId> <version>0.9</version> </dependency> <dependency> <groupId>commons-jexl</groupId> <artifactId>commons-jexl</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>xalan</groupId> <artifactId>xalan</artifactId> <version>2.6.0</version> </dependency> </dependencies>
创建状态机模型文件:stopwatch.xml
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="reset"> <state id="reset"> <transition event="watch.start" target="running"/> </state> <state id="running"> <transition event="watch.split" target="paused"/> <transition event="watch.stop" target="stopped"/> </state> <state id="paused"> <transition event="watch.unsplit" target="running"/> <transition event="watch.stop" target="stopped"/> </state> <state id="stopped"> <transition event="watch.reset" target="reset"/> </state></scxml>
创建状态机:StopWatch.java
import org.apache.commons.scxml.SCXMLListener;import org.apache.commons.scxml.env.AbstractStateMachine;import org.apache.commons.scxml.model.ModelException;import org.apache.commons.scxml.model.State;import org.apache.commons.scxml.model.Transition;import org.apache.commons.scxml.model.TransitionTarget; import java.util.Timer;import java.util.TimerTask; public class StopWatch extends AbstractStateMachine {/** * The events for the stop watch. */ public static final String EVENT_START = "watch.start", EVENT_STOP = "watch.stop", EVENT_SPLIT = "watch.split", EVENT_UNSPLIT = "watch.unsplit", EVENT_RESET = "watch.reset"; /** * The fragments of the elapsed time. */ private int hr, min, sec, fract; /** * The fragments of the display time. */ private int dhr, dmin, dsec, dfract; /** * The stopwatch "split" (display freeze). */ private boolean split; /** * The Timer to keep time. */ private Timer timer; /** * The display decorations. */ private static final String DELIM = ":", DOT = ".", EMPTY = "", ZERO = "0"; public StopWatch() throws ModelException { super(StopWatch.class.getClassLoader().getResource("stopwatch.xml")); getEngine().addListener(getEngine().getStateMachine(), new EntryListener()); System.out.println(StopWatch.class.getClassLoader().getResource("stopwatch.xml")); } public void reset() { hr = min = sec = fract = dhr = dmin = dsec = dfract = 0; split = false; } public void running() { split = false; if (timer == null) {timer = new Timer(true); timer.scheduleAtFixedRate(new TimerTask() {@Override public void run() { increment(); } }, 100, 100); } } public void paused() { split = true; } public void stopped() { timer.cancel(); timer = null; } public String getDisplay() { String padhr = dhr > 9 ? EMPTY : ZERO; String padmin = dmin > 9 ? EMPTY : ZERO; String padsec = dsec > 9 ? EMPTY : ZERO; return new StringBuffer().append(padhr).append(dhr).append(DELIM). append(padmin).append(dmin).append(DELIM).append(padsec). append(dsec).append(DOT).append(dfract).toString(); } public String getCurrentState() { return ((State)getEngine().getCurrentStatus().getStates().iterator().next()).getId(); } private void increment() {if (fract < 9) {fract++; } else {fract = 0; if (sec < 59) {sec++; } else {sec = 0; if (min < 59) {min++; } else {min = 0; if (hr < 99) {hr++; } else {hr = 0; //wrap } } } }if (!split) {dhr = hr; dmin = min; dsec = sec; dfract = fract; } } /** * A SCXMLListener that is only concerned about "onentry" * notifications. */ protected class EntryListener implements SCXMLListener {public void onEntry(final TransitionTarget entered) { System.out.println("Current State:"+entered.getId()); }public void onTransition(final TransitionTarget from, final TransitionTarget to, final Transition transition) {// nothing to do }public void onExit(final TransitionTarget exited) {// nothing to do } }}
测试代码:
public static void main(String[] args) throws ModelException { StopWatch stopWatch = new StopWatch(); Scanner input=new Scanner(System.in); System.out.println("event: watch.start watch.stop watch.reset watch.split watch.unsplit"); while(true){ String event=input.nextLine(); if(event.trim()!=null&&!event.trim().equals("")){if(event.equals("exit")) break; else{ stopWatch.fireEvent(event); System.out.println(stopWatch.getCurrentState()); System.out.print(stopWatch.getDisplay()); } } } }
每一个状态下会调用对应状态的方法来处理具体的业务处理逻辑。这样就可以实现一个简单的处理机。
Q&A:
关于序列化:2.0版本中可以通过将SCXML中attach的SCInstance实例的序列化来完成,恢复后通过deattach来恢复。
状态恢复:可以通过引入一个新的状态reset状态
reference:
http://www.ibm.com/developerworks/cn/opensource/os-cn-scxml/
http://www.w3.org/TR/scxml/
http://commons.apache.org/proper/commons-scxml/faq.html
时间: 2024-10-01 12:34:58