0
回答
SCXML有限状态机规范详解与使用
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

简介: 本文对SCXML有限状态机规范进行详细的介绍,介绍其由来、发展历程以及其W3C规范中各元素的详细描述。 并向读者介绍该规范的两种流行编程语言具体实现:Apache Commons SCXML和SCXML4Flex。读者可以通过此文学习有限状态机的规范详细内容并了解该规范在实际业务逻辑中使用范例。

文章转自:http://www.ibm.com/developerworks/cn/opensource/os-cn-scxml/

SCXML 有限状态机规范概述

State Chart XML(SCXML) 是 W3C 组织制定的一种有限状态机的规范,它提供了一个在 CCXML 和 Harel State Tables 基础之上演化而来的状态机执行环境。但此规范目前还处于 Working Draft,即修订当中,目前最新的修订版本是 W3C Working Draft 16 December 2010,相信随着时间的推移,此规范很快将会发布正式版。

SCXML 的概念源于 CCXML 和 Harel State Tables 的结合。CCXML 是一种用来支持具有呼叫中心功能特点的语音应用程序(例如 VoiceXML 但不仅限于此)基于事件驱动的状态机语言。CCXML 1.0 的说明书定义了状态机和事件处理的两种语法,以及标准化的一些呼叫控制基本单元。CCXML 和 Harel State Tables 的结合搭建起了一套表达了富有规则的、很有想法的语义,并具有精密的构架逻辑,例如:平行状态。它们定义了图形化的规范语言,然而却不再使用基本 XML 的呈现方式。

SCXML 是一种多元化、基于事件状态的机器语言,其用法主要有几个方面 :

  1. 它是在 VoiceXML 3.0 的基础之上发展而来的高级对话语言,可封装为不同的语言模块。
  2. 作为语言分析的应用开发程序,它在 VoiceXML 3.0 的基础功能之上,还具备了控制数据库连接的功能和商业业务逻辑模块。
  3. 作为一种多态的控制语言,在这种多形式的交互式构架中,SCXML 集成了 VoiceXML 3.0 的所有对话形式,这其中包括键盘鼠标,文字,视觉感官,触觉感受等多种交互形态。其可能包括一些综合技术如对口型的阅读(语音识别技术与视觉的结合表 现)。语音的输入以键盘作为载体,并且引入多个键盘和多个用户同步编写的功能。
  4. SCXML 还是 CCXML 未来版本的基础构架。
  5. 作为一种高级的、可扩展的呼叫中心管理语言,CCXML 又称为被呼叫中心使用的具有控制功能的计算机电话集成系统。这种集成系统应用计算机屏幕弹出窗口,并提供其他方式的信息交互,例如,在线聊天,发送即时信息等。

有限状态机 SCXML 简单实例

每个 SCXML 状态机文件主要是由多个状态元素以及状态变迁元素以及事件组成。


清单 1. 基于 SCXML 的计时器实例
        
 <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> 

这是一个基于 SCXML 有限状态机规范的计时器实例,在此状态描述文件中,一共有四个状态:rest,running,paused 和 stopped 状态。此状态机的初始状态是 reset 态,是通过 scxml 元素的 initialstate 元素来指定的。这意味当状态机启动时, 最先进入的将会是这个状态。当状态机处于 reset 态的同时有 watch.start 事件发生时,将会触发状态机进行状态迁移,进入 running 态,即计时器运行态。

每个 state 元素可以具有多个状态变迁 transition 子元素,从上例中可以看出,当状态机处于 running 态时,若有 watch.split 事件发生时,将会触发状态迁移事件

 <transition event="watch.split"   target="paused"/> 

使状态机进入 paused 态。而如果是有 watch.sop 事件发生时,将会触发状态迁移事件

 <transition event="watch.stop"    target="stopped"/> 

使状态机进入 stopped 态。

当然,这只是 SCXML 有限状态机最简单的一个示例,仅凭示例中这几个简单的元素,是无法有效的组织起一个成熟的商业应用的。本文后续将会对 SCXML 规范中的元素进行一个详尽的介绍讲解。

SCXML 规范组成及元素详解

核心模型元素

SCXML 元素

该元素是 SCXML 文档的最顶层元素,携带了文档版本等相关信息,当前状态机构成以及子元素等。此元素通过 initialState 属性来指定状态机的进入初始态,可包含 State、Parallel、Final、DataModel 和 Script 子元素。

如果该元素的执行模式处于宽松态,SCXML 引挚一般会忽略该元素所带的不符合规范的属性、子元素以及命名空间信息等。如果该元素的执行模式为严格态,SCXML 引挚将会要求该元素所带的所有属性、子元素和命名空间符合 W3C SCXML 的规范,否则则会认为该 SCXML 文档为不符合规范的文档而拒绝执行。

