Click here to Skip to main content
Click here to Skip to main content

A .NET State Machine Toolkit - Part III

By , 26 Oct 2006
 

Contents

Introduction

This is the third and final part in my series of articles about my .NET State Machine Toolkit. In Part I, I introduced the classes that make up the toolkit and demonstrated how to create a simple, flat state machine. In Part II, I discussed some of the advanced features of the toolkit and demonstrated how to create a hierarchical state machine. In this part, we will look at how to use code generation to create state machines.

Note: based on feedback, since originally submitting my article from Ramon Smits, I've vastly improved the toolkit's XML support. It now uses XML serialization directly instead of relying on a DataSet to read and write state machines as XML data. In addition, the XML schema has been greatly simplified. Many thanks to Ramon for his helpful suggestions and providing code to demonstrate his ideas.

top

The StateMachineBuilder class

Code generation is accomplished through the StateMachineBuilder class. This class follows the Builder design pattern. With this design pattern, an object is constructed in steps using a builder object. After all the necessary steps have been taken, the builder is instructed to build the object, usually by calling a Build method. After which the built object can be retrieved and used. This pattern helps break down the complex construction of an object into discrete steps. It also enables you to use the same construction process repeatedly to get different representations. With the StateMachineBuilder class, you build a CodeDom CodeNamespace object. The namespace contains an abstract class representing the state machine. This class serves as a base class for the class that you will write.

top

A Recursive State Machine Table

Originally, the StateMachineBuilder class used classes from ADO.NET for representing state machine data. Simple DataTables represented states, events, guards, etc. More complex DataTables represented state transitions, substate/superstate relationships, and so on. One-to-many relationships were established between the simple tables and the more complex tables. Basically, it was an in-memory relational database for representing state machines. Pretty nifty, or so I thought...

There were problems with this approach. The main one was that I couldn't enforce all of the rules for declaring a hierarchical state machine through data constraints alone. It was possible to enter illegal combinations of values in the tables. For example, you could declare a state to be a substate of one state and a superstate to that same state. Since a state cannot be a substate of one state and a superstate to the same state, this was nonsense. I was trying to make a relational database do the job of a compiler, and it wasn't working. In addition, the XML generated by the DataSet was overly verbose. A better approach was needed.

Instead of using a large number of DataTables to create a relational database, the StateMachineBuilder class now uses four custom classes for keeping track of states, events, guards, actions, transitions, etc. The classes are:

  • StateRow
  • StateRowCollection
  • TransitionRow
  • TransitionRowCollection

The StateRow class represents a single state. The StateRowCollection class represents a collection of StateRows. You can think of the StateRowCollection as a table of states. The TransitionRow and TransitionRowCollection classes together represent a state's transitions.

Let's look at the StateRow class first. It has the following properties:

  • Name
  • InitialState
  • HistoryType
  • Substates
  • Transitions

The Name property is the name of the state. The InitialState property is the state's initial state. If the state does not have any substates, this property is ignored when the state machine is built. The HistoryType property is the state's history type, obviously. This property is also ignored if the state does not have any substates. These three properties can be thought of as three columns in the state table.

The Substates property is interesting. It represents a StateRowCollection object. So if we can think of a collection of StateRows as belonging to a table, this property is a kind of table within a table. StateRows can be added to the Substates property, and in turn those StateRows can have StateRows added to their Substates property, and so on. This forms a tree like structure in which there is a top level of states, states that do not have a superstate, and branches descending from them representing their substates.

The Transitions property represents a TransitionRowCollection object. A state's transitions are added to this property. So each StateRow contains a table of its transitions.

Each of these classes have XML serialization attributes describing how they should be serialized as XML data. In addition, the StateRowCollection class and the TransitionRowCollection class can be data bound to a control such as the DataGrid. This makes it easy to create a GUI front end for creating state machines with the StateMachineBuilder class.

The StateMachineBuilder class has a States property representing a StateRowCollection object. It is the root of the state hierarchy. Once all of the states and their transitions have been added to the StateMachineBuilder, the state machine base class can be built. As stated earlier, the result is a CodeDom CodeNamespace object.

top

Generating Code

Let's look at some code that uses the StateMachineBuilder class to build the traffic light state machine described in Part II and display the results:

using System;
using System.Data;
using System.IO;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Xml.Serialization;
using Sanford.StateMachineToolkit;

namespace StateMachineBuilderDemo
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            try
            {
                StateMachineBuilder builder = new StateMachineBuilder();

                builder.NamespaceName = "StateMachineDemo";
                builder.StateMachineName = "TrafficLightBase";
                builder.InitialState = "Off";

                builder.States.Add("Disposed");

                int index = builder.States.Add("Off");
                builder.States[index].Transitions.Add("TurnOn", null, "On");
                builder.States[index].Transitions.Add("Dispose", 
                                              null, "Disposed");

                index = builder.States.Add("On", "Red", HistoryType.Shallow);
                builder.States[index].Transitions.Add("TurnOff", null, "Off");
                builder.States[index].Transitions.Add("Dispose", 
                                              null, "Disposed");

                StateRowCollection substates = builder.States[index].Substates;

                index = substates.Add("Red");
                substates[index].Transitions.Add("TimerElapsed", null, "Green");

                index = substates.Add("Yellow");
                substates[index].Transitions.Add("TimerElapsed", null, "Red"); 

                index = substates.Add("Green");
                substates[index].Transitions.Add("TimerElapsed", null, "Yellow");
                
                builder.Build();
                
                StringWriter writer = new StringWriter();

                CodeDomProvider provider = new CSharpCodeProvider();
                ICodeGenerator generator = provider.CreateGenerator();
                CodeGeneratorOptions options = new CodeGeneratorOptions();

                options.BracingStyle = "C";

                generator.GenerateCodeFromNamespace(builder.Result, 
                                                  writer, options);

                writer.Close();

                Console.Read();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.Read();
            }
        }
    }
}

Here is the generated code:

namespace StateMachineDemo
{


