State Machine Framework StaMa experience

Thanks a lot Roland. Good stuff for the docs :wink:

I tried the B approach but could not see the three parallel state machines. Can you show code that reads states and sends signals to each orthogonal state?

The below Windows console application creates the state machine from my last reply and sends the Init, Part3Initialized, Part1Initialized and Part2Initialized events to the state machine.
Trace output for state changes, entry and exit of states is written to the console.
Please note that the transition to the ā€œSystemInitializedā€ state is automatically triggered with the last event for the sub-parts (ā€œPart2Initializedā€) due to ā€œrun to completionā€ processing.
Please find the corresponding state machine diagram attached.


using System;
using StaMa;

namespace ForkAndJoin
{
    class Program
    {
        //## Begin StateAndTransitionNames71
        // Generated from <file:...\ForkAndJoin.vsd>
        // at 08-03-2014 20:03:35 using StaMaShapes Version 2
        private static readonly string Uninitialized = "Uninitialized";
        private static readonly string Transi43 = "Transi43";
        private static readonly string Init = "Init";
        private static readonly string SystemInitializing = "SystemInitializing";
        private static readonly string Transi57 = "Transi57";
        private static readonly string Initialized1 = "Initialized1";
        private static readonly string Initializing1 = "Initializing1";
        private static readonly string Transi48 = "Transi48";
        private static readonly string Part1Initialized = "Part1Initialized";
        private static readonly string Initialized2 = "Initialized2";
        private static readonly string Initializing2 = "Initializing2";
        private static readonly string Transi50 = "Transi50";
        private static readonly string Part2Initialized = "Part2Initialized";
        private static readonly string Initialized3 = "Initialized3";
        private static readonly string Initializing3 = "Initializing3";
        private static readonly string Transi52 = "Transi52";
        private static readonly string Part3Initialized = "Part3Initialized";
        private static readonly string SystemInitialized = "SystemInitialized";
        //## End StateAndTransitionNames71

