Click here to Skip to main content
15,884,177 members
Articles / Programming Languages / C#
Article

Finite State Machine with Sub-state

Rate me:
Please Sign up or sign in to vote.
1.67/5 (2 votes)
4 Oct 2008CPOL3 min read 41.1K   737   34   4
Implementing Finite State Machine with Sub-state

Sample_FSM.jpg

Introduction

This is an idea of implementing Finite State Machine with sub-state. Suppose there is a State Model as above picture. Below is transition table:

<o:p> 

Source state<o:p>

Trigger<o:p>

Target state<o:p>

Comment<o:p>

1<o:p>

No State<o:p>

Start<o:p>

(Init)<o:p>

Will go to any <st1:place w:st="on"><st1:placetype w:st="on">sub-state of <st1:placename w:st="on">Init (depends on configuration)<o:p>

2<o:p>

(Init)<o:p>

Enter to (Init)<o:p>

Init A<o:p>

In case configuration is go to Init A<o:p>

3<o:p>

(Init)<o:p>

Enter to (Init)<o:p>

Init B<o:p>

In case configuration is go to Init B<o:p>

4<o:p>

Init A<o:p>

Load A<o:p>

Loaded A<o:p>

Go directly to Loaded A (is <st1:place w:st="on"><st1:placetype w:st="on">sub-state of <st1:placename w:st="on">Loaded)<o:p>

5<o:p>

Init B<o:p>

Load B<o:p>

Loaded B<o:p>

Go directly to Loaded B (is <st1:place w:st="on"><st1:placetype w:st="on">sub-state of <st1:placename w:st="on">Loaded)<o:p>

6<o:p>

(Loaded)<o:p>

Run<o:p>

Processing<o:p>

From any <st1:place w:st="on"><st1:placetype w:st="on">sub-state of <st1:placename w:st="on">Loaded go to Processing<o:p>

7<o:p>

(Executing)<o:p>

Pause<o:p>

Paused<o:p>

From any <st1:place w:st="on"><st1:placetype w:st="on">sub-state of <st1:placename w:st="on">Executing go to Paused<o:p>

8<o:p>

Paused<o:p>

Resume<o:p>

(Executing)<o:p>

Will go to sub-state which was activated before leaving state Executing<o:p>

9<o:p>

(Executing)<o:p>

Enter to (Executing)<o:p>

Loaded<o:p>

In case Loaded was activated before leaving Executing<o:p>

10<o:p>

(Executing)<o:p>

Enter to (Executing)<o:p>

Processing<o:p>

In case Processing was activated before leaving Executing<o:p>

11<o:p>

Processing<o:p>

Stop<o:p>

No State<o:p>

<o:p> 

 

Framework classes 

Two abstract classes StateMgmt and State will be used to implement any particular state model

FSM_Framework.jpg

StateMgmt:<o:p> 

This class handles the entire state model. It handles triggers and changes current state according to received trigger.
This class contains only one state (called Root state). Root state contains sub-states and StateMgmt will use Root state to handle triggers. 

State:<o:p> 

This class represents one particular state in model (can be container-state or smallest sub-state). It can contains many sub-state, and at one time it can have only one sub-state is activated.
If one state has sub-states, it can activate the route to any state which is its child (can be sub-state, or sub-state of sub-state…) by using method GoToState.

Example (see below picture):If state [C] is activated: the route from [Root] to [C] is: [Root] --> [A] --> [C]
Active_route_1.jpg
 

By using method GoToState, state [Root] can activate its child state [F] directly, and the route will be: [Root]  --> [B] --> [F] 

Active_route_2.jpg

 

Each state must have a pointer to StateMgmt instance, so it can change from current state to any state in model (by using method GoToState of class StateMgmt).

As default, when a state handles a trigger, if it has sub-states it will call method HandleTrigger of its direct activated sub-state. Therefore, which state handles actual triggers, it has to override method HandleTrigger

 

Implementation of class StateMgmt: 

public abstract class StateMgmt
{
    protected State rootState = null;

    public State GetCurrentState()
    {
        if (this.rootState != null)
        {
            return rootState.GetCurrentState();
        }
        return null;
    }

    public virtual void GoToState(int stateId)
    {
        if (rootState != null)
        {
            rootState.GoToState(stateId);
        }
        else
        {
            throw new Exception("Root state is not initialized");
        }
    }

    public virtual void HandleTrigger(int trigger)
    {
        if (this.rootState != null)
        {
            rootState.HandleTrigger(trigger);
        }
    }
}

 

Implementation of class State: 

public abstract class State
{
    private int stateId = -1;

    protected State currentSubState = null;

    protected StateMgmt mgmt = null;

    protected ArrayList subStates = new ArrayList();

    public int StateId
    {
        get
        {
            return this.stateId;
        }
        set
        {
            this.stateId = value;
        }
    }

    public State(int stateId, StateMgmt mgmt)
    {
        this.stateId = stateId;
        this.mgmt = mgmt;
    }

    public abstract void Activated();

    public abstract string GetStateName();