    public abstract class TrafficLightBase : 
           Sanford.StateMachineToolkit.ActiveStateMachine
    {

        private Sanford.StateMachineToolkit.State stateDisposed;

        private Sanford.StateMachineToolkit.State stateOff;

        private Sanford.StateMachineToolkit.State stateOn;

        private Sanford.StateMachineToolkit.State stateRed;

        private Sanford.StateMachineToolkit.State stateYellow;

        private Sanford.StateMachineToolkit.State stateGreen;

        public TrafficLightBase()
        {
            this.Initialize();
        }

        private void Initialize()
        {
            this.InitializeStates();
            this.InitializeGuards();
            this.InitializeActions();
            this.InitializeTransitions();
            this.InitializeRelationships();
            this.InitializeHistoryTypes();
            this.InitializeInitialStates();
            this.Initialize(this.stateOff);
        }

        private void InitializeStates()
        {
            Sanford.StateMachineToolkit.EntryHandler enDisposed = 
              new Sanford.StateMachineToolkit.EntryHandler(this.EntryDisposed);
            Sanford.StateMachineToolkit.ExitHandler exDisposed = 
              new Sanford.StateMachineToolkit.ExitHandler(this.ExitDisposed);
            this.stateDisposed = new Sanford.StateMachineToolkit.State(
              ((int)(StateID.Disposed)), enDisposed, exDisposed);
            Sanford.StateMachineToolkit.EntryHandler enOff = 
              new Sanford.StateMachineToolkit.EntryHandler(this.EntryOff);
            Sanford.StateMachineToolkit.ExitHandler exOff = 
              new Sanford.StateMachineToolkit.ExitHandler(this.ExitOff);
            this.stateOff = new Sanford.StateMachineToolkit.State(
              ((int)(StateID.Off)), enOff, exOff);
            Sanford.StateMachineToolkit.EntryHandler enOn = 
              new Sanford.StateMachineToolkit.EntryHandler(this.EntryOn);
            Sanford.StateMachineToolkit.ExitHandler exOn = 
              new Sanford.StateMachineToolkit.ExitHandler(this.ExitOn);
            this.stateOn = new Sanford.StateMachineToolkit.State(
              ((int)(StateID.On)), enOn, exOn);
            Sanford.StateMachineToolkit.EntryHandler enRed = 
              new Sanford.StateMachineToolkit.EntryHandler(this.EntryRed);
            Sanford.StateMachineToolkit.ExitHandler exRed = 
              new Sanford.StateMachineToolkit.ExitHandler(this.ExitRed);
            this.stateRed = new Sanford.StateMachineToolkit.State(
              ((int)(StateID.Red)), enRed, exRed);
            Sanford.StateMachineToolkit.EntryHandler enYellow = 
              new Sanford.StateMachineToolkit.EntryHandler(this.EntryYellow);
            Sanford.StateMachineToolkit.ExitHandler exYellow = 
              new Sanford.StateMachineToolkit.ExitHandler(this.ExitYellow);
            this.stateYellow = new Sanford.StateMachineToolkit.State(
              ((int)(StateID.Yellow)), enYellow, exYellow);
            Sanford.StateMachineToolkit.EntryHandler enGreen = 
              new Sanford.StateMachineToolkit.EntryHandler(this.EntryGreen);
            Sanford.StateMachineToolkit.ExitHandler exGreen = 
              new Sanford.StateMachineToolkit.ExitHandler(this.ExitGreen);
            this.stateGreen = new Sanford.StateMachineToolkit.State(
              ((int)(StateID.Green)), enGreen, exGreen);
        }

        private void InitializeGuards()
        {
        }

        private void InitializeActions()
        {
        }

        private void InitializeTransitions()
        {
            Sanford.StateMachineToolkit.Transition trans;
            trans = new Sanford.StateMachineToolkit.Transition(null, 
                                                  this.stateYellow);
            this.stateGreen.Transitions.Add(((int)(EventID.TimerElapsed)), 
                                                                  trans);
            trans = new Sanford.StateMachineToolkit.Transition(null, 
                                                      this.stateOn);
            this.stateOff.Transitions.Add(((int)(EventID.TurnOn)), trans);
            trans = new Sanford.StateMachineToolkit.Transition(null, 
                                                this.stateDisposed);
            this.stateOff.Transitions.Add(((int)(EventID.Dispose)), trans);
            trans = new Sanford.StateMachineToolkit.Transition(null, 
                                                     this.stateOff);
            this.stateOn.Transitions.Add(((int)(EventID.TurnOff)), trans);
            trans = new Sanford.StateMachineToolkit.Transition(null, 
                                                this.stateDisposed);
            this.stateOn.Transitions.Add(((int)(EventID.Dispose)), trans);
            trans = new Sanford.StateMachineToolkit.Transition(null, 
                                                   this.stateGreen);
            this.stateRed.Transitions.Add(((int)(EventID.TimerElapsed)), 
                                                                 trans);
            trans = new Sanford.StateMachineToolkit.Transition(null, 
                                                     this.stateRed);
            this.stateYellow.Transitions.Add(((int)(EventID.TimerElapsed)), 
                                                                    trans);
        }

        private void InitializeRelationships()
        {
            this.stateOn.Substates.Add(this.stateGreen);
            this.stateOn.Substates.Add(this.stateRed);
            this.stateOn.Substates.Add(this.stateYellow);
        }

        private void InitializeHistoryTypes()
        {
            this.stateDisposed.HistoryType = 
              Sanford.StateMachineToolkit.HistoryType.None;
            this.stateGreen.HistoryType = 
              Sanford.StateMachineToolkit.HistoryType.None;
            this.stateOff.HistoryType = 
              Sanford.StateMachineToolkit.HistoryType.None;
            this.stateOn.HistoryType = 
              Sanford.StateMachineToolkit.HistoryType.Shallow;
            this.stateRed.HistoryType = 
              Sanford.StateMachineToolkit.HistoryType.None;
            this.stateYellow.HistoryType = 
              Sanford.StateMachineToolkit.HistoryType.None;
        }

        private void InitializeInitialStates()
        {
            this.stateOn.InitialState = this.stateRed;
        }

        protected virtual void EntryDisposed()
        {
        }

        protected virtual void EntryOff()
        {
        }

        protected virtual void EntryOn()
        {
        }

        protected virtual void EntryRed()
        {
        }

        protected virtual void EntryYellow()
        {
        }

        protected virtual void EntryGreen()
        {
        }

        protected virtual void ExitDisposed()
        {
        }

        protected virtual void ExitOff()
        {
        }

        protected virtual void ExitOn()
        {
        }

        protected virtual void ExitRed()
        {
        }

        protected virtual void ExitYellow()
        {
        }

        protected virtual void ExitGreen()
        {
        }

        public enum EventID
        {

            TurnOn,

            Dispose,

            TurnOff,

            TimerElapsed,
        }

        public enum StateID
        {

            Disposed,

            Off,

            On,

            Red,

            Yellow,

            Green,
        }
    }
}

