Apache Commons-SCXML系列之Demo:秒表

2023-12-12 15:30

本文主要是介绍Apache Commons-SCXML系列之Demo:秒表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 编程方式一

1.1 画状态图

秒表的状态图,其中秒表有:重置运行中,**已停止**3个状态

这里写图片描述

1.2 编写xml文件

秒表状态机定义文件:stopwatch1.xml,xml文件分析请看后面

<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"initial="reset"><state id="reset"><onentry><script>stopWatchEntity.reset()</script></onentry><transition event="watch.start" target="running" /></state><state id="running"><onentry><script>stopWatchEntity.run()</script></onentry><transition event="watch.stop" target="stopped" /></state><state id="stopped"><onentry><script>stopWatchEntity.stop()</script></onentry><transition event="watch.start" target="running" ></transition><transition event="watch.reset" target="reset" /></state></scxml>

1.3 编写程序控制状态转移

需要操作的实体类,用来约束秒表的行为:StopWatchEntity.java


package stopwatch;
import java.io.Serializable;
import java.util.Timer;
import java.util.TimerTask;public class StopWatchEntity implements Serializable {private static final long serialVersionUID = 1L;//时分秒private int hr;private int min;private int sec;//100毫秒private int fract;private transient Timer timer;/*** 重置当前状态机*/public synchronized void reset() {hr = min = sec = fract = 0;}/*** 运行秒表*/public synchronized void run() {if (timer == null) {timer = new Timer(true);//使用timer来定时执行,秒表计数,每100毫秒,执行一次increment方法timer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {increment();}}, 100, 100);}}/*** 停止秒表*/public synchronized void stop() {timer.cancel();timer = null;}/*** 得到当前秒表的时间* @return*/public synchronized String getDisplay() {return String.format("%d:%02d:%02d,%d", hr, min, sec, fract);}/*** 自增方法*/private synchronized void increment() {if (fract < 9) {fract++;} else {fract = 0;if (sec < 59) {sec++;} else {sec = 0;if (min < 59) {min++;} else {min = 0;hr++;}}}}
}

界面类:StopWatchFrame.java

