本文提到有限状态机(FSM),SCXML(状态图可扩展标记语言)和Apache Common的SCXML库。 本文还提供了基本的ATM有限状态机示例代码。
有限状态机:
您可能还记得计算机科学课程中的有限状态机。 FSM用于设计计算机程序或数字电路。
FSM只是一种抽象机器,可以处于有限状态中的一种。该机器一次仅处于一种状态;
它在任何给定时间所处的状态称为当前状态。
当由触发事件或条件启动时,它可以从一种状态更改为另一种状态,这称为过渡。
特定的FSM由来自每个当前状态的可能过渡状态列表以及每个过渡的触发条件定义。
SCXML语言:
可以使用称为SCXML (用于控制抽象的状态机表示法,由W3C出版)的工作草案来描述复杂的状态机。 SCXML是基于xml的通用状态机语言。 它仍然是草案,最新版本是2012年2月16日。单击此处以获取有关SCXML文档的五分钟介绍。
Apache Commons SCXML库:
Apache的实现旨在创建和维护Java SCXML引擎,该引擎能够执行使用SCXML文档定义的状态机,同时抽象出环境接口。 最新的稳定版本是0.9。
- 图书馆网站: http : //commons.apache.org/scxml/index.html
- Eclipse插件: http : //commons.apache.org/sandbox/gsoc/2010/scxml-eclipse/ (仍在开发中)
- 用例: http : //commons.apache.org/scxml/usecases.html
SCXML编辑器:
Apache的Eclipse插件旨在提供一个可视化编辑器来编辑SCXML文件,但它仍在开发中。 还有一个非常成功的scxml gui( http://code.google.com/p/scxmlgui/ )。 您也可以查看State Forge的可视状态机图: http : //www.stateforge.com/StateMachineDiagram/StateMachineDiagram.html
代码示例:
在本文的这一部分中,我们将实现一个基本的ATM状态状态机。 作为简要信息,我们假设ATM可以具有以下状态。 :
- 空闲:当ATM没有任何活动时,只是将其关闭
- 加载:当空闲的atm尝试连接到ATM服务器时,配置和信息开始加载
- 服务中断:如果ATM加载失败或ATM关闭
- 在服务 :如果ATM老丁是成功或ATM被启动
- 断开连接:如果ATM未连接到网络
很抱歉缺少有关ATM状态的信息。 这只是一个例子。 首先使用scxmlgui程序绘制状态机。 一个人可以编写自己的scxml文件,但是scxmlgui会为您完成这项艰巨的任务。 这是描述ATM的状态变化的状态图:
以及输出SCXML文件,该文件描述了上图中的转换:
<scxml initial="idle" name="atm.connRestored" version="0.9"
xmlns="http://www.w3.org/2005/07/scxml">
<state id="idle">
<transition event="atm.connected" target="loading"></transition>
</state>
<state id="loading">
<transition event="atm.loadSuccess" target="inService"></transition>
<transition event="atm.connClosed" target="disconnected"></transition>
<transition event="atm.loadFail" target="outOfService"></transition>
</state>
<state id="inService">
<transition event="atm.shutdown" target="outOfService"></transition>
<transition event="atm.connLost" target="disconnected"></transition>
</state>
<state id="outOfService">
<transition event="atm.startup" target="inService"></transition>
<transition event="atm.connLost" target="disconnected"></transition>
</state>
<state id="disconnected">
<transition event="atm.connRestored" target="inService"></transition>
</state>
</scxml>
我们的FSM实现在AtmStatusFSM类中。
- AtmStatusFSM类扩展了org.apache.commons.scxml.env.AbstractStateMachine。
- 通过向超级构造函数提供scxml文件( atm_status.xml )路径来配置FSM。
- ATM状态更改由事件控制。 当使用相关事件名称[例如fireEvent('atm.connected')]调用fireEvent方法时,FSM状态将自动更新。 您可以随时获取当前状态。
- 您还可以编写具有FSM状态名称的公共方法。 当相应状态被激活时,将调用这些方法。
package net.javafun.example.atmstatusfsm;
import java.util.Collection;
import java.util.Set;
import org.apache.commons.scxml.env.AbstractStateMachine;
import org.apache.commons.scxml.model.State;
/**
* Atm Status Finite State Machine
*
* @see Apache Commons Scxml Library
* @author ozkansari.com
*
*/
public class AtmStatusFSM extends AbstractStateMachine {
/**
* State Machine uses this scmxml config file
*/
private static final String SCXML_CONFIG_ATM_STATUS = "net/javafun/example/atmstatusfsm/atm_status.xml";
/** CONSTRUCTOR(S) */
public AtmStatusFSM() {
super(AtmStatusFSM.class.getClassLoader().getResource(SCXML_CONFIG_ATM_STATUS));
}
/** HELPER METHOD(S) */
/**
* Fire the event
*/
public void firePreDefinedEvent(AtmStatusEventEnum eventEnum){
System.out.println("EVENT: " + eventEnum);
this.fireEvent(eventEnum.getEventName());
}
public void callState(String name){
this.invoke(name);
}
/**
* Get current state ID as string
*/
public String getCurrentStateId() {
Set states = getEngine().getCurrentStatus().getStates();
State state = (State) states.iterator().next();
return state.getId();
}
/**
* Get current state as apache's State object
*/
public State getCurrentState() {
Set states = getEngine().getCurrentStatus().getStates();
return ( (State) states.iterator().next());
}
/**
* Get events belongs to current status of the FSM
*/
public Collection getCurrentStateEvents() {
return getEngine().getCurrentStatus().getEvents();
}
/** STATES */
// Each method below is the activity corresponding to a state in the
// SCXML document (see class constructor for pointer to the document).
public void idle() {
System.out.println("STATE: idle");
}
public void loading() {
System.out.println("STATE: loading");
}
public void inService() {
System.out.println("STATE: inService");
}
public void outOfService() {
System.out.println("STATE: outOfService");
}
public void disconnected() {
System.out.println("STATE: disconnected");
}
}
我们有以下枚举文件来描述我们的事件。 您不必编写此类代码,但这可能有助于定义事件。 您还可以使用getEngine()。getCurrentStatus()。getEvents()代码片段动态获取这些事件。
package net.javafun.example.atmstatusfsm;
/**
* Atm Status Change Events
*
* @author ozkansari.com
*
*/
public enum AtmStatusEventEnum {
CONNECT("atm.connected"),
CONNECTION_CLOSED("atm.connClosed"),
CONNECTION_LOST("atm.connLost"),
CONNECTION_RESTORED("atm.connRestored"),
LOAD_SUCCESS("atm.loadSuccess"),
LOAD_FAIL("atm.loadFail"),
SHUTDOWN("atm.shutdown"),
STARTUP("atm.startup");
private final String eventName;
private AtmStatusEventEnum(String eventName) {
this.eventName = eventName;
}
public String getEventName() {
return eventName;
}
public static String getNamesAsCsv(){
StringBuilder sb = new StringBuilder();
for (AtmStatusEventEnum e : AtmStatusEventEnum.values()) {
sb.append(e.name());
sb.append(",");
}
return sb.substring(0,sb.length()-2);
}
}
您可以在下面看到基本的GUI代码。 GUI首先显示可能触发的事件。 选择并提交事件后,将显示当前的ATM状态,并更新事件列表。
package net.javafun.example.atmstatusfsm;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.apache.commons.scxml.model.Transition;
/**
* Atm Status Change GUI
*
* @author ozkansari.com
*
*/
public class AtmDisplay extends JFrame implements ActionListener {
private static final long serialVersionUID = -5083315372455956151L;
private AtmStatusFSM atmStatusFSM;
private JButton button;
private JLabel state;
private JComboBox eventComboBox = new JComboBox();
public static void main(String[] args) {
new AtmDisplay();
}
public AtmDisplay() {
super("ATM Display Demo");
atmStatusFSM = new AtmStatusFSM();
setupUI();
}
@SuppressWarnings("deprecation")
private void setupUI() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
setContentPane(panel);
button = makeButton("FIRE_EVENT", AtmStatusEventEnum.getNamesAsCsv(), "Submit" );
panel.add(button, BorderLayout.CENTER);
state = new JLabel(atmStatusFSM.getCurrentStateId());
panel.add(state, BorderLayout.SOUTH);
initEvents();
panel.add(eventComboBox, BorderLayout.NORTH);
pack();
setLocation(200, 200);
setResizable(false);
setSize(300, 125);
show();
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
@SuppressWarnings("unchecked")
private void initEvents() {
eventComboBox.removeAllItems();
List transitionList = atmStatusFSM.getCurrentState().getTransitionsList();
for (Transition transition : transitionList) {
eventComboBox.addItem(transition.getEvent() );
}
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if(command.equals("FIRE_EVENT")) {
checkAndFireEvent();
}
}
private boolean checkAndFireEvent() {
atmStatusFSM.fireEvent(eventComboBox.getSelectedItem().toString());
state.setText(atmStatusFSM.getCurrentStateId());
initEvents();
repaint();
return true;
}
private JButton makeButton(final String actionCommand, final String toolTipText, final String altText) {
JButton button = new JButton(altText);
button.setActionCommand(actionCommand);
button.setToolTipText(toolTipText);
button.addActionListener(this);
button.setOpaque(false);
return button;
}
}
我们的简单程序的输出:
下图给出了Eclipse中显示的项目文件(带有必需的库): 有关完整的源代码,请访问https://github.com/ozkansari/atmstatemachine
参考:来自Java Fun博客的JCG合作伙伴 Ozkan SARI的Apache Commons SCXML轻松有限状态机实现 。
翻译自: https://www.javacodegeeks.com/2012/06/apache-commons-scxml-finite-state.html