Yes, the code is ugly and verbose. This is due in part to the fully qualified names CodeDom is using. However, this is a code you never have to touch or look at. The class generated is the base class from which you derive your own state machine class. The advantage of this approach is that if you need to change the state machine, such as adding an event, you can regenerate the code and your derived class is not touched, only the base class is regenerated. You may need to make some minor tweaks to your derived class depending on what changes you make, but your implementation is not overwritten.

The entry and exit methods are made virtual with do-nothing implementations. This in effect makes them optional. In your derived class, if you need to add behavior for entry and/or exit actions, you can override the methods you need and implement the behavior. The guard and action methods, however, are abstract. You must override these.

Here is the new TrafficLight class. It is derived from the TrafficLightBase generated by the StateMachineBuilder:

using System;
using Sanford.Threading;
using Sanford.StateMachineToolkit;

namespace StateMachineDemo
{
    public class TrafficLight : TrafficLightBase
    {
        private DelegateScheduler scheduler = new DelegateScheduler();

        public TrafficLight()
        {
        }

        #region Entry/Exit Methods

        protected override void EntryOn()
        {
            scheduler.Start();
        }

        protected override void EntryOff()
        {
            scheduler.Stop();
            scheduler.Clear();
        }

        protected override void EntryRed()
        {
            scheduler.Add(1, 5000, new SendTimerDelegate(SendTimerEvent));
        }

        protected override void EntryYellow()
        {
            scheduler.Add(1, 2000, new SendTimerDelegate(SendTimerEvent));
        }

        protected override void EntryGreen()
        {
            scheduler.Add(1, 5000, new SendTimerDelegate(SendTimerEvent));
        }

        protected override void EntryDisposed()
        {
            scheduler.Dispose();

            Dispose(true);
        }

        #endregion

        public override void Dispose()
        {
            #region Guard

            if(IsDisposed)
            {
                return;
            }

            #endregion

            Send((int)EventID.Dispose);            
        }

        private delegate void SendTimerDelegate();

        private void SendTimerEvent()
        {
            Send((int)EventID.TimerElapsed);
        }
    }
}

Compare this version with the version in Part II. All of the code for creating and initializing the State objects as well as their Transitions is hidden away in the base class.

top

Hierarchical State Machines in XML

The StateMachineBuilder class can be serialized as XML data. This lets you save state machine values and retrieve them later. Before looking at an XML representation of the traffic light state machine, let's look at the XML structure the toolkit uses to represent hierarchical state machines. We will examine each element and their attributes.

The root element is stateMachine, and it has three attributes:

  • namespace
  • name
  • initialState

The namespace attribute is the name of the namespace in which the state machine class resides. The name attribute is the name of the state machine. And the initialState attribute is the initial state of the state machine. The value of the initialState attribute must be one of the top level states. A top level state is a state that does not have a superstate; it exists at the top of the state hierarchy. Not surprisingly, states are represented by the state element. It has three attributes:

  • name
  • initialState
  • historyType

The name attribute is the name of the state. The initialState attribute is the initial state of the state; if the state has any substates, the initialState attribute represents which of its substates is entered after it is entered. And the historyType attribute represents the state's history type. It can have one of three values, None, Shallow, and Deep. If a state does not have any substates, the initialState and the historyType attributes are ignored. Otherwise the initialState attribute is required. The historyType attribute is optional, and if it is not present, the state will default to a history type value of None.

States can be nested inside other states. A nested state is the substate of the state that contains it, and it in turn can have nested states. Thus substate/superstate relationships are represented directly in the XML state machine structure.

State transitions are represented by the transition element. Transitions are nested inside the states to which they belong. The transition element has four attributes:

  • event
  • guard
  • action
  • target

The event attribute represents the event that triggered the transition. The guard attribute represents the guard that is evaluated to determine whether or not the transition should actually take place. The action attribute is the action that should be performed if the transition takes place. And the target attribute is the state target of the transition. All of the attributes are optional except for the event attribute. It must be present in all transitions.

To serialize a state machine, you would first build it with the StateMachineBuilder as we did above with the traffic light state machine. Then serialize the builder with the XmlSerializer class:

// ...



using System.Xml.Serialization;

// ...



builder.Build();

StringWriter writer = new StringWriter();
XmlSerializer serializer = 
   new XmlSerializer(typeof(StateMachineBuilder));
serializer.Serialize(writer, builder);
Console.WriteLine(writer.ToString());
writer.Close();

// ...

Here, we serialized the StateMachineBuilder to a StringWriter object so that we can display the resulting XML to the Console. This is the result of serializing the traffic light state machine:

<?xml version="1.0" encoding="utf-16"?>
<stateMachine xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      namespace="StateMachineDemo" name="TrafficLightBase" 
      initialState="Off">
  <state name="Disposed" historyType="None" />
  <state name="Off" historyType="None">
    <transition event="TurnOn" target="On" />
    <transition event="Dispose" target="Disposed" />
  </state>
  <state name="On" initialState="Red" historyType="Shallow">
    <state name="Red" historyType="None">
      <transition event="TimerElapsed" target="Green" />
    </state>
    <state name="Yellow" historyType="None">
      <transition event="TimerElapsed" target="Red" />
    </state>
    <state name="Green" historyType="None">
      <transition event="TimerElapsed" target="Yellow" />
    </state>
    <transition event="TurnOff" target="Off" />
    <transition event="Dispose" target="Disposed" />
  </state>
</stateMachine>

As you can see, the XML schema is straightforward and simple enough so that you can even declare a state machine in XML by hand.

top

The State Machine Maker

Included with the demo project is a program that provides a nice GUI for using the StateMachineBuilder class. It's easy to use. Simply enter the values into the DataGrid, build the state machine, and save the results as either C# or VB code. If you want to save the state machine values for editing later, you can save the data as an XML file. I'll explain how to use the State Machine Maker application below.