/*** Created by zhengshouzi on 2015/11/20.*/
package stopwatch;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;import javax.swing.*;import org.apache.commons.scxml2.Context;
import org.apache.commons.scxml2.Evaluator;
import org.apache.commons.scxml2.SCXMLExecutor;
import org.apache.commons.scxml2.TriggerEvent;
import org.apache.commons.scxml2.env.SimpleErrorReporter;
import org.apache.commons.scxml2.env.jexl.JexlEvaluator;
import org.apache.commons.scxml2.io.SCXMLReader;
import org.apache.commons.scxml2.model.ModelException;
import org.apache.commons.scxml2.model.SCXML;public class StopWatchFrame extends JFrame implements ActionListener {private static final long serialVersionUID = 1L;private JLabel displayLabel;private JButton startButton;private JButton stopButton;private JButton resetButton;private SCXMLExecutor executor;private StopWatchEntity stopWatchEntity;public static void main(String[] args) {new StopWatchFrame();}public StopWatchFrame() {super("SCXML StopWatch");//初始化状态机initStopWatch();//初始化界面initUI();}/*** 监听器需要执行的方法,自动调用* @param event 事件源*/public void actionPerformed(ActionEvent event) {//得到绑定在每个按钮上的命令String command = event.getActionCommand();//对各个命令进行判断,在执行相应的内容try {if ("START".equals(command)) {//生成watch.start事件,将转到running状态executor.triggerEvent(new TriggerEvent("watch.start", TriggerEvent.SIGNAL_EVENT));//设置一些列按钮的可见性startButton.setEnabled(false);stopButton.setEnabled(true);resetButton.setEnabled(false);} else if ("STOP".equals(command)) {//生成watch.stop事件,将转到stoped状态executor.triggerEvent(new TriggerEvent("watch.stop", TriggerEvent.SIGNAL_EVENT));startButton.setEnabled(true);stopButton.setEnabled(false);resetButton.setEnabled(true);} else if ("RESET".equals(command)) {//生成watch.reset事件,将转到reset状态executor.triggerEvent(new TriggerEvent("watch.reset", TriggerEvent.SIGNAL_EVENT));startButton.setEnabled(true);stopButton.setEnabled(false);resetButton.setEnabled(false);}} catch (ModelException e) {e.printStackTrace();}}/*** 初始化秒表*/private void initStopWatch() {//得到资源文件路径final  URL STOPWATCH = this.getClass().getResource("stopwatch1.xml");//实例化数据模型解析器Evaluator evaluator = new JexlEvaluator();//实例化引擎executor = new SCXMLExecutor(evaluator, null, new SimpleErrorReporter());try {//加载资源文件,实例化到一个SCXML对象,两者之间一一对应SCXML scxml = SCXMLReader.read(STOPWATCH);//将这样的一个SCXML实例,作为状态机对象,传入到引擎里面。executor.setStateMachine(scxml);//设置引擎执行的根上下文Context rootContext = evaluator.newContext(null);final StopWatchEntity stopWatchEntity = new StopWatchEntity();rootContext.set("stopWatchEntity", stopWatchEntity);executor.setRootContext(rootContext);//设置当前对象this.stopWatchEntity = stopWatchEntity;//开始启动流程executor.go();}catch (Exception e){e.printStackTrace();}}/*** 初始化界面*/private void initUI() {JPanel mainPanel = new JPanel();mainPanel.setLayout(new BorderLayout());setContentPane(mainPanel);JPanel contentPanel = new JPanel();contentPanel.setLayout(new FlowLayout());displayLabel = new JLabel("0:00:00,000");displayLabel.setFont(new Font(Font.DIALOG,100,50));contentPanel.add(displayLabel, BorderLayout.CENTER);mainPanel.add(contentPanel, BorderLayout.CENTER);JPanel buttonPanel = new JPanel();buttonPanel.setLayout(new FlowLayout());startButton = createButton("START", "Start");buttonPanel.add(startButton);stopButton = createButton("STOP", "Stop");stopButton.setEnabled(false);buttonPanel.add(stopButton);resetButton = createButton("RESET", "Reset");resetButton.setEnabled(false);buttonPanel.add(resetButton);mainPanel.add(buttonPanel, BorderLayout.SOUTH);setLocation(250,300);setSize(400,200);setResizable(true);setVisible(true);Timer displayTimer = new Timer();displayTimer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {displayLabel.setText(stopWatchEntity.getDisplay());}}, 100, 100);setDefaultCloseOperation(EXIT_ON_CLOSE);}/*** 创建一个按钮* @param command 按钮的命令* @param text 按钮上的文本* @return 返回一个新建的按钮*/private JButton createButton(final String command, final String text) {JButton button = new JButton(text);button.setActionCommand(command);button.addActionListener(this);return button;}}

1.4 程序结果分析

这里写图片描述
这里写图片描述
这里写图片描述

程序界面类中的定时器一直在访问 StopWatchEntity的getDisplay属性,来得到秒表的值。我们通过Start,Stop,Reset来控制秒表的状态,在进入某一个状态机的时候,我们调用这个状态机根上下文设置的stopWatcheEntity对象相应的方法,来改变秒表的值。当秒表一直处于某一个状态的时候,我们又通过点击按钮来改变秒表的状态。

2 编程方式2

2.1 画状态图

图和方式一一样

2.2 编写状态图xml文件

stopwatch2.xml,这个类里面没有了srcipt等标签。

<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml"version="1.0"initial="reset"><state id="reset"><transition event="watch.start"   target="running"/></state><state id="running"><transition event="watch.stop"    target="stopped"/></state><state id="stopped"><transition event="watch.start"   target="running"/><transition event="watch.reset"   target="reset"/></state></scxml>

2.3编写程序控制状态转移

需要操作的实体类(同时也是状态机类),用来约束秒表的行为:StopWatchStateMachine.java。这个类中的方法名字和上面的StopWatchEntity.java名字稍有不同,这个类里面的名字必须要和所对应的xml文件里面的状态名字相同。这是因为当状态发生转移的时候,进入某一个状态的时候,由框架自身根据反射机制去调用对应的方法。


package stopwatch;import org.apache.commons.scxml2.env.AbstractStateMachine;
import org.apache.commons.scxml2.model.ModelException;import java.util.Timer;
import java.util.TimerTask;public class StopWatchStateMachine extends AbstractStateMachine {public static final String EVENT_START = "watch.start";public static final String EVENT_STOP = "watch.stop";public static final String EVENT_RESET = "watch.reset";private int hr, min, sec, fract;private Timer timer;public StopWatchStateMachine() throws ModelException {super(StopWatchStateMachine.class.getResource("stopwatch3.xml"));}/*** 重置当前状态机,方法名和所在的状态名相同,又框架自己调用*/public void reset() {hr = min = sec = fract=0;timer=null;}/*** 运行秒表,方法名和所在的状态名相同,又框架自己调用*/public void running() {if (timer == null) {timer = new Timer(true);timer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {increment();}}, 100, 100);}}/*** 停止秒表,方法名和所在的状态名相同,又框架自己调用*/public void stopped() {timer.cancel();timer = null;}/*** 得到当前秒表的时间* @return*/public synchronized String getDisplay() {return String.format("%d:%02d:%02d,%d", hr, min, sec, fract);}//只是做一个演示,不使用这个方法public String getCurrentState() {return getEngine().getStatus().getStates().iterator().next().getId();}/*** 自增方法*/private synchronized void increment() {if (fract < 9) {fract++;} else {fract = 0;if (sec < 59) {sec++;} else {sec = 0;if (min < 59) {min++;} else {min = 0;hr++;}}}}
}

