page last updated 9/27/10

A state variable is used to keep track of the current state at a particular level.
A separate state variable is necessary for each concurrent statechart.
If a state has a greater detailed level, then you need a variable for that level also.
If you have a flat statechart, i.e., one that is similar to a finite state machine, you would only need one variable to track the state.
For the statechart on the right (from Ian Horrocks, Constructing the User Interface with Statecharts, Addison Wesley), you need 6 variables
A control object is associated with a set of interface or "controlled" items. An item should be controlled or managed only by at most one control object, but an object may control more than one item.
The set of state variables are private within the control object class. These state variables control states/properties of the items.
There is a set of state methods, one state method for each state represented at the level. The state method is responsible for setting the attributes of the controlled (IU) items represented by the state (e.g., enabled/disabled).
Any event that makes the transition to this state calls the state method.
Most state methods will be private because most states are transitioned from other states represented within the control object.
There may be a few state methods that are public because they may need to be referred to by other control objects.
The set of event procedures are public in the control object class. They are simply called from the event handlers and should be the only code in the event handlers. The event may be used in a number of transitions and will use the state variables to determine the appropriate actions to execute and the next state.
State methods should be simple in structure and should not contain any conditions to determine context. That would be a symptom of flawed statechart design. Do actions associated with the state here.
The state method typically has the pattern (where # is the state number or code)
private void goState#( )
{
//declarations
stateVarX = #;
set attributes to related items
do actions
}
A transient method (one with one or more conditions) could have the pattern:
private void goState#( )
{
//declarations
stateVarX = #;
if (cond1) {
goStateA( );
goStateC( ); //e.g. a lower level state
} else if (cond2) {
goStateB( );
}//etc
}
If there are lower level detail, then there should be calls to those [entry] states within the level(s). If there are concurrent lower levels, there would be a call for each of the current statechart.
An event can occur in a number of places in the statechart and in the context of several states. You will need to consult the state variable to correctly determine the next state. Be sure to consider handling events that should not have occurred for a given state. Do not call methods that do other actions; they should be handled in the state routine.
public void eventDescription
{
//local declarations
switch (stateVarX){
case stateC:
goStateD( ); //or exit
break;
case stateD:
goStateE( );
goStateF( ); //a lower level state
break;
case stateE:
....
}
}
Some states may have multiple events and conditions exiting them. In these cases, the event procedure needs to check the condition in priority order. We can assume that it is deterministic as to which event occurs, the non-determinism to watch for is that multiple conditions may be true.
The sequence of if-then-elseif structures will implement the condition priorities.
public void eventDescription
{
//local declarations
switch (stateVarX){
....
case sM:{...}break;
case sN: //event in state sN
if (cond1) {
goStateD( );
} else if (cond2) {
....
}
break;
case sN+1:
....
}
}
When an event exits from a level, the state needs to be preserved for reentry. The state variable appears to be sufficient, but some recommend explicit saving of the state variable in a history variable.
public void eventDescription
{
//local declarations
switch (stateVarX){ //check state to determine where to resume
case stateC:
goStateG(
);
switch
(stateVarY){
case stateM:
goStateM();
break;
case stateN:
goStateN();
break;
}
break;
case stateD:
goStateE(
);
goStateF(
); //a lower level state
break;
case stateE:
....
default:
....
}
}
Concurrency is simply implemented by dealing with each concurrent states in a series of switch blocks. One switch for each of the concurrent blocks. The block can be handled serially or if the activities are substantial, they could be threads.
The translation of a statechart to code is straight forward and algorithmic. In fact, most UML packages automatically generate code from the graphical chart.

/// this is working code and can be run as a C# program.
using System;
using System.Collections.Generic;
using System.Text;
namespace StateChartImpl
{
class Program
{
static void Main(string[] args)
{
Controller ctrl = new Controller();
while (!ctrl.IsFinalState())
{
string line = Console.ReadLine();
switch (line[0])
{
case 'A': ctrl.eventA(); break;
case 'B': ctrl.eventB(); break;
case 'C': ctrl.eventC(); break;
case 'E': ctrl.eventE(); break;
case 'F': ctrl.eventF(); break;
default:
Console.WriteLine("Input not an event: " + line);
break;
}
Console.WriteLine("STATUS: " + ctrl.ToString());
}
}
}
class Controller
{
private char stateOfU = '?';
private char stateOfS = '%';
private bool finalState = false;
public Controller ()
{
goStateS();
goStateQ();
}
override public string ToString()
{
return "state of U = " + stateOfU + " state of S = " + stateOfS;
}
///
/// State entry routines
///
private void goStateS()
{
stateOfU = 'S';
Console.WriteLine("Entering 'S'\n");
}
private void goStateT()
{
stateOfU = 'T';
Console.WriteLine("Entering 'T'\n");
}
private void goStateP()
{
stateOfS = 'P';
Console.WriteLine("Entering 'P'\n");
}
private void goStateQ()
{
stateOfS = 'Q';
Console.WriteLine("Entering 'Q'\n");
}
private void goStateFinal()
{
finalState = true;
}
public void eventA()
{
if (stateOfU == 'S' && stateOfS == 'Q')
{
goStateT();
}
else
{
Console.WriteLine("Event A ignored");
}
}
public void eventB()
{
if (stateOfU == 'T')
{
goStateS();
goStateP();
}
else
{
Console.WriteLine("Event B ignored");
}
}
public void eventC()
{
if (stateOfU == 'S' && stateOfS == 'P')
{
goStateQ();
}
else
{
Console.WriteLine("Event C ignored");
}
}
public void eventE()
{
if (stateOfU == 'S')
{
goStateT();
}
else
{
Console.WriteLine("Event F ignored");
}
}
public void eventF()
{
if (stateOfU == 'S')
{
goStateFinal();
}
else
{
Console.WriteLine("Event F ignored");
}
}
public bool IsFinalState()
{
return finalState;
}
}
}