        static void Main(string[] args)
        {
            StateMachineTemplate t = new StateMachineTemplate();

                //## Begin StateMachineTemplate66
                // Generated from <file:...\ForkAndJoin\ForkAndJoin.vsd>
                // at 08-03-2014 20:03:35 using StaMaShapes Version 2
                t.Region(Uninitialized, false);
                    t.State(Uninitialized, (stm, ev, arg) => Console.WriteLine("Enter Unintialized"), (stm, ev, arg) => Console.WriteLine("Exit Uninitialized"));
                        t.Transition(Transi43, Uninitialized, new string[] {Initializing1, Initializing2, Initializing3}, Init, null, null);
                    t.EndState();
                    t.State(SystemInitializing, (stm, ev, arg) => Console.WriteLine("Enter SystemInitializing"), (stm, ev, arg) => Console.WriteLine("Exit SystemInitializing"));
                        t.Transition(Transi57, new string[] {Initialized1, Initialized2, Initialized3}, SystemInitialized, null, null, null);
                        t.Region(Initializing1, false);
                            t.State(Initialized1, (stm, ev, arg) => Console.WriteLine("Enter Initialized1"), (stm, ev, arg) => Console.WriteLine("Exit Initialized1"));
                            t.EndState();
                            t.State(Initializing1, (stm, ev, arg) => Console.WriteLine("Enter Initializing1"), (stm, ev, arg) => Console.WriteLine("Exit Initializing1"));
                                t.Transition(Transi48, Initializing1, Initialized1, Part1Initialized, null, null);
                            t.EndState();
                        t.EndRegion();
                        t.Region(Initializing2, false);
                            t.State(Initialized2, (stm, ev, arg) => Console.WriteLine("Enter Initialized2"), (stm, ev, arg) => Console.WriteLine("Exit Initialized2"));
                            t.EndState();
                            t.State(Initializing2, (stm, ev, arg) => Console.WriteLine("Enter Initializing2"), (stm, ev, arg) => Console.WriteLine("Exit Initializing2"));
                                t.Transition(Transi50, Initializing2, Initialized2, Part2Initialized, null, null);
                            t.EndState();
                        t.EndRegion();
                        t.Region(Initializing3, false);
                            t.State(Initialized3, (stm, ev, arg) => Console.WriteLine("Enter Initialized3"), (stm, ev, arg) => Console.WriteLine("Exit Initialized3"));
                            t.EndState();
                            t.State(Initializing3, (stm, ev, arg) => Console.WriteLine("Enter Initializing3"), (stm, ev, arg) => Console.WriteLine("Exit Initializing3"));
                                t.Transition(Transi52, Initializing3, Initialized3, Part3Initialized, null, null);
                            t.EndState();
                        t.EndRegion();
                    t.EndState();
                    t.State(SystemInitialized, (stm, ev, arg) => Console.WriteLine("Enter SystemInitialized"), (stm, ev, arg) => Console.WriteLine("Exit SystemInitialized"));
                    t.EndState();
                t.EndRegion();
                //## End StateMachineTemplate66

            StateMachine stateMachine = t.CreateStateMachine();
            stateMachine.TraceStateChange = delegate(StateMachine stm, StateConfiguration from, StateConfiguration to, Transition transi)
                                            {
                                                Console.WriteLine(String.Format("StateChange From={0} To={1} Transition={2}", from.ToString(), to.ToString(), (transi != null) ? transi.Name : "Startup/Finish"));
                                            };

            stateMachine.Startup();
            stateMachine.SendTriggerEvent(Init);
            stateMachine.SendTriggerEvent(Part3Initialized);
            stateMachine.SendTriggerEvent(Part1Initialized);
            stateMachine.SendTriggerEvent(Part2Initialized);
            stateMachine.Finish();

            // The above sequence of events creates the following output:
            //  StateChange From=* To=Uninitialized Transition=Startup/Finish
            //  Enter Unintialized
            //  StateChange From=Uninitialized To=SystemInitializing(Initializing1,Initializing2,Initializing3) Transition=Transi43
            //  Exit Uninitialized
            //  Enter SystemInitializing
            //  Enter Initializing1
            //  Enter Initializing2
            //  Enter Initializing3
            //  StateChange From=SystemInitializing(Initializing1,Initializing2,Initializing3) To=SystemInitializing(Initializing1,Initializing2,Initialized3) Transition=Transi52
            //  Exit Initializing3
            //  Enter Initialized3
            //  StateChange From=SystemInitializing(Initializing1,Initializing2,Initialized3) To=SystemInitializing(Initialized1,Initializing2,Initialized3) Transition=Transi48
            //  Exit Initializing1
            //  Enter Initialized1
            //  StateChange From=SystemInitializing(Initialized1,Initializing2,Initialized3) To=SystemInitializing(Initialized1,Initialized2,Initialized3) Transition=Transi50
            //  Exit Initializing2
            //  Enter Initialized2
            //  StateChange From=SystemInitializing(Initialized1,Initialized2,Initialized3) To=SystemInitialized Transition=Transi57
            //  Exit Initialized3
            //  Exit Initialized2
            //  Exit Initialized1
            //  Exit SystemInitializing
            //  Enter SystemInitialized
            //  StateChange From=SystemInitialized To=* Transition=Startup/Finish
            //  Exit SystemInitialized
        }
    }
}

1 Like

@ Roland - thanks again for carefully crafted response. Thanks

Its all good, makes me want to experiment. What I dont see is how I check the current state of the middle orthogonal state.

There is three simultaneous states in progress, and they can be any state. And what if the two orthogonal states receive the same signal, will only one get it, or both?

I’m hoping you’ll let me participate in this thread in the hopes of becoming user #2. I use state machines as a mater of course for almost all my projects. However, I’m having trouble mapping my understanding and usage to your implementation.

In my current application, I have a state machine implemented in my usual crude way as an switch/case block inside a while loop. Each case is a distinct state.

Each state has a state entry block for stuff that happens once at the beginning of the state, a main body that handles what ever the state needs to do and looks for the the state exit conditions each pass through the while loop, and a state exit block that does the cleanup when transitioning to a new state.

I also have queue for all the event handlers in my program to dump data structures into. Anything in the queue gets taken care of each pass through the while loop. For example, all my serial port event handlers dump their messages (as a data structure) onto the queue when a complete message arrives along with a time stamp and an error code.

This might be a tough question but can I ask how that maps to your state machine implementation?

@ Gene - thanks Gene and welcome.

I guess your approach is ā€œnormalā€ and you would benefit a lot from seperation of concerns. Put your io into threads and model your states with stama. You will find the benefits in your speed to deliver and reduced ā€œweird behaviourā€.

