#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 } }