123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- using System;
- using System.Globalization;
- using Wayne.Lib.Log;
- namespace Wayne.Lib.StateEngine
- {
- /// <summary>
- /// History type defines the way that a composite state is entered.
- /// </summary>
- public enum HistoryType
- {
- #region Fields
- /// <summary>
- /// Only the initial state is entered. No history is recalled
- /// </summary>
- None,
- /// <summary>
- /// If the composite has been active before, the state that was
- /// active last is entered. Shallow means that entering the
- /// recalled last state but when entering that state, it is done with
- /// no history.
- /// </summary>
- Shallow,
- /// <summary>
- /// If a composite state has been active before, the state that was active
- /// last is entered, Deep means that if the recalled state is a composite
- /// it will also be entered with deep history.
- /// </summary>
- Deep,
- /// <summary>
- /// Explicit history type is *only* used when issuing explicit transitions. If
- /// a state machine is configured with this history type, an error will be thrown.
- /// </summary>
- Explicit
- #endregion
- }
- // StateType for future use.
- /// <summary>
- /// The state types as an enumeration. This can be used in a future design tool.
- /// </summary>
- public enum StateType
- {
- #region Fields
- /// <summary>
- /// Intial state
- /// </summary>
- InitialState,
- /// <summary>
- /// Pseudo state
- /// </summary>
- PseudoState,
- /// <summary>
- /// Ordinary state
- /// </summary>
- State,
- /// <summary>
- /// Composite state
- /// </summary>
- CompositeState,
- /// <summary>
- /// Final state
- /// </summary>
- FinalState
- #endregion
- }
- /// <summary>
- /// State is the base state of all states in the state machine.
- /// </summary>
- abstract public class State : IDisposable
- {
- #region Fields
- private Wayne.Lib.StateEngine.StateMachine parentStateMachine;
- private string createdByFactory = "";
- private bool active;
- private CompositeState parentState;
- private IDebugLogger debugLogger;
- private object logCategory;
- private string instanceName;
- private string factoryName;
- #endregion
- #region Methods
- /// <summary>
- /// Enter is called when the state machine enters the state. Override this method to be able to
- /// run code at the state entry. If a transition should be performed, create a transition object
- /// and return it in the transition out property.
- /// </summary>
- /// <param name="stateEntry">Information about the entry of the state.</param>
- /// <param name="transition">Out parameter, that should be set to either the reference to a transition object or null.</param>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")]
- protected virtual void Enter(StateEntry stateEntry, ref Transition transition)
- {
- }
- /// <summary>
- /// Override this method to implement code that should be run at state exit.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Wayne.Lib.StateEngine.State.DebugLog(System.String)")]
- protected virtual void Exit()
- {
- }
- /// <summary>
- /// Override to receive incoming events. If the event is handled, the
- /// application must set the event.Handled = true.
- /// </summary>
- /// <param name="stateEngineEvent">The event object that should be handled.</param>
- /// <param name="transition">Out parameter that should be set to either the reference to a transition object or null.</param>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")]
- protected virtual void HandleEvent(StateEngineEvent stateEngineEvent, ref Transition transition)
- {
- }
- /// <summary>
- /// Activates the supplied timer.
- /// </summary>
- /// <param name="timer">The timer to activate.</param>
- /// <see cref="Timer"/>
- protected virtual void ActivateTimer(Timer timer)
- {
- if (parentStateMachine != null)
- parentStateMachine.ActivateTimer(timer);
- }
- /// <summary>
- /// Disposes the resources owned by the state object.
- /// </summary>
- /// <param name="disposing"></param>
- protected virtual void Dispose(bool disposing)
- {
- //Nothing in an ordinary state.
- }
- /// <summary>
- /// Clears all events waiting in the event queues.
- /// </summary>
- protected void ClearPendingEvents()
- {
- this.parentStateMachine.ClearPendingEvents();
- }
- /// <summary>
- /// Clears all the events in the resend queue that matches the eventType.
- /// </summary>
- /// <param name="eventType"></param>
- protected void RemovePendingEventsOfType(object eventType)
- {
- this.parentStateMachine.RemovePendingEventsOfType(eventType);
- }
- /// <summary>
- /// Removes all pending event that matches the supplied predicate.
- /// </summary>
- /// <typeparam name="TComparisonObject">Type of the comparison object</typeparam>
- /// <param name="predicate">The predicate that is used to match the event.</param>
- /// <param name="comparisonObject">The comparison object that is used in the StateEngineEventPredicate.</param>
- protected void RemovePendingEvents<TComparisonObject>(StateEngineEventPredicate<TComparisonObject> predicate, TComparisonObject comparisonObject)
- {
- this.ParentStateMachine.RemovePendingEvents(predicate, comparisonObject);
- }
- /// <summary>
- /// Disposes all the owned resources in the state.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
- #endregion
- #region Internal Methods
- /// <summary>
- /// Incoming event is called from the state machine when an event should be handled.
- /// </summary>
- /// <param name="stateEngineEvent"></param>
- /// <param name="transition">Perform a transition by assigning a transition object to this reference parameter.</param>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Wayne.Lib.StateEngine.State.DebugLog(System.String)")]
- internal virtual void IncomingEvent(StateEngineEvent stateEngineEvent, ref Transition transition)
- {
- try
- {
- if (!(this is CompositeState))
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- debugLogger.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, " [{0}] HandleEvent: {1}", this.LogName, stateEngineEvent), logCategory);
- }
- if (!stateEngineEvent.Handled)
- {
- //Call the HandleEvent method of the state
- HandleEvent(stateEngineEvent, ref transition);
- }
- //If a transition was issued, equip it with the event as source event.
- if (transition != null)
- transition.WritableSourceEvent = stateEngineEvent;
- }
- catch (Exception exception)
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- {
- System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
- stringBuilder.Append("EXCEPTION IN HandleEvent (");
- stringBuilder.Append(this.LogName);
- stringBuilder.Append(")\r\n");
- stringBuilder.Append(exception.ToString());
- debugLogger.Add(stringBuilder, logCategory);
- }
- transition = new ExceptionTransition(this, BasicTransitionType.Error, exception);
- }
- #if WindowsCE
- catch
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- {
- debugLogger.Add("Unknown exception in HandleEvent (" + this.LogName + ")");
- }
- transition = new Transition(this, BasicTransitionType.Error);
- }
- #endif
- }
- /// <summary>
- /// Called from the state machine when the state is entered. It is up to the state to
- /// call the appropriate virtual Enter methods. If a transition should be performed it
- /// is returned in the Transition ref parameter.
- /// </summary>
- /// <param name="stateEntry">State entry object containing information about the state entry.</param>
- /// <param name="transition">Perform a transition by assigning a transition object to this ref parameter.</param>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Wayne.Lib.StateEngine.State.DebugLog(System.String)")]
- internal virtual void PerformEnter(StateEntry stateEntry, ref Transition transition)
- {
- active = true;
- try
- {
- if (!(this is CompositeState))
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- debugLogger.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, " [{0}] Enter: Transition={1}", this.LogName, stateEntry.SourceTransition.Name), logCategory);
- //Call the enter method of the state.
- Enter(stateEntry, ref transition);
- //If a transition was issued, equip it with the event as source event.
- if (transition != null)
- if ((stateEntry != null) && (stateEntry.SourceTransition != null))
- transition.WritableSourceEvent = stateEntry.SourceTransition.SourceEvent;
- }
- catch (Exception exception)
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- {
- System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
- stringBuilder.Append("EXCEPTION IN Enter (");
- stringBuilder.Append(this.LogName);
- stringBuilder.Append(")\r\n");
- stringBuilder.Append(exception.ToString());
- debugLogger.Add(stringBuilder.ToString(), logCategory);
- }
- transition = new ExceptionTransition(this, BasicTransitionType.Error, exception);
- }
- #if WindowsCE
- catch
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- debugLogger.Add("Unknown exception in Enter (" + this.LogName + ")");
- transition = new Transition(this, BasicTransitionType.Error);
- }
- #endif
- }
- /// <summary>
- /// This method is called by the state machine when the state should be exited. It contains the error handling
- /// logic and logging of the exit.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "Wayne.Lib.StateEngine.State.DebugLog(System.String)")]
- internal virtual void PerformExit()
- {
- active = false;
- try
- {
- Exit();
- }
- catch (Exception exception)
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- {
- System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
- stringBuilder.Append("EXCEPTION IN Exit (");
- stringBuilder.Append(this.LogName);
- stringBuilder.Append(")\r\n");
- stringBuilder.Append(exception.ToString());
- debugLogger.Add(stringBuilder, logCategory);
- }
- }
- #if WindowsCE
- catch
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- debugLogger.Add("Unknown exception in Exit (" + this.LogName + ")");
- }
- #endif
- }
- /// <summary>
- /// This function is used to locate a specific state in the state machine.
- /// In the state class it is simply compared to the state Factory Name. More complex comparisons
- /// is done in composite states.
- /// </summary>
- /// <param name="stateFactoryName">The factory name of the state that should be looked up.</param>
- /// <returns>True if the state is or contains a state with this name.</returns>
- internal virtual bool LookupState(string stateFactoryName)
- {
- return (stateFactoryName == this.FactoryName);
- }
- internal void AssignParentState(CompositeState parentState)
- {
- this.parentState = parentState;
- }
- #endregion
- #region Properties
- /// <summary>
- /// The factory name of the state (the full class name).
- /// </summary>
- public string FactoryName
- {
- get
- {
- return factoryName;
- }
- }
- /// <summary>
- /// Factory name of the class assigned after factory created the state.
- /// </summary>
- internal string WritableFactoryName
- {
- get { return factoryName; }
- set { this.factoryName = value; }
- }
- /// <summary>
- /// The name of this particular instance of this state (the hierarchical name of the state,
- /// starting with the name of the statemachine, through all parent composite states up to this state).
- /// </summary>
- public string InstanceName
- {
- get
- {
- if (instanceName != null)
- {
- return instanceName;
- }
- //I have a parent state - use that and append my name
- if (parentState != null)
- {
- return instanceName = string.Format("{0}.{1}", parentState.InstanceName, this.Name);
- }
- //I'm on the root - just use state machine name and append my name
- return instanceName = string.Format("{0}.{1}", ParentStateMachine.Name, Name);
- }
- }
- public virtual string Name
- {
- get { return this.GetType().Name; }
- }
- /// <summary>
- /// The name of the state used for logging.
- /// </summary>
- public string LogName
- {
- get
- {
- if (parentStateMachine != null)
- {
- switch (parentStateMachine.LogNameKind)
- {
- case StateNameKind.FactoryName: return FactoryName;
- case StateNameKind.InstanceName: return InstanceName;
- }
- }
- return FactoryName;
- }
- }
- /// <summary>
- /// Name of the state factory that created the state object.
- /// </summary>
- public string CreatedByFactory
- {
- get
- {
- return createdByFactory;
- }
- set
- {
- createdByFactory = value;
- }
- }
- /// <summary>
- /// Indicates that this is the current active state of the machine.
- /// </summary>
- public bool Active
- {
- get
- {
- return active;
- }
- }
- /// <summary>
- /// Provides a reference to the composite state this state is contained in. If it is in the root of the
- /// state machine, it will be null.
- /// </summary>
- public CompositeState ParentState
- {
- get { return parentState; }
- }
- /// <summary>
- /// The StateType.
- /// </summary>
- public StateType StateType
- {
- get
- {
- if (this is InitialState)
- return StateType.InitialState;
- else if (this is FinalState)
- return StateType.FinalState;
- else if (this is PseudoState)
- return StateType.PseudoState;
- else if (this is CompositeState)
- return StateType.CompositeState;
- return StateType.State;
- }
- }
- /// <summary>
- /// The parent state machine for the state.
- /// </summary>
- public StateMachine ParentStateMachine
- {
- get { return parentStateMachine; }
- }
- /// <summary>
- /// An additional text that shows up in the visualizer, that for instance can
- /// be used to point out application specific states.
- /// </summary>
- public virtual string ApplicationText
- {
- get;
- set;
- }
- #endregion
- #region Internal properties
- /// <summary>
- /// Set the parent state machine for the state.
- /// </summary>
- internal virtual void SetParentStateMachine(StateMachine parentStateMachine)
- {
- this.parentStateMachine = parentStateMachine;
- }
- /// <summary>
- /// Sets the debug logger.
- /// </summary>
- /// <param name="debugLogger"></param>
- /// <param name="logCategory"></param>
- internal virtual void SetDebugLogger(IDebugLogger debugLogger, object logCategory)
- {
- this.debugLogger = debugLogger;
- this.logCategory = logCategory;
- }
- #endregion
- /// <summary>
- /// Uses the debug logger passed in to the StateMachine upon creation to
- /// log the supplied logging if the logger is active.
- /// </summary>
- /// <param name="format"></param>
- /// <param name="params"></param>
- protected void DebugLog(string format, params object[] @params)
- {
- try
- {
- if (debugLogger.IsActive())
- {
- var message = @params != null && @params.Length > 0 ?
- string.Format(CultureInfo.InvariantCulture, format, @params) :
- format;
- debugLogger.Add(message);
- }
- }
- catch (FormatException)
- {
- if (debugLogger.IsActive())
- debugLogger.Add("LOGERROR: Could not format string: " + format);
- }
- }
- /// <summary>
- /// Uses the debug logger passed in to the StateMachine upon creation to
- /// log the supplied logging if the logger is active.
- /// </summary>
- /// <param name="format"></param>
- /// <param name="params"></param>
- protected void DebugLogDetailed(string format, params object[] @params)
- {
- try
- {
- if (debugLogger.IsActive(DebugLogLevel.Detailed))
- {
- var message = @params != null && @params.Length > 0 ?
- string.Format(CultureInfo.InvariantCulture, format, @params) :
- format;
- debugLogger.Add(message, DebugLogLevel.Detailed);
- }
- }
- catch (FormatException)
- {
- if (debugLogger.IsActive(DebugLogLevel.Detailed))
- debugLogger.Add("LOGERROR: Could not format string: " + format, DebugLogLevel.Detailed);
- }
- }
- /// <summary>
- /// Uses the debug logger passed in to the StateMachine upon creation to
- /// log the supplied logging if the logger is active.
- /// </summary>
- /// <param name="format"></param>
- /// <param name="params"></param>
- protected void DebugLogMaximized(string format, params object[] @params)
- {
- try
- {
- if (debugLogger.IsActive(DebugLogLevel.Detailed))
- {
- var message = @params != null && @params.Length > 0 ?
- string.Format(CultureInfo.InvariantCulture, format, @params) :
- format;
- debugLogger.Add(message, DebugLogLevel.Maximized);
- }
- }
- catch (FormatException)
- {
- if (debugLogger.IsActive(DebugLogLevel.Detailed))
- debugLogger.Add("LOGERROR: Could not format string: " + format, DebugLogLevel.Maximized);
- }
- }
- }
- }
|