StopWatchDisplay.java 界面展现类


package stopwatch;import org.apache.commons.scxml2.model.ModelException;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;public class StopWatchDisplay extends JFrameimplements ActionListener {private static final long serialVersionUID = 1L;private JLabel displayLabel;private JButton startButton;private JButton stopButton;private JButton resetButton;private StopWatchStateMachine stopWatchStateMachine;public static void main(String[] args) throws Exception {new StopWatchDisplay();}public StopWatchDisplay() throws ModelException {super("SCXML StopWatch StateMachine");stopWatchStateMachine = new StopWatchStateMachine();initUI();}/*** 监听器需要执行的方法,自动调用* @param event 事件源*/public void actionPerformed(ActionEvent event) {//得到绑定在每个按钮上的命令String command = event.getActionCommand();//对各个命令进行判断,在执行相应的内容if ("START".equals(command)) {//生成watch.start事件,将转到running状态stopWatchStateMachine.fireEvent(StopWatchStateMachine.EVENT_START);//设置一些列按钮的可见性startButton.setEnabled(false);stopButton.setEnabled(true);resetButton.setEnabled(false);} else if ("STOP".equals(command)) {//生成watch.stop事件,将转到stoped状态stopWatchStateMachine.fireEvent(StopWatchStateMachine.EVENT_STOP);startButton.setEnabled(true);stopButton.setEnabled(false);resetButton.setEnabled(true);} else if ("RESET".equals(command)) {//生成watch.reset事件,将转到reset状态stopWatchStateMachine.fireEvent(StopWatchStateMachine.EVENT_RESET);startButton.setEnabled(true);stopButton.setEnabled(false);resetButton.setEnabled(false);}}/*** 初始化界面*/private void initUI() {JPanel mainPanel = new JPanel();mainPanel.setLayout(new BorderLayout());setContentPane(mainPanel);JPanel contentPanel = new JPanel();contentPanel.setLayout(new FlowLayout());displayLabel = new JLabel("0:00:00,000");displayLabel.setFont(new Font(Font.DIALOG, 100, 50));contentPanel.add(displayLabel, BorderLayout.CENTER);mainPanel.add(contentPanel, BorderLayout.CENTER);JPanel buttonPanel = new JPanel();buttonPanel.setLayout(new FlowLayout());startButton = createButton("START", "Start");buttonPanel.add(startButton);stopButton = createButton("STOP", "Stop");stopButton.setEnabled(false);buttonPanel.add(stopButton);resetButton = createButton("RESET", "Reset");resetButton.setEnabled(false);buttonPanel.add(resetButton);mainPanel.add(buttonPanel, BorderLayout.SOUTH);setLocation(250, 300);setSize(400,200);setResizable(true);setVisible(true);Timer displayTimer = new Timer();displayTimer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {displayLabel.setText(stopWatchStateMachine.getDisplay());}}, 100, 100);setDefaultCloseOperation(EXIT_ON_CLOSE);}/*** 创建一个按钮* @param command 按钮的命令* @param text 按钮上的文本* @return 返回一个新建的按钮*/private JButton createButton(final String command, final String text) {JButton button = new JButton(text);button.setActionCommand(command);button.addActionListener(this);return button;}}

2.4 程序结果分析

这里写图片描述
这里写图片描述
这里写图片描述

程序界面类中的定时器一直在访问 StopWatctStateMachine的getDisplay方法,来得到秒表的值。我们通过Start,Stop,Reset来控制秒表的状态,在进入某一个状态机的时候,由框架自动调用对应状态名相同的的函数,来改变秒表的值。当秒表一直处于某一个状态的时候,我们又通过点击按钮来改变秒表的状态。

源代码AbstractStateMachine.java中对应的调用语句如下

         /*** {@inheritDoc}*/public void onEntry(final EnterableState entered) {invoke(entered.getId());}/*** Invoke the no argument method with the following name.** @param methodName The method to invoke.* @return Whether the invoke was successful.*/public boolean invoke(final String methodName) {Class<?> clas = this.getClass();try {Method method = clas.getDeclaredMethod(methodName, SIGNATURE);method.invoke(this, PARAMETERS);} catch (SecurityException se) {logError(se);return false;} catch (NoSuchMethodException nsme) {logError(nsme);return false;} catch (IllegalArgumentException iae) {logError(iae);return false;} catch (IllegalAccessException iae) {logError(iae);return false;} catch (InvocationTargetException ite) {logError(ite);return false;}return true;}

3 两种方式的总结

其实第二种方式是官网给出的例子里面的,同时也是更贴近状态机对象的思想。但是也有如下缺点(也许)
1、 stopWatchStateMachine.fireEvent(StopWatchStateMachine.EVENT_START);只有这一个触发事件的函数,不能传递数据。而第一种方式里面的executor.triggerEvent(new TriggerEvent("watch.start", TriggerEvent.SIGNAL_EVENT),数据);可以通过触发时间传递数据进入状态机里面。

这篇关于Apache Commons-SCXML系列之Demo:秒表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/485085

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

GPT系列之:GPT-1,GPT-2,GPT-3详细解读

一、GPT1 论文:Improving Language Understanding by Generative Pre-Training 链接:https://cdn.openai.com/research-covers/languageunsupervised/language_understanding_paper.pdf 启发点:生成loss和微调loss同时作用,让下游任务来适应预训

Java基础回顾系列-第七天-高级编程之IO

Java基础回顾系列-第七天-高级编程之IO 文件操作字节流与字符流OutputStream字节输出流FileOutputStream InputStream字节输入流FileInputStream Writer字符输出流FileWriter Reader字符输入流字节流与字符流的区别转换流InputStreamReaderOutputStreamWriter 文件复制 字符编码内存操作流(

Java基础回顾系列-第五天-高级编程之API类库

Java基础回顾系列-第五天-高级编程之API类库 Java基础类库StringBufferStringBuilderStringCharSequence接口AutoCloseable接口RuntimeSystemCleaner对象克隆 数字操作类Math数学计算类Random随机数生成类BigInteger/BigDecimal大数字操作类 日期操作类DateSimpleDateForma

Java基础回顾系列-第三天-Lambda表达式

Java基础回顾系列-第三天-Lambda表达式 Lambda表达式方法引用引用静态方法引用实例化对象的方法引用特定类型的方法引用构造方法 内建函数式接口Function基础接口DoubleToIntFunction 类型转换接口Consumer消费型函数式接口Supplier供给型函数式接口Predicate断言型函数式接口 Stream API 该篇博文需重点了解:内建函数式

Java基础回顾系列-第二天-面向对象编程

面向对象编程 Java类核心开发结构面向对象封装继承多态 抽象类abstract接口interface抽象类与接口的区别深入分析类与对象内存分析 继承extends重写(Override)与重载(Overload)重写(Override)重载(Overload)重写与重载之间的区别总结 this关键字static关键字static变量static方法static代码块 代码块String类特

Java基础回顾系列-第六天-Java集合

Java基础回顾系列-第六天-Java集合 集合概述数组的弊端集合框架的优点Java集合关系图集合框架体系图java.util.Collection接口 List集合java.util.List接口java.util.ArrayListjava.util.LinkedListjava.util.Vector Set集合java.util.Set接口java.util.HashSetjava