Copyright © 2007 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability, trademark and document use rules apply.
This document describes SCXML, or the "State Chart extensible Markup Language". SCXML provides a generic state-machine based execution environment based on CCXML and Harel State Tables.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This document is the third Public Working Draft of SCXML for review by W3C Members and other interested parties, and has been developed by the Voice Browser Working Group (W3C Members Only) as part of the W3C Voice Browser Activity. The most significant changes since the last draft are the addition of an algorithm for interpreting SCXML and a more detailed specification of the data model.
Comments for this specification are welcomed to www-voice@w3.org (archives).
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
1 Terminology
2 Overview
3 Basic State Notation
3.1 <scxml>
3.1.1 Attribute Details
3.1.2 Children
3.2 <state>
3.2.1 Attribute Details
3.2.2 Children
3.3 <transition>
3.3.1 Attribute Details
3.3.2 Children
3.4 <parallel>
3.4.1 Attribute Details
3.4.2 Children
3.5 <final>
3.5.1 Attribute Details
3.5.2 Children
3.6 <onentry>
3.6.1 Attribute Details
3.6.2 Children
3.7 <onexit>
3.7.1 Attribute Details
3.7.2 Children
4 Pseudo-States
4.1 <initial>
4.1.1 Attribute Details
4.1.2 Children
4.2 <history>
4.2.1 Attribute Details
4.2.2 Children
5 Referencing External Files
6 Extensions to the Basic State Machine
Model
6.1 <invoke>
6.1.1 Attribute Details
6.1.2 Children
6.2 <param>
6.2.1 Attribute Details
6.2.2 Children
6.3 <finalize>
6.3.1 Attribute Details
6.3.2 Children
6.4 <content>
6.4.1 Attribute Details
6.4.2 Children
6.5 <anchor>
6.5.1 Attribute Details
6.5.2 Children
7 Data Model
7.1 Overview of
Data Model
7.2 <datamodel>
7.2.1 Attribute Details
7.2.2 Children
7.3 <data>
7.3.1 Attribute Details
7.3.2 Children
8 Executable Content
8.1 Data Model
Operations
8.1.1 <assign>
8.1.1.1
Attribute Details
8.1.1.2
Children
8.1.2 _eventdata
8.1.3 <validate>
8.1.3.1
Attribute Details
8.1.3.2
Children
8.2 Event
Generation
8.2.1 <event>
8.2.1.1
Attribute Details
8.2.1.2
Children
8.2.2 <send>
8.2.2.1
Overview
8.2.2.2
Attribute Details
8.3 Flow Control
Elements
8.3.1 <if>
8.3.1.1
Overview
8.3.1.2
Attribute Details
8.3.2 <elseif>
8.3.2.1
Overview
8.3.2.2
Attribute Details
8.3.3 <else>
8.3.3.1
Overview
8.3.3.2
Attribute Details
8.4 Debugging
Elements
8.4.1 <log>
8.4.1.1
Overview
8.4.1.2
Attribute Details
8.5 Custom Action
Elements
9 Boolean Expressions
9.1 In
9.1.1 Overview
9.1.2 Attribute Details
10 SCXML Events
11 Related Work
A Contributors
B Open Issues
B.1 undefined behavior in
algorithm
B.2 parallel transition
order
B.3 error
handling
B.4 session ID
B.5 do we need
<returndata>
B.6 Task
Logging
B.7 updates to external
data sources
C Algorithm for SCXML Interpretation
C.1 Initialization
Phase
C.2 Immediately Enabled
Transitions Phase
C.3 Invoke
Phase
C.4 Wait External Event
Phase
C.5 Execute Transitions
Phase
C.5.1 Remove Conflicting Transitions Phase
C.5.2 Exit State Phase
C.5.3 Execute Content Phase
C.5.4 Enter State Phase
C.6 Utility
Functions
C.6.1 SelectTransitions(event)
C.6.2 findStatesToExit(trans)
C.6.3 findLeastCommonAncestor(state-list)
C.6.4 addDefaultStatesToEnter(state, rootState,
[in/out]stateList, [in/out]defaultEntryList)
C.6.5 EnterState(state, rollbackArray, defaultEntryList,
currentEvent)
C.6.6 Miscellaneous Functions
C.7 Semantics of
<anchor>
D Probabilistic State Machines
E Schema
F Examples
F.1 Language
Overview
F.2 Shale
Example
F.3 Examples of Invoke
and finalize
G References
[Definition: The key words must, must not, required, shall, shall not, should, should not, recommended, may, and optional in this specification are to be interpreted as described in [IETF RFC 2119].]
The terms base URI and relative URI are used in this specification as they are defined in [IETF RFC 2396].
This document outlines State Chart XML (SCXML), which is a general-purpose event-based state machine language that can be used in many ways, including:
SCXML combines concepts from CCXML and Harel State Tables. CCXML [W3C CCXML 1.0] is an event-based state machine language designed to support call control features in Voice Applications (specifically including VoiceXML but not limited to it). The CCXML 1.0 specification defines both a state machine and event handing syntax and a standardized set of call control elements. Harel State Tables are a state machine notation that was developed by the mathematician David Harel [Harel and Politi] and is included in UML [UML 2.0]. They offer a clean and well-thought out semantics for sophisticated constructs such as a parallel states. They have been defined as a graphical specification language, however, and hence do not have an XML representation. The goal of this document is to combine Harel semantics with an XML syntax that is a logical extension of CCXML's state and event notation.
The top-level root element, which carries version information etc.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
initialstate | true | ID | none | Valid state ID | The id of the initial state for the document. The statemachine will transition to this state as the last step in document initialization. | |
xmlns | false | URI | none | |||
version | false | none |
<state> A compound or atomic state. Occurs zero or more times. See 3.2 <state> for details.
<parallel> A parallel state. Occurs zero or more times. See 3.4 <parallel> for details.
<final> A top-level final state in the state machine. Occurs one or more times. When the state machine reaches a top-level final state (i.e., one that is an immediate child of <scxml>), it has finished processing and will terminate. See 3.5 <final> for details.
<datamodel> A data model. Occurs 0 or 1 times. See 7 Data Model for details.
Holds the representation of a state.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
id | true | id | none | Valid state id | Identifier for this state. It MUST be unique within the document. | |
src | false | URI | none | Any URI | URI of a file containing material to be inserted into this state. See 5 Referencing External Files for details. | |
task | False | Boolean | False | Boolean | If true the state is considered to be a task for reporting purposes and entry and exit will be logged automatically. The logging shall include at least the state's ID and the status upon exit. The legal status values are 'success' and 'failure' where 'success' indicates that the task ran to completion, while 'failure' indicates that the task did not reach completion. By default, the task will be considered to have completed successfully if the state reaches a final substate while any other exit will be considered failure. Implementations are encouraged to include more detailed information in addition to status, such as reason for failure, etc. This attribute may occur only on complex states, i.e., those with <state> or <parallel> children. |
<onentry> Optional element holding executable content to be run upon entering this <state>. Occurs 0 or 1 times. See 8 Executable Content
<onexit> Optional element holding executable content to be run when exiting this <state>. Occurs 0 or 1 times. See 8 Executable Content
<transition> Defines an outgoing transition from this <state>. Occurs 0 or more times. See 3.3 <transition>
<initial> A child which identifies the initial state for state machines that have substates. This child is MUST occur if and only if the machine has one or more <state> or <parallel> children. See 4.1 <initial>
<state> Defines a sequential substate of the parent state. Occurs 0 or more times.
<parallel> Defines a parallel substate. Occurs 0 or more times. See 3.4 <parallel>
<final>. Defines a final substate. Occurs 0 or more times. When the state machine enters a final substate, an event "ParentID.done" is generated, where 'ParentID' is the ID of the final state's parent state. This event indicates that the parent state has reached completion. See 3.5 <final>.
<history> A child which represents the descendant state that the parent state was in the last time the system transitioned from the parent. A transition with this state as its target is in fact a transition to one of the other descendant states of the parent. May occur 0 or more times. See 4.2 <history>.
<anchor> Marks a milestone in the interaction to which the state machine may return. May appear 0 or more times. See 6.5 <anchor>
<datamodel> Optional specification of a datamodel. See 7 Data Model for details.
<invoke> Used to invoke external components. Occurs 0 or 1 times. For further details, see 6.1 <invoke>
<invoke> occurs only in atomic states, namely those that do not have <state> or <parallel> children. Thus, a state may have either <state> and <parallel> children or an <invoke> child,but not both.
Transitions between states are triggered by events and conditionalized via guard-conditions. The optional attribute "target" specifies the destination of the transition, which may be a <state> or a <parallel> region. If the "target" on a <transition> is omitted, then taking the transition has the effect of leaving the machine in the same state after invoking any executable content that is included in the transition. Such a transition is equivalent to an 'event' handler in Harel State Table notation. Note that this is different from a <transition> whose "target" is its source state. In the latter case, the state is exited and reentered, triggering execution of its <onentry> and <onexit> executable content.
Any executable content contained in a transition is executed after the <onexit> handlers of the source state and before the <onentry> handlers of the target state. Within both the 'cond' attribute of the transition and the executable content, the special variable '_eventdata' can be used to access data contained within the triggering event. See 8.1.2 _eventdata for details.
An anchor MAY be specified instead of a "target" for the transition. In this case, the system transitions to the last state visited containing an <anchor> whose "type" matches the "anchor" specified in the transition. See Section 6.5 <anchor> for details.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
event | false | String | none | Any event name. The wildcard '*' is permitted. See 10 SCXML Events for details. | The event trigger for this transition. The transition will be taken only when the event is generated. See C Algorithm for SCXML Interpretation for details on how transitions are selected. | |
cond | false | Boolean expression | none | Any boolean expression. | Guard condition for this transition. If it is present, the
transition is taken only if the guard condition evaluates to
true . See 9 Boolean
Expressions for details. |
|
target | false | state or parallel region ID | none | Whitespace separated list of state IDs in the current state machine. If multiple states are specified all must be descendants of the same <parallel> element. | The identifier(s) of the state or parallel region to transition to. | |
anchor | false | NMTOKEN | none | Any anchor type | The type of the anchor to transition to. See 6.5 <anchor> for details. |
Only one of "target" and "anchor" may be specified.
Immediate children of <transition> above are executable content that is run after the <onexit> handler for this state and before the <onentry> handler in the target state. See 8 Executable Content
If a transition has both "event" and "cond" attributes, it will be taken only if an event is raised that matches the "event" pattern and the "cond" condition evaluates to true. If the "event" clause is missing, the transition is taken whenever the "cond" evaluates to true. If the "cond" clause is also empty, the transition is taken as soon as the state is entered. Note that the data model can be changed only by the execution of <invoke> or executable content. Therefore transitions with empty "event" conditions need be checked only 1) upon arrival into a state, after the completion of all <onentry> handlers and 2) after an event has been processed (since it may have triggered executable content which could have updated the data model). See C Algorithm for SCXML Interpretation for details.
A wrapper element that encapsulates a set of parallel states. The <parallel> element has <onentry> and <onexit> elements analogous to <state>. In addition, the <parallel> element holds a set of <state> elements that execute in parallel and join at the <onexit> handler of the <parallel> element. In particular, when all of the parallel substates reach final states, a completion event "ID.done" is generated, where "ID" is the "id" of the <parallel> element. Either the <parallel> element or one of its ancestors can trigger a transition off this event, at which time the <onexit> handler of the element will be executed.
When the state machine enters the parent <parallel> element, it simultaneously enters each child state. Transitions within the individual child elements operate normally. However any of the child elements may take a transition outside the <parallel> element. When this happens, the <parallel> element and all of its child elements are exited and the corresponding <onexit> handlers are executed. The handlers for the child elements execute first, in document order, followed by those of the parent <parallel> element, followed by an action expression in the <transition> element, and then the <onentry> handlers in the "target" state. See C Algorithm for SCXML Interpretation for a detailed description of the semantics <parallel> and the rest of SCXML.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
id | true | ID | none | Valid state ID | Indicates the name of the parallel state. | |
src | false | URI | none | Any URI | URI of a file containing material to be inserted into this state. See 5 Referencing External Files for details. | |
task | False | Boolean | false | Boolean | If true the parallel is considered to be a task for reporting purposes and entry and exit will be logged automatically. The legal status values are 'success' and 'failure' where 'success' indicates that the task ran to completion, while 'failure' indicates that the task did not reach completion. By default, the task will be considered to have completed successfully if the state reaches a final substate while any other exit will be considered failure. Implementations are encouraged to include more detailed information in addition to status, such as reason for failure, etc. |
<onentry> Holds executable content to be run upon entering the <parallel> element. Occurs 0 or one times. See 8 Executable Content
<onexit> Holds executable content to be run when exiting this element. Occurs 0 or one times. See 8 Executable Content
<state> Defines a parallel substate region. Occurs 1 or more times.
<parallel> Defines a nested set of parallel regions. Occurs 1 or more times.
<history> A child which represents the state configuration that this state was in the last time the system transitioned from it. A transition with this history pseudo-state as its target is in fact a transition to the set of descendant states that were active the last time this state was exited. Occurs 0 or more times. See 4.2 <history>.
<anchor> Marks a milestone in the interaction to which the state machine may return. Occurs 0 or more times. See 6.5 <anchor>
<datamodel> Optional specification of a data model. See 7 Data Model for details.
<final> repesents a final state of a compound state. When the system reaches a final state, it means that its parent state has completed its activity. Upon entry to the final state, the event "ParentID.done" is generated, where 'ParentID' is the ID of the parent state. Note that even though the parent state has run to completion, more remote ancestors may still be active. When the state machine reaches a top-level final state (namely one that is a child of <scxml>), it has finished processing and will terminate.
The children of the <onentry> handler consist of executable content as defined in 8 Executable Content
The children of the <onexit> handler consist of executable content as defined in 8 Executable Content
The following elements are called 'pseudo-states' because they do not have the full properties of states (for example they lack <onentry> and <onexit> handlers) and do not map to states in the underlying semantics of the language. However, they can be used like states in many places. In particular, they can often be the "target" of <transition>. They are a notational shorthand allowing the simple statement of some sophisticated control constructs.
This element represents the default initial state for a complex state with sequential substates. Suppose <state> S1 has child states S11, S12, and S13. If the system is in S1, it must also be in one (and only one) of S11, S12, or S13. A <transition> in a distinct <state> S2 may take S11, S12, or S13 as its "target", but it may also simply specify the parent S1. In that case, the <initial> child of S1 specifies which of S11, S12, or S13 the system should transition to.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
id | true | id | none | Valid state id | Identifier for this pseudo-state. It MUST be unique within the document. |
<transition> A conditionless transition (i.e., one without a "cond" or "event" attribute). Such a transition is always enabled and will be taken as soon as the state is entered. The "target" of the transition must be a descendant of the <initial> element's parent <state>. If <state>S1 has an <initial>child with a <transition> with no executable content and "target" S12, there is no semantic difference between the following three transitions: a) one that takes S1 as its "target" b) one that takes S1's <initial> child as a "target" c) one that takes S12 as its "target". The result in all three cases is that the system enters both S1 and S12 as an atomic action, executing the <onentry> handlers of first S1 and then S12. Note, however, that it is possible to put executable content inside the <transition> in <initial>. If such executable content is present, then cases a) and b) are different from c) because in a) and b) the default <initial> <transition>'s executable content will be executed, while in case c) it will not.
The <history> pseudo-state allows for 'pause and resume' control flow. Whenever a complex <state> is exited, its <history> pseudo-state, if present, records the state configuration at exit. Later a <transition> taking the <history> state as its "target" allows the <state> to pick up where it left off. If the state containing the <history> state is a compound state, then the history value will be a single state. However, if the parent state is a <parallel> state, the history value will be a set of states, one for each parallel child region.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
id | true | id | none | Valid state id | Identifier for this pseudo-state. It MUST be unique within the document. | |
type | false | string | shallow | "deep" or "shallow" | If the value is 'shallow' then this state records only the immediate children of the parent <state>. In this case a <transition> with the history pseudo-state as its "target" will end up in the immediate child state that the parent was in the last time it was exited. On the other hand, if this attribute is set to 'deep', the history pseudo-state will remember nested states. In this case a <transition> with the history pseudo-state as its "target" will end up in the most deeply nested descendant <state> the parent was in the last time it was exited. |
<transition> A conditionless transition (i.e., one without a "cond" or "event"; attribute). Such a transition is always enabled and may be taken as soon as the state is entered. This <transition> represents the default history state and indicates the <state> to transition to if the parent <state> has never been entered before. if "type" is 'shallow', then the "target" of this <transition> must be an immediate child of the parent <state>. Otherwise it can be any descendant <state>. If the parent of the <history> state is a <parallel> state, then this transition may take multiple targets (each much be in a distinct parallel child state).
The "src" attribute on <state> and <parallel> contains a URL which is used to reference an external file containing all or part of the definition of the state. The file must contain a document that matches the SCXML schema but it need not match the extra-schematic constraints contained in this specification. (For example, the document may contain transitions that reference state IDs that are not defined within it.) The exact treatment of the file depends on the format of the URL. If it ends with '#' followed by a string, the string is taken to be the ID of an element in the file and the children of that element are inserted into the state's definition before any other material contained in the <state>. Thus the state is incorporating the definition of a state defined within the file. If, on the other hand, the URL does not contain '#', the contents of the <scxml> element are inserted into the definition of the state before any other material in the state, along with an <initial> element whose <transition> takes as its target the value of the <scxml> element's 'initial' attribute. Thus in this case the contents of the entire state machine are inserted into the definiiton of the state.
Suppose that the file at "someURL" looks as follows:
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="someState"> <state id="someState"> <transition event="foo" target="someOtherState"/> </state> <state id="someOtherState"> <transition event="bar" target="finalState"/> </state> </scxml>
A 'src' expression referencing 'someOtherState' will result in 'someOtherState's contents being inserted into the state's definition, as shown below. The following markup:
<state id="state1" src="someURL#someOtherState"> <transition event="state1.Done" target="yetAnotherState"/> <final id="finalState"/> </state>
will expand to:
<state id="state1"> <transition event="bar" target="finalState"/> <transition event="state1.Done" target="yetOtherState"/> <final id="finalState"/> </state>
On the other hand, a 'src' expression referencing the whole file will result in both 'someState' and 'someOther' state being inserted into the state's definition, along with the appropriate <initial> element. Thus
<state id="state1" src="someURL"> <transition event="state1.Done" target="yetOtherState"/> <final id="finalState"/> </state>
becomes
<state id="state1"> <initial> <transition target="someState"/> </initial> <state id="someState"> <transition event="foo" target="someOtherState"/> </state> <state id="someOtherState"> <transition event="bar" target="finalState"/> </state> <transition event="state1.Done" target="yetOtherState"/> <final id="finalState"/> </state>
File inclusion takes place at load time. Implementations are free to perform document validation by any method they choose as long as the result is logically equivalent to a single pass of validation occurring after the material from the external file has been incorporated. (Note that implementations are not required to validate the external file. All that is required is that the document resulting from the incorporation be valid.)
This section describes extensions that SCXML has added to the basic Harel state machine model.
<invoke> and its child <finalize> are useful in states that model the behavior of an external service. The <invoke> element is executed immediately after the state's <onentry> element and sends an event invoking the external service. The <param> element may be used to pass data to the service. Any events that are received by the state machine from the invoked component during the invocation are preprocessed by the <finalize> handler before transitions are selected. The <finalize> code is used to normalize the form of the returned data and to update the data model before the transitions' "event" and "cond" clauses are evaluated.
When the <invoke> element is executed, the platform MUST start a new logical instance of the external service specified in "targettype" and pass it the data specified by "src", "srcexpr", <content>, or <param>. The service instance MAY be local or remote. In addition to the explicit arguments, the platform MUST pass a unique id to the external service . The external service MUST include the same id in all events that it returns to the invoking machine. (Syntax TBD.)
The external service MAY generate multiple events while it is processing, but it MUST generate a special ''id.Done' event , (where the 'id' is the id for this invocation) once it has finished processing. It MUST not generate any other events afterwards. If the invoking state machine takes a transition out of the state containing the <invoke> before it receives the 'Done' event, it MUST automatically send a 'Cancel' event to the invoked component to notify it that it may stop its processing. The 'Cancel' event will be sent as if it were the final <onexit> handler in the invoking state. The invoked component MUST respond to the 'Cancel' event with a 'CancelResponse' event (syntax of both events TBD). The invoking state machine will take its transition without waiting for the CancelResponse event, which will be processed like any other external event.
Note that the <invoke> element can be used to invoke an external SCXML interpreter to execute a different state machine. In this case, the external state machine acts as a set of substates of the invoking state. The behavior is thus similar to a complex state defined with <state> child elements. The most important difference is that in the <invoke> case, the external state machine does not share context with the invoking machine. In particular, the invoked machine cannot access any variables declared in the invoking machine and data can be shared only by being explicitly passed via the <param> element. Furthermore, state IDs need not be distinct across the two machines (though they must, of course, be distinct within each machine.) The <invoke> element thus provides a means of well-encapsulated code reuse.
When parallel states invoke the same external service concurrently, separate instances of the external service will be started. They can be distinguished by the id which is passed with the invocation. Similarly, the id contained in the events returned from the external services can be used to determine which events are responses to which invocation. Each event that is returned will be processed only by the <finalize> in the state that invoked it, but that event is then processed like any other event that the state machine receives. The finalize code can thus be thought of as a preprocessing stage that applies before the event is added to the event queue. Note that the event will be passed to all parallel states simultaneously to check for transitions.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
targettype | true | string | 'scxml' | 'scxml', 'vxml', 'ccxml', plus other platform-specific values. | A string specifying the type of the external service. Default value is 'scxml'. 'vxml'indicates a VoiceXML 2.x interpreter, while 'ccxml'indicates a CCXML 1.0 interpreter. Platforms may provide additional values. However, platforms SHOULD name the values beginning with "x-" to signify that they are platform dependent. | |
src | false | URI | none | Any valid URI | A URL that will be passed to the external service when it is invoked. If "targettype" is one of the predefined types, the document at the URI will contain the corresponding markup to execute (i.e., an SCXML, VoiceXML, or CCXML document). In other cases, the content of the document will depend on the external service. | |
srcexpr | false | Expression | None | Any expression evaluating to a valid URI. | A data model expression that will be evaluated at the time that invoke is executed to produce the URI to pass to the external service. |
<param>. Optional child element containing data to be passed to the external service. Occurs 0 or more times. See 6.2 <param>.
<finalize>. Optional element containing executable content to massage the data returned from the invoked component. Occurs 0 or 1 times. See 6.3 <finalize> for details.
<content>. Optional element containing arbitrary material to be passed to the invoked component. The material may consist of non-XML markup or XML markup in any namespace. Occurs 0 or 1 times. See 6.4 <content> for details.
At most one of "src" and "srcexpr" and <content>may be specified.
<invoke> MUST not be a sibling of the <state> or <parallel> elements. It thus occurs only in atomic states, namely those which do not have substates.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
name | true | Data model expression | none | Valid data model path expression | The name of the parameter. It will be passed unmodified to the external service. It need not specify a node in the surrounding data model. | |
expr | false | Data model expression | none | Valid data model expression | An optional expression in the data model language which evaluates to an XML tree. The tree may be part of the data model of the surrounding state machine or may be specified in-line. Normal data model scoping rules apply as specified in 7 Data Model . |
If the "expr" attribute is missing, the "name" attribute will be taken as a data model expression referring to a location within the data model and the corresponding data model tree will be passed to the invoked component. If the "name" attribute does not refer to a location in the data model, an error will be generated. Normal data model access and scoping rules apply as specified in 7 Data Model .
<finalize>'s children consist of executable content. The content will be invoked on any event that the external service returns after <invoke> has been executed. In the case of parallel states, only the finalize code in the original invoking state will be executed Within the executable content, the special variable '_eventdata' may be used to refer to the data contained in the event which is being finalized.
If no executable content is specified, a default canonicalization handler will be invoked which will update the data model with any return values corresponding to <param> elements with missing "expr" attributes. Thus the effect of a <param> element with an empty "expr" attribute coupled with an empty <finalize> element is first to send all or part of the data model to the invoked component and then to update that part of the state machine's data model with the returned values. Note that the automatic update does not take place if the <finalize> element is absent as opposed to empty.
The purpose of the executable content is to normalize the format of data and update the data model before transitions are selected. <finalize> code may only massage data. It may not raise events or invoke external actions.
The <anchor> element is intended to provide 'go back' or 'redo' functionality that is useful in some applications. When a state containing an <anchor> element is entered, a snapshot of the data model is taken before the state's <onentry> handler is executed. The default is to snapshot the entire datamodel, but the application author can restrict the snapshot to part of the datamodel using the 'snapshot' attribute. When a transition is taken with an 'anchor' as its target, the state machine returns to the most recently-visited state with an <anchor> whose 'type' attribute matches the 'anchor' in the transition. In the course of the transition, before the state containing the anchor element is entered, the datamodel is restored from the snapshot. The effect is thus to jump back and restart processing at that state. As part of this restart, all intermediate anchors are erased. Intermediate anchors are those that were set between the time the anchor state was last visited and the point at which the transition with the anchor state as target is taken. Note that all these anchors must have a different type from the one that is the target of the transition. If no state with a matching <anchor> type has been visited, the current state is exited and re- entered. For a complete definition of the semantics of <anchor>, see C Algorithm for SCXML Interpretation.
The ability to reset the datamodel may lead to accidental, unterminated loops in the state machine. The interpreter context MAY detect these and exit. Support for the <anchor> element is optional.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
type | true | NMTOKEN | none | Defines the type of this anchor instance. | ||
snapshot | false | Datamodel location | none | Any valid data model path expression | A location in the datamodel to snapshot. If no snapshot value is provided, the entire data model is copied. |
N.B. This section is provisional and may end up being part of a separate specification. In either case, the definition of the data model may change significantly in the future.
To provide a uniform method of access to both local and remote data, we introduce a <datamodel> element which encapsulates any number of <data> elements. Each such element defines a single data element whose value is an XML tree. The XML tree may be fetched from an external source or specified in-line. Note that atomic variable values are simply degenerate XML trees consisting of a single leaf, so that the <data> element subsumes the <var> element of CCXML.
Data accessed via the <data> element is local to the SCXML interpreter and not shared with any external resource. However, the local data model, or parts of it, may be sent to an external resource via the <send> element. (See 8.2.2 <send>.) Existing data values may be modified with the <assign> element. (See 8.1.1 <assign>).
Logically, there is a single globally visible data model for the entire state machine. As an authoring convenience, however, we allow <datamodel> as a child of any <state>, thus allowing parts of the data model to be distributed throughout the document closer to the locations where the data will be accessed. However, all instances of the <data> element are created and initialized when the state machine is instantiated and may be accessed from any state at any time.
As a further authoring convenience, ambiguous data references will be resolved by standard lexical scoping rules. For example, suppose <state> S has a child S1, S1 has children S11 and S12, and that both S1 and S11 contain <data> elements defining a data tree called 'foo'. References to 'foo' in both S1 and S12 will refer to S1's definition, while a reference inside S11 will refer to S11's definition. A reference to 'foo' in root state S would result in an error because no instance of 'foo' is declared in any surrounding scope. To avoid such errors and override the default scoping rules, the <data> element's name may be prefixed with the ID of the state in which it is declared. Thus in any of the above states, 'S1.foo' will unambiguously refer to the instance of 'foo' defined in S1, while 'S11.foo' will refer to the instance defined in S11. <data> elements defined at the top level under the <scxml> element may be unambiguously designated by using the '_dm.' prefix.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
schema | false | URI | none | Location of the schema for this datamodel | URL of the schema for this datamodel. See 8.1.3 <validate> for its use. This attribute may occur only on the highest-level <datamodel> element, namely the one that is a child of <scxml> |
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
name | true | string | none | Any valid string | The name of the data item. It MUST be unique within the <state> but need not be unique within the document. | |
src | false | URI | none | Any URI referencing a valid XML tree | Gives the location from which the data XML tree should be downloaded. | |
expr | false | Expression | none | Any valid value expression | Evaluates to provide the value of the data item. Implementations MUST support ECMAScript as an expression language and MAY support other languages. |
The children of the <data> element consist of zero or more XML trees, which represent the value of the element.
At most one of "src" and "expr" may occur. If either is present, then the element MUST not have any children. Thus "src", "expr" and XML tree children are mutually exclusive inside the <data> element.
In addition to the "src" attribute and in-line child elements, values for the top-level <data> elements can be provided by the environment at instantiation time. The exact mechanism for this is implementation dependent, but values provided at instantiation time override those contained in the <scxml> element, whether they are specified in-line or via the "src" attribute.
Executable content consists of actions that are performed as part of taking transitions and entering and leaving states (<onentry> and <onexit>, etc.) In general, such content needs to be able to modify the data model, raise events, and invoke functionality in the underlying platform. On the other hand, executable content cannot cause transitions or any form of change of state, except indirectly, by raising events that are then caught by transitions. The content presented here represents a flexible, powerful, minimum set that all applications can rely on. Platforms are free to provide additional executable content corresponding to special features of their implementations. Such extensions SHOULD be defined in a separate namespace, not in the 'scxml' namespace. It is important to remember that SCXML treats executable content as a single blocking atomic operation and that no events are processed until all executable content has completed. For example, upon entering a state, the SCXML processor will not process any events or take any transitions until all <onentry> handlers have finished. Thus all executable content, including platform-specific extensions, SHOULD complete quickly under all circumstances. A general method for implementing extensions using the <send> element is presented in 8.5 Custom Action Elements below.
The <assign> element may be used to modify the data model.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
location | true | path expression | none | Any valid path expression denoting a location in the data model | The location in the data model into which to insert the new value. Standard lexical scoping rules apply. It is not required to assign to the root of a tree (i.e., the "name" value in a <data> tag), since the path expression can reach down into the tree to assign a new value to an internal node. Implementations MUST support XPath [XPath] as a path expression language and MAY support other languages if they choose. | |
src | false | URI | none | Any URI referencing a valid XML tree | The location from which the new value should be read. | |
expr | false | data model language expression | none | Any expression evaluating to a valid XML tree | An expression returning the value to be assigned. Only one of src and expr may be specified. Implementations MUST support ECMAScript as a data model language and MAY support other languages if they choose. |
The children of the <assign>element consist of zero or more XML trees, which represent the value of the element.
At most one of "src" and "expr" may occur. If either is present, then the element MUST not have any children. Thus "src", "expr" and XML tree children are mutually exclusive inside the <assign> element.
Assignment to a data model is done by using an XPath expression to denote the location in the data model where the change is to be made. There are three categories of assignments:
<data name="cart"> <myCart> <books> <book> <title>The Zen Mind </title> </book> <book> <title>Freakonomics </title> </book> </books> <cds> <cd name="something"/> </cds> </myCart> </data>
Here is an example of assignment of a string to an element node.
<assign location="/data[@name=\"cart\"]/myCart/books/book[0]/title" expr="'My favorite book'"/>
results in
<data name="cart"> <myCart> <books> <book> <title>My favorite book </title> </book> <book>> <title>Freakonomics </title> </book> ... </data>
Now suppose we assign an xml structure to an element node. The following assignment statement would have the effect of replacing the children of the element "/data[@name='cart']/myCart/books/book[0]" by the XML tree rooted in <bookinfo>.
<assign location="/data[@name='cart']/myCart/books/book[0]"> <bookinfo> <isdn>12334455</isdn> <author>some author</author> </bookinfo> </assign>
results in
<data name="cart"> <myCart> <books> <book> <bookinfo> <isdn>12334455</isdn> <author>some author</author> </bookinfo> </book> <book>> <title>Freakonomics </title> </book> ... </data>
Here are examples of legal and illegal assignment to an attribute:
<!-- Legal assignment: --> <assign location="/data[@name='cart']/myCart/cds/cd/@name" expr"'Something Else'"/> <!-- Illegal assignment: --> <assign location="/data[@name='cart']/myCart/cds/cd/@name" > <foo> <boo> </foo> </assign>
Now suppose we assign a string to a nodeset. The following assignment statement would have the effect of replacing the text child of element of each node in the nodeset /myCart/books/book with the string "The Zen Mind":
<assign location="/data[@name='cart']/myCart/books/book" expr="'The Zen Mind'"/>
produces
<data name="cart"> <myCart> <books> <book>The Zen Mind <title>...</title> </book> <book>The Zen Mind <title>..</title> </book> </books>> ... </data>
Finally suppose we assign a structure to a nodeset. The following statement would iterate over the elements in the <book> nodeset and replace each text child with the <price> structure:
<assign location="/data[@name='cart']/myCart/books/book"> <price>20.0</price> </assign>
yields
<data name="cart"> <myCart> <books> <book> <title>...</title> <price>20.0</price> </book> <book>> <title>..</title> <price>20.0</price> </book> </books> ... </data>
SCXML events carry data and executable content needs to be able to access that data in order to update the data model. The special variable '_eventdata' can be used within executable content to access the data in the current event. In the executable content of <onentry>, <onexit> and <transition> elements, the 'current event' is defined to be the event that triggered the transition. If there is no such event or the event contains no data, _eventdata is undefined. In the "cond" attribute of <transition>, the current event is the one that is currently being matched against the transition. In all other locations, '_eventdata' is undefined.
If '_eventdata' is defined, but the event in question contains no data, '_eventdata' will be an empty XML tree.
The <validate> element causes the datamodel to be validated. Note that validation of the datamodel occurs only when explicitly invoked by this element.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
location | false | Path expression | none | Any valid path expression. | The location of the subtree to validate. If it is not present, the entire datamodel is validated. Implementations MUST support XPath [XPath] as a path language and MAY support other languages if they choose. | |
schema | false | URI | none | Any valid URI | The location of the schema to use for validation. If this attribute is not present, the schema specified in the top-level <datamodel> is used. It is an error if no schema is specified in either element. |
The <event> element may be used to raise an event in the current SCXML session. The event will be added to the session's internal event queue, but will not be processed until later, following the algorithm specified in C Algorithm for SCXML Interpretation. Note in particular that the event cannot be processed until all current executable content has been executed. For example, if the <event> element occurs in the the <onentry> handlers of a state, at the very least the event will not be processed until all the executable content in the <onentry> handler has completed.
<send> is used to send events and data to external systems, including external SCXML Interpreters, or to raise external events in the current SCXML session.
The target of <send> is specified using the "target" and "targettype" attributes. These attributes control how the platform should dispatch the event to its final destination.
The "target" attribute specifies the unique identifier of the event target that the system should send the event to. This can be the session ID of another SCXML session. In other cases the value of this attribute will depend on the type of the target. (For example a SIP URL for SIP-INFO messages or a HTTP URL for Web Services). If no "target" attribute is specified the default target is the current SCXML session. If the value of the "target" attribute is not supported, invalid or unreachable by the platform, the system will throw a <error.send.targetunavailable> event.
The "targettype" attribute describes the type of the event target and is used in conjunction with the "target" attribute to determine how to connect to the target. The default value of this attribute is 'scxml'. If the event "targettype" specified is not supported, the platform will throw a <error.send.targettypeinvalid> event.
A platform must support the following value for the "targettype" attribute:
Value | Details |
---|---|
"scxml" | Target is an SCXML session. |
Support for HTTP POST is optional, however platforms that support it must use the following value for the "targettype" attribute:
Value | Details |
---|---|
"basichttp" | Target is a URL. Data is sent via HTTP POST |
Platforms may support other types of Event I/O Processors, for example: Web-services, SIP or basic HTTP GET. However, platforms SHOULD name the targettype beginning with "x-" to signify that they are platform dependent.
Note that <event> is used to raise internal events in the current SCXML session. Thus both <send> and <event> may be used to raise events in the current session, the only difference being whether the event is added to the session's internal event queue or external event queue. This difference may produce subtle differences in timing between the two methods since external events are processed only when the internal event queue is empty. See C Algorithm for SCXML Interpretation for details.
<send> also specifies the content of the message to be sent. <send> may specify message content in one of the two following mutually exclusive ways:
"event" attribute with an optional "namelist";
The "event" attribute specifies an expression that returns the name of the event.
The "namelist" attribute specifies a space separated list of data model locations to be included with the message.
<datamodel> <data name="target" expr="'tel:+18005551212'"/> <data name="content" expr="'http://www.example.com/mycontent.txt'"/> </datamodel> ... <send target="target" targettype="'x-messaging'" event="'fax.SEND'" namelist="content"/>
"xmlns" attribute with explicit inline XML content specifying the message to send. The xmlns:{namespace} defines a namespace for the inline content
<send target="'csta://csta-server.example.com/'" targettype="'x-csta'" xmlns:csta="http://www.ecma.ch/standards/ecma-323/csta"> <csta:MakeCall> <csta:callingDevice>22343</callingDevice> <csta:calledDirectoryNumber>18005551212</csta:calledDirectoryNumber> </csta:MakeCall> </send>
If an explicit namespace is provided as in the "xmlns" attribute of the <send>, this namespace can be used to validate the content of the <send>. A namespace specified on a <send> applies only to the attributes and content of the <send>. Multiple namespaces may be included in the <send> to associate message content with more than one namespace.
When an explicit namespace is specified for the <send>, the content of the <send> is parsed but can be ignored by the sending SCXML interpreter until the <send> is executed. XML namespace identifiers contained in the <send> must be preserved and it is the responsibility of the platform to parse the incoming message and remove the namespace prefix, if required by the <send> target.
The sending SCXML Interpreter MUST not alter the content of the <send> and send all the data contained within the message to the destination specified in the target attribute of <send>. When <send> is used with inline XML content, and the target is a SCXML session, the mapping of that XML content to event object properties is implementation-specific, and outside the scope of this specification.
When a message is successfully sent to the target, the platform will raise a <send.successful> event. Note that this does not mean that the target processed the message successfully. It is up to the target to generate events specific to the message. These events are application specific.
If the send request fails, an "error.send.failed" event will be raised.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
event | false | This attribute may not be specified in conjunction with inline content | Expression | none | An expression which returns a character string that indicates
the type of event being generated. The event type may include
alphanumeric characters and the "." (dot) character. The first
character may not be a dot or a digit. Event type names are
case-insensitive. If neither the event attribue or in-line content
is specified, an error.fetch event will be thrown. If
used in conjunction with the inline content, an
error.fetch will be thrown. |
|
target | false | Expression | none | A valid target URL | An expression returning a unique identifier of the event target that the platform should send the event to. | |
targettype | false | Expression | scxml | scxml webservice | An expression which returns a character string that specifies
the type of the event target. Values defined by the specification
are:
|
|
sendid | false | Left Hand Side Expression | none | Variable | Any data model expression evaluating to a data model location. The location's value will be set to an internally generated unique string identifier to be associated with the event being sent. If this attribute is not specified, the event identifier is dropped. | |
delay | false | Expression | '0s' | An expression which returns a character string in CSS2 [CSS2] format | The character string returned is interpreted as a time
interval. The send tag will return immediately, but
the event is not dispatched until the delay interval elapses.
Timers are useful for a wide variety of programming tasks, and can
be implemented using this attribute. Note: The queue
for sending events is maintained locally. Any events waiting to be
sent will be purged when the session that issued this request
terminates. |
|
xmlns:[YourNameSpacePrefix] | false | string | none | This returns a namespace identifying the contained message
format. More than one xmlns attribute may be
included. |
||
namelist | false | This attribute may not be specified in conjunction with inline content | Var List | none | List of data model locations | A list of zero or more data model locations to be included with
the event. If used in conjunction with the inline content, an
error.fetch will be thrown. |
hints | false | Expression | none | An expression. | The data returned contains information which may be used by the implementing platform to optimize message transmission. The meaning of these hints is platform-specific. |
<if> is a container for conditionally executed elements. <else> and <elseif> can optionally appear within an <if> as immediate children, and serve to partition the elements within an <if>. <else> and <elseif> have no content. <else/> is a synonym for <elseif cond="true"/>.
Each partition within an <if> is preceded by an element having a "cond" attribute. The initial partition is preceded by the <if> and subsequent partitions by <elseif>s (or <else>s). The first partition in document order with a "cond" that evaluates to true is selected. <else> always evaluates to true. A partition MAY be empty.
If an <if> has no immediate <elseif> or <else> children, the full contents of the <if> will be selected when the "cond" attribute evaluates to true.
<else> was chosen to match similar concepts in other languages, and supports examples such as
<if cond="..."> <!-- selected when <if cond> is true --> <else/> <!-- selected when <if cond> is false --> </if>
However, <else> is a synonym for <elseif cond="true"/>, so an example such as:
<if cond="..."> <!-- selected when <if cond> is true --> <else/> <!-- selected when <if cond> is false --> <else/> <!-- never selected --> </if>
is also possible and MUST be interpreted as:
<if cond="..."> <!-- selected when <if cond> is true --> <elseif cond="true"/> <!-- selected when <if cond> is false --> <elseif cond="true"/> <!-- never selected --> </if>
With this definition for <else>, a valid XML [XML] document is also a valid SCXML document.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
cond | true | Expression | none | A valid expression | A boolean expression. See 9 Boolean Expressions for details. |
An <elseif> partitions the content of an <if>, and provides a condition that determines the selection of the partition it begins. <elseif> can appear optionally as an immediate child of an <if>.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
cond | true | Expression | none | A valid expression | An boolean expression. See 9 Boolean Expressions for details. |
<log> allows an application to generate a logging or debug message which a developer can use to help in application development or post-execution analysis of application performance. The manner in which the message is displayed or logged is platform-dependent. The usage of label is platform-dependent. The use of <log> MUST have no other side-effects on interpretation. <log>is an empty element.
Name | Required | Attribute Constraints | Type | Default Value | Valid Values | Description |
---|---|---|---|---|---|---|
label | false | Expression | none | An expression which returns a character string which may be used, for example, to indicate the purpose of the log. | ||
expr | true | Expression | none | An expression evaluating to a string to be logged. | ||
level | false | TBD | TBD | TBD | This information will be logged only if the system's log level is at this level or higher. The mechanism for specifying the log level and its legal values will be specified in a future version of this document. |
Custom Action Elements can be defined in other specifications/namespaces and are responsible for performing actions on behalf of custom components. Logically Custom Action Elements can be thought of as a collection of actions and handlers to perform specific tasks. An example of this is a CCXML <accept> element that is a Custom Action Element:
<transition event="ccxml:connection.alerting"> <ccxml:accept connectionid="_eventdata.connectionid"/> </transition>
This could be written using a <send> element using the following syntax:
<datamodel> <data name="connectionid"/> </datamodel> <transition event="ccxml:connection.alerting"> <assign name="connectionid" expr="_eventdata.connectionid"/> <send targettype="ccxml" event="ccxml:accept" namelist="connectionid"/> </transition>
A more complicated example might be a CCXML <createcall> where you are both providing variables and getting values back that using only the <send> syntax would be more complex as it would need to be broken over several steps. For example:
<onentry> <ccxml:createcall dest="'tel:+18315552020'" connectionid="myConnectionID"/> </onentry>
Would need to be modeled in two steps using <send> as you would need to do something like the following:
<datamodel> <data name="dest" expr="'tel:+18315552020'"/> <data name="connectionid"/> </datamodel> <onentry> <send targettype="ccxml" event="ccxml:createcall" namelist="dest"/> </onentry> <transition event="ccxml:createcall.success"> <assign name="connectionid" expr="_eventdata.connectionid"/> </transition>
The exact mappings between Custom Action Elements and <send> actions are to be defined in the individual Custom Action Element specifications.
This section describes the content that can be placed in "cond" attributes. In general, a restricted subset of data model expressions is permitted, namely those which evaluate to a boolean and do not have side effects. Data model expressions may refer to the special variable '_eventdata', as described in 8.1.2 _eventdata and to the special predicate In as described in 9.1 In .
Implementations must support ECMAScript [ECMAScript] as a boolean expression language, and may support additional languages if they choose.
Events are one of the basic concepts in SCXML since they drive most transitions. The internal structure of events is platform-specific as long as the following external interface is observed:
Events have names which are matched against the "event" attribute of transitions. These names are strings consisting of alphanumeric characters plus the character '.' which is used to segment names into tokens. The "event" attribute of a transition may end with the wildcard '.*', which will match zero or more tokens at the end of the processed event's name. Thus a transition with an "event" attribute of 'error.*' will match 'error', 'error.send', 'error.send.failed', etc. Furthermore, a transition with an "event" attribute consisting solely of '*' will match any event.
Events optionally contain data organized into XML trees. If data is present, it must be able to be accessed via the '_eventdata' variable, as specified in 8.1.2 _eventdata. If the event does not contain data, '_eventdata' is an empty XML tree.
Events are immutable read-only data structures. Neither the browser's processing nor the user's markup may modify an event once it is placed in queue. Note that this implies that modifications to the _eventdata structure are not permitted.
There is a class of error events whose names begin with 'error.'. They may be raised by the platform, as specified in this document, or under application control. They are placed in the event queue and processed like any other event. In particular, they are ignored if no transition is found that matches them. Note however that authors can arrange for otherwise unhandled errors to cause the interpreter to exit by creating a transition with "event" attribute of 'error.*' and a target of any top-level final state (i.e. one that is a child of <scxml>). If such a transition T is placed in a state S, it will cause the state machine to terminate on any error that is raised in S or one of its substates and is not handled by another transition that is placed in a substate of S or in S and preceding T in document order.
In many applications, it is important for SCXML to receive events from external entities (for example those which it contacted with <invoke> and <send>.) For the moment, the delivery of external events to SCXML is left as a platform-specific detail, but it may be specified in a future version of this document. In any case, external events must be placed in the event queue for processing and must obey the external interface specified above.
For the most part, the set of events raised during the execution of an SCXML document is application-specific and generated under author control by use of the <event> and <send> elements. However, certain events are mandatory and generated automatically by the interpeter. In this version of the specification, in addition to error events, there is only the 'Cancel' event which is sent if the state machine leaves the containing state after executing the <invoke> element but before receiving the 'Done' event. Note that there are also mandatory events that invoked components must return to the invoking SCXML state machine. In this version of the specification, there are two such events, namely 'Done' and 'CancelResponse'. We may well change or expand this set of mandatory events in future drafts of this specification. Furthermore, it is likely that there will be distinct profiles of SCXML for specific applications, such as the Multi-modal Interaction Framework [W3C MMI] and VoiceXML 3.0. These profiles will define extensions to the core language and will likely extend the set of mandatory events with additional events appropriate to the specific application.
A number of other XML-based state machine notations have been developed, but none serves the same purpose as SCXML. XMI [UML XMI] is a notation developed for representing UML diagrams, including Harel State charts. However it is intended as a machine interchange format and is not readily authorable by humans. ebXML [OASIS ebXML] is a language for business process specification intended to support B2B e-commerce applications. It contains a state machine language that is in some ways similar to the one presented here, but its syntax and semantics are closely tied to its intended use in e-commerce. It is therefore not suitable as a general-purpose state machine language. XTND [XTND], also called XML Transition Network Definition, is a notation for simple finite state machines but lacks Harel's notions of hierarchical and parallel states and are thus not suitable for a general-purpose state machine that is semantically equivalent to Harel statecharts.
The following people contributed to the development of this specification.
R.J. Auburn
Marc Helbing
T.V. Raman
Klaus Reifenrath
The current algorithm leaves the system's behavior undefined in the case of 'sideways' transitions between parallel siblings. Suppose <parallel> element P has children S1 and S2. The undefined behavior arises if S1 and S2 are both active and S1 takes a transition T with a target in S2. The current algorithm allows this transition to be taken, but that can result in two states being active inside S2 (namely T's target state and whatever state was already active in S2). This is an undefined situation. There are two possible ways of resolving the issue. The first would be to follow the UML specification and forbid such transitions. The second solution would be to permit such transitions, but define them as exiting and then re-entering P. By exiting P, we would intially exit both S1 and S2, and upon reentry only T's target state would be active in S2. We solicit feedback on these possible solutions.
Resolution:
None recorded.
In the current algorithm, when transitions are taken in parallel states, they are taken 'in lockstep', meaning that first all source states and their children are exited for all the transitions, then all executable content from all the transitions is executed, then all target states are entered. It might be simpler to execute the transitions one at a time (i.e., exit source state, perform executable content, and enter target states for first transition, then exit source state, perform executable content and enter target states for second transition, etc.). There would be subtle differences in the value of the In() predicate and in the values of the datamodel, but the resulting state configuration would be the same. We solicit feedback on these alternatives.
Resolution:
None recorded.
Issue ():
We need to specify the full set of errors that the platform may raise.
Resolution:
None recorded.
Issue ():
We need to specify how SCXML session IDs are created an accessed.
Resolution:
None recorded.
This section presents a normative algorithm for the interpretation of an SCXML docment. Implementations are free to implement SCXML intepreters in any way they choose, but they must behave as if they were using the algorithm defined here.
N.B.: in the following algorithm, we define the source state of a transition to be the state the transition is leaving and the target state to be the one it is entering. Furthermore, <state>, <parallel>, <history> <final> and <scxml> are all considered to be states. (Note that they all can be targets of transitions.)
Load and parse the SCXML state machine and any included state machine documents. Instantiate the data model, loading any external data variables and then updating the model with any data passed in by the invoking context.
Initiate 2 different currently empty FIFO event queues: one for internal events and one for external events. Throughout processing, any events received from external sources are added to the external event queue. Create an (initally empty) list of ActiveStates. Create an array associating invocations with the states that invoked them. Create a two dimensional array to hold DataModel snapshots for the <anchor> element. Create an activeTransitions list containing a single transition whose source is the <scxml> element and whose target is the machines 'initialState'. Goto the Enter State Phase.
// Global Data Structures: environment env; // represents the data passed in from the environment, the URL of the script, etc. state SCXML; // structure representing the parsed state machine list<state> ActiveStates; //list of currently active states queue<event> InternalEvents; //FIFO event queue queue<event> ExternalEvents; //FIFO event queue datamodel DataModel; // the data model. Details will depend on the model chosen stateID InvokeIDArray[]; //array associating ids of invocations with the ids of states that executed the invocations. invocationID InvokeStateArray[];//array associating state ids with the ids of currently active invocations that // they have executed. dataValues DataSnapshots[][];//array associating tuple of state ID and anchor type with a subtree of the DataModel. //Data Passing Structures - the following variables are used to pass //data between phases and could be replaced by parameters list<transition> activeTransitions;//list of transitions to take list <state> newlyEnteredAtomicStates;// list of states entered as a result of the most recent set of transitions event currentEvent;// the event that triggered activeTransitions. SCXML = loadAndParseStateMachine(env); // load the state machine DataModel = buildDataModel(env, SCXML);//initialize data model activeTransitions = new List(new Transition(source = getAttribute(SCXML, 'id')), target = getAttribute(SCXML, 'initialState')); goto Enter_State_Phase;
Call SelectTransitions with Null event. If any transitions are found, set ActiveTransitions and goto the Execute Transitions Phase. Otherwise, see if there is an event on the internal or external event queues that triggers a transition. If there is, set activeTransitions and goto the Execute Transitions Phase. Otherwise there are no immediately enabled transitions, so goto the Invoke Phase.
label Immediately_Enabled_Transitions_Phase; currentEvent = NULL; selectedTransitions = SelectTransitions(NULL); if (!empty(selectedTransitions)){ goto Execute_Transitions_Phase;} while (!empty(InternalEvents) { currentEvent = pop(InternalEvents); activeTransitions = SelectTransitions(currentEvent); if (!empty(activeTransitions)) { goto Execute_Transitions_Phase;} } //end while while (!empty(ExternalEvents)) { currentEvent = pop(ExternalEvents); activeTransitions = SelectTransitions(currentEvent); if (!empty(activeTransitions)) { goto Execute_Transitions_Phase; } } //end while //if we get here, there are no immediately enabled transitions goto Invoke_Phase;
For state in newlyEnteredAtomicStates, execute the invoke element if any. When all newlyEnteredAtomicStates have been visited, goto the Wait External Event Phase.
label Invoke_Phase for(state in newlyEnteredAtomicStates){ if (getChild(state, 'invoke') != NULL) { invokeID = executeInvoke(getChild(state, 'invoke')); // record that state triggered this invocation. This will be used later for <finalize> processing. InvokeIDArray[invokeId] = getID(state); // record that this invocation is active in this state; InvokeStateArray[getID(state)] = invokeID; } //end if } //end for goto Wait_External_Event_Phase;
. Clear the newlyActiveAtomicStates list. Wait until an event appears on the external event queue. When one appears, remove it from the queue. If it is a return event from an <invoke>, pass it to the <finalize> handler (if any) of the state that invoked it. Then call SelectTransitions on it. If any transitions are found, goto the Execute Transitions Phase. Otherwise return to the Wait External Event Phase.
label Wait_External_Event_Phase; local id invokeID; newlyActiveAtomicStates = NULL; wait (!empty(ExternalEvents)) { // following code executes once ExternalEvents is not empty currentEvent = pop(ExternalEvents); // if this is an event generated by an <invoke>, execute the <finalize> code invokeID = getAttribute(currentEvent, 'invokeID'); if (invokeID != NULL) { processFinalize(currentEvent, InvokeArray[invokeID], DataModel); // if this is a Done event, clean up the InvokeArrays; if(getAttribute(currentEvent, 'name')) == "invokeID.done") { deleteArrayElement(InvokeStateArray, InvokeIDArray[invokeID]); deleteArrayElement(InvokeIDArray, invokeID); }//end if(getAttribute }//end if(invokeID activeTransitions = SelectTransitions(currentEvent); if (!empty(activeTransitions)) { goto Execute_Transitions_Phase; } else { goto Wait_External_Event_Phase; } } //end wait
Note: in this phase we treat all transitions as if any 'anchor' attributes have been used to set the corresponding 'target' in the transition. See Section C.7 Semantics of <anchor> for the explanation of this interpretation.
The Execute Transitions Phase consists of four sub-phases. First we resolve conflicts among transitions. Then we exit all states that the transitions leave, executing their <onexit> handlers. Then we execute the executable content included in the transitions. Then we enter all states that the transitions enter, executing their <onentry> handlers. Note that there will be multiple transitions on activeTransitions only if the state machine is in a parallel region. In all other cases, activeTransitions will contain only a single item and the Remove Conflicting Transitions Phase may be skipped.
Two transitions conflict if the sets of states that they exit have a non-null intersection. To resolve conflicts, we first sort the transition list in document order. Then we pick the first transition from the ActiveTransitions list and form its conflict set by finding all transitions that conflict with it (there may not be any). We then resolve the (possible) conflict by selecting one member of the conflict set. We then remove all members of the conflict from consideration and iterate on the rest of the ActiveTransitions list until all members have been tested for conflicts. The criterion for selection within a conflict set is first scope and then document order. That is, we chose transition T1 over T2 if a) T1's source state is a descendant of T2's or b) if neither transition's source state is a descendant of the other's and T1 precedes T2 in document order.
label Execute_Transitions_Phase; label Remove_Conflicting_Transitions_Phase; list<transition> transitionsToTest = sort(activeTransitions, 'documentOrder'); //we will rebuild the activeTransitions list as we examine transitionsToTest activeTransitions = NULL; while(transitionsToTest){ transition currentTr = first(transitionsToTest); list<transition> conflictSet = new list(currentTr); for restTrans in rest(transitionsToTest) { if (intersection(getStatesToExit(restTrans), getStatesToExit(currentTr) != NULL) { push(restTrans, conflictSet);} }//end for restTrans //now choose one member from the conflict set. Note that this is trivial for sets of size one. for trans in sort(conflictSet, 'documentOrder') { boolean narrowScope = true; for otherTrans in remove(trans, conflictSet) { if (isDescendant(getSourceState(trans), getSourceState(otherTrans))) { narrowScope = false; break; }//end if }//end for otherTrans //this is the first transition in document order such that there is no more narrowly scoped transition // in this conflict set. if (narrowScope) { push(trans, activeTransitions); break; //end if }//end for trans //now remove all transitions in conflictSet from transitionsToTest. transitionsToTest gets smaller // on each iteration so the while loop is guaranteed to terminate. for trans in conflictSet { delete(trans, transitionsToTest); } }//end while activeTransitions = sort(activeTransitions, 'documentOrder'); goto Exit_State_Phase;
Create a statesToExit List. For each transition T in activeTransitions, if T has no target state, go on to the next transition. Otherwise, let LCA be the least common ancestor state of the target states of T and the active descendents of the source state of T. Add to the statesToExit list of all active states that are descendents of LCA.
Now sort the list so that state S1 proceeds S2 if 1) S1 is a descendant of S2 or 2) S1 preceeds S2 in document order. Finally, for each state S on the list, 1) if S has a shallow history state, sets its value to be the list of all immediate children of S that are active 2) if S has a deep history state, set its value to be the list of all atomic descendants of S that are active 3)if S has an ongoing invocation,cancel it, 4) perform S's <onexit> actions 4) remove S from the ActiveStates list.
Goto Execute Content Phase.
label Exit_State_Phase; list<state> statesToExit; for trans in activeTransitions { statesToExit = append(statesToExit, getStatesToExit(trans)); } statesToExit = sort(statesToExit, 'exitOrder'); for state in statesToExit { list<state> activeChildren; list<state> activeAtomicDescendents; // record currently active descendents for use as possible history values for activeState in ActiveStates { if(getParent(activeState) == state) { push(activeState activeChildren);} if(isAtomic(activeState) && isDescendent(activeState, state)) { push(activeState activeAtomicDescendents); } //end for activeState // update history states for histState in getHistoryStates(state) { if(getAttribute(histState, 'type') == 'deep') { setHistoryValue(histState, activeChildren); } else (setHistoryValue(histState, activeAtomicDescendents); } }//end for histState // now the executable content if(getElement(state, 'onexit')) { executeContent(getChildren(getElement(state, 'onexit')) currentEvent);} // cancel any ongoing invocation if(InvokeStateArray[getID(state)] != NULL; { //cancel ongoing invocation and clean up Invoke Arrays; cancelInvoke(InvokeStateArray[getID(state)]; deleteArrayElement(InvokeIDArray, InvokeStateArray[getID(state)]); deleteArrayElement(InvokeStateArray, getID(state); }//end if(InvokeStateArray // make state inactive delete(state, ActiveStates); }//end for state goto Execute_Content_Phase;
For each transition in the activeTransitions list, execute its executable content. Then goto Enter State Phase.
label Execute_Content_Phase; for transition in activeTransitions { executeContent(getChildren(transition), currentEvent);}
Create a Target State list, a States to Enter List, and a Default Entry List. Also create a Rollback array associating stateIDs with anchors to be rolled back. For each transition T in ActiveTransitions, if T has no target state, go on to the next transition. If T was originally specified by an anchor element, add T's target state and the anchor name to the Rollback array. Set Target State list to be the list of T's target states. If any member of Target States is a history state, replace it on Target States with its history value. For each state in Target States, call addDefaultStatesToEnter on state, the least common ancestor of T's source and target states, the States to Enter List and the Default Entry list. (Note that this modifies the states to Enter and the Default Entry List.)
Sort the States to Enter list so that S1 preceeds S2 if 1) S1 is an ancestor of S2 or 2) S1 preceeds S2 in document order. Call EnterState on each state S on the States to Enter List along with the RollBack List and the Default Entry List. Finally if we have reached a top-level final state, exit the interpreter. Otherwise goto the Immediately Enabled Transitions phase.
list <state> targetStates; list <state> statesToEnter; <state> statesToRollback[]; list <state> statesForDefaultEntry; for trans in activeTransitions { if (getTargetStates(trans) != NULL) { if (getAttribute(trans, 'anchor')){ // transitions with anchors always have a single target statesToRollback[first(getTargetStates(trans)] = getAttribute(trans, 'anchor');} targetStates = getTargetStates(trans); for state in targetStates { if(isHistoryState(state)) { delete(state, targetStates); if(getHistoryValue(state) != NULL) { targetStates = append(targetStates, getHistoryValue(state)); // if state has no history value, use default history value } else { targetStates = append(targetStates, getHistoryDefault(state)); }//end if(getHistoryValue }//end if(isHistoryState }//end for state state LCA = findLeastCommonAncestor(add(getSourceState(trans), getTargetStates(trans)); for state in targetStates { addDefaultStatesToEnter(state, LCA, statesToEnterList, statesForDefaultEntry);} }//end for trans statesToEnter = sort(statesToEnter, 'entryOrder'); for state in statesToEnter { EnterState(state, statesToRollback, statesForDefaultEntry, currentEvent);} for state in ActiveStates { if (isFinal(state) && getParent(state>) == SCXML){ exitInterpreter();} }//end for goto Immediately_Enabled_Transistions_Phase;
For each atomic state in ActiveStates in document order, find a transition whose "event" attribute is either empty or matches event and whose condition evaluates to true. (N.B. If event is null only transitions with empty event attributes match it.) If multiple matching transitions are present, take the first in document order. If none are present, search in the state's ancestors in ancestory oder until one is found. As soon as such a transition is found, add it to the back of the ActiveTransitions list, and proceed to the next state in ActiveStates. If no such transition is found in the state or its ancestors, proceed to the next state in ActiveStates. When all atomic states have been visited and transitions selected, return the list of selected transitions.
transition_list SelectTransitions(event) { list <state> atomicStates = (); list <transition> transitions = (); for state in ActiveStates { if (isAtomic(state)) { push(state, atomicStates); } }//end for atomicStates = sort(atomicStates, 'documentOrder'); for state in atomicStates { transition tr = findTransitionMatchingEvent(event, state); if (tr != NULL && !member(tr, transitions)) { push(tr, transitions); }//end if }//end for return transitions; } transition findTransitionMatchingEvent(event, state) { transition matchingTransition = NULL; for trans in getTransitions(state) { if ((event == NULL || nameMatch(event, transition)) && conditionMatch(event, transition)) { return trans; // return as soon as we find a transition that matches } }//end for // if no transition found in this state, check parent recursively if (getParent(state) !== NULL) { return findTransitionMatchingEvent(event, getParent(state); } else { return NULL; } }
Return a list of all states that will be existed when trans is taken. These are all active states that are descendents of the least common ancestor of the source and target states of trans.
list<state> getStatesToExit(transition trans) { list<state> transitionStates = NULL; list<state> statesToExit = NULL; //if trans has no target state, no states will be exited if (getTargetStates(trans) != NULL) { push (getSourceState(trans), getTargetStates(trans)); state LCA = getLeastCommonAncestor(transitionStates); for state in ActiveStates { if(isDescendant(state, LCA)) { push(state statesToExit); } }//end for }//end if(getTargetStates return statesToExit; }
Return the state S such that 1) S is an ancestor all states on state-list and 2) no descendent of S has this property. Note that there is guaranteed to be such a state since the <scxml> element (which we treat as a state here) is a common ancestor of all states.
state findLeastCommonAncestor(state-list) { state LCA; if (isNull(state-list)) { LCA = SCXML; } else { LCA = first(state-list); for state in rest(state-list) { LCA = findLeastCommonAncestor(LCA, state);} }//end if return LCA; } state findLeastCommonAncestor(state1, state2) { if (isDescendent(state2, state1)) { return state1; } elsif(isDescendent(state1, state2)) { return state2; } elsif (state1 == state2) { return state1; } else for anc in listAncestors(state1, SCXML) { if (isDescendant(state2, anc)){ return anc;} }//end for //end else }
The purpose of this routine is to add to stateList all states that must be entered as a result of entering state. These include ancestors of state, parallel siblings of state or its ancestors, and state's default children, if it is a parallel or compound state. Note that this procedure permanently modifies both stateList and defaultEntryList.
For all parallel states S between state and rootState, for each parallel child that does not have a descendant on stateList, call addDefaultStatesToEnter on the child with S as the rootState. Now, add state ito StateList if it is not already there. If state is a parallel state, call addDefaultStatesToEnter on each of its children with state as the rootState. Otherwise if state is a compond state, add it to defaultEntryList. Call addDefaultStatesToEnter on its default initial state with state as the rootState. Finally add all of state's ancestors up to but not including rootState to stateList if they are not already there.
void addDefaultStatesToEnter(state, rootState, [in/out]stateList, [in/out]defaultEntryList) { for ancestor in getAncestors(state, rootState) { if(isParallel(ancestor)) { for pChild in getChildStates(ancestor) { boolean descendentPresent = false; for enterState in stateList { if (isDescendent(enterState, ancestor)) { descendentPresent = true; } }//end for enterState if(!descendentPresent) { addDefaultStatesToEnter(pChild, ancestor, stateList, defaultEntryList); } //end for pChild //end if(isParallel //end for ancestor if (!member(state, stateList)) { push(state, stateList); } if (isParallel(state)) { for child in getChildStates(state) { addDefaultStatesToEnter(child, state, stateList, defaultEntryList); } } elsif (isCompound(state)) { push(state, defaultEntryList); addDefaultStatesToEnter(getDefaultInitialState(state), state, stateList, defaultEntryList); }//end elsif for ancestor in getAncestors(state, rootState) { if(!member(ancestor, stateList)) { push(ancestor, stateList); } }//end for ancestor }
1) if state contains one or more anchors, take the corresponding snapshots. 2) If state has an entry on the rollbackArray, rollback the datamodel using the anchor recorded in the rollbackArray. 3) Now store the new snapshots in state. 4) Add state to the ActiveStates list. 5) Execute state's <onentry> handlers 6) If state is on defaultEntryList, execute the executable content in the transition's state's <initial> element. 7) If state is a final state, then generate a Done event for its parent <state>. 8) Then if the parent's parent is a <parallel> state and all its children are in final states, generate the Done event for the parent's parent. Exit.
void EnterState(state, rollbackArray, defaultEntryList) { dataValue tempArray[]; for ancElement in getElements(state, 'anchor') { tempArray[getAttribute(ancElement), 'anchortype'] = getDMValue(DataModel, getAttribute(ancElement, 'snapshot'); } if(rollbackArray[getID(state)] != NULL) { anchor rollback; // find the corresponding anchor in state for ancElement in getElements(state, 'anchor')) { if (getAttriubte(ancElement, 'anchortype') == rollbackArray[getID(state)]) { rollback = ancElement; } }//end for // roll back the data model setDMValue(DataModel, getAttribute(ancElement, 'snapshot'), DataSnapshots[getID(state)][getAttribute(ancElement, 'anchortype')]; }//end if (rollbackArray // now update snapshot array with values from before the rollback for key and value in tempArray { DataSnapshots[getID(state)][key] = value; } // now make state active push(state, ActiveStates); if(getElement(state, 'onentry')) { executeContent(getChildren(getElement(state, 'onentry')), currentEvent); if(member(state, defaultEntryList)) { transition trans = getElement(getElement(state, 'initial'), 'transition')); executeContent(getChildren(transition), currentEvent); }//end if if(isFinal(state)) { state parent = getParent(state); state grandparent = getParent(parent); event parentDoneEvent = createEvent(concat(getID(parent), ".Done")); push(parentDoneEvent, InternalEvents); if(isParallel(grandparent)) { boolean allFinal = true; for state in getChildren(grandparent) { if (!isInFinalState(state)) { allFinal = false; break; }//end if }//end for if (allFinal) { event grandparentDoneEvent = createEvent(concat(getID(grandparent), ".Done")); push grandparentDoneEvent, InternalEvents); {//end if(allFinal }//end if(isFinal }
boolean isInFinalState(state) { if(isCompound(state)) { boolean finalChildActive = false; for child in getChildren(state) { if (isFinal(child) && member(child, ActiveStates)) { finalChildActive = true; break;} }//end for if (finalChildActive) return true; else return false; }//end if(isCompound elsif(isParallel(state)) { boolean allFinal = true; for child in getChildren(state) { if(! isInFinalState(child)) { allFinal = false; break; }//end if }//end for if (allFinal) return true; else return false; }//end elsif else return false; }
We assume that the sorting order functions take two arguments and return the one that comes first in the resulting order. We take the 'documentOrder' sorting function as given (it returns whichever of the two states or transitions comes first in the defining document.)
state exitOrder(state1, state2) { if(isDescendent(state1, state2)) { return state1; } elsif(isDescendent(state2, state1) { return state2; else return first(sort(list(state1, state2), 'documentOrder')); } state enterOrder(state1, state2) { if(isDescendent(state1, state2)) { return state2; } elsif(isDescendent(state2, state1) { return state1; else return first(sort(list(state1, state2), 'documentOrder')); }
id executeInvoke(tag; //tag is the parsed representation of the <invoke> element. // this function invokes the service specified in tag passing any parameters specified by <param> elements //plus the value of 'src' or 'srcexpr'. This function must generate and return a unique id that will be used to track //this invocation. void cancelInvoke(invokeID);// cancel the invocation with id invokeID. This id was returned by an earlier //call to executeInvoke; void processFinalize(event, stateID, DataModel) { //event is the return event from an <invoke> clause, stateID is the ID of the state that executed the <invoke>. // This function should use stateID to find the executable content in <finalize> element (if it is present) and update // the data model accordingly. It is an error if the executable content tries to raise events or takes an action //other than updating the datamodel. //void executeContent(content-list, event); //Execute the items in content-list in document order. Each item must complete before //the execution of the next one. The special variable '_eventData' should be bound to the data contained in event //before execution takes place. If event is NULL or contains no data, '_eventData' should be set to the empty string. //Afterwards, '_eventData' should be set back to 'undefined'. Exit when all items have completed. //NOTE: executable content items must execute in their own 'sandbox' where they cannot modify any of the internal data //structures defined in this algorithm, except for the data model. boolean nameMatch(event, transition); // return true if event's name matches the "event" attribute of transition>. The wildcard '*' //in transition's "event" attribute matches any string of 0 or more characters. boolean conditionMatch(event, transition); //return true if transition has an empty 'cond' attribute or if its 'cond' attribute is non-empty and evaluates to //'true' in DataModel. The special variable '_eventData' should be bound to the data contained in event before //the evaluation takes place. If event is NULL or contains no data, '_eventData' should be set to the empty string. //Afterwards, it should be set back to 'undefined'. dataValue getDMValue(datamodel, path); //return the value of datamodel located at end of path. If path is NULL, //return whole datamodel. void setDMValue(datamodel, location, value);// set location in datamodel to //have value value. If location is NULL, //replace the whole datamodel with value. state list getAncestors(childState, ancestorState); // return all ancestors of childState, up to but not including ancestorState. If ancestorState is NULL // or not an ancestor of childState, return all ancestors of childState up to but not includig the root <scxml> element. // States on the returned list are sorted so that children preceed ancestors. Note that if childState equals //ancestorState, the empty list is returned. state loadAndParseStateMachine(env); // load and parse the state machine, including any datamodel = buildDataModel(env, state);//initialize data model void addElement(state, element);// add element as a child of state. Called only during the intitialization phase // to add default behaviors. Once the state machine starts running, it may not be modified. ID getID(state);// returns state's Id attribute. transition list getTransitions(state);//return list of transitions in state in document order. boolean isAtomic(state); // return true if state is atomic (i.e. has no child <state> or <parallel> elements.) boolean isCompound(state);// return true if state has <state> children boolean isParallel(state);//return true if state is defined by a <parallel> element. boolean isFinal(state);// return true if state is a <final> state. boolean isHistoryState(state);// return true if state is a history pseudo-state. state getParent(state);// returns parent state of state if any, otherwise NULL; boolean isDescendant(state1, state2);//return true if state1 is a descendant (child or child of a child, etc.) of state2. attribute getAttribute(state, string);//return the attribute of state named string. element getElement(state, string);//return the element named string that is an immediate child of state. list<element> getElements(state, string); // return list of all elements named string that are an immediate children //of state. list<element> getChildren(state);//return a list of all the immediate child elements of state. list <state> getChildStates(state);// returns a list, in document order, of all states that are children of state. list <state> getHistoryStates(state, type);// return a list of the history states in state. type should be //'deep' or 'shallow'. list<state> getHistoryValue(pseudoState);// returns the value of pseudoState, if any, otherwise NULL; void setHistoryValue(pseudoState, >state_list);// sets pseudoState's value to be state_list. list<state> getHistoryDefault(pseudoState);//returns the target state(s) of pseudoState's <transition> child. list<anchor> getAnchors(state);//returns a list of all <anchor> elements in state. state getDefaultInitialState(state);;//return the default initial state of compound state state. state getSourceState(transition);// returns ID of source state of transition. list<state> getTargetStates(transition);// returns the target states of transition. void exitInterpreter();// causes the interpreter to terminate. event createEvent(name);//returns an instance of event with name attribute of name. boolean empty(list); // returns true if list is empty item pop([in/out]list); //removes and returns first member of list. void push(item, [in/out]list);//adds item as the first member of list list delete(item, [in/out]list);;/modifies list so that it no longer contains item. //the following list functions are all non-destructive and do not modify the list that is passed to them list add(item, list); //return new list with item added in front of the members of list. sitem first(list); //returns first member of list list rest(list);// returns a list of all members of list except the first. list tail(item, list); // return list of all members of list that occur after item or empty list if item is not //a member of list. list append(list1, list2);// returns a list containing all members of list1 and list2. list remove(item, list);;/returns a containing all the members of list except item. boolean member(item, list);; return true if item is in list. void deleteArrayElement([in/out]array, key);// remove key and its corresponding value from array. string concat(string1, string2);// returns new string representing concatenation of string1 with string2
There are a number of ways of defining the semantics of <anchor>. We choose the following because it shows that, except for the rollback of the data model, <anchor> can be viewed as a syntactic shortcut that does not extend the basic Harel semantics. We emphasize that implementations are required only to behave as if they assigned the following intepretation to <anchor> and that simpler and more efficient implementations are possible.
First, we add a special branch 'anchors' to the data model, with a child for each anchor type defined in the document. Thus if the document defines anchor types "foo" and "bar", the data model will have paths 'anchors.foo' and 'anchors.bar'. Whenever the state machine enters a state with an <anchor> element, it sets the value of the corresponding anchor entry in the datamodel to the ID of that state. Thus if a <state> with ID 'state27' defines an <anchor> with 'type' "foo", whenever the state machine visits this state, it sets 'anchors.foo; to 'state27'. Furthermore, the state machine automatically adds the 'anchors' branch to the snapshot specified in the <anchor> element.
Now any <transition> which specifies an 'anchor' is replaced with a set of <transition>s, one for each <state> that defines that <anchor>. Each of the <transition>s takes as its 'target' one of the <state>s that defines the <anchor>. Furthermore, an extra clause is added to the 'cond' of the original <transition>, specifying that the corresponding location in the data model match the target <state>'s ID. Finally, a default transition is added reentering the current <state>. For example, if the anchor type "foo" is defined in <state>s 'state27' and 'state33', then in <state> 'thisState', a transition with "event" "someEvent", "cond" "someCond" and "anchor" 'foo' will be replaced by three transitions, as shown below:
<transition event="someEvent" cond="someCond" anchor="foo"/>
is equivalent to
<transition event="someEvent" cond="datamodel.anchors.foo==state27 && someCond" target="state27"/> <transition event="someEvent" cond="datamodel.anchors.foo==state33 && someCond" target="state33"/> <transition event="someEvent" cond="someCond" target="state33"/> target="thisState"/>
At runtime when "someEvent" is raised and "someCond" is true, if either state27 or state33 has been visited, 'datamodel.anchors.foo' will have the corresponding stateID as its value and the corresponding transition will be selected. The only complication is that the transitions must retain the original 'anchor' attribute, so that the datamodel must be rolled back to the snapshot specified in the corresponding <anchor> element. Note that the 'anchors' branch must be automatically added to this snapshot, so that it will be automatically rolled back as well. (This has the effect of erasing any intermediate anchors that have been visited since the target state was exited.) If neither state27 nor state33 has been visited, the third transition will be taken, causing the state machine to exit and reenter 'thisState' without any rollback of the datamodel.
In some applications, it is useful for a state machine to behave non-deterministically according to a probability distribution. For example, we may want to have a state S1 which transitions to S2 30% of the time and to S3 70% of the time. Such behavior can be modelled in SCXML as long as the executable content includes a random number generator. In the example below, we use ECMAScript to generate a random number between 0.0 and 1.0 in the <onentry> handler of state S1. S1 has two transitions, both triggered by E1, going to states S2 and S3. S1 takes the first transition if the variable <rand> is ≤ 0.3 but the second transition if <rand> is > 0.3. Since <rand> is evenly distributed between 0.0 and 1.0, S1 will transition to S2 30% of the time and S3 70% of the time on event E1. Note that it is important that the random number generation take place outside the <cond> clause of the transition since there are no guarantees of when or how often that will be evaluated.
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initalstate="S1"> <state id="S1"> <datamodel> <data name="rand"> </datamodel> <onentry> <assign name="rand" expr="Math.random()"/> </onentry> <transition event="E1" cond="rand <= 0.3" target="S2"/> <transition event="E1" cond="rand > 0.3" target="S3"> </state> <state id="S2"/> <state id="S3"/> </scxml>
SCXML.xsd
<?xml version="1.0" encoding="UTF-8" ?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3.org/2005/07/scxml" xmlns="http://www.w3.org/2005/07/scxml"> <xsd:annotation> <xsd:documentation>State Chart XML Schema (20060118)</xsd:documentation> </xsd:annotation> <xsd:annotation> <xsd:documentation> Copyright 1998-2006 W3C (MIT, ERCIM, Keio), All Rights Reserved. Permission to use, copy, modify and distribute the VoiceXML schema and its accompanying documentation for any purpose and without fee is hereby granted in perpetuity, provided that the above copyright notice and this paragraph appear in all copies. The copyright holders make no representation about the suitability of the schema for any purpose. It is provided "as is" without expressed or implied warranty. </xsd:documentation> </xsd:annotation> <xsd:element name="scxml"> <xsd:complexType> <xsd:choice minOccurs="1" maxOccurs="unbounded"> <xsd:element ref="datamodel" minOccurs="0" maxOccurs="1" /> <xsd:element ref="state" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="parallel" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="final" minOccurs="0" maxOccurs="unbounded" /> </xsd:choice> <xsd:attribute name="version" type="xsd:string" fixed="1.0" /> <xsd:attribute name="initialstate" type="xsd:IDREF" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="datamodel"> <xsd:complexType> <xsd:sequence> <xsd:element ref="data" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="data"> <xsd:complexType> <xsd:sequence> <xsd:any processContents="lax" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> <xsd:attribute name="src" type="xsd:anyURI" /> <xsd:attribute name="expr" type="xsd:string" /> <xsd:attribute name="name" type="xsd:NMTOKEN" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="state"> <xsd:complexType> <xsd:choice minOccurs="0" maxOccurs="unbounded"> <xsd:element ref="onentry" minOccurs="0" maxOccurs="1" /> <xsd:element ref="invoke" minOccurs="0" maxOccurs="1" /> <xsd:element ref="transition" minOccurs="0" /> <xsd:element ref="initial" minOccurs="0" maxOccurs="1" /> <xsd:element ref="state" minOccurs="0" /> <xsd:element ref="history" minOccurs="0" /> <xsd:element ref="parallel" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="anchor" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="onexit" minOccurs="0" maxOccurs="1" /> <xsd:element ref="datamodel" minOccurs="0" maxOccurs="1" /> </xsd:choice> <xsd:attribute name="id" type="xsd:ID" use="required" /> <xsd:attribute name="src" type="xsd:anyURI" /> <xsd:attribute name="final" type="xsd:boolean" /> <xsd:attribute name="task" type="xsd:boolean" /> </xsd:complexType> </xsd:element> <xsd:element name="initial"> <xsd:complexType> <xsd:sequence> <xsd:element ref="transition" minOccurs="1" maxOccurs="1"/> </xsd:sequence> <xsd:attribute name="id" type="xsd:ID" use="optional" /> <xsd:attribute name="src" type="xsd:anyURI" /> </xsd:complexType> </xsd:element> <xsd:element name="onentry"> <xsd:complexType> <xsd:group ref="executablecontent" minOccurs="0" maxOccurs="unbounded" /> </xsd:complexType> </xsd:element> <xsd:element name="onexit"> <xsd:complexType> <xsd:group ref="executablecontent" minOccurs="0" maxOccurs="unbounded" /> </xsd:complexType> </xsd:element> <xsd:element name="transition"> <xsd:complexType> <xsd:sequence> <xsd:group ref="executablecontent" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> <xsd:attribute name="event" type="xsd:string" /> <xsd:attribute name="cond" type="xsd:string" /> <xsd:attribute name="target" type="xsd:IDREF" /> <xsd:attribute name="anchor" type="NMTOKEN"/> </xsd:complexType> </xsd:element> <xsd:element name="invoke"> <xsd:complexType> <xsd:choice> <xsd:element ref="param" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="finalize" minOccurs="0" maxOccurs="1" /> <xsd:element ref="content" minOccurs="0" maxOccurs="1"/> </xsd:choice> <xsd:attribute name="src" type="xsd:anyURI" /> <xsd:attribute name="srcexpr" type="xsd:string" /> <xsd:attribute name="targettype" type="targettype" use="required" /> </xsd:complexType> </xsd:choice minOcccurs="0" maxOccurs="unbounded"> <xsd:element name="finalize"> <xsd:complexType> <xsd:group ref="executablecontent" minOccurs="0" maxOccurs="unbounded" /> </xsd:complexType> </xsd:element> <xsd:element name="content"> <xsd:complexType> <xsd:sequence> <xsd:complexType mixed="true"></xsd:complexType> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="parallel"> <xsd:complexType> <xsd:choice minOccurs="0" maxOccurs="unbounded"> <xsd:element ref="onentry" minOccurs="0" maxOccurs="1" /> <xsd:element ref="onexit" minOccurs="0" maxOccurs="1" /> <xsd:element ref="state" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="parallel" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="datamodel" minOccurs="0" maxOccurs="1" /> <xsd:element ref="join" minOccurs="0" /> </xsd:choice> <xsd:attribute name="id" type="xsd:ID" use="required" /> <xsd:attribute name="src" type="xsd:anyURI" /> <xsd:attribute name="task" type="xsd:boolean" /> </xsd:complexType> </xsd:element> <xsd:element name="final"> <xsd:complexType> <xsd:attribute name="id" type="xsd:ID" use="optional" /> </xsd:complexType> </xsd:element> <xsd:element name="history"> <xsd:complexType> <xsd:sequence minOccurs="1" maxOccurs="1"> <xsd:element ref="transition" /> </xsd:sequence> <xsd:attribute name="id" type="xsd:ID" use="required" /> <xsd:attribute name="src" type="xsd:anyURI" /> <xsd:attribute name="type" type="historytype" /> </xsd:complexType> </xsd:element> <!-- possible values for the 'type' attribute of the 'history' element --> <xsd:simpleType name="historytype"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="shallow" /> <xsd:enumeration value="deep" /> </xsd:restriction> </xsd:simpleType> <xsd:element name="anchor"> <xsd:complexType> <xsd:attribute name="type" type="NMTOKEN" use="required"/> <xsd:attribute name="snapshot" type="xpathExpr"/> </xsd:complexType> </xsd:element> <xsd:element name="param"> <xsd:complexType> <xsd:attribute name="name" type="xsd:Name" use="required" /> <xsd:attribute name="expr" type="xsd:string" /> </xsd:complexType> </xsd:element> <!-- all declarations for executable content go in this group --> <!-- those elements are declared after this group --> <xsd:group name="executablecontent"> <xsd:choice> <xsd:element ref="assign" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="send" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="event" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="if" minOccurs="0" maxOccurs="unbounded" /> <xsd:element ref="log" minOccurs="0" maxOccurs="unbounded" /> <xsd:any minOccurs="0" maxOccurs="unbounded" /> </xsd:choice> </xsd:group> <xsd:element name="assign"> <xsd:complexType> <xsd:sequence> <xsd:any processContents="lax" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> <xsd:attribute name="location" type="xpathExpr" use="required" /> <xsd:attribute name="src" type="xsd:anyURI" /> <xsd:attribute name="expr" type="xsd:string" /> </xsd:complexType> </xsd:element> <xsd:element name="send"> <xsd:complexType> <xsd:attribute name="event" type="xsd:string" /> <xsd:attribute name="target" type="xsd:anyURI" /> <xsd:attribute name="targettype" type="xsd:string" default="scxml" /> <xsd:attribute name="sendid" type="xsd:string" /> <!-- attention: "xsd:string" is not the proper type for the following attribute, rather a string in CSS2 format is required --> <xsd:attribute name="delay" type="xsd:string" default="'0s'" /> <xsd:attribute name="namelist" type="xsd:string" /> <xsd:attribute name="hints" type="xsd:string" /> </xsd:complexType> </xsd:element> <xsd:element name="if"> <xsd:complexType> <xsd:choice minOccurs="0" maxOccurs="unbounded"> <xsd:element ref="elseif" /> <xsd:element ref="else" /> <xsd:group ref="executablecontent" /> </xsd:choice> <xsd:attribute name="cond" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="elseif"> <xsd:complexType> <xsd:choice> <xsd:group ref="executablecontent" /> </xsd:choice> <xsd:attribute name="cond" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="else"> <xsd:complexType> <xsd:choice> <xsd:group ref="executablecontent" /> </xsd:choice> </xsd:complexType> </xsd:element> <xsd:element name="event"> <xsd:complexType> <xsd:attribute name="name" type="NMTOKEN" /> </xsd:complexType> </xsd:element> <xsd:element name="log"> <xsd:complexType> <xsd:attribute name="label" type="xsd:string" /> <xsd:attribute name="expr" type="xsd:string" use="required" /> <xsd:attribute name="level" type="loglevel" /> </xsd:complexType> </xsd:element> <!-- log levels are TBD; values below are taken from Java Commons Logging --> <xsd:simpleType name="loglevel"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="trace" /> <xsd:enumeration value="debug" /> <xsd:enumeration value="info" /> <xsd:enumeration value="warn" /> <xsd:enumeration value="error" /> <xsd:enumeration value="fatal" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="xpathExpr"> <xsd:restriction base="xs:token"/> </xsd:simpleType> </xsd:schema>
This SCXML document gives an overview of the SCXML language and shows the use of its state machine transition flows:
<?xml version="1.0" encoding="us-ascii"?> <!-- A wrapper state that contains all other states in this file - it represents the complete state machine --> <scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="Main"> <state id="Main"> <!-- its initial state is Test1 --> <initial> <transition target="Test1"/> </initial> <!-- Really simple state showing the basic syntax. --> <state id="Test1"> <initial> <transition target="Test1Sub1"/> </initial> <!-- Runs before we go into the substate --> <onentry> <log expr="'Inside Test1'"/> </onentry> <!-- Here is our first substate --> <state id="Test1Sub1"> <onentry> <log expr="'Inside Test1Sub1.'"/> </onentry> <onexit> <log expr="'Leaving Test1Sub1'"/> </onexit> <!-- Go to Sub2 on Event1 --> <transition event="Event1" target="Test1Sub2"/> </state> <!-- Here is the second substate It is final, so Test1 is done when we get here --> <final id="Test1Sub2"/> <!-- We get this event when we reach Test1Sub2. --> <transition event="Test1.done" target="Test2"/> <!-- We run this on the way out of Test1 --> <onexit> <log expr="'Leaving Test1...'"/> </onexit> </state> <state id="Test2"> <initial> <transition target="Test2Sub1"/> </initial> <!-- This time we reference a state defined in an external file. Note that we could have loaded the entire file by leaving off the #Test2Sub1, but in that case we would need to rename one of the Test2Sub1 states (here or in the external file) to avoid the name collision --> <state id="Test2Sub1" src="External.scxml#Test2Sub1"/> <final id="Test2Sub2"/> <!-- Test2Sub2 is defined as final, so this event is generated when we reach it --> <transition event="Test2.done" next="Test3"/> </state> <state id="Test3"> <initial> <transition target="Test3Sub1"/> </initial> <state id="Test3Sub1"> <onentry> <log expr="'Inside Test3Sub1...'"/> <!-- Send our self an event in 5s --> <send event="Timer" delay="5s"/> </onentry> <!-- Transition on to Test4. This will exit both us and our parent. --> <transition event="Timer" target="Test4"/> <onexit> <log expr="'Leaving Test3Sub1...'"/> </onexit> </state> <onexit> <log expr="'Leaving Test3...'"/> </onexit> </state> <state id="Test4"> <onentry> <log expr="'Inside Test4...'"/> </onentry> <initial> <transition target="Test4Sub1"/> </initial> <state id="Test4Sub1"> <onexit> <log expr="'Leaving Test4Sub1...'"/> </onexit> <!-- This transition causes the state to exit immediately after entering Test4Sub1. The transition has no event or guard so it is always active --> <transition target="Test5"/> </state> </state> <state id="Test5"> <onentry> <log expr="'Inside Test5...'"/> </onentry> <initial> <transition target="Test5P"/> </initial> <!-- Fire off our parallel states --> <parallel id="Test5P"> <final id="Test5PSub1"/> <final id="Test5PSub2"/> <onexit> <log expr="'all parallel states done'"/> </onexit> </parallel> <!-- The parallel states are all final, so this event is generated immediately. Although not shown, compound states (i.e., <state>s with content) are permitted within <parallel> as well. --> <transition event="Test5P.done" target="Test6"/> </state> <!-- - This state shows invocation of an external component. - We will use CCXML + VoiceXML actions as an example - as it is a good smoke test to show how it all - fits together. - Note: In a real app you would likely - split this over several states but we - are trying to keep it simple here. --> <state id="Test6" xmlns:ccxml="http://www.w3.org/2002/09/ccxml" xmlns:v3="http://www.w3.org/2005/07/vxml3"> <datamodel> <data name="ccxmlid" expr="32459"/> <date name="v3id" expr="17620"/> <data name="dest" expr="'tel:+18315552020'"/> <data name="src" expr="'helloworld2.vxml'"/> <data name="id" expr="'HelloWorld'"/> </datamodel> <onentry> <!-- Use <send> to run a createcall using the CCXML component / Event I/O Processor --> <send target="ccxmlid" targettype="ccxml" event="ccxml:createcall" namelist="dest"/> </onentry> <transition event="ccxml:connection.connected"> <!-- Here as a platform-specific extension we use example V3 Custom Action Elements instead of send. --> <v3:form id="HelloWorld"> <v3:block><v3:prompt>Hello World!</v3:prompt></v3:block> </v3:form> </transition> <transition event="v3:HelloWorld.done"> <!-- Here we are using the low level <send> element to run a v3 form. Note that the event "v3:HelloWorld.done" is assumed either to be set/sent explicitly by the v3:form code or implicitly by some process outside of the v3:form --> <send target="v3id" targettype="v3" event="v3:formstart" namelist="src id"/> </transition> <transition event="v3:HelloWorld2.done"> <!-- we use _eventdata to access data in the event we're processing. Again we assume the v3:HelloWorld2.done is set/sent from outside this document --> <ccxml:disconnect connectionid="_eventdata.connectionid"/> </transition> <transition event="ccxml:connection.disconnected" target="Done"/> <!-- Now some transitions to handle events generated by the component --> <transition event="send.successful"> <!-- Component invoked successfully. This transition has no target so Test6 is not exited. We are just going to log that we were able to send an event. --> <log expr="'Event was able to be sent'"/> </transition> <transition event="error.send" target="Done"> <!-- If we get an error event we move to the Done state that is a final state. --> <log expr="'Sending to and External component failed'"/> </transition> <onexit> <log expr="'Finished with external component'"/> </onexit> </state> <!-- This final state is an immediate child of Main - when we get here, Main.done is generated. --> <final id="Done"/> <!-- End of Main > --> </state> </scxml>
<?xml version="1.0" encoding="us-ascii"?> <scxml version="1.0" xmlns="http://www.w3.org/2005/07/scxml" initialstate="Test2Sub1"> <!-- - - This is an example substate defined in - an external file referenced by Main.scxml. - --> <state id="Test2Sub1"> <onentry> <log expr="'Inside Test2Sub1'"/> </onentry> <transition event="Event2" target="Test2Sub2"/> </state> </scxml>
The example below, which is from the Apache Shale Project. Shale is a web application framework based on JavaServer Faces (JSF). It's composed of loosely coupled services that provide functionality such as application event callbacks, dialogs with conversation-scoped state, a view technology called Clay, annotation-based functionality to reduce configuration requirements and support for remoting. For more information on Shale please see http://shale.apache.org/. SCXML is used as a "dialog manager" service in Shale (for details on the integration of SCXML in Shale please see http://shale.apache.org/shale-dialog-scxml/index.html). It allows Shale application authors to express navigation across multiple JSF views and/or other conversations with users of a JSF application using the SCXML markup notation. The example below describes how the navigation across multiple JSF views can be expressed using SCXML. It also shows how a submachine (edit-profile-config.scxml) can be used within an SCXML file. The binding language used in these examples is EL [EL], which is the expression language supported in the JSF environment.
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright 2005-2006 The Apache Software Foundation. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. $Id: Overview.html,v 1.8 2018/10/09 13:17:08 denis Exp $ --> <!-- Dialog definitions for Shale Use Cases Example Web Application written out as SCXML to demonstrate use of Commons SCXML as one of Shale's Dialog Manager implementations. Related artifacts from <dialog name="Log On">...</dialog> in original dialogs definition file from Shale nightlies. --> <scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="checkCookie"> <state id="checkCookie"> <onentry> <var name="cookieOutcome" expr="#{profile$logon.check}" /> </onentry> <transition cond="${cookieOutcome eq 'authenticated'}" target="exit"/> <transition cond="${cookieOutcome eq 'unauthenticated'}" target="logon"/> </state> <state id="logon"> <transition event="faces.outcome" cond="${outcome eq 'authenticated'}" target="exit"/> <transition event="faces.outcome" cond="${outcome eq 'create'}" target="createProfile"/> </state> <state id="createProfile" src="edit-profile-config.xml" > <transition event="createProfile.done" cond="${outcome eq 'success' or outcome eq 'cancel'}" target="exit"/> </state> <final id="exit"/> </scxml>
<?xml version="1.0" encoding="UTF-8"?> <!-- Copyright 2005-2006 The Apache Software Foundation. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. $Id: Overview.html,v 1.8 2018/10/09 13:17:08 denis Exp $ --> <!-- Dialog definitions for Shale Use Cases Example Web Application written out as SCXML to demonstrate use of Commons SCXML as one of Shale's Dialog Manager implementations. Related artifacts from <dialog name="Edit Profile">...</dialog> in original dialogs definition file from Shale nightlies. --> <scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initialstate="edit"> <state id="edit"> <initial> <transition target="setup"/> </initial> <!-- global transitions (within state "edit") --> <transition event="faces.outcome" cond="${outcome eq 'cancel'}" target="cancel"/> <transition event="faces.outcome" cond="${outcome eq 'finish'}" target="finish"/> <state id="setup"> <onentry> <var name="setupOutcome" expr="#{profile$edit.setup}" /> </onentry> <transition cond="${setupOutcome eq 'success'}" target="page1"/> </state> <state id="page1"> <transition event="faces.outcome" cond="${outcome eq 'next'}" target="page2"/> </state> <state id="page2"> <transition event="faces.outcome" cond="${outcome eq 'previous'}" target="page1"/> <transition event="faces.outcome" cond="${outcome eq 'next'}" target="page3"/> </state> <state id="page3"> <transition event="faces.outcome" cond="${outcome eq 'previous'}" target="page2"/> <transition event="faces.outcome" cond="${outcome eq 'next'}" target="editExit"/> </state> </state> <state id="cancel"> <onentry> <var name="cancelOutcome" expr="#{profile$edit.cancel}" /> </onentry> <transition cond="${cancelOutcome eq 'success'}" target="editExit"> <var name="outcome" expr="cancel"/> </transition> </state> <state id="finish"> <onentry> <var name="finishOutcome" expr="#{profile$edit.finish}" /> </onentry> <transition cond="${finishOutcome eq 'username'}" target="page1"/> <transition cond="${finishOutcome eq 'password'}" target="page1"/> <transition cond="${finishOutcome eq 'success'}" target="editExit"> <var name="outcome" expr="success"/> </transition> </state> <final id="editExit"/> </scxml>
The following two SCXML documents demonstrate the use of Invoke and finalize. The first example shows the control flow for a voice portal offering traffic reports.
<?xml version="1.0"?> <?access-control allow="*"?> <scxml initialstate="Intro"> <state id="Intro"> <invoke src="dialog.vxml#Intro"/> <transition event="success" cond="g_DataModel.sessionChrome.playAds" target="PlayAds"/> <transition event="success" cond="!g_DataModel.sessionChrome.playAds && g_DataModel.ANIQuality" target="ShouldGoBack"/> <transition event="success" cond="!g_DataModel.sessionChrome.playAds && !g_DataModel.ANIQuality" target="StartOver"/> </state> <state id="PlayAds"> <invoke src="dialog.vxml#PlayAds"/> <transition event="success" cond="g_DataModel.ANIQuality" target="ShouldGoBack"/> <transition event="success" cond="!g_DataModel.ANIQuality" target="StartOver"/> </state> <state id="StartOver"> <onenter> <script>enterStartOver();</script> </onenter> <invoke src="dialog.vxml#StartOver"> <param name="gotItFromANI" expr="g_DataModel.gotItFromANI"/> <finalize> <script>finalizeStartOver();</script> </finalize> </invoke> <transition event="success" target="ShouldGoBack"/> <transition event="doOver" target="StartOver"/> <transition event="restart" target="Intro"/> <!-- bail out to caller --> </state> <state id="ShouldGoBack"> <invoke src="dialog.vxml#ShouldGoBack"> <param name="cityState" expr="g_DataModel.cityState"/> <param name="gotItFromANI" expr="g_DataModel.gotItFromANI"/> <finalize> <script>finalizeShouldGoBack();</script> </finalize> </invoke> <transition event="highWay" target="HighwayReport"/> <transition event="go_back" target="StartOver"/> <transition event="doOver" target="ShouldGoBack"/> <transition event="restart" target="Intro"/> </state> <state id="HighwayReport"> <invoke src="dialog.vxml#HighwayReport"> <param name="cityState" expr="g_DataModel.cityState"/> <param name="gotItFromANI" expr="g_DataModel.gotItFromANI"/> <param name="playHRPrompt" expr="g_DataModel.playHRPrompt"/> <param name="metroArea" expr="g_DataModel.metroArea"/> <finalize> <script>finalizeHighwayReport();</script> </finalize> </invoke> <transition event="highway" target="PlayHighway"/> <transition event="go_back" target="StartOver"/> <transition event="doOver" target="HighwayReport"/> <transition event="fullreport" target="FullReport"/> <transition event="restart" target="Intro"/> </state> <state id="FullReport"> <invoke src="dialog.vxml#FullReport"> <param name="cityState" expr="g_DataModel.cityState"/> <param name="metroArea" expr="g_DataModel.metroArea"/> <finalize> <script>finalizeFullReport();</script> </finalize> </invoke> <transition event="go_back" target="HighwayReport"/> <transition event="new_city" target="StartOver"/> </state> <state id="PlayHighway"> <invoke src="dialog.vxml#PlayHighway"> <param name="cityState" expr="g_DataModel.cityState"/> <param name="curHighway" expr="g_DataModel.curHighway"/> <finalize> <script>finalizePlayHighway();</script> </finalize> </invoke> <transition event="go_back" target="HighwayReport"/> </state> </scxml>
The following example shows a the control flow for a blackjack game.
<?xml version="1.0"?> <?access-control allow="*"?> <scxml initialstate="master"> <state id="master"> <initial id="init1"> <transition target="_home"/> </initial> <transition event="new_dealer" target="NewDealer"/> <transition event="mumble" target="_home"/> <!-- bail out to caller --> <transition event="silence" target="_home"/> <!-- bail out to caller --> <state id="_home"> <onenter> <script> g_DataModel = {}; </script> </onenter> <invoke src="datamodel.v3#InitDataModel"> <finalize> <script> var n; for (n in g_InvokeResults) { g_DataModel[n] = g_InvokeResults[n]; } </script> </finalize> </invoke> <transition event="success" target="Welcome"/> </state> <state id="Welcome"> <invoke src="dialog.vxml#Welcome"> <param name="skinpath" expr="g_DataModel.skinpath"/> </invoke> <transition event="success" target="Intro2"/> </state> <state id="Intro2"> <invoke src="dialog.vxml#Intro2"> <param name="skinpath" expr="g_DataModel.skinpath"/> </invoke> <transition event="success" target="EvalDeal"/> </state> <state id="EvalDeal"> <onenter> <script>enterEvalDeal();</script> </onenter> <invoke src="dialog.vxml#EvalDeal"> <param name="skinpath" expr="g_DataModel.skinpath"/> <param name="playercard1" expr="g_DataModel.playercard1"/> <param name="playercard2" expr="g_DataModel.playercard2"/> <param name="playertotal" expr="g_DataModel.blackjack.GetTotalOf('caller').toString()"/> <param name="dealercardshowing" expr="g_DataModel.dealercardshowing"/> </invoke> <transition event="success" target="AskHit"/> </state> <state id="AskHit"> <invoke src="dialog.vxml#AskHit"> <param name="skinpath" expr="g_DataModel.skinpath"/> <finalize> <script>finalizeAskHit();</script> </finalize> </invoke> <transition event="hit" target="PlayNewCard"/> <transition event="stand" target="PlayDone"/> </state> <state id="PlayNewCard"> <invoke src="dialog.vxml#PlayNewCard"> <param name="skinpath" expr="g_DataModel.skinpath"/> <param name="playernewcard" expr="g_DataModel.playernewcard"/> <param name="playertotal" expr="g_DataModel.blackjack.GetTotalOf('caller').toString()"/> </invoke> <transition event="success" cond="g_DataModel.blackjack.GetTotalOf('caller') >= 21" target="PlayDone"/> <transition event="success" target="AskHit"/> <!-- less than 21 --> </state> <state id="PlayDone"> <onenter> <script>enterPlayDone();</script> </onenter> <invoke src="dialog.vxml#PlayDone"> <param name="skinpath" expr="g_DataModel.skinpath"/> <param name="gameresult" expr="g_DataModel.blackjack.GetGameResult()"/> <param name="dealertotal" expr="g_DataModel.blackjack.GetTotalOf('dealer').toString()"/> </invoke> <transition event="playagain" target="Intro2"/> <transition event="quit" target="_home"/> </state> <state id="NewDealer"> <onenter> <script>enterNewDealer();</script> </onenter> <invoke src="dialog.vxml#Dummy"/> <transition event="success" target="Welcome"/> </state> </state> </scxml>