The Abstract State Machine API bundle provides a utility which represents a finite state machine. Such a state machine can be used for simulating the states and behavior of a specific device.
Contents:
The JAR file of the Abstract State Machine API bundle is asm.jar, and resides in the bundles folder.
The Abstract State Machine bundle imports the com.prosyst.util.xml
package. It contains the ProSyst XML utility and is exported by the ProSyst Util Full Bundle.
|
The Abstract State Machine can be used to define a state machine featuring the behavior of a specific device type. The Abstract State Machine consists of the following building elements:
The Abstract State Machine is represented by the com.prosyst.util.asm.StateMachine class. In addition, the com.prosyst.util.asm.xml.XMLStateMachine offers an Abstract State Machine modification which uses XML files for state machine definition.
This section describes how to define a state machine by using StateMachine and by using XMLStateMachine.
To get more information on how to trigger changes in the state machine, refer to the "Causing Transitions in the State Machine" section.
StateMachine class.To define the states of your state machine, use the addState method as many times as the states are. A state machine must have an initial state - specify one of the already defined ones by using the setInitialState method.
To define a state machine property, use the setProperty method. This method can used further to change the property value which, if included in a trigger, may cause a state transition.
Transitions are represented as Transition objects. To define a transition for your state machine, create a Transition instance and assign it name, target state and, if the transition is regular, source state.
If the transition will have a guard condition, use the setFilter method defining the condition as an LDAP-based org.osgi.framework.Filter on the value of a specific state machine property.
Tip: You can use the createFilter method of the BundleContext for your bundle to create a Filter from its String equivalent.
addTriggerAction method providing the command's unique name and the transition that will be started when the triggering action is called. addTriggerEvent method providing definition of the event condition as an LDAP-based Filter on a property's value and of the transition to take place when the event occurs. Listing 1.1 contains the definition of a state machine of an abstract player device - from OFF it turned on to STANDBY. Next, when play is invoked, the state machine enters the PLAYING state from which it can go to PAUSED state or can return to STANDBY state. A guard condition for available tape is defined in the "play" transition. If the devices goes out of tape, an event trigger launches the transition from PLAYING to STANDBY state.
import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import com.prosyst.util.asm.StateMachine; import com.prosyst.util.asm.Transition; // Method inherited from BundleActivator. It contains the definition of the state machine stateMachine = new StateMachine();
// Define states
stateMachine.addState(STATE_OFF);
stateMachine.addState(STATE_STANDBY);
stateMachine.addState(STATE_PLAY);
stateMachine.addState(STATE_PAUSE);
stateMachine.setInitialState(STATE_OFF);
// Define properties
stateMachine.setProperty(MORE_TAPE, Boolean.FALSE);
//Define transitions
Transition turnOn = new Transition("turn on", STATE_OFF, STATE_STANDBY);
Transition turnOff = new Transition("turn off", STATE_STANDBY, STATE_OFF);
Filter tapeFilter = bc.createFilter(TAPE_CONDITION);
play.setFilter(tapeFilter);
Transition stop = new Transition("stop", STATE_PLAY, STATE_STANDBY);
Transition stopFromPause = new Transition("stop from pause", STATE_PAUSE, STATE_STANDBY);
Transition pause = new Transition("pause", STATE_PLAY, STATE_PAUSE);
Transition resume = new Transition("resume", STATE_PAUSE, STATE_PLAY);
// Define triggers
// Define action triggers
// Define a filter defining an event for unavailable tape // Initiate changes in the state machine // Method inherited from BundleActivator. |
DTD.
|
Example State Machine XML. Listing 1.2 contains an XML equivalent of the "player" state machine from Listing 1.1.
<?xml version="1.0" encoding="iso-8859-1"?> <smc> <states initial="OFF"> <state>OFF</state> <state>STANDBY</state> <state>PLAYING</state> <state>PAUSED</state> </states> <props> <prop name="moreTape" value="false"/> </props> <state-machine> <trigger command="turnOn"> <transition name="turn on" fromState="OFF" toState="STANDBY"/> </trigger> <trigger command="turnOff"> <transition name="turn off" fromState="STANDBY" toState="OFF"/> </trigger> <trigger command="play"> <transition name="play" fromState="STANDBY" toState="PLAYING" if="(moreTape=true)"/> <transition name="resume" fromState="PAUSED" toState="PLAYING"/> </trigger> <trigger command="pause"> <transition name="pause" fromState="PLAYING" toState="PAUSED"/> </trigger> <trigger command="stop"> <transition name="stop" fromState="PLAYING" toState="STANDBY"/> <transition name="stop from paused" fromState="PAUSED" toState="STANDBY"/> </trigger> <trigger expr="(!(moreTape=true))"> <transition name="stop" fromState="PLAYING" toState="STANDBY"/> </trigger> </state-machine> </smc> |
To transfer the state machine components defined in an XML into a real state machine implementation, which can be mapped to a device's status and behavior, you should use the XMLStateMachine class.
Loading can be done when instantiating the XMLStateMachine class or at a later point by calling the load method. If you have used the no-argument constructor to create the XML state machine, besides the load method you should also provide the state machine with the BundleContext instance allocated for your bundle by invoking the setBundleContext method as the machine uses this BundleContext to create Filter objects for transition guard conditions and trigger events.
Listing 1.3 loads the state machine XML shown in Listing 1.2.
import java.io.IOException; import java.io.InputStream; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import com.prosyst.util.asm.xml.XMLStateMachine; import com.prosyst.util.timer.TimerListener; public class MyXmlStateMachine implements BundleActivator {
XMLStateMachine stateMachine = null;
// Method inherited from BundleActivator. It loads the definition of the state machine // Method inherited from BundleActivator |
This section provides information on calling the commands of a state machine thus causing transition from one state to another.
If defined in the state machine, calling an action can trigger a state transition. To call such an action, use the call method on the corresponding state machine instance.
Changing the value of a property can be done with the setProperty method. If the property is included in a transition guard condition, this condition can be satisfied or not and as a result the related transition will be executed or not. If the property is used in the definition of an event trigger, then depending on the property value the state machine can automatically enter into a specific state.
Listing 2 uses the "player" state machine previously defined (see Listing 1.x) and by calling actions and changing property values triggers state transitions. Basically, within regular time intervals of 20 seconds the example consecutively calls the actions "turn on", "play", "pause", and "play". While the player is playing, it is indicated that no more tape is available (setting "moreTape" to false) and the state machine automatically enters the STANDBY state.
import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import com.prosyst.util.asm.StateMachine; public class MyStateMachine implements BundleActivator, TimerListener {
// Variables related to the Timer service invocation String timerName = Timer.class.getName(); Timer timer = null; ServiceReference timerRef = null; // Variable counting time notifications // Method inherited from BundleActivator. It contains the definition of the state machine // Method inherited from BundleActivator. // Helper method which registers for timer notifications each 20 seconds. // Method inherited from TimerListener |
You can get notified about changes in a specific state machine instance, such as leaving and entering a particular state, executing a transition and changing a property value.
To receive such events, implement a com.prosyst.util.asm.StateMachineListener and register it in the target state machine by calling its setStateListener method.