State 元素

此元素用以表示有限状态机中的一个基本状态,它具有可选的属性 id 和 initial。具有 State 或是 Parallel 子元素的复杂态必须为其指定 initial 属性,用于表示进入此复杂态所应该进入的第一个子状态。

此元素的子元素可以为 OnEntry 元素、OnExit 元素、Transition 元素、State 元素、Parallel 元素、DataModel 元素和 Invoke 元素。

Transition 元素

此元素用以表示状态间的变迁,状态变迁是以事件以驱动的,并会进行变迁条件的判断操作。并且在进行状态迁移时,子元素可以包含执行模块,用以表示在状态迁移时进行的操作。其主要属性包括:

  • Event 属性,用以表示触发此状态迁移的事件。
  • Cond 属性,用以表示此状态迁移进行的限制条件。
  • Target 属性,用以表示此状态迁移的目标态。

此元素可以包括执行模块子元素,例如 Log 元素,下面通过举例进行讲解。


清单 2.Transition 元素实例
        
 <state id=s"> 
   <transition event="e1" cond="x==1" target="s1"/> 
   <transition event="e2" target="s2"> 
        <log expr="'executing transition'"/> 
   </transition> 
 </state> 

此代码清单中的 State 元素具有两个 Transition 子元素。第一个 Transition 元素的触发事件是 s1,当 s1 事件发生时,将会进行迁移条件的判断,如果 x==1,状态机将会迁移至 s1 态。第二个 Transition 元素的触发事件是 s2,当 s2 事件发生时,状态机将会直接迁移至 s2 态,但在状态迁移的过程当中,会执行 Log 操作,输出一行日志内容。

Parallel 元素

此元素表示具有并行执行子元素的状态,与 State 元素类似,此元素可以包含 OnEntry 元素、OnExit 元素、Transition 元素、State 元素、Parallel 元素、DataModel 元素和 Invoke 元素为子元素。

但是 Paralle 元素与 State 元素有着不同的语义,当某个 State 元素处于 active 态,其有且只有一个子元素处于 active 态,但是当某个 Parallel 元素处于 active 态时,其所有子元素均处于 active 态。特别的是,当状态机进入某个 Parallel 元素时,将会同时进入其所有的子元素。Parallel 元素和其所有子元素的 Transition 独立的进行,但这些 Transition 中有一个触发,并且其目标态位于 Parallel 之外,状态机将会退出 Parallel 元素与其所有子元素,转而进入目标态。

OnEntry 元素

此元素是一个容器元素,用以包含状态机进入某个状态时所进行的全部操作,其子元素均为可执行模块内容。

OnExit 元素

类似于 OnEntry 元素,此元素也是一个容器元素,用以包含状态机退出某个状态之前所进行的全部操作,其子元素均为可执行模块内容。

可执行内容元素

可执行模块允许状态在运行过程当中做一些实际的事件,它允许某个状态机对话修改自身的数据模型,或是与外部实例进行交互操作。如上文所 述,由事件组成的可执行模块可以是 Transition 元素的子无素。特别的是,位于 OnEntry 和 OnExit 元素内的可执行元素模块往往和 Transtion 元素里的可执行元素具有等同的作用效果。当状态机执行 Transition 元素时,它将会执行它离开的那个 State 元素的 OnExit 包含的可执行内容模块,当它进入目标 State 元素时,会执行目标态 OnEntry 元素所包含的可执行内容模块。

此模块主要包含如下几个元素:

  • Raise 元素,此元素用于在当前状态机会话当中产生一个内部事件,SCXML 引挚必须将此事件放置在当前会话的内部事件序列的尾部。
  • If 元素,此元素是有条件执行元素的容器,ElseIf 元素和 Else 元素是此元素的可选子元素。If 元素具有 cond 属性,当此属性值为 true 时,状态机将会执行该 If 后的事件子元素,读者可以在后续的代码清单 3 中看到 If 元素的使用示例。
  • ElseIf 元素,此元素是 If 元素的子元素,当其前续的 If 元素或是 ElseIf 元素条件判断值为 false 时,状态机将会进行当前 ElseIf 元素 cond 值的判断,如果值为 true, 将会执行此 ElseIf 元素后续的事件元素。
  • Else 元素,此元素是 If 元素的子元素,当其前续的 If 元素或是 ElseIf 元素条件判断值均为 false 时,状态机将会执行此元素后续的事件元素。
  • Log 元素,此元素为应用提供日志或是调试信息输出的功能,输出的日志信息风格是依赖平台而定的。它具有可选的 label 属性和必选的 expr 属性。
  • 其它元素,SCXML 规范还定义了其它的可执行元素,例如 assign、validate 和 script 等,在这里就不赘述,请读者自行查看。