The State Machine Maker has three text boxes for setting the state machine's namespace, name, and initial state respectively. The important thing to note here is that you will get an error if you forget to enter an initial state. Every state machine must have an initial state it enters into when it is first run.

In addition, there is a DataGrid control where you add states and their transitions. The DataGrid is data bound to the StateMachineBuilder's States property so that entries made to the DataGrid are added to the StateMachineBuilder automatically. Initially, you will enter the top level states for the state machines; these are states that do not have a superstate.

After entering a top level state, you can add its substates by expanding its row and clicking the Substates link:

There you will be taken to its Substates table:

After adding the substates, you can navigate back to the State table by clicking on the navigation arrow:

A state's transitions are added the same way, only you click on the Transitions link. This takes you to the state's Transition table:

Once all of the states and their transitions have been added, you can build the state machine. An error message will be displayed if the build failed. For example, say that you forgot to enter a state's name:

If the build succeeded, you'll get a message letting you know:

After the build, you can save the results as C# or VB code:

When you save the results as code, it will save the results from the last build, not the last edit. In other words, be sure to remember to build the state machine immediately before saving it as code. You may make a change to the state machine after a build and forget this when saving it to code and wonder why your last edit isn't showing up.

top

Dependencies

Be sure to read the dependencies section in Part I.

top

Conclusion

Well, this wraps up the last article in the series. With the second version of the toolkit, I'm now comfortable with it overall. While the engine was something that I worked hard on and was satisfied with, aspects of the code generation process still felt rough around the edges to me. With some help from a fellow CP'ian, that is no longer the case. I now feel that support for code generation is up to the same level of quality as the rest of the toolkit. And I hope you find it useful. Thanks for your time.

top

History

  • September 21, 2005 - First version completed.
  • October 5, 2005 - Second version completed. Major rewrite of the article and reworking of the StateMachineBuilder class.
  • October 25, 2005 - Third version completed. Article revised.
  • May 15, 2006 - Version 4.1 completed. Article revised.
  • October 21, 2006 - Version 5 completed. Article revised.

top

License

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

About the Author

Leslie Sanford
United States United States
Member
Aside from dabbling in BASIC on his old Atari 1040ST years ago, Leslie's programming experience didn't really begin until he discovered the Internet in the late 90s. There he found a treasure trove of information about two of his favorite interests: MIDI and sound synthesis.
 
After spending a good deal of time calculating formulas he found on the Internet for creating new sounds by hand, he decided that an easier way would be to program the computer to do the work for him. This led him to learn C. He discovered that beyond using programming as a tool for synthesizing sound, he loved programming in and of itself.
 
Eventually he taught himself C++ and C#, and along the way he immersed himself in the ideas of object oriented programming. Like many of us, he gotten bitten by the design patterns bug and a copy of GOF is never far from his hands.
 
Now his primary interest is in creating a complete MIDI toolkit using the C# language. He hopes to create something that will become an indispensable tool for those wanting to write MIDI applications for the .NET framework.
 
Besides programming, his other interests are photography and playing his Les Paul guitars.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
BugProblem with Relationships order because it is sortedmembermartinhutterschule7 Nov '11 - 5:15 
I had to use a Dictionary instead of the SkipList. Because the SkipList sorts the Initialisation code depending its Name. That's why the childs can be initialized before its parent States.
 

Class:StateMachineBuider
 
Method:Build
 
//IDictionary stateRelationships = new SkipList();
 
IDictionary stateRelationships = new System.Collections.Generic.Dictionary();
QuestionWhat does History option do?memberMember 484703421 Oct '10 - 4:45 
You posted this code in 2005, and 5 years later it is still as relevant as ever! Many thanks for taking the time to do it. I am using the Maker but I am unsure what effect the History option has on the state machine? What is the difference btw Shallow, Deep, None? In which situations should I set it?
 
Rgds
 
Splat
AnswerRe: What does History option do?memberMember 484703421 Oct '10 - 21:07 
Ignore my question, the answer lies in part 2 of the post: A .NET State Machine Toolkit - Part II
GeneralAmazingmemberbluerutas24 Jul '08 - 10:18 
I was in the design phase of implementing a very very similiar toolkit while i found this. Have you used this for any real world working projects and/or is it a stabilised toolkit? Can you please let me know if you are planning on any commerical version? Just FYI, there is a commerical version by UnitCircle[^]Thanks.
GeneralRe: AmazingmemberLeslie Sanford24 Jul '08 - 11:07 
bluerutas wrote:
I was in the design phase of implementing a very very similiar toolkit while i found this. Have you used this for any real world working projects and/or is it a stabilised toolkit? Can you please let me know if you are planning on any commerical version? Just FYI, there is a commerical version by UnitCircle.

 
Hi. Thanks for your comments.
 
I haven't used the toolkit much since I wrote it, to be honest. I haven't found much use for hierarchical state machines in my code. When I need a state machine, a flat one with a half-dozen states at most usually does the trick. So rather than use the toolkit with all of its scafolding, I find it simpler to hand code simple state machines.
 
That's not to say that my toolkit may not be useful to others who's domain requires hierarchical state machines; it's just that my path has taken me in a different direction since writing the toolkit.
 
So I don't know how stable it is, actually. Smile | :) It's not had a lot of testing beyond the initial testing I did while writing it.
GeneralRe: Amazingmemberbluerutas24 Jul '08 - 13:48 
Thanks for that info. I think I'm going to find great use for your toolkit..however, I will simplify it for synchronous execution only and alter the behaviour quite a bit with several constraints and such..but your layout and collections will come extremely handy. So, once again, thanks a bunch..but have to say you got commerical quality work here, and if you could plug in a simple GUI, you can evolve this into a pretty competitive UML tool. Smile | :)
QuestionAction parametersmemberNikus739 Oct '07 - 4:07 
Hi,
Thank's for job ... that run well !!!
I still don't understand one think :
How can I call a transition action with parameters ?
How can I write it in the StateMachineMaker ?
 
Regards
 
Nikus
GeneralGraphical designer (UML based)memberdkuttel23 Aug '07 - 1:36 
Hello,
 
Could you think that we can easily use your StateMachineBuilder to implement a graphical designer ?
 
I think it will not be too complicated using a graphical library (e.g. GoDiagramm Express).
 
