#region --------------- Copyright Dresser Wayne Pignone -------------
/*
* $Log: /Wrk/WayneLibraries/Wrk/StateEngine/StateMachine.cs $
*
* 22 08-09-19 11:05 roger.månsson
*
* 21 08-03-25 14:15 Mattias.larsson
* Clean-up.
*
* 20 08-03-25 10:32 Mattias.larsson
* Ensure debugLogger is not null.
*
* 19 08-03-19 11:44 roger.månsson
*
* 18 08-03-19 11:40 roger.månsson
* Fixes after code review. Added some lock protections.
*
* 17 08-03-19 11:15 roger.månsson
* Made timer debug logging Maximized.
*
* 16 08-03-19 9:19 roger.månsson
* Removed debug code that caused compiler warning.
*
* 15 08-03-18 11:10 roger.månsson
* Each timer does not have it's own real timer anymore. Instead there are
* a pool of timers in the StateMachine that can be shared among the
* timers in the same state machine.
*
* 14 08-03-10 14:59 Mattias.larsson
* Removed Paused and StepEvent().
*
* 13 08-02-26 14:14 Mattias.larsson
* Added LogNameKind.
* Renamed stateName to stateFactoryName.
*
* 12 08-01-23 16:34 Mattias.larsson
*
* 11 07-10-28 14:48 roger.månsson
* Check if currentState is null before exiting it.
*
* 10 07-09-26 10:51 roger.månsson
* Throw exception if a state object not can be created by any of the
* state factories.
*
* 9 07-09-06 7:33 roger.månsson
* Removed the OnLog event finally, so we can get rid of the warnings.
*
* 8 07-08-17 15:46 roger.månsson
* Made Initialize() method public, so the state machine can be built up
* of state objects without starting it.
*
* 7 07-08-15 15:29 roger.månsson
* Support for new debug log feature. Misc shutdown fixes.
*
* 6 07-08-07 8:26 Mattias.larsson
*
* 5 07-03-12 15:17 roger.månsson
* Documentation update.
*
* 4 07-03-12 15:00 roger.månsson
* Implement a Create method that can create both threaded and synchronous
* state machines.
*/
#endregion
#region Old change history
/*===============================================================================
* Change history
* When Who Comment
* ---------- ------ ------------------------------------
* 2006-08-08 RMa Added a predicate-based RemovePendingEvents method.
* 2006-08-08 RMa Added LogType to the log event args.
* 2006-07-18 RMa Major changes, StateMachine is now abstract base class, moved out logic to sub classes CompositeStateMachine,
* RootStateMachine, ThreadedRootStateMachine. Changed transition handling and event handling.
* 2006-07-03 MLa Added Transition in StateChangedEventArgs
* 2006-05-17 RMa Replaced diagnostic logging with OnLog event. Now it is up to the application
* how the logging should be performed (preferrably using the Wayne.Lib.Log.Logger class)
* 2006-05-09 RMa Changed dispose implementation, Obsoleted Stop() method. Use Dispose() instead.
* 2006-04-26 RMa Moved out StateChangedEventArgs to its own source file. Implemented IDisposable correctly.
* 2006-03-02 RMa FXCop updates: Changed delegate for the OnStateChanged event. Misc updates.
* 2006-02-23 RMa Added time to logging.
* 2006-02-03 RMa FXCop updates: Implement IDisposable, set internal messages as handled + misc.
* 2006-02-01 RMa Check if the state machine is running before stopping it in the destructor.
* 2006-01-27 RMa Removed interface Event. Use Event class instead.
* 2006-01-09 RMa Create all state objects at startup, otherwise explicit transitions will not work.
* Changed some of the explicit transition handling.
* 2006-01-06 RMa Added handling of Explicit transititions.
* Handle transitions asynchronously via the message thread.
* 2005-12-14 RMa Changed so Internal_Enter is called by the state machine, so we are sure
* that the internal stuff gets called ,if the user forgets to call base.Enter()....
* 2005-12-09 RMa Added IncomingEvent without the highpriority flag that not is used anyway.
* Changed the Init transition so it uses the BasicTransitionType.Init istead of the sting "Init" as type.
* 2005-12-09 RMa Fixed some issues with initial state entries.
* 2005-12-05 RMa This header added.
*
---------------------------------------------------------------------------------*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Wayne.Lib.Log;
namespace Wayne.Lib.StateEngine
{
///
/// State machine is the core engine in the Wayne.Lib.StateEngine model. It contains the states, contains the
/// information about the state- transition lookup table, and handles the external and internal events.
/// Normally it should not be neccesary to override this class.
///
//[System.Diagnostics.DebuggerNonUserCode()]
public abstract class StateMachine : IEventConsumer, IDisposable
{
#region Fields
private const bool TimerWrapperCaching = false;
private InitialState initialState;
private Dictionary createdStates = new Dictionary();
private State currentState;
private StateTransitionLookup stateLookup;
internal StateFactories stateFactories = new StateFactories();
private StateTypeContainer stateTypeContainer;
internal List timerWrapperList = new List();
private object timerWrapperListLock = new object();
private EventHandler timerOnEnableEventHandler;
private EventHandler timerOnDisableEventHandler;
private StateMachine parentStateMachine;
private int depth;
private bool initialized;
private object initalizeLock = new object();
private string name;
private StateNameKind logNameKind;
private StateMachine mainStateMachine; // Refers to the main statemachine.
///
/// Tells whether the object is disposed or not.
///
protected internal bool disposed;
///
/// The DebugLogger.
///
internal protected IDebugLogger debugLogger;
///
/// The LogCategory.
///
internal protected object logCategory;
#endregion
#region Events
///
/// Event that signals just before a state change will occur.
///
public event EventHandler OnStateChange;
///
/// Event that signals just after a state change has occured.
///
public event EventHandler OnStateChanged;
///
/// Even that is fired when the final state of a state machine is reached.
///
public event EventHandler OnFinalStateEntered;
#endregion
#region Construction
///
/// Constructor for the StateMachine class.
///
internal StateMachine(string name, IDebugLogger debugLogger, object logCategory)
{
this.name = name;
this.debugLogger = debugLogger;
this.logCategory = logCategory;
this.stateTypeContainer = new StateTypeContainer();
stateLookup = new StateTransitionLookup(stateTypeContainer);
timerOnDisableEventHandler = new EventHandler(timer_OnDisable);
timerOnEnableEventHandler = new EventHandler(timer_OnEnable);
}
///
/// Destructor (finalizer). Stops the state machine if it has not already been done.
///
~StateMachine()
{
Dispose(false);
}
#endregion
#region Public Methods
///
/// Starts the state machine. This method should be called after the machine has been configured and
/// equipped with at least one state factory.
///
/// Thrown if the engine is already started.
public abstract void Start();
///
/// Sends an event into the state machine.
///
/// Incoming event
public abstract void IncomingEvent(StateEngineEvent stateEngineEvent);
///
/// Gets the pending envents from the parentStateMachine.
///
///
/// null or an IEnumberable
public virtual IEnumerable GetPendingEventsOfType(object eventType)
{
return parentStateMachine != null ? parentStateMachine.GetPendingEventsOfType(eventType) : null;
}
///
/// DO NOT USE for any isfs based event!
/// Removes all pending events that matches the specified event type. Comparison is made
/// with the .Equals method.
///
///
public virtual void RemovePendingEventsOfType(object eventType)
{
if (parentStateMachine != null)
parentStateMachine.RemovePendingEventsOfType(eventType);
}
///
/// Removes all pending event that matches the supplied predicate.
///
/// Type of the comparison object
/// The predicate that is used to match the event.
/// The comparison object that is used in the StateEngineEventPredicate.
public virtual void RemovePendingEvents(StateEngineEventPredicate predicate, TComparisonObject comparisonObject)
{
if (parentStateMachine != null)
parentStateMachine.RemovePendingEvents(predicate, comparisonObject);
}
///
/// Gets all pending events that matches the supplied predicate
///
/// Type of the comparison object
/// The predicate that is used to match the event.
/// The comparison object that is used in the StateEngineEventPredicate.
/// enumberable of StateEngineEvents
public virtual IEnumerable GetPendingEvents(
StateEngineEventPredicate predicate, TComparisonObject comparisonObject)
{
return parentStateMachine != null ? parentStateMachine.GetPendingEvents(predicate, comparisonObject) : null;
}
///
/// Presents the class as a string.
///
///
public override string ToString()
{
return name;
}
#endregion
#region State factory methods
///
/// Clears the list of state factories.
///
public void ClearStateFactories()
{
stateFactories.Clear();
}
///
/// Adds a custom state factory to the factory collection, so it can be used
/// to create State objects.
///
/// The factory to add
public void AddStateFactory(IStateFactory factory)
{
stateFactories.AddFactory(factory);
}
#endregion
#region Private Methods
///
/// Performs the actual state transition.
///
///
/// The state that the machine shall enter.
///
///
/// Parameters that defines the entry, like HistoryType and the source transition.
///
/// Perform a transaction by assigning a transition object to this ref parameter.
private void PerformStateChange(State newState, Wayne.Lib.StateEngine.StateEntry entry, ref Transition transition)
{
// First, save the current state
State oldState = currentState;
CheckAndRemoveTimersForCurrentState();
// Set which is the current state.
currentState = newState;
// Exit the old state
if ((oldState != null) && oldState.Active)
oldState.PerformExit();
StateChangedEventArgs stateChangeEventArgs = new StateChangedEventArgs(oldState, newState, entry.SourceTransition);
// Notify descendant classes and others intrested.
if (OnStateChange != null)
OnStateChange.Invoke(this, stateChangeEventArgs);
// Enter the new state.
currentState.PerformEnter(entry, ref transition);
// Notify descendant classes and others intrested.
if (OnStateChanged != null)
OnStateChanged.Invoke(this, stateChangeEventArgs);
}
///
/// Returns a state object that corresponds to the string passed in.
/// It first checks if the state object already is created and if it
/// not finds the state, it asks the connected state factories to
/// create it.
///
///
/// Name of the state. This should correspond to a state that can
/// be created by any of the state factories connected to the
/// state machine.
///
///
/// A state object that corresponds to the state name.
/// If the state can not be found, it will return null.
///
private State GetState(string stateFactoryName)
{
State result;
if (!createdStates.TryGetValue(stateFactoryName, out result))
{
createdStates.Add(stateFactoryName, result = CreateState(stateFactoryName));
}
return result;
}
///
/// Creates a state from the supplied state name.
///
///
///
internal protected virtual State CreateState(string stateFactoryName)
{
State newState = stateFactories.CreateState(stateFactoryName, stateTypeContainer);
if (newState == null && ParentStateMachine != null)
newState = ParentStateMachine.stateFactories.CreateState(stateFactoryName, stateTypeContainer);
if (newState == null && MainStateMachine != null)
MainStateMachine.stateFactories.CreateState(stateFactoryName, stateTypeContainer);
if (newState != null)
{
newState.WritableFactoryName = stateFactoryName;
newState.SetParentStateMachine(this);
newState.SetDebugLogger(debugLogger, logCategory);
}
return newState;
}
///
/// Forces the state machine to create all its states. It
/// uses the state transition table to determine which objects are included in
/// the machine.
///
private void CreateStateObjects()
{
string[] stateFactoryNames = stateLookup.GetStateNameList(false);
foreach (string stateFactoryName in stateFactoryNames)
{
State state = this.GetState(stateFactoryName);
if (state != null)
{
InitialState tempInitState = state as InitialState;
if (tempInitState != null)
{
initialState = tempInitState;
}
else
{
CompositeState compositeState = state as CompositeState;
if (compositeState != null)
{
compositeState.Initialize();
}
}
}
else
{
throw new InvalidOperationException("Can not create state " + stateFactoryName);
}
}
}
#endregion
#region Public Properties
///
/// The state transition lookup object. Use to set up the state machine in code.
///
public StateTransitionLookup StateTransitionLookup
{
get { return stateLookup; }
}
///
/// Indicates how many levels the state machine is from the bottom state machine. The first state machine has
/// depth 0. States in a composite state to that machine has depth 1 and so on.
///
public int Depth
{
get { return depth; }
set { depth = value; }
}
///
/// Current state object.
///
public State CurrentState
{
get { return currentState; }
}
///
/// Gets the current state by recursively entering composite states and returning the innermost state.
///
public State CurrentStateRecursive
{
get
{
return GetStateRecursively(currentState);
}
}
private State GetStateRecursively(State state)
{
if (state is CompositeState)
{
var compositeState = ((CompositeState)state);
if (compositeState.StateMachine.CurrentState == null)
return compositeState;
else
return GetStateRecursively(compositeState.StateMachine.CurrentState);
}
else
{
return state;
}
}
///
/// List of the created states in the machine.
///
public System.Collections.ObjectModel.ReadOnlyCollection CreatedStates
{
get { return createdStates.Values.ToList().AsReadOnly(); }
}
///
/// Name of the state machine given when the state machine was created.
///
public string Name
{
get { return name; }
}
///
/// Indicates that the state machine has been started.
///
public abstract bool Started { get; }
///
/// Initializes the state machine, creates all state objects.
///
public void Initialize()
{
lock (initalizeLock)
{
if (!initialized)
{
//Force a creation of all states in the state machine.
CreateStateObjects();
if (initialState == null)
throw new StateEngineException("Must set an initial state for the state machine! In " + name);
initialized = true;
}
}
}
///
/// The kind of name to use for logging the state name.
///
public StateNameKind LogNameKind
{
get { return MainStateMachine.logNameKind; }
set { MainStateMachine.logNameKind = value; }
}
private StateMachine MainStateMachine
{
get
{
if (mainStateMachine == null)
{
if (parentStateMachine != null)
{
mainStateMachine = parentStateMachine;
while (mainStateMachine.parentStateMachine != null)
mainStateMachine = mainStateMachine.parentStateMachine;
}
else
mainStateMachine = this;
}
return mainStateMachine;
}
}
#endregion
#region Internal Properties
///
/// If the state machine is contained in a composite state, the state machine
/// contains a reference to the state machine that owns the composite state.
///
internal StateMachine ParentStateMachine
{
get { return parentStateMachine; }
set { parentStateMachine = value; }
}
///
/// Indicates that all state objects have been created.
///
internal bool Initialized
{
get
{
lock (initalizeLock)
{
return initialized;
}
}
}
///
/// Sets and merges the new state type container with the one that were there to begin with.
///
///
public void SetAndMergeTypeContainer(StateTypeContainer newStateTypeContainer)
{
newStateTypeContainer.Register(stateTypeContainer); //Copy the registered types from the current type container to the new one.
stateTypeContainer = newStateTypeContainer;//Overwrite the old one.
}
#endregion
#region Internal Methods
///
/// Performs the entry of the initial state of the state machine.
///
///
/// If a transition should be performed, the transition reference is assigned to.
internal void EnterInitialState(Transition sourceTransition, ref Transition transition)
{
StateEntry stateEntry = null;
if (sourceTransition == null)
{
stateEntry = new StateEntry(new Transition(null, BasicTransitionType.Init),
this.initialState.FactoryName,
HistoryType.None);
}
else
{
stateEntry = new StateEntry(sourceTransition, this.initialState.FactoryName, HistoryType.None);
}
PerformStateChange(this.initialState, stateEntry, ref transition);
if (transition != null)
this.HandleTransition(ref transition);
}
///
/// When the State machine is contained in a composite state, and the composite state is
/// entered, this method is called , so the entry can be done.
///
/// The entry containing information about with which history type to enter the state.
/// If a transition should be performed, the transition reference is assigned to.
internal void EnterState(StateEntry stateEntry, ref Transition transition)
{
switch (stateEntry.HistoryType)
{
/////////////////////////////////
case HistoryType.None:
{
EnterInitialState(stateEntry.SourceTransition, ref transition);
break;
}
////////////////////////////////
case HistoryType.Shallow:
{
if (currentState != null)
{
var initEntry = new StateEntry(stateEntry.SourceTransition,
initialState.FactoryName,
HistoryType.None);
PerformStateChange(currentState, initEntry, ref transition);
}
else
EnterInitialState(stateEntry.SourceTransition, ref transition);
break;
}
/////////////////////////////////
case HistoryType.Deep:
{
if (currentState != null)
{
var InitEntry = new StateEntry(stateEntry.SourceTransition,
initialState.FactoryName,
HistoryType.Deep);
PerformStateChange(currentState, InitEntry, ref transition);
}
else
EnterInitialState(stateEntry.SourceTransition, ref transition);
break;
}
/////////////////////////////////
case HistoryType.Explicit:
{
var explicitTransition = stateEntry.SourceTransition;
this.HandleTransition(ref explicitTransition);
if (explicitTransition != null) //The handleTransition returned a new transition.
transition = explicitTransition;
break;
}
}
}
///
/// When the State machine is contained in a composite state, and the composite state is
/// exited, this method is called, so the current state of the machine can be exited.
///
internal void ExitState()
{
lock (this)
{
if (currentState != null)//Current state can be null in some rare startup cases.
currentState.PerformExit();
//Clear the timers when exiting.
lock (timerWrapperListLock)
{
if ((debugLogger != null) && debugLogger.IsActive(DebugLogLevel.Maximized))
debugLogger.Add("Disabling all TimerWrappers", DebugLogLevel.Maximized);
var tempTimerWrapperList = new List(timerWrapperList);
foreach (var wrapper in tempTimerWrapperList)
wrapper.Disable();
}
}
}
///
/// Handles events
///
///
///
internal virtual void HandleEvent(StateEngineEvent stateEngineEvent, ref Transition transition)
{
currentState.IncomingEvent(stateEngineEvent, ref transition);
if (transition != null)
this.HandleTransition(ref transition);
}
///
/// Receives a transition from a state object. Looks up the transition and enters the correct next state.
/// If this state machine is embedded in a composite state and the transition not resulted in a state change
/// in this machine, the transition is passed on to the parent machine by changing the reference parameter to a new transition object.
///
/// It is forbidden to transition from a final state in a state machine, so transitions made while we are
/// in a final state is sent right to the state machine that contains the composite state that owns this
/// machine.
///
///
internal void HandleTransition(ref Transition transition)
{
var explicitTransition = transition as ExplicitTransition;
bool transitionHandled;
if (disposed)
return;
do
{
transitionHandled = false;
//-------------------------------------
//If it is NOT an explict transition
if (explicitTransition == null)
{
//First, Check if this is a transition that has a configured target state
//in this state machine.
var entry = StateTransitionLookup.GetNextState(currentState.FactoryName, transition);
if ((entry != null) && !(currentState is FinalState))
{
transitionHandled = true;
transition = null;
var St = this.GetState(entry.TargetStateFactoryName);
if (St != null)
PerformStateChange(St, entry, ref transition);
}
//If the current state is a final state, just drop the transition undhandled, and it will be
//passed on to the parent state machine automatically through the ref parameter
}
//-------------------------------------
//Explicit transition
else
{
State nextState = null;
//First search for the state pointed out by the target state, or at least a composite state that
//contains the target state.
foreach (var state in createdStates)
{
if (state.Value.LookupState(explicitTransition.TargetStateFactoryName))
{
nextState = state.Value;
break;
}
}
//If a state was found in this machine, enter the state.
if (nextState != null)
{
var entry = new StateEntry(explicitTransition, nextState.FactoryName, HistoryType.Explicit);
transition = null;
transitionHandled = true;
PerformStateChange(nextState, entry, ref transition);
}
//If the target state not is found, just drop the transition unhandled and it will be passed
//on to the parent state machine automatically through the ref parameter
}
} while (transitionHandled && transition != null && !disposed);
}
///
/// Clears all pending events from the state machine.
///
internal virtual void ClearPendingEvents()
{
if (parentStateMachine != null)
parentStateMachine.ClearPendingEvents();
}
///
/// Fires the OnFinalStateEntered event.
///
internal void FireFinalStateEntered()
{
if (OnFinalStateEntered != null)
OnFinalStateEntered(this, EventArgs.Empty);
}
internal void SetDebugLogger(IDebugLogger debugLogger, object logCategory)
{
this.debugLogger = debugLogger;
this.logCategory = logCategory;
}
#endregion
#region IDisposable Members
///
/// The private dispose method taking into account if it is disposed by the
/// finalizer or through a explicit Dispose() call.
///
///
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
disposed = true;
if (Started && (debugLogger != null) && debugLogger.IsActive(logCategory))
debugLogger.Add("--------- Stopping State machine (Dispose)---------", logCategory);
if ((currentState != null) && currentState.Active)
{
currentState.PerformExit();
currentState = null;
}
if (disposing)
{
//Dispose the timer wrappers
lock (timerWrapperListLock)
{
List tempTimerWrapperList = new List(timerWrapperList);
foreach (TimerWrapper timerWrapper in tempTimerWrapperList)
{
timerWrapper.Dispose();
}
timerWrapperList.Clear();
}
createdStates.Values.ForEach(x => x.Dispose());
createdStates.Clear();
timerOnDisableEventHandler -= timer_OnDisable;
timerOnEnableEventHandler -= timer_OnEnable;
}
}
}
///
/// Disposes resources.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Timer methods
///
/// Activate the supplied timer. This should be called from state object when they want a timer functionality.
///
/// The timer to activate
///
public void ActivateTimer(Wayne.Lib.StateEngine.Timer timer)
{
timer.OnEnable += timerOnEnableEventHandler;
timer.OnDisable += timerOnDisableEventHandler;
timer.Enable();
if ((debugLogger != null) && debugLogger.IsActive(DebugLogLevel.Maximized))
debugLogger.Add("Activating timer for " + timer.ToString(), DebugLogLevel.Maximized);
}
///
/// A timer should be disabled. Get the timerwrapper for this stateengine timer and
/// disable it if it exists.
///
///
///
void timer_OnDisable(object sender, EventArgs e)
{
Timer stateEngineTimer = (Timer)sender;
if ((debugLogger != null) && debugLogger.IsActive(DebugLogLevel.Maximized))
debugLogger.Add("Disabling StateEngineTimer " + stateEngineTimer.ToString(), DebugLogLevel.Maximized);
lock (timerWrapperListLock)
{
// try to find a timer that is mapped to this state engine timer.
TimerWrapper foundTimerWrapper = null;
foreach (TimerWrapper wrapper in timerWrapperList)
{
if (wrapper.StateEngineTimer == stateEngineTimer)
{
foundTimerWrapper = wrapper;
break;
}
}
if (foundTimerWrapper != null)
{
foundTimerWrapper.StateEngineTimer = null;
if (!TimerWrapperCaching)//Dispose the timer wrapper and remove it from the list.
{
foundTimerWrapper.Dispose();
timerWrapperList.Remove(foundTimerWrapper);
}
//else //Old logic, just disable the timer, and cache it.
//{
// foundTimerWrapper.Disable();
//}
}
}
}
///
/// A timer should be enabled, Get a TimerWrapper and attach the state engine timer.
///
///
///
void timer_OnEnable(object sender, EventArgs e)
{
Timer stateEngineTimer = (Timer)sender;
if ((debugLogger != null) && (debugLogger.IsActive(DebugLogLevel.Maximized)))
debugLogger.Add("Enabling StateEngineTimer for " + stateEngineTimer.ToString(), DebugLogLevel.Maximized);
lock (timerWrapperListLock) //Lock here just in case. . .
{
TimerWrapper wrapper = GetTimerWrapper(stateEngineTimer);
wrapper.Enable();
}
}
///
/// Get a TimerWrapper that is 1)Already assigned to this state engine timer, 2) Available to use, 3)If none available, create a new wrapper.
/// It does also assign the StateEngineTimer to the wrapper, so it is directly marked that it is taken.
///
///
///
private TimerWrapper GetTimerWrapper(Timer stateEngineTimer)
{
TimerWrapper wrapper = null;
lock (timerWrapperListLock)
{
//First try to find a timer that already is mapped to this state engine timer.
foreach (TimerWrapper tempWrapper in timerWrapperList)
{
if (tempWrapper.StateEngineTimer == stateEngineTimer)
{
wrapper = tempWrapper;
break;
}
}
//If not found, search for a timerwrapper that is free to use
if (wrapper == null)
{
foreach (TimerWrapper tempWrapper in timerWrapperList)
{
if (tempWrapper.StateEngineTimer == null)
{
wrapper = tempWrapper;
wrapper.StateEngineTimer = stateEngineTimer;
break;
}
}
}
//Otherwise, create a new wrapper and add it to the list.
if (wrapper == null)
{
wrapper = new TimerWrapper(TimerFired);
wrapper.StateEngineTimer = stateEngineTimer;
timerWrapperList.Add(wrapper);
}
}
return wrapper;
}
///
/// One of the timers in the timer list has fired. Create a timer event and enqueue it.
///
///
void TimerFired(object state)
{
TimerWrapper timerWrapper = (TimerWrapper)state;
StateEngineEvent stateEngineEvent = null;
lock (timerWrapperListLock)
{
Timer stateEngineTimer = timerWrapper.StateEngineTimer;
if (stateEngineTimer != null)
{
if ((debugLogger != null) && debugLogger.IsActive(DebugLogLevel.Maximized))
debugLogger.Add("StateEngineTimer Fired " + stateEngineTimer.ToString(), DebugLogLevel.Maximized);
//If this is a one time timer, it should be removed from the timer list, and be disabled.
if (!stateEngineTimer.IsPeriodic)
{
stateEngineTimer.Disable();
}
stateEngineEvent = new TimerEvent(stateEngineTimer.UserToken, stateEngineTimer.EventType);
}
}
if (stateEngineEvent != null)
IncomingEvent(stateEngineEvent); //We want to fire the event outside the lock to see so we don't happen to deadlock with other locks in the state machine.
}
///
/// Removes the timers that is owned by the current state, and that is specified to be cleared at exit.
///
private void CheckAndRemoveTimersForCurrentState()
{
//Check if there are any timers that should be removed before leaving the state.
lock (timerWrapperListLock)
{
List tempTimerWrapperList = new List(timerWrapperList); //Create a copy of the timer wrapper list to iterate since it will modify.
foreach (TimerWrapper timerWrapper in tempTimerWrapperList)
{
if (timerWrapper.StateEngineTimer != null)
{
if (timerWrapper.StateEngineTimer.OwnerState == currentState)
{
if (timerWrapper.StateEngineTimer.ClearAtStateExit)
{
if ((debugLogger != null) && debugLogger.IsActive(DebugLogLevel.Maximized))
debugLogger.Add("!IsPeriodic: Disabling timer because of state change " + timerWrapper.StateEngineTimer.ToString(), DebugLogLevel.Maximized);
timerWrapper.StateEngineTimer.Disable();//This will trigger the wrapper to remove its StateEngineTimer.
}
}
}
}
}
}
#endregion
#region Static Methods
///
/// Creates and returns StateMachine object. This method creates Threaded Statemachine. To create other implementations,
/// use the Create(StateMachineType stateMachineType, string name) method instead.
///
/// Name of the state machine.
/// The DebugLogger to use.
/// The log category.
/// A statemachine object
public static StateMachine Create(string name, IDebugLogger debugLogger, object logCategory)
{
return Create(name, StateMachineType.Threaded, debugLogger, logCategory);
}
///
/// Creates and returns StateMachine object. The state machine type determines wich implementation should be used.
///
/// Name of the state machine.
/// Type of state machine implementation that should be created.
/// The DebugLogger to use.
/// The log category.
///
public static StateMachine Create(string name, StateMachineType stateMachineType, IDebugLogger debugLogger, object logCategory)
{
switch (stateMachineType)
{
case StateMachineType.Threaded: return new ThreadedRootStateMachine(name, debugLogger, logCategory);
case StateMachineType.Synchronous: return new SynchronousRootStateMachine(name, debugLogger, logCategory);
default: throw new NotImplementedException("This statemachine type is not yet implemented.");
}
}
#endregion
}
}