Click here to Skip to main content
15,895,800 members
Articles / Programming Languages / C#

Finite State Machine and Multithreading using .NET

Rate me:
Please Sign up or sign in to vote.
4.79/5 (37 votes)
2 Mar 20068 min read 200.7K   2K   146  
An article on classes for finite state machines, events and threads.
//------------------------------------------------------------------------------
//  File: Fsm.cs
//------------------------------------------------------------------------------
//	Finite State machine Library for .NET.
//	(C) Copyright 2003 - 2005, Linus Flueler (linus.flueler@siemens.com)
//	All rights reserved.
//	The code and information is provided "as-is" without waranty of any kind,
//	either expressed or implied.
//-----------------------------------------------------------------------------
//  Project                 : .NET Realtime Library RTLib
//  Namespace				        : RTLib
//  Language/Compiler       : Microsoft Visual C# .NET 
//  Date of Creation        : 01-Dec-03
//  Author                  : Linus Flueler
//-----------------------------------------------------------------------------
//	History:
//		01-Dec-03	Linus Flueler				Initial Version
//-----------------------------------------------------------------------------
//

using System;
using System.Reflection;
using System.Threading;
using System.Collections;
using System.Diagnostics;

namespace RTLib
{
	/// <summary>
	/// Fsm is the base class for user defined finite state machines.
	/// During startup time, Fsm investigates the user defined handler methods 
	/// with reflection and creates appropriate structures for run time.
	/// At run time it dispatches the events according to the investigated info. 
  /// The attribute FsmCoding can bee used to force using of attributes 
  /// (ECodingType.WithAttributes) or naming convention and signature match 
  /// (ECodingType.Automatic). Default is ECodingType.Automatic.
	/// </summary>
	public abstract class Fsm : FsmEventBase
	{
		/// <summary>
		/// Default constructor
		/// </summary>
		public Fsm()
			: this( true )
		{
		}

		/// <summary>
		/// Constructor for synchronized FSM
		/// </summary>
		/// <param name="synchronized"></param>
		public Fsm( bool synchronized )
			: base( synchronized )
		{
			classInfo = (FsmClassInfo)FsmClassInfo.fsmClassInfoMap[GetType()];
			if ( classInfo == null )
			{
				InitializeFsmClassInfo();
			}
			Debug.Assert( classInfo != null );
		}

		/// <summary>
		/// The Processor (Thread) which is associated with this FSM
		/// </summary>
		public FsmProcessor FsmProcessor
		{
			set { fsmProcessor = value; }
			get { return fsmProcessor; }
		}

		/// <summary>
		/// Returns true if there exists an enum data member called "state"
		/// or with the attribute "FsmState". Otherwise, this class is used as
		/// a projection plane for events in form of a event handlers without an
		/// exlicit state. 
		/// </summary>
		public bool HasStates
		{
			get { return this.classInfo.hasStates; }
		}

		/// <summary>
		/// Send an event to this FSM. The event will be forwarded to the processor.
		/// It is not called directly but in the context of the processor thread.
		/// </summary>
		/// <param name="ev"></param>
		public void PushEvent( FsmEvent ev )
		{
			FsmProcessor.PushEvent( ev, this ); 
		}

		/// <summary>
		/// Send a timer event to this FSM. The event will be forwarded to the processor.
		/// It is not called directly but in the context of the processor thread.
		/// </summary>
		/// <param name="ev"></param>
		public void PushEvent( FsmTimerEvent ev )
		{
			FsmProcessor.PushEvent( ev, this ); 
		}

		/// <summary>
		/// Force this FSM to terminate. The other FSM's and the processor will
		/// continue to run.
		/// </summary>
		public void Terminate()
		{
			FsmProcessor.TerminateFsm( this );
		}

		/// <summary>
		/// This override is called as the first entry point in the life of this
		/// FSM. It is always and only called in the context of the associated processor / thread.
		/// </summary>
		protected internal virtual void OnFsmEntry()
		{
			// do nothing, override this method in derived classes if needed.
		}