Interested in such a tool ?
 
Bye
Didier
GeneralRe: Graphical designer (UML based)memberLeslie Sanford23 Aug '07 - 14:50 
dkuttel wrote:
Could you think that we can easily use your StateMachineBuilder to implement a graphical designer ?

 
Since the toolkit uses XML, a graphical designer capable of generating XML the toolkit could read might be the best way to approach it.
GeneralRe: Graphical designer (UML based)memberkrn_2k9 Apr '08 - 6:13 
A reference might be of interest that appears to do just this: Visual Finite State Machine AI Systems[^]
 
"The elements of our system begin with Microsoft Visio, which is the front-end GUI that will be used to layout state machine diagrams. The FSM stencil is a document maintained by the programmer and created to define the shapes which designers will be using in their diagrams. Once the FSM has been laid out by a designer, it will be saved as an XML document. This is the final build asset for the game."
GeneralAction in OnEntry/OnExitmemberdkuttel23 Aug '07 - 1:33 
Hello,
Thanks for your work, it is running nicely.
 
As far as it is nice to see the different actions performed in OnEntry/OnExit at the state machine definition (instead defining them in overrided methods), do you think it will be possible to easily change the callback to one method (xEntry, xExit) to a sequence of methods (actions defined in OnEntry/OnExit) ?
 
Thanks
Bye
Didier
GeneralRe: Action in OnEntry/OnExitmemberLeslie Sanford23 Aug '07 - 14:49 
dkuttel wrote:
do you think it will be possible to easily change the callback to one method (xEntry, xExit) to a sequence of methods (actions defined in OnEntry/OnExit) ?

 
Hmm, it probably wouldn't be too hard. The toolkit supports multiple actions on each transition, so the same approach could be applied to entry and exit events.
GeneralUsing the State Machine Makermembersnebel23 Jul '07 - 4:11 
Hi Leslie,
 
after playing around with the state machine a bit I must say that I find this very useful and very well done.
Nevertheless, when using the State Machine Maker, I am not able to generate code that works. To go into details: when building passive state machines the genereated code is producing an abstract class, from which one can not create an instance of course. As the "guards" are also abstact I wonder if the State Machine Maker was ever used by someone to generate passive machines... Wink | ;)
 
Cheers
Sascha
 

GeneralRe: Using the State Machine MakermemberLeslie Sanford23 Jul '07 - 5:29 
snebel wrote:
Nevertheless, when using the State Machine Maker, I am not able to generate code that works. To go into details: when building passive state machines the genereated code is producing an abstract class, from which one can not create an instance of course.

 
This is by design. The State Machine Maker generates an abstract class from which you derive your own class that overrides the guards. In addition, virtual entrance and exit methods are provided that you may override. In other words, if you specify guards in your design, you must override them in your derived class but entrance and exit methods are optional.
 
Look at the "Generating Code"[^] section in the article. First, it shows setting up the StateMachineMaker in code. Second, it shows the generated code, which is the abstract base class from which you derive your state machine by hand. And finally, it shows the derived class.
 
The advantage of this approach is that you can regenerate your code after making changes while minimizing the impact of those changes on your hand written code. This is covered in the article.
GeneralRe: Using the State Machine Makermembersnebel23 Jul '07 - 22:41 
I sould have read your article further instead of playing around without knowing how to use the state machine maker... thank you again for your fast answer!
GeneralNewbie Can't Compilememberdbarchitect6 Jul '06 - 6:41 
I am a long-time C programmer (UNIX environment) trying to learn C#. I liked your article(s) and was looking forward to compiling and playing with it to learn more, but the solution won't compile for me. I get a lot of unresolved references, but they all seem to boil down to a couple of missing namespaces and/or classes: namespace LSCollections and class PriorityQueue. A search of all the files in the project can't find them.
 
What am I missing?
 
Thanks
GeneralRe: Newbie Can't CompilememberLeslie Sanford6 Jul '06 - 7:46 
Unfortunately, you have to explicitely add the LSCollections assembly to the projects. The LSCollections.dll file is included with the zip file.
 
This is a pain, but I haven't found a way to share solutions without requiring that users add the LSCollection assembly themselves. I've tried leaving the assembly in the obj and bin folders with other solutions I've shared here at Code Project, but then there seems to be a problem with reference paths. It's all confusing, and I'm sorry for the trouble. Just add the LSCollections assembly to all of the projects in the solution, and you'll be fine.
 
Let me know if you need any more help.
Generalsteed.net Litemembermarc.thom8 Jun '06 - 4:15 
Hello,
for your interest look at www.steed.de there is a lite version of a statemachine designer
integrated in visual studio.
 
greetings marc
QuestionHave you seen this?memberdzCepheus23 Mar '06 - 7:06 
Windows Workflow Foundation[^]
 
This is a system Microsoft has added to WinFX to allow people to create workflow applications in .NET. You should check it out - it's in the same scope as your project.
 
PS: This is what part of the alphabet would look like if the letters Q and R were removed.
AnswerRe: Have you seen this?memberLeslie Sanford23 Mar '06 - 7:37 
I've heard about it, but haven't explored it.
GeneralHSM CollectionmemberHoodsmack19 Mar '06 - 2:57 
Very nice work with your articles and toolkit. Thank you for sharing it.
 
I have read (based on your suggestion) the Harel article. I think your HSM providing Xor qualities will most likely support the problem domain I am in. If it turns out that I need Xand (saw the other thread on Orthogonality btw), I was wondering your thoughts on this potential solution:
 
Have a collection of hierarchical state machines, with relationships declared between separate state machine's states that define those independent states as being siblings. While not a trivial change to the toolkit, as well as the Maker UI, what would you think of that as a solution to have Xand capability?
 
While I have not thought thru it fully, I could see a state having an attribute that is a siblings collection of which it is a member. I am not sure of the implications of saying that all siblings would fire transitions simultaneously(would it be possible to have them on separate threads?) Each sibling would need to have knowledge of the other siblings states of course.
 
I understand you have completed your scope of work on the toolkit. My thought was possibly to extend it for Xand myself. I am sure I would learn something along the way no matter what.
 