My app is moderately straight forward to explain. It is a profiling float for oceanographic research. Basically a bunch of RS232 instruments stuck on a pressure housing that profiles vertically up and down in the ocean sometimes stopping at a particular depth and sometime anchoring on the bottom. Several times a day the float comes to the surface and telemeters all its data over Iridium…

Here’s what I’m thinking for how I might use StaMa. Make the whole app an event driven state machine. I’ve only got a few events like: 1) a character comes in on a serial port. 2) I’ve reached a certain depth and need to do something like sample an instrument or enter the ā€œparkā€ state 3) One of several timers goes off that says to do something like sample a specific instrument, adjust my buoyancy control engine, stop descending or a couple of other time driven things.

So here’s a specific example to see if stama is appropriate. I’m descending through the water column and the state machine (SM) is in the idle state. The depth sensor spits up one character of its depth message on its serial port. Arrival of the character transitions the SM into the state that calls the method that handles the depth sensor data. If the character that came in is the last character in the depth message, update the current depth value Then back to the idle state. Nothing happens until another character comes in or a timer goes off. Let’s say it’s my buoyancy control timer. That transitions the SM into the buoyancy control method where I do a PID loop on the latest depth and adjust the buoyancy engine. Then back to the idle state and so on for every event that might happen.

What I haven’t been able to figure out is how to get these kind of events to trigger a state transition in stama.

Hello Niels,

Checking for a state can be done through the StateMachine.IsInState method.
The IsInState method requires a so-called StateConfiguration which in turn can be created through the StateMachineTemplate.CreateStateConfiguration method.
Checking if the stateMachine in the previous example is in state ā€œInitializing2ā€ could be acconplished like this:


StateConfiguration stateCfgInitializing2 = t.CreateStateConfiguration(Initializing2);
bool isInInitializing2 = stateMachine.IsInState(stateCfgInitializing2);

The StateConfiguration class generalizes the specification of a state for the case of hierarchy and orthogonality where a single state name isn’t sufficient to uniquely specify the StateMachine current state.
Passing in the single state name creates a ā€œpartiallyā€ defined StateConfiguration that leaves the first and third orthogonal state unspecified.
StateConfiguration instances are constructed separately through the StateMachineTemplate because they are a little more time consuming to build and involve consistency checks.
Once created, the comparison with the StateMachine current state should be quite fast.

Events are propagated to each orthogonal region. All transitions that are active (i.e. the trigger type matches and the guard returns true) are collected and will be executed one after each other according to the defined order.
Active higher level transitions take precendence over nested transitions.

Roland.

1 Like

Hello Gene,

welcome. Your project sounds exciting.

I’m not sure if I understood everything correctly.
Your software reads single bytes from RS232 through the same state machine that controls higher level functions like ascending/descending?
Control of the continuous diving depth is controlled through a state machine with discrete states?
At first sight this sounds unexpected for me, but maybe it is due to lack of understanding and knowledge.
I would expect some separation/layering of lower level component controllers which would act asynchronously and a higher level main operations controller. The higher level operations controller is a clear candidate for a state machine, the lower level component controllers could be implemented through any suitable approach, where applicable also with a state machine.
So I agree with Niels’ reply #24.

I’m not sure if this thread will become messy, if we start discussing the system design of Your application. Maybe this is worth it’s own thread?
Questions regarding StaMa as a library and tool are welcome here.
But this is Niels’ thread.

Roland.

1 Like

Thanks for your reply. I am working on implementing your replies. Have some 4.3 challenges to solve before testing commences. But these are examples of things that would be impossible without you or documentation.

But I MISS the documentation :wink:

Thanks again

@ Roland - Are you planning to port to 4.3 soon?

You have many ā€œ#if !MF_FRAMEWORK_VERSION_V4_2ā€ which probably should be changed to ā€œ#if !MF_FRAMEWORK_VERSION_V4_3ā€ and that should be it…?

Niels,
locally I ā€œportedā€ to NETMF 4.3 with the following results:

  • No adaptions are needed in the code.
  • It doesn’t make sense to frequently update to whatever version comes next. I cannot generate the versions in time as my personal schedule and installation may be different. Clients must be enabled to do it themselves. I described how to do this in the manual.