清单 3. If、ElseIf 和 Else 元素使用示例
        
 <if cond="cond1"> 
  <!-- selected when "cond1" is true --> 
 <elseif cond="cond2"/> 
  <!-- selected when "cond1" is false and "cond2" is true --> 
 <elseif cond="cond3"/> 
  <!-- selected when "cond1" and "cond2" are false and "cond3" is true --> 
 <else/> 
  <!-- selected when "cond1", "cond2", and "cond3" are false --> 
 </if> 

读者可以查看 SCXML 规范的 Executable Content内容模块来查看更多可执行内容元素的信息。

数据模型与数据操作元素

数据模型模块为 SCXML 有限状态机提供了内置的数据存储、读写、修改数据集的能力。SCXML 规范中数据模型用 DataModel 元素来表示,其可以包含一个或是多个 Data 元素,每个 Data 元素表示一个数据项,并可以此数据项指定初始值。可以通过 Assign 元素来修改 Data 数据项的值,通过 Validate 元素来验证 Data 数据值的合法性,并且可以通过 Donedata、Param 等这些元素来完成 Data 元素与外部实例的交互。下面对数据模型模块的 SCXML 元素进行基础的介绍。

  • Datamodel 元素,此元素为容器元素,可以包含一个或是多个 Data 元素,SCXML 通过 Data 元素来完成基本数据项的定义与使用。
  • Data 元素,该元素用来声明和组成 Datamodel 元素,具有必选的 id 属性和可选的 src 和 expr 属性。Data 的 id 属性用来唯一标识某一数据项,通过 src 或是 expr 属性来指定属性值。
  • Assign 元素,此元素用于修改 Data 元素的数据值。具有必选的属性 location,该属性与 Data 元素的 id 属性对应,用于标识某一数据项。通过可选的 expr 为 Data 元素指定新的数据值。
  • Validate 元素,此元素用于验证 Data 数据值的合法性。当通过 src 为 data 指定外部数据源时,例如 XML 数据源,则可利用此元素和 XML Schema 来验证 Data 数据值的合法性。
  • Donedata 元素,此元素为容器元素,用以装载 SCXML 状态机进入 Final 态时所返回的数据。当 SCXML 引挚进入 Final 态时,会产生一个 done 事件,引挚必须将 Donedata 元素的数据值指定为 _event.data 全局数据值。此元素的子元素可以为 Content 元素或是 Param 元素。
  • Content 元素,此元素为将内嵌数据传送至外部服务的容器元素。
  • Param 元素,此元素提供了一个一般性的用来标识 name/key 或是动态计算的值的方法,该值可传送至外部服务或是被包含在一个事件中。
  • Script 元素,该模块为有限状态机添加了内置脚本的支持。

清单 4. 数据模型实例
        
 <scxml version="1.0" datamodel="ecmascript"> 
  <datamodel> 
   <data id="employees" src="http://example.com/employees.json"/> 
   <data id="year" expr="2008"/> 
   <data id="CEO" expr="\"Mr Big\""/> 
   <data id="profitable" expr="true"/> 
  </datamodel> 
 </scxml> 

通过 Data 元素实现数据定义,可以是内置的数据值或是外部数据源。

外部交互元素

外部交互能力允许 SCXML 会话与外部节点间进行事件的发送或是接收,并且同时提供调用外部服务的能力。

Send 元素提供“触发就忘记”的数据传输能力,可以将事件传送至外部环境或是其它的 SCXML 会话。事件传输的细节以及数据格式是 SCXML 引挚所选择的 Event I/O Processor 所决定的,每个 SCXML 引挚都会实现一个或是多个 Event I/O Processor,SCXML 的使用者可以根据需要选择一个他理想的事件 IO 处理器。

Invoke 元素提供一种紧藕合的与外部系统进行交互的方式,特别的是提供触发外部平台定义的服务并传送数据的能力,该服务在完成的时候将会产生一个 done 事件。

SCXML 引擎的实现与使用案例

以上介绍了 W3C SCXML 规范的起源、发展及该规范元素组成等详细内容。本文接下来将会通过实例为读者介绍几种使用比较广泛的 SCXML 引擎,让大家进一步明确如何在实现业务中使用 SCXML 有限状态机。

SCXML 引擎的 Java 实现 --Apache Commons SCXML 实现分析与使用案例

