Project Description
Runtime interpreted statechart with hierarchical (composite) and orthogonal (concurrent) states. Join and fork transitions. History. Source code in C#. Independent from threading, synchronization and timer runtime services. A state machine "template" precomputes time consuming runtime checks (like finding the LCA of transitions) and thus reduces the per-instance costs.

The state machine approach is a fairly common technique for controlling a system with complex behaviour.
Several free implementations are available in different languages with partly subtle differences, e.g.
Andreas Huber "The Boost Statechart Library" (http://www.boost.org/libs/statechart/doc/index.html)
Rainer Hessmer "qf4net: Quantum Framework for .Net" (http://www.hessmer.org/dev/qhsm/)
Leslie Sanford "A .NET State Machine Toolkit" (http://www.codeproject.com/script/Articles/Article.aspx?aid=11663)

Unfortunately the very thorough implementation from Boost is in C++ and I doubt it can be ported to C# due to its massive usage of C++ generic programming. Therefore other approaches and proposals are needed. This project provides one.

Using StaMa a state machine is defined in code by building a tree structure of states and transitions. States carry entry and exit actions as delegates, likewise transitions carry a delegate for the transition action. During excution the tree of states is traversed to find the transition triggered by a signal. Action delegates are executed traversing the tree of states.

Supplementary visual modelling and partial code generation from the visual model is provided through a Microsoft Visio script.



.NET Micro Framework

StaMa perfectly matches the limitations of small devices and NETMF.
In particular...
  • StaMa doesn't use any runtime resources like timers, threads or thread synchronization objects. Clients can implement threading and synchronization according to their projects needs.
  • During state machine operation only small object allocations are necessary (mostly iterators), StaMa doesn't stress the garbage collector.
  • StaMa isn't intrinsically tied to generics which are (unfortunately) not supported by NETMF.
  • StaMa has a state machine instance concept. In case multiple instances with the same behavior are needed, there is only little memory and initialization overhead for the 2nd, 3rd, ... 100th instance. All structural and descriptive information (strings and delegates) are kept in the StateMachineTemplate, StateMachine instances are definitely flyweight.

Ticket vending machine sample
A sample demonstrates StaMa usage in the NETMF Sample Emulator

TicketVendingNETMF.png

TicketVendingNETMFVisio.png




Sample complex state machine
Code and Microsoft Visio diagram is part of the release download.

ClockSample.png


Sample code for a simple state machine
Corresponding Visio diagram below.

namespace SimpleWatch
{
    class AppMain
    {
        [STAThread]
        static void Main(string[] args)
        {
            SimpleWatch watch = new SimpleWatch();

            watch.SendTriggerEvent(SimpleWatch.Event.LeftButtonPressed);
            watch.SendTriggerEvent(SimpleWatch.Event.RightButtonPressed);

            watch = null;
        }
    }


    // The wrapper around the state machine controller provides the code of the entry and exit callbacks.
    class SimpleWatch
    {
        public enum Event
        {
            RightButtonPressed,
            LeftButtonPressed
        }

        // The state machine controller.
        private StateMachine m_stateMachine = null;


        public SimpleWatch()
        {
            StateMachineTemplate t = new StateMachineTemplate();

            //## Begin Structure
            // Generated from <file:S:\StaMa_State_Machine_Controller_Library\Samples\Clock\DlgMain.vst>
            // at 01-31-2008 23:20:24 using StaMaShapes Version 2
            t.Region("DisplayTime", false);
                t.State("DisplayDate", EnterDisplayDate, null);
                    t.Transition("RightButtonPressed", "DisplayDate", "DisplayTime", Event.RightButtonPressed, null, null);
                t.EndState();
                t.State("DisplayTime", EnterDisplayTime, null);
                    t.Transition("LeftButtonPressed", "DisplayTime", "DisplayDate", Event.LeftButtonPressed, null, null);
                t.EndState();
            t.EndRegion();
            //## End Structure

            m_stateMachine = t.CreateStateMachine();

            m_stateMachine.Startup();
            System.Console.WriteLine("After startup. Active state is {0}", m_stateMachine.ToString());
        }


        // Forwards the trigger events to the embedded state machine
        public void SendTriggerEvent(Event ev)
        {
            m_stateMachine.SendTriggerEvent(ev, null);
            System.Console.WriteLine("After event {0}. Active state is {1}", ev, m_stateMachine.ToString());
        }

  
        // Entry callback method for state "DisplayTime".
        private void EnterDisplayDate(StateMachine stateMachine, object triggerEvent, EventArgs eventArgs)
        {
            System.Console.WriteLine("EnterDisplayDate called.");
        }


        // Entry callback method for state "DisplayDate".
        private void EnterDisplayTime(StateMachine stateMachine, object triggerEvent, EventArgs eventArgs)
        {
            System.Console.WriteLine("EnterDisplayTime called.");
        }
    }
}


SimpleClockSample.png



Comments are greatly appreciated. If You decide to evaluate or actually use StaMa in some real world application it would be excellent if You leave a note whether or not StaMa met Your needs.

Last edited May 30, 2013 at 7:56 PM by RolandSchneider, version 10