Anyway - just a thought. I am somewhat new to working with state machines, although i have understood the concept for a while. I have not been able to begin a new project with this approach until recently, and I am having a great time with it. Your contribution have been a great help, so thank you very much!
 

 
Chris
GeneralFundamental design issuememberHypnotron11 Mar '06 - 22:42 
I'm a bit of a design pattern purist I guess, and the fundamental incorrectness I find with the code generated is that the base class ends up knowing all about every state it supports and this is in contrary to a proper implementation of the "state pattern" (a design pattern) whereby an object that can be in a state, only has one set of accessors to that state and it is the state's themselves, which handle the transitions to new states.
 
for instance, a "Green" state would be responsible for changing the state of its parent object to "Yellow" after x interval. But the parent object itself need know nothing about its own state.
 
In your code (as far as i can tell from the article and source listings) if you need to add a new state, a new base class gets generated. But ideally (in my view as being a fan of the state pattern) adding a new state would never change the base class, only the sibling states that would need to interact with it.
 
All that said, I realize you dont have to use the "state" design pattern to implement a finite state machine. Your code certainly does the job and I think for lightweight state machines its as good a choice as any.
 
-Mike
GeneralRe: Fundamental design issuememberLeslie Sanford12 Mar '06 - 3:32 
Hypnotron wrote:
I'm a bit of a design pattern purist I guess, and the fundamental incorrectness I find with the code generated is that the base class ends up knowing all about every state it supports and this is in contrary to a proper implementation of the "state pattern" (a design pattern) whereby an object that can be in a state, only has one set of accessors to that state and it is the state's themselves, which handle the transitions to new states.

 
Actually, in my toolkit it's the Transition class that handles transitioning from one state to another. Transition objects are added to the State objects. When an event is dispatched to a StateMachine's current State, the State checks to see if it has Transition for that event. If so, the Transition is triggered and it is the Transition that returns the new State. The current State then returns the result of the transiton to the parent StateMachine.
 
And I guess I'd quibble with your description of the code having a "fundamental incorrectness" with regards to the state pattern, since as you note later I'm not trying to implement that pattern. There are many ways to implement state machines. For example, the approach taken here[^] is very interesting. It's nothing like the state pattern.
 
Hypnotron wrote:
All that said, I realize you dont have to use the "state" design pattern to implement a finite state machine. Your code certainly does the job and I think for lightweight state machines its as good a choice as any.

 
Well, I hope I've created something more capable than just implementing "lightweight" state machines, but I respect your opinion and appreciate your taking the time to express it here.
 

 

-- modified at 9:35 Sunday 12th March, 2006
GeneralDo / Action in StatememberFrank Weingaertner15 Feb '06 - 8:48 
Hi Leslie,
 
first of all, thank you very much for sharing this great toolkit with us.
 
What I am actually missing is a "Do" or "Action" activity for the states. There is a entry and a exit Handler, but no Action which is called periodically until the state is changed. Is it possible to add this easily in the toolkit?
I know that this is not needed within a GUI-project, but I need this for a library where I have to poll different things.....
 
Best regards
Frank Weingaertner

GeneralRe: Do / Action in StatememberLeslie Sanford15 Feb '06 - 9:04 
Frank Weingaertner wrote:
first of all, thank you very much for sharing this great toolkit with us.

 
Thank you!
 
Frank Weingaertner wrote:
What I am actually missing is a "Do" or "Action" activity for the states. There is a entry and a exit Handler, but no Action which is called periodically until the state is changed. Is it possible to add this easily in the toolkit?

 
Activites are something that I haven't looked too closely at. I will need to study them.
 
Just off the top of my head, in order to call an activity periodically, we'd need a timer event of some sort. Each time the timer generates a tick event, the activity would be run. For thread safety sake, we'd probably want to run the activity on the same thread as the state machine, so we will want to marshal the tick event to the state machine's thread.
 
I'm just thinking out loud here. I will have to give it some thought.
 
Thanks for your suggestion.
 

GeneralRe: Do / Action in Statemembermarc.thom8 Jun '06 - 11:17 
I think that a state shuld be a fix point in an application and has no activity. This is different from the clasic statemachie approach. The activity shuld be placed in the transition. The movement form State A to State B at a Trigger/Event. So leave state A - execute the methods defined in the transition ( way) enter new state. Your application has reache a new fix point.
So your application moves from one fixpont to an other.
 
see also www.steed.de
 
greeting from heidelberg
marc
GeneralRe: Do / Action in StatememberMember 45364793 May '09 - 22:12 
Did you implement the Do action? can you send me the code please.
Thanks.
GeneralGraph visualizationmemberAdrian Bacaianu5 Jan '06 - 10:45 
Hy !
Ithink will be very usefull to add an utility to be possible to shown graphicaly the state graph.
What is in design , what is in the tree... like on big programs with uml , matlab etc
 
i sugest you a free library which ihave seen used before on other utility on codeproject who need graphs:
http://www.codeproject.com/tools/pdfview.asp[^]
 
Adrian Bacaianu
GeneralOrthogonal statesmemberMondragon Goi Eskola Politeknikoa21 Dec '05 - 0:33 
How can I create orthogonal states?
 
I have try to create to Initial substates, but it does not work.
GeneralRe: Orthogonal statesmemberLeslie Sanford21 Dec '05 - 1:28 
Mondragon Goi Eskola Politeknikoa wrote:
How can I create orthogonal states?

 
The toolkit doesn't support orthogonal states/regions.
GeneralRe: Orthogonal statesmemberMondragon Goi Eskola Politeknikoa21 Dec '05 - 3:19 
Any further improvement?
GeneralRe: Orthogonal statesmemberLeslie Sanford21 Dec '05 - 6:45 
Unfortunately, I doubt I'll add orthogonal regions. I've made a few small improvements to the toolkit as well as fixing a few bugs. I hope to release a new version after the first of the year. But I don't think I will be adding any new major features.
Questionwhy the states are usefull ?memberAdrian Bacaianu13 Nov '05 - 0:26 
Hy Lesly !
 
First of all i want to thank you for your work.
I'm an automatist engineer, im very used with that kind of states but commonly only on hardware or in very dedicated and sofisticated special software !!
very nice impressed to see after the years, finnaly a common and easy to use sdk.
 
PLease tell as if you have an update for the toolkit.
 