Apache Commons SCXML是 SCXML 引擎的 Java 实现版,最新发布的版本是 0.9,但是功能基本上已经覆盖了 SCXML W3C 规范的全部内容。

Apache Commons SCXML 结构分析


图 1. Apache Commons SCXML 系统结构
图 1. Apache Commons SCXML 系统结构

如图 1 所示,各部分功能详细说明如下:

  • 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 的使用范例。

清单 5.Apache Commons SCXML 自定义事件范例
        
 <?xml version="1.0"?> 
 <scxml xmlns="http://www.w3.org/2005/07/scxml"
       xmlns:smcavs="http://smcavs.ustb.edu.cn"
       version="1.0"
       initialstate="custom"> 
    <state id="custom" final="true"> 
        <onentry> 
            <smcavs:smcadd name="dataquery" /> 
        </onentry> 
   <onexit> 
            <smcavs:smcremove name="dataquery" /> 
        </onexit> 
    </state> 
 </scxml> 

首先在 SCXML 根目录中完成 smcavs 外部 Custom Action 的定义,在状态机文件中通过

定义名 : 操作名称

的方式进行实际的 Custom Actions 调用操作,在此状态机文档中,通过 smcavs:smcadd 调用构件添加操作,通过 smcavs:smcremove 调用构件删除操作。

Custom Semantics

高级 API,SCXML 状态机的自定义语义实现,主要用于异常捕捉等高级语言的特性补充实现。

Apache Commons SCXML 使用案例

下面通过计时器的完整实例向大家介绍 Apache Commons SCXML 的使用。计时器有四种状态:ready、running、stopped 和 paused,下面是状态变迁的 UML 图示。


图 2. 计时器状态变迁图示
图 2. 计时器状态变迁图示

如图 2 所示,状态机在初始化后进入 ready 态,当 watch.start 事件发生时,触发 Transition 状态变迁事件,状态机迁移至 running 态。

当状态机进入 running 态时,watch.stop 事件触发 Transitioin 状态变迁,进入 stopped 态;watch.split 事件触发 Transition 状态变迁,进入 paused 态。

当状态机进入 paused 态时,watch.unsplit 事件触发 Transition 状态变迁,返回至 running 态;而 watch.stop 事件触发 Transition 状态变迁,进入 stopped 态。

当状态机位于 stopped 态时,watch.reset 事件将触发 Transition 状态变迁事件,状态机返回至 ready 态。

此计时器具体的 SCXML 状态机文档如清单 1 所示,下面介绍如何使用 Apache Commons SCXML 构建并运行此状态机文件。