		/// <summary>
		/// This override is called as the last entry point in the life of this
		/// FSM. It is always and only called in the context of the associated processor / thread.
		/// </summary>
		protected internal virtual void OnFsmExit()
		{
			// do nothing, override this method in derived classes if needed.
		}

		/// <summary>
		/// This method is called by the processor thread in order to execute the
		/// first state. The first state is the initial value of the "state" member
		/// variable.
		/// Override is possible but not recommended.
		/// </summary>
		protected internal virtual void EnterFirstState()
		{
			Debug.Assert( classInfo != null );
			if ( classInfo.hasStates )
			{
				Type typeFsm = this.GetType();
				Int32 currState = GetCurrentState();
				StateInfo stateInfo = classInfo.stateInfoArray[currState];
				// Execute Entry state handler of current first state
				if ( stateInfo.entryMethod != null )
				{
					stateInfo.entryMethod.Invoke(this, 
						new Object[]{null, Enum.ToObject(classInfo.stateEnumType,currState)});
					// It is not allowed to change state in State Entry Handler!
					Debug.Assert( currState == GetCurrentState() );
				}
			}
		}

		/// <summary>
		/// This is the core event dispatching of the FSM. The FSM looks up the
		/// curent state and calls the state and transition handlers if they are 
		/// defined.
		/// Override is possible but not recommended.
		/// </summary>
		/// <param name="fsmEvent"></param>
		protected internal virtual void OnFsmEvent(FsmEvent fsmEvent)
		{
			Debug.Assert( classInfo != null );
			if ( classInfo.hasStates )
			{
				Int32 currState = GetCurrentState();
				// execute transition
				StateInfo stateInfo1 = classInfo.stateInfoArray[currState];
				Debug.Assert( stateInfo1 != null );
				MethodInfo methInfoTransition = (MethodInfo)stateInfo1.transitions[fsmEvent.GetType()];
				if ( methInfoTransition != null )
				{
					// first execute exit method
					if ( stateInfo1.exitMethod != null )
					{
						stateInfo1.exitMethod.Invoke(this, new Object[] {fsmEvent});
						// it is not allowed to change state in State Exit Handler!
						Debug.Assert( currState == GetCurrentState() );
					}
					
					// now execute transition method
					methInfoTransition.Invoke(this, new Object[] {fsmEvent} );
					// on return, FSM has probably a new state

					int newState = GetCurrentState();
					if ( newState != currState )
					{
						// Transition to other state
						StateInfo stateInfo2 = classInfo.stateInfoArray[newState];
						Debug.Assert( stateInfo2 != null );

						// Execute Entry state handler of new state
						if ( stateInfo2.entryMethod != null )
						{
							stateInfo2.entryMethod.Invoke(this, new Object[] {fsmEvent, 
												 Enum.ToObject(classInfo.stateEnumType,currState)});
							// it is not allowed to change state in State Entry Handler!
							Debug.Assert( newState == GetCurrentState() );
						}
					}
					else
					{ // It is the same state --> transition loop
						if ( stateInfo1.entryMethod != null )
						{
							stateInfo1.entryMethod.Invoke(this, new Object[] {fsmEvent, 
									Enum.ToObject(classInfo.stateEnumType,currState)});
							// it is not allowed to change state in State Entry Handler!
							Debug.Assert( currState == GetCurrentState() );
						}
					}
				}
				else
				{ 
					// There is no transition for this event in current state.
					if ( stateInfo1.defaultTransitionMethod != null )
					{
						stateInfo1.defaultTransitionMethod.Invoke(this, new Object[] {fsmEvent} );
					}
					else
					{
						// Try to find an event handler
						MethodInfo methInfoEvHnd = (MethodInfo)classInfo.eventHandlers[fsmEvent.GetType()];
						if ( methInfoEvHnd != null )
						{
							methInfoEvHnd.Invoke(this, new Object[] {fsmEvent} );
						}
						else
						{
							// Call default handler
							OnFsmEventDefault(fsmEvent);
						}
					}
				}
			}
			else
			{ 
				// FSM has no states. Therefore event handlers are the only handlers to check.
				MethodInfo methodInfo = (MethodInfo)classInfo.eventHandlers[fsmEvent.GetType()];
				if ( methodInfo != null )
				{
					methodInfo.Invoke(this, new Object[] {fsmEvent} );
				}
				else
				{
					// error: No matching event handler available
					Trace.WriteLine("FSM:" + this.GetType().Name + " Event:" + fsmEvent.GetType().Name + " no event handler found");
				}
			}
		}