In next release the StaMaNETMF.csproj defines a generic version independent symbol MF_FRAMEWORK. Clients may switch to whatever framework version they use and recompile the assembly.
You don’t have to wait, You could do that today if needed:

  1. Replace MF_FRAMEWORK_VERSION_V4_2 with MF_FRAMEWORK in all source code files.
  2. Additionally set MF_FRAMEWORK in the project properties ā€œConditional Compilation Symbolsā€ field.
  3. Select the .NET Micro Framework 4.3 in the project properties ā€œTarget Frameworkā€.
  4. Recompile.
    Please don’t expect the documentation before end of September. Sorry I have to say this. It is a spare time project and the time slots for it are not the most productive ones of the day.
    Roland.

Gene,

from my perspective I see now 2 basic kinds of state machine applications:

  1. Event driven state machines used to coordinate asynchronous operations in ā€œcommonā€ applications
  2. Open loop control state machines that are executed in an endless cycle and read input variables and write output variables, like in programmable logic controller (PLC) applications.

In case Your application is an open loop controller You would have to do the following to integrate StaMa:

  • Setup the StateMachineTemplate and instantiate the StateMachine in the initialization phase.
  • Use only transition guards, don’t use signals at the transitions.
  • Invoke StateMachine.SendTriggerEvent(null) in every cycle to trigger evaluation of the transition guards. In case the guard of a transition returns true, the transition will execute.
  • Invoke the state ā€œdo-activitiesā€ in every cycle. do-activities are not (yet) an integral part of StaMa, however a workaround exists:
    There is an API that traverses each state of the active state configuration through a visitor pattern.
    A switch-case statement or whatever can be used to execute code specific for each particular state.

The below Windows console application shows how this would actually work.
The application cycles with 100ms and reads input and writes text.
The state machine has 2 states. It switches from StateA to StateB when the B key is pressed and falls back to StateA when the do-activity counter in StateB reaches 20.


using System;
using System.Runtime.InteropServices;
using StaMa;

namespace ControlLoop
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Console.WriteLine(@ "Hello");

            bool aPressed = false;
            bool bPressed = false;
            bool rShiftKeyPressed = false;
            int counter = 0;

            // Initialize the (here simple) state machine structure
            StateMachineTemplate t = new StateMachineTemplate();
            t.Region("StateA", false);
                t.State("StateA", null, null);
                    t.Transition("T1", "StateA", "StateB", null, (stm, ev, args) => bPressed, null);
                t.EndState();
                t.State("StateB", null, (stm, ev, args) => counter = 0);
                    // t.StateDoActivity((stm) => counter += 1); Proposal how to configure do-activities
                    t.Transition("T2", "StateB", "StateA", null, (stm, ev, args) => counter == 20, null);
                t.EndState();
            t.EndRegion();

            // Define do-activities to be executed while the state machine is in the state
            DoActions doActions = new DoActions(delegate(string stateName)
                                                {
                                                    switch (stateName)
                                                    {
                                                        default:
                                                            break;
                                                        case "StateB":
                                                            counter += 1;
                                                            break;
                                                    }
                                                });

            // Init state machine instance
            StateMachine stateMachine = t.CreateStateMachine();
            stateMachine.Startup();

            bool runLoop = true;
            while (runLoop)
            {
                // Get some input
                aPressed = GetKeyPressed(ConsoleKey.A);
                bPressed = GetKeyPressed(ConsoleKey.B);
                rShiftKeyPressed = GetKeyPressed((ConsoleKey)0xa1);

                // Throw away unneccessary characters
                while (Console.KeyAvailable)
                {
                    Console.ReadKey(true);
                }

                // Execute the state machine
                stateMachine.SendTriggerEvent(null);
                StateConfiguration currentState = stateMachine.ActiveStateConfiguration;

                // Execute the do-activities of the active states
                currentState.PassThrough(doActions);

                // Write info about current state
                Console.Write(String.Format("{0}              \r", currentState.ToString()));

                // Throttle speed
                System.Threading.Thread.Sleep(100);
            }
        }


        private class DoActions : IStateConfigurationVisitor
        {
            private Action<string> myDoActions;

            public DoActions(Action<string> doActions)
            {
                myDoActions = doActions;
            }

            void IStateConfigurationVisitor.BeginSubStates()
            {
            }

            void IStateConfigurationVisitor.EndSubStates()
            {
            }

            void IStateConfigurationVisitor.NextSubState()
            {
            }

            void IStateConfigurationVisitor.State(string stateName)
            {
                myDoActions(stateName);
            }

            void IStateConfigurationVisitor.StateAny()
            {
                throw new NotImplementedException();
            }
        }


        [DllImport("User32.dll")]
        private static extern ushort GetAsyncKeyState(int vKey);

        private static bool GetKeyPressed(ConsoleKey key)
        {
            return (GetAsyncKeyState((int)key) & 0xF000) != 0;
        }
    }
}