清单 6.Apache Commons SCXML 构建状态机实例
        
 public class SCXMLEngine { 
   private static SCXMLExecutor engine = null; 

   public static void main(String[] args) { 
     engine = new SCXMLExecutor(new JexlEvaluator(), 
         new SimpleDispatcher(), new SimpleErrorReporter()); 
    
     SCXML stateMachine; 
     try { 
       stateMachine = SCXMLParser.parse(StopWatch.class.getClassLoader(). 
              getResource("org/apache/commons/scxml/env/stopwatch.xml"), 
              new SimpleErrorHandler()); 
       engine.setStateMachine(stateMachine); 
       engine.setSuperStep(true); 
       engine.setRootContext(new JexlContext()); 
       engine.addListener(stateMachine, new SCXMLEngine().new EntryListener()); 
     } catch (IOException e) { 
       e.printStackTrace(); 
     } catch (SAXException e) { 
       e.printStackTrace(); 
     } catch (ModelException e) { 
       e.printStackTrace(); 
     } 
    
     try { 
       engine.go(); 
     } catch (ModelException me) { 
       me.printStackTrace(); 
     } 
    
     String event = null; 
     Scanner input=new Scanner(System.in); 
     while(true){ 
       event=input.nextLine(); 
       if(event.trim()!=null&&!event.trim().equals("")){ 
         if(event.equals("exit")) break; 
         else{ 
           TriggerEvent[] evts = {new TriggerEvent(event, 
                      TriggerEvent.SIGNAL_EVENT, null)}; 
              try { 
                  engine.triggerEvents(evts); 
              } catch (ModelException me) { 
                 me.printStackTrace(); 
              } 
         } 
       } 
     } 
    
   } 

    /** 
     * A SCXMLListener that is only concerned about &quot;onentry&quot; 
     * 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 
        } 

    } 
 } 

此代码片段中,通过 SCXMLParser 解析 SCXML 文档生成 SCXML 对象,SCXMLExecutor 通过封装解析之后的 SCXML 对象实例化一个 SCXML 引挚,并为此引挚添加了状态监叶器,用于对状态机进入、退出某状态或是发生状态变迁事件时进行响应操作。具体运行结果如下图:


图 3. 计时器状态机运行效果图
图 3. 计时器状态机运行效果图

SCXML 引擎的 Flex ActionScript 实现 -- 开源项目 SCXML4Flex 实现分析与使用案例

SCXML4Flex 提供了 SCXML 引挚的 Flex ActionScript 实现方案,该项目为 Apache 2.0 Licence 下的开源项目,目前还处于开发阶段,但已经实现了大部分的 SCXML 规范内容。同样以计时器状态机为例,向读者介绍 SCXML4Flex 的使用。

通过 Flex 技术可以方例的构建计时器的运行界面,计时器运行界面只需两个功能按钮,启动 / 结束按钮和暂停 / 继续按钮,要注意的是,计时器处于不同状态时,这两个按钮所起的作用是不用的。实现运行效果如下图所示。


图 4.SCXML4Flex 计时器初始态图示
图 4.SCXML4Flex 计时器初始态图示

图 5.SCXML4Flex 计时器运行时图示
图 5.SCXML4Flex 计时器运行时图示

Flex 提供了简单易用的应用程序界面构建方案,本示例中界面实现部分代码片段如下所示:


清单 7. 计时器界面实现片段
        
<mx:Button x="38" y="141" label="Start" id="start" click="click(event)"/> 
<mx:Button x="185" y="141" label="Split" id="split" enabled="false" 
   click="click(event)"/> 
<mx:Text x="45" y="49" text="00" id="hour" fontSize="19"/> 
   <mx:Label x="87" y="52" text=":" width="12"/> 
   <mx:Text x="107" y="49" text="00" id="minute" fontSize="19"/> 
   <mx:Label x="142" y="52" text=":" width="8"/> 
   <mx:Text x="158" y="49" text="00" id="second" fontSize="19"/> 
   <mx:Label x="194" y="51" text=":" width="15"/> 
   <mx:Text x="216" y="49" text="0" id="fract" fontSize="19"/> 
   <mx:Text x="94" y="97" text="" id="show_status" width="120" fontSize="15"/> 

从清单 7 中可以看出,通过为功能按钮添加监听事件的方式实现对计时器的控制,用户每次点击功能按钮,会触发一个事件,该事件将导致计时器状态机进行状态迁移,改变当前所处的状态。


清单 8.SCXML4Flex 状态机构件代码
        
 var engine:SCXML= new SCXML(); 
 var xml:XML=XML(loader.data); 
 engine.source=xml; 
 engine.start(); 

使用 SCXML4Flex 构件状态机非常简单,只需先得到一个 SCXML 文档的 XML 实例,再构件一个 SCXML engine 对象,然后通过 engine.source=xml语句将 XML 实例传递至 engine,刚可完成状态机的构建,随后即可启动它。


清单 9. 功能按钮响应方法
        
 protected function click(eve:MouseEvent):void{ 
   var command:String = eve.target.label; 
   if(command=="Start"){ 
     engine.send("watch.start"); 
     start.label="Stop"; 
     split.enabled=true; 
   }else if(command=="Stop"){ 
     engine.send("watch.stop"); 
     start.label="Reset"; 
     split.enabled=false; 
   }else if(command=="Reset"){ 
     engine.send("watch.reset"); 
     start.label="Start"; 
     split.label="Split"; 
   }else if(command=="Split"){ 
     engine.send("watch.split"); 
     split.label="Unsplit"; 
   }else{ 
     engine.send("watch.unsplit"); 
     split.label="Split"; 
   } 
 } 

从清单 8 中的代码中可以看到,通过 engine.send("event name")向状态机引挚中发送事件,触发状态变迁事件。当状态机处于 running 态时,要实现计时功能,这可以通过 ActionScript 的 Timer 类来实现,具体代码在这里不再赘述,读者可以在本文最后的参考资源中找到该示例代码的下载地址。

结束语与总结

本文首先详细介绍了 W3C SCXML 规范内容,让读者对 SCXML 有限状态机有了个清楚的认识。接下来向大家介绍两种常用的 SCXML 引挚,并结合计时器的实例向读者讲述如何构建 Apache Commons SCXML 和 SCXML4Flex 有限状态机来处理实际业务。当然,限于篇幅的原因,不可能面面俱到,读者如果有兴趣对 SCXML 进入更加深入的学习研究,可以跟笔者进行进一步的交流。


参考资料

学习

举报
IBMdW
发帖于6年前 0回/530阅
顶部