		/// <summary>
		/// This is the default handler if there was neither a transition handler nor an event handler
		/// defined. Overriding this method allows to establish a custom error handler or a default 
		/// event handler defining a default behavior.
		/// </summary>
		/// <param name="fsmEvent"></param>
		protected internal virtual void OnFsmEventDefault(FsmEvent fsmEvent)
		{
			// error: There exists neither a transition handler nor an event handler
			if ( classInfo.hasStates )
			{
				Trace.WriteLine("FSM:" + this.GetType().Name + " Event:" + fsmEvent.GetType().Name + " not handled in state:" 
						+ Enum.GetName( classInfo.stateEnumType, GetCurrentState() ) );
			}
			else
			{
				Trace.WriteLine("FSM:" + this.GetType().Name + " Event:" + fsmEvent.GetType().Name + " not handled");
			}
		}

		/// <summary>
		/// Used in a event-, state- or transition handler to terminate the FSM. 
		/// </summary>
		protected void TerminateFsm()
		{
			Debug.Assert(Thread.CurrentThread.GetHashCode() == fsmProcessor.Thread.GetHashCode());
				fsmProcessor.PopFsm(this);
			OnFsmExit();
			Done();
		}

		/// <summary>
		/// Legacy method to terminate an FSM from inside (handler) or from outside
		/// (other thread). For inside calls, please use "TerminateFsm()" now.
		/// For outside calls, please use "Terminate" now.
		/// </summary>
		public virtual void FsmDone()
		{
			if (Thread.CurrentThread.GetHashCode() == fsmProcessor.Thread.GetHashCode())
			{
				TerminateFsm();
			}
			else
			{
				fsmProcessor.TerminateFsm(this);
			}
		}
		
		/// <summary>
		/// Unfortunately, this method MUST be overridden, if states are used in this FSM. 
		/// It is needed since this FSM base class needs knowledge about the state defined in the derived 
		/// class. If you forget to define it, you will notice it immediately, since the default implementation
		/// asserts to false.
		/// </summary>
		/// <returns></returns>
		protected internal virtual int GetCurrentState()
		{
			Debug.Assert(false);			
			Trace.WriteLine("GetCurrentState must be overridden when defining a state variable");
			return -1;
		}

		/// <summary>
		/// Used internally to seach for attributes of members in the derived class.
		/// </summary>
		/// <param name="memberInfo"></param>
		/// <param name="filterCriteria"></param>
		/// <returns></returns>
		private static bool HasFieldAttributeOfType(
			MemberInfo memberInfo,
			object filterCriteria	)
		{
			return memberInfo.GetCustomAttributes((Type)filterCriteria,false).Length > 0;
		}

		/// <summary>
		/// Used internally to find event-, state- and transition handlers in the derived class.
		/// </summary>
		/// <param name="memberInfo"></param>
		/// <param name="filterCriteria"></param>
		/// <returns></returns>
		private static bool IsValidFsmHandler(
			MemberInfo memberInfo,
			object filterCriteria	)
		{
			Debug.Assert(memberInfo.MemberType == MemberTypes.Method);
			MethodInfo methodInfo = (MethodInfo)memberInfo;

			// handler methods are never static
			if ( methodInfo.IsStatic )
				return false;

			// discriminate methods with "FsmNoHandler" attribute
			if (methodInfo.GetCustomAttributes(typeof(FsmNoHandlerAttribute),false).Length > 0 )
				return false;

			// check parameter(s)

			// Method must not have a return type
			if ( methodInfo.ReturnType != typeof(void) )
				return false;