The switch/case doesn’t look nice. But this is a sample to demonstrate that programmable logic controller like execution can be supported in StaMa.
Does this help?
Roland.

1 Like

This is a lot of help. I’m at the tail end of getting my current switch/case statement state machine with asynchronous queue for IO fully tested. When things calm down a little bit I’m planning on taking a good look at your StaMa.

Thanks for the help.

Cheers - Gene

@ Roland Schneider - This is exceptionally cool stuff, I did not even imagine how cool this is.

Thanks

@ Roland Schneider

Hi Roland.

We’re starting a new project using your amazing StaMa library.

Since we’d like to add some small changes (e.g. Transition parameter on TransitionActivity and TransitionGuard delegates, in order to promote usage of generic code), do you accept pull requests or similar in order to (validate and) merge 3rd party contributions?

Also, as you surely know, Visio running on x64 doesn’t allow use of Scripting COM component, so it’s very hard to get your VBA code running. Did you ever try any alternative code generation technique?

We would be also interested in scenarios, best practices and patterns concerning usage of StaMa framework in automation ā€œspaceā€,so it would be great if you could provide some reference sample related to, for example, coordination among async and partially inter-dependent automas.

Thanks!

1 Like

Hello www.innovactive.it (?),

thank You very much for Your interest in StaMa.

Of course You are welcome to make proposals for improvement.
Obviously the fork button is missing in the StaMa Codeplex source code page, other pages have it.
Do You know how I can enable it? I’d gladly turn that functionality on, if possible.
[Update 2014-12-10] Fork of TFS based projects is not supported, see http://codeplex.codeplex.com/discussions/355769. Would require to convert to Git or Mercurial.

Regarding parameters for transitions and guards: There is already an overload StateMachine.SendTriggerEvent(Object,EventArgs) which forwards an additional EventArgs parameter to the StateMachineGuardCallback or StateMachineActivityCallback.
Could You use this?

A long time ago I tried out making StaMa more typesafe with generic parameters, but it ended that nearly all methods or classes throughout StaMa required this generic parameter. So I discarded this change, as it makes so much of the source code lengthy.
Further NETMF doesn’t support generics (or?), therefore introducing generics would require to deliver separate code for NETMF and .NET Framework for Windows.
But generally I’m open towards generics in case a majority wants it.

Visio is a controversial tool.
Definitely it would be nice to have a freeware tool for drawing and generating code.
Could You use the 32bit version of Visio? From my point of view there is no advantage in running MS Office applications as 64bit.

Unfortunately I cannot provide more complex samples or specific samples for a technical domain.
I’m currently preparing a developers guide that explains the library in more detail, however the documentation doesn’t contain more complex samples.
Eventually Niels has something that helps You.

Roland.

1 Like

@ Roland Schneider - The version I saw of the developer guide, where state of the art documentation, and deserves a release! :slight_smile:

I dont have problems with Visio on 32bit, and its the same license…

Looking forward to more updates from you…

StaMa State Machine Controller Library 2.2 is released and targets NETMF 4.3 / VS2012

The documentation for the library and Visio shapes is included in the package as a .chm file and available through http://StaMa.org/ .

Please have a look into it and report at StaMa home if something is unclear or needs improvement.

@ Gene: StaMa now allows to specify do-actions at the state definition.
Do-actions are executed within the StateMachine.SendTriggerEvent method.
Periodically calling SendTriggerEvent in a loop with e.g. 100ms should allow to execute an open or closed loop control algorithm within the do-actions, like described above.
Do-actions have to be enabled explicitly at the StateMachineTemplate constructor.

Thanks Niels for contributing the stama.org domain.

Roland.

2 Likes

Now there is no excuse for getting started with state machines and increase the quality of your projects. :slight_smile:

This might be of interest to you: http://www.visual-paradigm.com/support/documents/vpuserguide/94/2579/6714_creatingstat.html