This project has moved and is read-only. For the latest updates, please go here.

State

Mar 9, 2009 at 1:05 PM
hello!

there is a State class, but StateMachineTemplate.State() only accepts strings and callbacks. if i define several states (derived from StaMa.State) with callbacks, i have to reference to their own callbacks in the State constructor (?) and again in the StateMachineTemplate. why is that?
Mar 11, 2009 at 12:00 AM

The approach here is somewhat different from what You might be accustomed to by other state machine implementations.

One basic goal was to reduce footprint in case there are lots of instances with the same state machine behaviour. This is achieved by separating the state machine structure (defined in StaMa.StateMachineTemplate) from the active state (represented through StaMa.StateMachine). The approach is somewhat similar to the flyweight pattern that separates common aspects (StateMachineTemplate) from individual aspects of an instance (StateMachine).
Maybe the footprint question is somewhat old fashioned thinking today, but the design of this state machine dates back about 10 years and was originally done with C++ considering a very restrictive environment.

The StaMa.State instance is a composite part of the StateMachineTemplate structure. StaMa.State provides stateless callbacks (.NET: delegates) as properties that will be invoked whenever a StateMachine instance enters or leaves the particular StaMa.State.
Back to Your question: It was not intended that clients have to subclass StaMa.State and give the subclass a particular behaviour. Instead let StaMa.State instances be created through the StateMachineTemplate.State(...) method, pass in the callbacks as parameters to that method and let the StateMachine invoke the callbacks when needed.

Please find attached a modified SimpleWatch sample that shows how the flyweight concept works with StaMa.

Hope this helps, Roland.

 

using System;
using StaMa;

namespace SimpleWatchMulti
{
    class AppMain
    {
        [STAThread]
        static void Main(string[] args)
        {
            SimpleWatch watch1 = new SimpleWatch("1");
            SimpleWatch watch2 = new SimpleWatch("2");
            SimpleWatch watch3 = new SimpleWatch("3");

            watch1.SendTriggerEvent(SimpleWatch.Event.LeftButtonPressed);
            watch2.SendTriggerEvent(SimpleWatch.Event.LeftButtonPressed);
            watch3.SendTriggerEvent(SimpleWatch.Event.LeftButtonPressed);
            watch1.SendTriggerEvent(SimpleWatch.Event.RightButtonPressed);
            watch2.SendTriggerEvent(SimpleWatch.Event.RightButtonPressed);
            watch3.SendTriggerEvent(SimpleWatch.Event.RightButtonPressed);
            // ... or any other sequence of events.

            watch1 = null;
            watch2 = null;
            watch3 = 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 behaviour definition. Only once needed.
        private static StateMachineTemplate m_stateMachineTemplate;

        // The state machine ("flyweight").
        private StateMachine m_stateMachine;

        private string m_name;

        // Setup the state machine behaviour definition.
        static 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:36:01 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_stateMachineTemplate = t;
        }

        public SimpleWatch(string name)
        {
            m_name = name;

            m_stateMachine = m_stateMachineTemplate.CreateStateMachine(this);

            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 static void EnterDisplayDate(StateMachine stateMachine, object triggerEvent, EventArgs eventArgs)
        {
            SimpleWatch that = (SimpleWatch)stateMachine.Context;
            System.Console.WriteLine(String.Format("EnterDisplayDate called from {0}.", that.m_name));
        }

        // Entry callback method for state "DisplayDate".
        private static void EnterDisplayTime(StateMachine stateMachine, object triggerEvent, EventArgs eventArgs)
        {
            SimpleWatch that = (SimpleWatch)stateMachine.Context;
            System.Console.WriteLine(String.Format("EnterDisplayTime called from {0}.", that.m_name));
        }
    }
}