			// Method must have max. two parameters 
			ParameterInfo[] parameterInfo = methodInfo.GetParameters();
			if ( parameterInfo.Length > 2 )
				return false;

			// Method must not be defined in the Fsm base type
			if ( methodInfo.DeclaringType == typeof(Fsm)) 
				return false;

			// else succeed
			return true;
		}

		/// <summary>
		/// Initializes the class info for an FSM. It is called when 
		/// the construcor is called the first time. This can not be done
		/// in static constructor since derived class must already exist.
		/// Constructing this information at startup time saves a lot of execution
		/// time while dispatching events!
		/// </summary>
		private void InitializeFsmClassInfo()
		{
			Type typeFsm = GetType();
			Debug.Assert( classInfo == null );
			classInfo = new FsmClassInfo();
			classInfo.name = typeFsm.Name;
			FsmClassInfo.fsmClassInfoMap[typeFsm] = classInfo;

			// Check, if client want to use attributes for defining state var and handler
			Object[] fsmCodingAttributes = typeFsm.GetCustomAttributes(typeof(FsmCodingAttribute),true);
			Debug.Assert( fsmCodingAttributes.Length <= 1 );
			if ( fsmCodingAttributes.Length > 0 )
			{
				FsmCodingAttribute attr = (FsmCodingAttribute)fsmCodingAttributes[0];
				classInfo.fsmCodingType = attr.CodingType;
			}

			// get state member variable if available
			MemberInfo[] stateFields;
			if ( classInfo.fsmCodingType == ECodingType.Automatic )
			{
				// No attribute needed, just take the member with the name "state"
				stateFields = typeFsm.GetMember(
					"state",
					MemberTypes.Field,
					BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
			}
			else
			{
				// Find a enum member with the attribute "FsmState"
				stateFields = typeFsm.FindMembers(MemberTypes.Field,
					BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
					new MemberFilter(HasFieldAttributeOfType), typeof(FsmStateAttribute));

				// Generate a GetCurrentState method for this FSM class
				// Note: don't know how to implement ....
			}

			// Get all info from state valiable and prepare state info
			FieldInfo stateField;
			FieldInfo[] StateEnums;
			Hashtable stringToStateMap = new Hashtable();
			if ( stateFields.Length > 0 )
			{
				classInfo.hasStates = true;
				// Get transition handlers and state handlers
				// This FSM has a state field, get the filed info of it.
				// Fill a string map with the enumeration value names for faster lookup
				Debug.Assert( stateFields.Length == 1 );
				stateField = (FieldInfo)stateFields[0];
				classInfo.stateEnumType = stateField.FieldType;
				Debug.Assert(classInfo.stateEnumType.IsSubclassOf(typeof(System.Enum)));
				StateEnums = classInfo.stateEnumType.GetFields(
					BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic);
				classInfo.stateInfoArray = new StateInfo[StateEnums.Length];

				foreach ( FieldInfo fieldInfo in StateEnums )
				{
					int val = (int)fieldInfo.GetValue(null);
					StateInfo stateInfo = new StateInfo();
					classInfo.stateInfoArray[val] = stateInfo;
					Debug.Assert(classInfo.stateInfoArray[val] == stateInfo);
					stateInfo.name = fieldInfo.Name;
					stateInfo.transitions = new Hashtable();
					stringToStateMap.Add(fieldInfo.Name, stateInfo );
				}
			}

			// Get all methods which are candidates for any kind of handlers
			MemberInfo[] handlers = typeFsm.FindMembers(MemberTypes.Method,
				BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
				new MemberFilter(IsValidFsmHandler), null);

			// Loop over all these methods
			foreach ( MemberInfo mi in handlers )
			{
				MethodInfo meth = (MethodInfo)mi;
				StateInfo stateInfo = null;
				if ( classInfo.fsmCodingType == ECodingType.Automatic )
				{
					// check if it is a state or transition handler
					bool bIsStateOrTransitionHandler = false;
					int separatorPos = meth.Name.IndexOf("_");
					if ( separatorPos >= 0 )
					{
						string prefix = meth.Name.Substring(0, separatorPos);
						stateInfo = (StateInfo)stringToStateMap[prefix];
						if ( stateInfo != null )
						{
							// It is a state or transition handler
							bIsStateOrTransitionHandler = true;
						}
					}

					if ( bIsStateOrTransitionHandler )
					{
						if ( meth.Name.EndsWith("_EntryState") )
						{
							Debug.Assert(meth.GetParameters().Length == 2);
							Debug.Assert(meth.GetParameters()[0].ParameterType == typeof(FsmEvent)); 
							Debug.Assert(meth.GetParameters()[1].ParameterType == classInfo.stateEnumType);
							stateInfo.entryMethod = meth; 
						}
						else if ( meth.Name.EndsWith("_ExitState") )
						{
							Debug.Assert(meth.GetParameters().Length == 1);
							Debug.Assert(meth.GetParameters()[0].ParameterType == typeof(FsmEvent));
							stateInfo.exitMethod = meth; 
						}
						else if ( meth.GetParameters().Length == 1 ) 
						{
							if ( meth.GetParameters()[0].ParameterType == typeof(FsmEvent) )
							{
								// it is a default transition
								stateInfo.defaultTransitionMethod = meth;
							}
							else if ( meth.GetParameters()[0].ParameterType.IsSubclassOf(typeof(FsmEvent)) )
							{
								// it is a transition
								Type eventParamType = meth.GetParameters()[0].ParameterType;
								Debug.Assert( stateInfo.transitions[eventParamType] == null );
								stateInfo.transitions[eventParamType] = meth;
							}
							else
							{
								// Do nothing, it is not a FSM method
							}
						}
						else
						{
							// Do nothing, it is not a FSM method
						}
					}
					else
					{
						if ( meth.GetParameters().Length == 1 &&
							meth.GetParameters()[0].ParameterType.IsSubclassOf(typeof(FsmEvent))	)
						{ 
							// Its an event handler
							Type eventParamType = meth.GetParameters()[0].ParameterType;
							// add [FsmNoHandler] to failing method when asserting here!
							Debug.Assert( classInfo.eventHandlers[eventParamType] == null );
							classInfo.eventHandlers[eventParamType] = meth;
						}
						else
						{
							// Do nothing, it is not a FSM method
						}
					}
				}
				else
				{
					// NOT Automatic, with attributes
					object[] attribs;

					// Is it a transition handler ?
					attribs = mi.GetCustomAttributes(typeof(FsmTransitionHandlerAttribute), false);
					if ( attribs.Length > 0 )
					{
						// yes, it is a transition handler, assign it to state info
						FsmTransitionHandlerAttribute attrib = (FsmTransitionHandlerAttribute)attribs[0];
						stateInfo = (StateInfo)stringToStateMap[attrib.FromState];
						Debug.Assert( stateInfo != null );
						Type eventParamType = meth.GetParameters()[0].ParameterType;

						// Is it the default transition handler ?
						if ( eventParamType == typeof(FsmEvent) )
						{
							// Yes, store it
							stateInfo.defaultTransitionMethod = meth;
						}
						else
						{
							// It is a normal transiton handler
							Debug.Assert( eventParamType.IsSubclassOf(typeof(FsmEvent)) );
							Debug.Assert( stateInfo.transitions[eventParamType] == null );
							stateInfo.transitions[eventParamType] = meth;
						}
					}
					else 
					{
						attribs = mi.GetCustomAttributes(typeof(FsmStateHandlerAttribute), false);
						if ( attribs.Length > 0 )
						{
							// yes, it is a state handler
							FsmStateHandlerAttribute attrib = (FsmStateHandlerAttribute)attribs[0];
							stateInfo = (StateInfo)stringToStateMap[attrib.State];
							Debug.Assert( stateInfo != null );
							if ( attrib.HandlerType == EStateHandlerType.Entry )
							{
								Debug.Assert( meth.GetParameters().Length == 2 );
								Debug.Assert( meth.GetParameters()[0].ParameterType == typeof(FsmEvent));
								Debug.Assert( meth.GetParameters()[1].ParameterType == classInfo.stateEnumType);
								Debug.Assert( stateInfo.entryMethod == null );
								stateInfo.entryMethod = meth; 
							}
							else if ( attrib.HandlerType == EStateHandlerType.Exit )
							{
								Debug.Assert( meth.GetParameters().Length == 1 );
								Debug.Assert( meth.GetParameters()[0].ParameterType == typeof(FsmEvent));
								Debug.Assert( stateInfo.exitMethod == null );
								stateInfo.exitMethod = meth; 
							}
							else
							{
								Trace.WriteLine( "Unexpected State Handler attribute value" );
								Debug.Assert( false );
							}
						}
						else
						{
							// it is neither a transition nor a state handler
							// Is it a event handler
							attribs = mi.GetCustomAttributes(typeof(FsmEventHandlerAttribute), false);
							if ( attribs.Length > 0 )
							{
								// yes, it is an event handler
								FsmEventHandlerAttribute attrib = (FsmEventHandlerAttribute)attribs[0];
								Type eventParamType = meth.GetParameters()[0].ParameterType;
								Debug.Assert( classInfo.eventHandlers[eventParamType] == null );
								Debug.Assert( eventParamType.IsSubclassOf(typeof(FsmEvent)) );
								classInfo.eventHandlers[eventParamType] = meth;
							}
						}
					}
				}
			}
		}

		/// <summary>
		/// The associated processor / thread
		/// </summary>
		private FsmProcessor fsmProcessor = null;

		/// <summary>
		/// Dispatching information of the derived class.
		/// </summary>
		private FsmClassInfo classInfo = null;
	} // class Fsm


