Click here to Skip to main content
15,885,947 members
Articles / Web Development / HTML

C#/XML Based State Engine

Rate me:
Please Sign up or sign in to vote.
4.00/5 (7 votes)
20 Jul 20043 min read 47.8K   626   30  
How to create an XML based state engine for controlling a simple real-time system
using System;
using System.Threading;

namespace RQAssemblies.StateEngine
{
	/// <summary>
	/// Summary description for Engine.
	/// </summary>
	public class Engine : System.Collections.IList
	{
		#region Variables
		private System.Collections.ArrayList stateList = new System.Collections.ArrayList();
		private System.Xml.XmlDocument stateTable = new System.Xml.XmlDocument();
		private object engineMethodsClass = null;
		#endregion Variables

		#region Threading

		#region Thread Variables
		private Thread engineThread = null;
		private long currentStateNumber = -1;
		private bool exitThread = false;
		#endregion Thread Variables

		#region Public methods
		public void LoadStateTable(string fileName)
		{
			try
			{
				stateTable.Load(fileName);
				PopulateStateEngine();
			}
			catch(System.Xml.XmlException ex)
			{
			}
			catch(System.IO.FileNotFoundException)
			{
			}
		}

		public void Run()
		{
			if(engineThread == null)
			{
				engineThread = new Thread(new ThreadStart(EngineThread));
			}
			engineThread.Start();
		}

		public void Stop()
		{
			exitThread = true;
			engineThread.Abort();
		}

		public object EngineMethodsClass
		{
			get
			{
				return engineMethodsClass;
			}
			set
			{
				engineMethodsClass = value;
			}
		}
		#endregion Public methods

		#region Private methods

		/// <summary>
		/// populates the state list with available states from the xml document
		/// </summary>
		private void PopulateStateEngine()
		{
			try
			{
				System.Xml.XmlNode stateTableNode = stateTable.SelectSingleNode("StateTable");
				if(stateTableNode != null)
				{
					System.Xml.XmlNodeList stateNodeList = stateTableNode.SelectNodes("State");
					if(stateNodeList != null)
					{
						foreach(System.Xml.XmlNode node in stateNodeList)
						{
							PopulateStateEngine(node);
						}
					}
				}
			}
			catch(System.Xml.XPath.XPathException ex)
			{
			}
		}


		/// <summary>
		/// creates a new state and adds it to the state list
		/// </summary>
		/// <param name="stateNode">the xml node where the state to be added is</param>
		private void PopulateStateEngine(System.Xml.XmlNode stateNode)
		{
			State state = new State();

			// state number
			System.Xml.XmlAttribute stateNumber = stateNode.Attributes["Number"];
			state.StateNumber = System.Convert.ToInt32(stateNumber.Value);

			// function
			System.Xml.XmlNode exitFunction = stateNode.SelectSingleNode("Function");
			string functionName = exitFunction.InnerText;
			if(functionName.Length > 0)
			{
				// get our OnState event, then createand add a new delegate to the event
				System.Reflection.EventInfo eventInfo = state.GetType().GetEvent("OnState");
				System.Delegate eventDelegate = System.Delegate.CreateDelegate(eventInfo.EventHandlerType, this.engineMethodsClass, functionName);
				eventInfo.AddEventHandler(state, eventDelegate);
			}

			// action
			System.Xml.XmlNode actionNode = stateNode.SelectSingleNode("Action");
			if(actionNode != null)
			{
				System.Xml.XmlAttribute actionTypeAttribute = actionNode.Attributes["Type"];

				// iterate through the enumeration types and find the one that the state engine references
				Type enumType = typeof(RQAssemblies.StateEngine.Action.ActionType);
				foreach(System.Reflection.FieldInfo info in enumType.GetFields())
				{
					if(info.Name == actionTypeAttribute.Value)
					{
						// now create and add the new action 
						RQAssemblies.StateEngine.Action.ActionType actionType = (RQAssemblies.StateEngine.Action.ActionType)info.GetValue(null);
						Action actionToAdd = new Action(actionType);

						switch(actionType)
						{
							case RQAssemblies.StateEngine.Action.ActionType.SLEEP:
							{
								System.Xml.XmlNode sleepTime = actionNode.SelectSingleNode("SleepTime");
								if(sleepTime != null)
								{
									actionToAdd.SleepTime = System.Convert.ToInt32(sleepTime.InnerText);
								}
								break;
							}
							case RQAssemblies.StateEngine.Action.ActionType.MOVETONEXT:
							{
								System.Xml.XmlNode sleepTime = actionNode.SelectSingleNode("SleepTime");
								if(sleepTime != null)
								{
									actionToAdd.SleepTime = System.Convert.ToInt32(sleepTime.InnerText);
								}
								break;
							}
						}

						state.ActionOnExit = actionToAdd;
					}
				}
			}

			Add(state);
		}