    public State GetCurrentState()
    {
        if (this.currentSubState == null)
        {
            return this;
        }
        return this.currentSubState.GetCurrentState();
    }

    public virtual void GoToState(int stateId)
    {
        foreach (State s in subStates)
        {
            if (s.StateId == stateId)
            {
                this.currentSubState = s;
                s.Activated();
                return;
            }
            else
            {
                try
                {
                    s.GoToState(stateId);
                    this.currentSubState = s;
                    return;
                }
                catch
                {
                }
            }
        }
        throw new Exception("Invalid target state");
    }

    public virtual void HandleTrigger(int trigger)
    {
        if (this.currentSubState != null)
        {
            this.currentSubState.HandleTrigger(trigger);
        }
    }

    public void AddSubState(State newState)
    {
        this.subStates.Add(newState);
    }
}

Implementing an entire State model

Create classes inherit from two Framework classes: 

ClassesDemoFSM.jpg

In class DemoStateMgmt:
- Define some necessary constants (State Id and Trigger Id)
- Build up the structure of model by adding sub-states to root state of DemoStateMgmt

C#
public class DemoStateMgmt : StateMgmt
{
    public const int State_NoState = 1;
    public const int State_Init = 2;
    public const int State_InitA = 3;
    public const int State_InitB = 4;
    public const int State_Executing = 5;
    public const int State_Loaded = 6;
    public const int State_LoadedA = 7;
    public const int State_LoadedB = 8;
    public const int State_Processing = 9;
    public const int State_Paused = 10;

    public const int Trigger_Start = 1;
    public const int Trigger_LoadA = 2;
    public const int Trigger_LoadB = 3;
    public const int Trigger_Run = 4;
    public const int Trigger_Pause = 5;
    public const int Trigger_Resume = 6;
    public const int Trigger_Stop = 7;

    public DemoStateMgmt()
    {
        //Create state Root
        this.rootState = new RootState(-1, this);

        //Create state Init and its sub-states
        Init stateInit = new Init(State_Init, this);
        stateInit.AddSubState(new InitA(State_InitA, this));
        stateInit.AddSubState(new InitB(State_InitB, this));

        //Create state Loaded and its sub-states
        Loaded stateLoaded = new Loaded(State_Loaded, this);
        stateLoaded.AddSubState(new LoadedA(State_LoadedA, this));
        stateLoaded.AddSubState(new LoadedB(State_LoadedB, this));

        //Create state Executing and its sub-states
        Executing stateExecuting = new Executing(State_Executing, this);
        stateExecuting.AddSubState(stateLoaded);
        stateExecuting.AddSubState(new Processing(State_Processing, this));

        //Add sub-states to state Root
        this.rootState.AddSubState(new NoState(State_NoState, this));
        this.rootState.AddSubState(stateInit);
        this.rootState.AddSubState(stateExecuting);
        this.rootState.AddSubState(new Paused(State_Paused, this));

        //Go to first state
        this.rootState.GoToState(State_NoState);
    }
}

Normally, each smallest sub-state will override method HandleTrigger. But in some cases when the model leaves out from a state which has sub-states, only this state needs to override method HandleTrigger.

Similar idea, when the model goes to a state which has sub-states, it only needs to go to this state. It is not necessary to indicate exactly which sub-sate it will go to. But in this case, it is very important to override method Activated of this state. This method will activate (go to) exactly its sub-state as desired.

Example with class Init: 

C#
public class Init : State
{
    public Init(int stateId, StateMgmt mgmt)
        : base(stateId, mgmt)
    {
    }

    public override void Activated()
    {
        switch (Configuration.DefaultInit)
        {
            case Configuration.InitA:
                GoToState(DemoStateMgmt.State_InitA);
                return;
            case Configuration.InitB:
                GoToState(DemoStateMgmt.State_InitB);
                return;
        }
    }

    public override string GetStateName()
    {
        return "Init";
    }
}


Example with class Executing: 

C#
public class Executing : State
{
    public Executing(int stateId, StateMgmt mgmt)
        : base(stateId, mgmt)
    {
    }

    public override void Activated()
    {
        this.currentSubState.Activated();
    }

    public override string GetStateName()
    {
        return "Executing";
    }

    public override void HandleTrigger(int trigger)
    {
        switch (trigger)
        {
            case DemoStateMgmt.Trigger_Pause:
                Pause();
                return;
            //case another:
            //case another:
        }
        base.HandleTrigger(trigger);
    }

    private void Pause()
    {
        this.mgmt.GoToState(DemoStateMgmt.State_Paused);
    }
}

Above is explanation about the pattern and there is only source code of some classes. You can download the entire project. If you are not clear about any, or if you have any idea for this pattern, please feel free to contact with me by email: caohuuloc@gmail.com


History 

2008-10-05: Version 1.0. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Vietnam Vietnam
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralYou may want to take a look at qf4net Pin
Magnus Werner6-Oct-08 21:58
Magnus Werner6-Oct-08 21:58 
GeneralRe: You may want to take a look at qf4net Pin
caohuuloc7-Oct-08 3:00
caohuuloc7-Oct-08 3:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.