本文提到有限狀態機(FSM),SCXML(狀態圖可擴展標記語言)和Apache Common的SCXML庫。 本文還提供了基本的ATM有限狀態機示例代碼。
有限狀態機:
您可能還記得計算機科學課程中的有限狀態機。 FSM用于設計計算機程序或數字電路。
有限狀態機樣本[2]
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