Also, an article with some samples of using it, how to build simple problems using that approach, with simple demo will be really helpfull and intuitive and easy to understand for all programmers. Think very very few programmers know what the states are !
 
Adrian Bacaianu
AnswerRe: why the states are usefull ?memberLeslie Sanford13 Nov '05 - 4:39 
Adrian Bacaianu wrote:
First of all i want to thank you for your work.
I'm an automatist engineer, im very used with that kind of states but commonly only on hardware or in very dedicated and sofisticated special software !!
very nice impressed to see after the years, finnaly a common and easy to use sdk.

 
Thank you! I appreciate your comments.
 
Adrian Bacaianu wrote:
PLease tell as if you have an update for the toolkit.

 
There will be an minor update soon. I've fixed a couple of bugs in the toolkit. Right now, I'm just playing around with it to see if I need to make any other changes before posting an update.
 
Adrian Bacaianu wrote:
Also, an article with some samples of using it, how to build simple problems using that approach, with simple demo will be really helpfull and intuitive and easy to understand for all programmers.

 
Maybe. Smile | :) I agree that it would be useful to have an article that shows how to solve problems with state machines. Unfortunately, there aren't many examples of this on the Internet, in my opinion.
 
Thanks again for your comments!
GeneralIcons on BitMapmemberJuanR8 Nov '05 - 2:00 
Sniff | :^) How to extract images from Single Bitmap using VB.NET
 