		/// <summary>
		/// main thread function
		/// </summary>
		private void EngineThread()
		{
			do
			{
				// re-start engine case
				if(currentStateNumber == -1)
				{
					try
					{
						currentStateNumber = this[0].StateNumber;
					}
					catch(IndexOutOfRangeException ex)
					{
					}
				}

				// if we have a valid state then process it...
				if(currentStateNumber != -1)
				{
					State state = Find(currentStateNumber);
					if(state != null)
					{
						System.Diagnostics.Trace.WriteLine(string.Format("RQAssemblies.StateEngine.Engine - Thread found state ({0})", state.StateNumber));

						// a chance for us to do something if we wish
						
						Action actionOnExit = state.Method();

						// determine what to do next...
						if(actionOnExit != null)
						{
							switch(actionOnExit.Type)
							{
								case RQAssemblies.StateEngine.Action.ActionType.MOVETONEXT:
								{
									System.Threading.Thread.Sleep(System.Convert.ToInt32(actionOnExit.SleepTime));
									currentStateNumber = actionOnExit.StateNumberToMoveTo;
									break;
								}
								case RQAssemblies.StateEngine.Action.ActionType.EXIT:
								{
									exitThread = true;
									break;
								}
								case RQAssemblies.StateEngine.Action.ActionType.SLEEP:
								{
									System.Threading.Thread.Sleep(System.Convert.ToInt32(actionOnExit.SleepTime));
									break;
								}
							}
						}
					}
					else
					{
						System.Diagnostics.Trace.WriteLine(string.Format("RQAssemblies.StateEngine.Engine - ERROR STATE ({0}) NOT FOUND", currentStateNumber));
						exitThread = true;
					}
				}
			}
			while (exitThread == false);
		}


		/// <summary>
		/// finds a state based on its number
		/// </summary>
		/// <param name="stateNumber">the state number to find</param>
		/// <returns></returns>
		private State Find(long stateNumber)
		{
			foreach(State state in stateList)
			{
				if(state.StateNumber == stateNumber)
				{
					return state;
				}
			}

			return null;
		}


		#endregion Private methods

		#endregion Threading

		#region Collection
		// Throw an Exception if object is not an Exception
		private void ValidateType(object O)
		{
			if (O.GetType() != typeof(State))
			{
				throw new ArgumentException("Value must be of type State");
			}
		}

		public State this[int Index]
		{
			get
			{
				return (State)stateList[Index];
			}
			set
			{
				// Cannot be null
				if (value == null)
				{
					throw new ArgumentNullException();
				}
				stateList[Index] = value;
			}
		}

		#region IList methods

		object System.Collections.IList.this[int Index]
		{
			get
			{
				return this[Index];
			}
			set
			{
				ValidateType(value);
				stateList[Index] = value as State;
			}
		}

		public bool Contains(object value)
		{
			ValidateType(value);
			return stateList.Contains(value);
		}

		public void Clear()
		{
			stateList.Clear();
		}

		public int Add(object value)
		{
			ValidateType(value);
			return stateList.Add(value);
		}

		public void RemoveAt(int index)
		{
			stateList.RemoveAt(index);
		}

		public void Remove(object value)
		{
			stateList.Remove(value);
		}

		bool System.Collections.IList.IsReadOnly
		{
			get
			{
				return false;
			}
		}

		public bool IsFixedSize
		{
			get
			{
				return false;
			}
		}

		public void Insert(int index, object value)
		{
			stateList.Insert(index, value);
		}

		public int IndexOf(object value)
		{
			ValidateType(value);
			return stateList.IndexOf(value);
		}

		#endregion IList methods

		#region IEnumerator methods
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return stateList.GetEnumerator();
		}

		public object SyncRoot
		{
			get
			{
				return stateList.SyncRoot;
			}
		}

		public bool IsSynchronized
		{
			get
			{
				return stateList.IsSynchronized;
			}
		}

		public int Count
		{
			get
			{
				return stateList.Count;
			}
		}

		public void CopyTo(System.Array array, int index)
		{
			stateList.CopyTo(array, index);
		}
		#endregion IEnumerator methods
		#endregion Collection
	}
}

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.


Written By
Web Developer
Australia Australia
Web developer with 15 years of commercial and industrial experience in the software world.
Now working as a contractor through his own company http://codeconsults.com/. See his blog here
Personal homepage http://russquinn.com/

Comments and Discussions