	/// <summary>
	/// Synchronized FSM.
	/// </summary>
	 public abstract class FsmSync : Fsm
	{
		/// <summary>
		/// Default constructor
		/// </summary>
		public FsmSync()
		: base( true )
		{
		}
	}


	/// <summary>
	/// This class encapsulates the dispatching information for an FSM class.
	/// Constructing this information at startup time saves a lot of execution
	/// time while dispatching events!
	/// </summary>
	internal class FsmClassInfo
	{
		/// <summary>
		/// Constructor
		/// </summary>
		internal FsmClassInfo()
		{
			eventHandlers = new Hashtable();
			fsmCodingType = ECodingType.Automatic;
			stateInfoArray = null;
		}
		internal Hashtable eventHandlers;
		internal StateInfo[] stateInfoArray;
		internal ECodingType fsmCodingType;
		internal bool hasStates = false;
		internal Type stateEnumType;
		internal string name; // debugging only
		internal static Hashtable fsmClassInfoMap = new Hashtable();
	}

	/// <summary>
	/// This class describes the dispatching information for a single
	/// state in an FSM. Is part of FsmClassInfo.
	/// </summary>
	internal class StateInfo
	{
		internal StateInfo()
		{
			transitions = new Hashtable();
		}

		internal MethodInfo entryMethod;
		internal MethodInfo exitMethod;
		internal MethodInfo defaultTransitionMethod;
		internal Hashtable transitions;
		internal string name; // for debugging purposes only
	}


//------------------------------------------------------------------------------
// Attributes		

	/// <summary>
	/// Describes the way, FSM method mapping should be done in the startup phase. 
	/// </summary>
	public enum ECodingType 
	{
		/// <summary>
		/// Mapping is done by naming convention
		/// </summary>
		Automatic,
		/// <summary>
		/// Mapping is done by attributes only
		/// </summary>
		WithAttributes
	}

	/// <summary>
	/// Attribute defining the coding type:
	/// "[FsmCoding(ECodingType.Automatic)]", default 
	/// "[FsmCoding(ECodingType.WithAttributes)]"
	/// </summary>
	[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
	public class FsmCodingAttribute : System.Attribute
	{
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="codingType">Kind of mapping to be used</param>
		public FsmCodingAttribute(ECodingType codingType)
		{
			this.codingType = codingType;
		}

		/// <summary>
		/// Kind of mapping to be used: Automatic or WithAttributes
		/// </summary>
		public ECodingType CodingType
		{
			get
			{
				return codingType;
			}
		}

		/// <summary>
		/// Coding type
		/// </summary>
		private ECodingType codingType;
	}

	/// <summary>
	/// Defines the enum member, which is the state variable. There must be 
	/// defined only one state member! 
	/// With automatic mapping, the state variable must be named "state".
	/// Example:
	/// [FsmState()]
	/// MyStateType m_myStateEnum;
	/// </summary>
	[AttributeUsage(AttributeTargets.Field, AllowMultiple=false)]
	public sealed class FsmStateAttribute : Attribute
	{
		/// <summary>
		/// Constructor
		/// </summary>
		public FsmStateAttribute()
		{
		}
	}

	/// <summary>
	/// Defines that this method is an event handler. 
	/// The method must have exactly one parameter which is of a type 
	/// which is derived from FsmEvent.
	/// Example: 
	///  [FsmEventHandler]
	/// </summary>
	[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
	public sealed class FsmEventHandlerAttribute : Attribute
	{
		/// <summary>
		/// Constructor
		/// </summary>
		public FsmEventHandlerAttribute()
		{
		}
	}

	/// <summary>
	/// Discriminator attribute. This is used with automatic mapping: 
	/// If a method has a name according to the naming convention, but it 
	/// should be presereved as a normal method, use this arribute.
	/// Example:
	///  [FsmNoHandler]
	/// </summary>
	[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
	public sealed class FsmNoHandlerAttribute : Attribute
	{
		/// <summary>
		/// Constructor
		/// </summary>
		public FsmNoHandlerAttribute()
		{
		}
	}

	/// <summary>
	/// Defines a method as a transition handler. The method must have exactly
	/// one parameter which is of a type which is derived from FsmEvent.
	/// As a parameter, define the state for this transition.
	/// Example:
	/// [FsmTransitionHandler("StateA")]
	/// </summary>
	[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
	public sealed class FsmTransitionHandlerAttribute : Attribute
	{
		/// <summary>
		/// 
		/// </summary>
		/// <param name="fromState"></param>
		public FsmTransitionHandlerAttribute( string fromState )
		{
			this.fromState = fromState;
		}

		/// <summary>
		/// The state this transition is leaving from.
		/// </summary>
		public string FromState
		{
			get 
			{
				return fromState;
			}
		}

		private string fromState;
	}
	
	/// <summary>
	/// Defines the kind of state handler. 
	/// </summary>
	public enum EStateHandlerType 
	{
		/// <summary>
		/// State handler which is called on state entry.
		/// </summary>
		Entry,
		/// <summary>
		/// State handler handler which is called on state exit.
		/// </summary>
		Exit
	};

	/// <summary>
	/// Defines a method as a state handler. The method must have exactly two 
	/// parameters: One which is of a type which is derived from FsmEvent and
	/// one of the state type. With handlerType you define the kind of 
	/// state handler: enter or exit.
	/// Example: 
	/// [FsmStateHandler("StateA", EStateHandlerType.Entry )]
	/// </summary>
	[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
	public sealed class FsmStateHandlerAttribute : Attribute
	{
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="state">State this state handler works on</param>
		/// <param name="handlerType">Enter or Exit</param>
		public FsmStateHandlerAttribute(
			string state,
			EStateHandlerType handlerType )
		{
			this.state = state;
			this.handlerType = handlerType;
		}

		/// <summary>
		/// The state, the state handler is is assigned to
		/// </summary>
		public string State
		{
			get 
			{
				return state;
			}
		}

		/// <summary>
		/// The type of state handler which is used: entry or exit.
		/// </summary>
		public EStateHandlerType HandlerType
		{
			get 
			{
				return handlerType;
			}
		}

		private string state;
		private EStateHandlerType handlerType;
	}

} // namespace RTLib

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


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

Comments and Discussions