ninguno
GeneralTimer Eventsmemberleeloo99928 Oct '05 - 4:32 
Thanks a lot for your excellent article concerning state machines...
Proposal to make the toolkit really usable :
Exchange your simple EventQueue with one that can handle real timer events (priority queue, ex. http://www.codeproject.com/csharp/UsePriorityQueue.asp[^]) and implement some appropriate transition Send methods where you can set times... (Your traffic light example is only just an example! and timing things are seldom as easy as to use a simple tick timer)
 
.... and another thing... what's the reason to make "LSCollections" a secret?
 
Greetings and thanks
 
Beat
GeneralRe: Timer EventsmemberLeslie Sanford28 Oct '05 - 5:09 
leeloo999 wrote:
Thanks a lot for your excellent article concerning state machines...
Proposal to make the toolkit really usable :
Exchange your simple EventQueue with one that can handle real timer events (priority queue, ex. http://www.codeproject.com/csharp/UsePriorityQueue.asp[^]) and implement some appropriate transition Send methods where you can set times... (Your traffic light example is only just an example! and timing things are seldom as easy as to use a simple tick timer)

 
That is a very interesting suggestion, and one I will give a lot of thought to. Thanks!
 
leeloo999 wrote:
.... and another thing... what's the reason to make "LSCollections" a secret?

 
Oh, there's no secret there. That's my namespace for the collections classes I've written. I added a Deque[^] class that the EventQueue class used internally. I didn't want to include the entire LSCollections project with the state machine toolkit, so I just left in the LSCollections.dll file.
 
This will change with the next version of the state machine toolkit. I've done away with the EventQueue class and replaced it with the DelegateQueue[^] class.
 
I will study your suggestion and possibly add it to the DelegateQueue class. Thanks, again! Big Grin | :-D
 

QuestionA more complete Demo solution?memberEd Sutton21 Oct '05 - 8:43 
I typically like to use a demo to dive immediately into the code to understand how it works. I could not do that with Part III demo. Apparently I need to create a LSCollections class library and include the Deque class downloaded from one of your other another article. Then I need to add this project to the solution and add a reference to this project where the missing references were needed. I have still not been succesful in building it. Is it possible that a new demo solution be created containing all required components?
 
Thank you for writing this article. I bought the Samek book on "Practical Statecharts in C/C++" about a year ago. I found it difficult to get through and was hoping to finally understand it well enough to convert to C#. I was quite pleased when I recently found your series of articles. I am hoping to use your series to help rewrite my traditional style state machine to use your toolkit.
 
Thanks again for your series and sharing your ideas.
 
-Ed
 
-- modified at 14:43 Friday 21st October, 2005
AnswerRe: A more complete Demo solution?memberLeslie Sanford21 Oct '05 - 10:02 
Ed Sutton wrote:
I typically like to use a demo to dive immediately into the code to understand how it works. I could not do that with Part III demo. Apparently I need to create a LSCollections class library and include the Deque class downloaded from one of your other another article. Then I need to add this project to the solution and add a reference to this project where the missing references were needed. I have still not been succesful in building it. Is it possible that a new demo solution be created containing all required components?

 
The demo project includes the LSCollection.dll file, so to use the demo project, you shouldn't have to compile a LSCollections class library. Be sure to look at the paragraph right before the Conclusion. The dll file is there, you just have to make sure your project links to it.
 
The next version of the toolkit, which I'm currently working on, will remove the dependency on the LSCollection Deque class. If you're curious about the latest version, email me. Smile | :) Overall it's basically the same as the version currently posted, but I have made a few improvements. I hope to submit the improved version within a month or so.
 
As far as a more complete demo, I'm currently rewriting my Midi toolkit using the state machine toolkit. Smile | :) I'm happy with how that's turning out. When I finally release the next version of the Midi toolkit, it will be a kind of demo of what the state machine toolkit is capable of.
 
Ed Sutton wrote:
Thank you for writing this article. I bought the Samek book on "Practical Statecharts in C/C++" about a year ago. I found it difficult to get through and was hoping to finally understand it well enough to convert to C#. I was quite pleased when I recently found your series of articles. I am hoping to use your series to help rewrite my traditional style state machine to use your toolkit.

 
Thank you!
 
I also have Samek's book. It's very educational, but I took a different approach, mainly because I wanted the toolkit to make it easy to create state machines by hand and create them through code generation.
 
Ed Sutton wrote:
Thanks again for your series and sharing your ideas.

 
You're welcome. And thank you for your comments. Let me know if I can do anything to help.
 

 

 

 

-- modified at 16:02 Friday 21st October, 2005
GeneralXML..memberRamon Smits27 Sep '05 - 13:01 
I took a look at your xml definition and maybe it is a better aproach for substate to define them within other states. This way you don't have to add three substates to a superstate.
 
I added an extra substate "warning" so it is easier to understand the structure.
 
<machine id="TrafficLight" xmlns="Com.Gmail.Smits.Ramon.XmlStateMachine">
 
   <event id="TurnOn"/>
   <event id="TurnOff"/>
   <event id="LowPower"/>
   <event id="PowerNormal"/>
   <event id="TimerElapsed"/>
 
   <state id="initial" history="none" initialstate="on">
      <state id="on" history="none" initialstate="normal">
         <state id="normal" history="none" initialstate="red">
            <state id="red">
               <transition event="TimerElapsed" target="green" guard="CounterEquals4" action="ResetCounter"/>
               <transition event="TimerElapsed" target="red" action="IncrementCounter"/>
            </state>
            <state id="orange">
               <transition event="TimerElapsed" target="red" guard="CounterEquals2"/>
               <transition event="TimerElapsed" target="orange" action="IncrementCounter"/>
            </state>
            <state id="green">
               <transition event="TimerElapsed" target="orange" guard="CounterEquals4"/>
               <transition event="TimerElapsed" target="green" action="IncrementCounter"/>
            </state>
 
            <transition event="LowPower" target="warning"/>
         </state>
 
         <state id="warning" history="none" initialstate="orange"
            <state id="orange">
               <transition event="TimerElapsed" target="none" guard="CounterEquals2"/>
               <transition event="TimerElapsed" target="orange" action="IncrementCounter"/>
            </state>
            <state id="none">
               <transition event="TimerElapsed" target="orange" guard="CounterEquals2"/>
               <transition event="TimerElapsed" target="none" action="IncrementCounter"/>
            </state>
            <transition event="PowerNormal" target="normal"/>
         </state>
 
         <transition event="TurnOff" target="off"/>
      </state>
 
      <state id="off">
         <transition event="TurnOn" target="on"/>
      </state>
   </state>
</machine>
 

You could even have a structure like:
 
Machine
(Root)
   State A - Initial B - History None
      State B - Initial C - History Shallow
         State C
         State D
         State E
      State F
      State G - Initial I - History None
         State H
         State I
 
This way you don't have to add initial and history attributes to other element that aren't "State" like the element Machine.
 
I do not have a xml schema editor here at the moment or else I would have specified a schema but I don't write fluent xml schema yet Wink | ;) .
 
I really like your idea of code generation. The most cool implementation would be as a custom tool that generates .cs code within a VS.NET project.

GeneralRe: XML..memberLeslie Sanford27 Sep '05 - 14:53 
I like your schema overall. The problem is that I'm using the DataSet class to generate the XML. I set up the relations and constraints between tables the DataSet uses, and let it generate the XML. Unfortunately, it has its own ideas of what the XML should look like.
 
I suppose one could write a custom schema and the DataSet would read and write following that schema? This is an area I want to explore more.
 
Ramon Smits wrote:
I really like your idea of code generation. The most cool implementation would be as a custom tool that generates .cs code within a VS.NET project.
 
Currently, I've been using my State Machine Maker application within Visual Studio. I added it as a custom tool. Basically, when you save the code, you make sure you save it to the folder in which they code for your current project resides. Add the file to your project, and your set.
AnswerRe: XML..memberRamon Smits28 Sep '05 - 0:34 
Maybe it is possible to just use standard XML serialization? This way you can easily modify the xml definition to change the statemachine at runtime.
 
I will write such a structure today if time permits and post it as a comment.
GeneralRe: XML..memberLeslie Sanford28 Sep '05 - 5:59 
Ramon Smits wrote:
Maybe it is possible to just use standard XML serialization?
 
I don't know much about XML serialization, but at a glance it looks like an interesting possibility. I'll study this and take a look at your code.
AnswerRe: XML..memberRamon Smits28 Sep '05 - 3:20 
I've made an implementation for the class design for you. You can download the code at my blog at the following url:
 
http://bloggingabout.net/blogs/ramon/archive/2005/09/28/9544.aspx
 
An example:
 
<?xml version="1.0"?>
<Machine Id="Switch" InitialState="On" xmlns="Exyll.StateMachine">
   <SubStates>
      <State Id="On">
         <Transitions>
            <Transition EventName="TurnOff" Target="Off" />
         </Transitions>
      </State>
      <State Id="Off">
         <Transitions>
            <Transition EventName="TurnOn" Target="On" />
         </Transitions>
      </State>
   </SubStates>
   <Events>
      <Event>TurnOff</Event>
      <Event>TurnOn</Event>
   </Events>
</Machine>
GeneralRe: XML..memberLeslie Sanford29 Sep '05 - 20:09 
Ramon Smits wrote:
 <state id="initial" history="none" initialstate="on">
    <state id="on" history="none" initialstate="normal">
      <state id="normal" history="none" initialstate="red">
        <state id="red"></pre>
 
I've been experimenting with XML serialization, and it seems to choke on nesting the tag like this. I must not be doing something right. I like the idea, btw. It makes the relationships between states more explicit.
GeneralRe: XML..memberLeslie Sanford29 Sep '05 - 20:44 
I wrote:
I've been experimenting with XML serialization, and it seems to choke on nesting the tag like this. I must not be doing something right.
 
Erm, yeah, I found my mistake. Ok, I'm back on track. What I would like to do is write up an XML file representing the TrafficLight state machine as described in the article. Then we can figure out how to realize that in the toolkit through XML serialization.

AnswerRe: XML..memberRamon Smits29 Sep '05 - 21:29 
Ok.. let's see if I can help you out.
 
E-mail me personally at ramon.smits at gmail.com
 

GeneralRe: XML..memberRamon Smits29 Sep '05 - 22:03 
I've updates the sourcecode that does the xml serialisation and added a unittest to it for the trafficsign statemachine.
 
+StateMachine in c# and xml, v3
 
The post contains a download link to the updated sources.
 
-- modified at 5:45 Friday 30th September, 2005
 
I changed the source. It's now v3 Smile | :) .. it has the whole circus. Actions, guard, delegates, substates, history and initialstate.
GeneralImpressivememberClevedon_Peanut23 Sep '05 - 11:49 
The amount of work that's gone into this is phenominal - very impressive: thanks for sharing the code Big Grin | :-D
GeneralRe: ImpressivememberLeslie Sanford24 Sep '05 - 6:42 
Thank you very, very much. Smile | :)

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 26 Oct 2006
Article Copyright 2005 by Leslie Sanford
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid