Статьи

Apache Commons SCXML: реализация конечного автомата

В этой статье упоминаются конечные автоматы (FSM), SCXML (расширяемый язык разметки диаграмм состояний) и библиотека SCXML Apache Common. Базовый пример кода конечного автомата банкомата также предоставляется в статье.

Конечные автоматы:

Вы, наверное, помните Finite State Machines из ваших курсов по информатике. Автоматы используются для разработки компьютерных программ или цифровых схем.

Образец конечного автомата [2]

FSM — это просто абстрактная машина, которая может находиться в одном из конечного числа состояний. Машина находится одновременно только в одном состоянии; состояние, в котором оно находится в любой момент времени, называется текущим состоянием. Он может переходить из одного состояния в другое, когда инициируется инициирующим событием или условием, это называется переходом. Конкретный FSM определяется списком возможных состояний перехода из каждого текущего состояния и условием запуска для каждого перехода .

Язык SCXML:

Рабочий черновик SCXML (обозначение конечного автомата для абстракции управления, опубликованный W3C ) может использоваться для описания сложных конечных автоматов. SCXML — это язык конечных автоматов общего назначения на основе xml. Это все еще черновик, последняя версия — 16 февраля 2012 г. Нажмите здесь, чтобы получить пятиминутное введение в документы SCXML.

Библиотека Apache Commons SCXML:

Apache имеет реализацию, направленную на создание и поддержку механизма Java SCXML, способного выполнять конечный автомат, определенный с использованием документа SCXML, при абстрагировании интерфейсов среды. Последняя стабильная версия 0.9.

Редакторы SCXML:

Плагин Apache Eclipse предназначен для предоставления визуального редактора для редактирования файлов SCXML, но он все еще находится в стадии разработки. Существует также scxml gui ( http://code.google.com/p/scxmlgui/ ), который очень успешен. Вы также можете проверить визуальную диаграмму состояний State Forge: http://www.stateforge.com/StateMachineDiagram/StateMachineDiagram.html

Пример кода:

В этой части статьи мы реализуем базовый конечный автомат состояния банкомата. В качестве краткой информации мы предполагаем, что банкомат может иметь следующие статусы. :

  • простаивает: когда банкомат не работает, он просто закрыт
  • загрузка: когда бездействующий банкомат пытается подключиться к ATM-серверу, запускаются конфигурации и информация
  • Не работает : если происходит сбой загрузки банкомата или отключен банкомат
  • В процессе эксплуатации : если загрузка банкомата прошла успешно или банкомат запущен
  • Отключено: если банкомат не подключен к сети

Извините за недостающую или неверную информацию о статусах банкоматов. Это всего лишь пример. Давайте сначала нарисуем наш конечный автомат с помощью программы scxmlgui . Можно написать свой собственный файл scxml, но scxmlgui делает эту уродливую задачу за вас. Вот диаграмма диаграммы состояния, которая описывает изменение статуса банкомата:

И выходной файл SCXML, описывающий переходы на диаграмме выше:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<scxml initial="idle" name="atm.connRestored" version="0.9"
     
 <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.
  • FSM настраивается путем предоставления пути к файлу scxml ( atm_status.xml ) супер-конструктору.
  • Изменения состояния банкомата контролируются событиями. Когда метод fireEvent вызывается со связанным именем события [например, fireEvent (‘atm.connected’)], состояние FSM обновляется автоматически. Вы можете получить текущее состояние, когда захотите.
  • Вы также можете написать общедоступные методы с именами состояний нашего FSM. Эти методы вызываются, когда соответствующее состояние активировано.
01
02
03
04
05
06
07
08
09
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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");
 }
}

У нас есть следующий файл enum для описания наших событий. Вам не нужно кодировать такой класс, но это может помочь определить события. Вы также можете получить эти события динамически, используя фрагмент кода getEngine (). GetCurrentStatus (). GetEvents ().

01
02
03
04
05
06
07
08
09
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
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 сначала показывает возможные события, которые могут быть запущены. Когда событие выбрано и отправлено, отображается текущее состояние банкомата и обновляется список событий.

01
02
03
04
05
06
07
08
09
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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

Ссылка: легкая реализация конечного автомата с помощью Apache Commons SCXML от нашего партнера JCG Озкана САРИ в блоге Java Fun .