123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- using System;
- using System.Collections.Generic;
- using Wayne.Lib.Log;
- namespace Wayne.Lib.StateEngine
- {
- //[System.Diagnostics.DebuggerNonUserCode()]
- abstract class EventBufferingRootStateMachine : RootStateMachine
- {
- #region Fields
- private readonly List<StateEngineEvent> eventList = new List<StateEngineEvent>();
- private readonly object eventQueueSyncObj = new object();
- private readonly Queue<StateEngineEvent> resendQueue = new Queue<StateEngineEvent>();
- private uint nextSequenceNumber;
- private readonly object nextSequenceNumberLock = new object();
- private bool stateChanged;
- #endregion
- #region Constants
- private const uint sequenceNumberWindowSize = 200;
- private const uint sequenceNumberWindowStart = uint.MaxValue - sequenceNumberWindowSize / 2;
- private const uint sequenceNumberWindowEnd = uint.MinValue + sequenceNumberWindowSize / 2;
- #endregion
- #region Construction
- protected EventBufferingRootStateMachine(string name, IDebugLogger debugLogger, object logCategory)
- : base(name, debugLogger, logCategory)
- {
- }
- #endregion
- #region Protected Methods
- /// <summary>
- /// Recursively hook on the OnStateChanged for the state machine and all sub composite states.
- /// </summary>
- /// <param name="stateMachine"></param>
- protected internal void CreateOnStateChangeEventHandler(StateMachine stateMachine)
- {
- stateMachine.OnStateChanged += new EventHandler<StateChangedEventArgs>(StateChangedNotification);
- foreach (State state in stateMachine.CreatedStates)
- {
- CompositeState compositeState = state as CompositeState;
- if (compositeState != null)
- {
- CreateOnStateChangeEventHandler(compositeState.StateMachine);
- }
- }
- }
- /// <summary>
- /// Processes all queued events.
- /// </summary>
- protected internal void HandleQueuedEvents(ReentrancyMutex mutexToReleaseWhenEventProcessingIsDone)
- {
- int eventCount;
- lock (eventQueueSyncObj)
- {
- eventCount = eventList.Count;
- if ((eventCount == 0) && (mutexToReleaseWhenEventProcessingIsDone != null))
- mutexToReleaseWhenEventProcessingIsDone.Release();
- }
- if (eventCount > 0)
- {
- do
- {
- //Pick out the new event.
- StateEngineEvent stateEngineEvent;
- lock (eventQueueSyncObj)
- {
- stateEngineEvent = eventList[0];
- eventList.RemoveAt(0);
- }
- //Reset the state changed flag. This flag is set in the OnStateChange event handler
- stateChanged = false;
- if (disposed)
- return;
- //Handle the event
- HandleEventInCurrentState(stateEngineEvent);
- //The state changed during the event handling.
- if (stateChanged)
- {
- lock (eventQueueSyncObj)
- {
- //Move all the events waiting for resend to the normal event queue.
- while (resendQueue.Count > 0)
- eventList.Add(resendQueue.Dequeue());
- SortEventList();
- }
- }
- lock (eventQueueSyncObj)
- {
- eventCount = eventList.Count;
- if ((eventCount == 0) && (mutexToReleaseWhenEventProcessingIsDone != null))
- mutexToReleaseWhenEventProcessingIsDone.Release();
- }
- } while ((eventCount > 0) && !disposed);
- }
- }
- /// <summary>
- /// Handle the event in the current state, and if the event is returned unhandled, it
- /// is put on the resend queue, to be sent to the next state we enter.
- /// </summary>
- /// <param name="stateEngineEvent"></param>
- protected override void HandleEventInCurrentState(StateEngineEvent stateEngineEvent)
- {
- base.HandleEventInCurrentState(stateEngineEvent);
- //If the event was not handled, it should be queued to be re-sent in the next state.
- if (!stateEngineEvent.Handled)
- {
- lock (eventQueueSyncObj)
- resendQueue.Enqueue(stateEngineEvent);
- }
- }
- #endregion
- /// <summary>
- /// Sorts the event list.
- /// </summary>
- private void SortEventList()
- {
- eventList.Sort(CompareEvents);
- }
- /// <summary>
- /// Compares two state engine event objects.
- /// </summary>
- /// <param name="ev1"></param>
- /// <param name="ev2"></param>
- /// <returns></returns>
- private static int CompareEvents(StateEngineEvent ev1, StateEngineEvent ev2)
- {
- if (ev1.Priority != ev2.Priority)
- return ev1.Priority.CompareTo(ev2.Priority);
- else
- {
- //Get the result through a normal comparison.
- int result = ev1.SequenceNumber.CompareTo(ev2.SequenceNumber);
- //Check, we leave a window for 200 events between uint.MaxValue and uint.MinValue
- if ((ev1.SequenceNumber >= sequenceNumberWindowStart) && (ev2.SequenceNumber <= sequenceNumberWindowEnd) ||
- (ev2.SequenceNumber >= sequenceNumberWindowStart) && (ev1.SequenceNumber <= sequenceNumberWindowEnd))
- {
- result *= -1;
- }
- return result;
- }
- }
- /// <summary>
- /// Generates a new sequence number for incoming events.
- /// </summary>
- /// <returns></returns>
- private uint GetNextEventSequenceNumber()
- {
- lock (nextSequenceNumberLock)
- {
- unchecked
- {
- return nextSequenceNumber++;
- }
- }
- }
- /// <summary>
- /// Event hanler for state changes.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- void StateChangedNotification(object sender, StateChangedEventArgs e)
- {
- stateChanged = true;
- }
- /// <summary>
- /// Entry point for incoming events. Stores the event in the event list and sorts the list. Nothing more happens, it is up to descendant classes
- /// to add functionality on how to handle this.
- /// </summary>
- /// <param name="stateEngineEvent"></param>
- public override void IncomingEvent(StateEngineEvent stateEngineEvent)
- {
- if (!disposed)
- {
- if (stateEngineEvent.ArrivalTime != DateTime.MinValue) //The arrival time is always set to minvalue when it is created.
- //If it has another value, it has been added to another statemachine.
- throw new StateEngineException(string.Format("The event ({0}) has already been sent to another statemachine. It can not be sent to more than one.", stateEngineEvent.ToString()));
- stateEngineEvent.ArrivalTime = DateTime.Now;
- stateEngineEvent.SequenceNumber = GetNextEventSequenceNumber();
- lock (eventQueueSyncObj)
- {
- //Add the event to the list.
- eventList.Add(stateEngineEvent);
- //Sort the event list.
- SortEventList();
- }
- }
- }
-
- /// <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>
- public override void RemovePendingEvents<TComparisonObject>(StateEngineEventPredicate<TComparisonObject> predicate, TComparisonObject comparisonObject)
- {
- lock (eventQueueSyncObj)
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory, DebugLogLevel.Maximized)))
- debugLogger.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, "ClearPendingEvents using predicate {0} using comparisonObject {1}", predicate, comparisonObject), logCategory, DebugLogLevel.Maximized);
- //Remove the matching events in the resend queue.
- Queue<StateEngineEvent> tempQueue = new Queue<StateEngineEvent>(resendQueue);
- resendQueue.Clear();
- while (tempQueue.Count > 0)
- {
- StateEngineEvent tempEvent = tempQueue.Dequeue();
- if (!predicate(tempEvent, comparisonObject)) //Call the predicate delegate to evaluate the event.
- {
- resendQueue.Enqueue(tempEvent); //Put back the event in the queue
- }
- else
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- debugLogger.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, " Removed event [{0}] from resend queue", tempEvent), logCategory);
- }
- }
- //Remove the matching events in the event queue.
- List<int> indicesToRemove = new List<int>();
- for (int i = 0; i < eventList.Count; i++)
- {
- if (predicate(eventList[i], comparisonObject))
- {
- indicesToRemove.Add(i);
- }
- }
- foreach (int i in indicesToRemove)
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- debugLogger.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, " Removed event [{0}] from event list", eventList[i]), logCategory);
- eventList[i] = null;
- }
- while (eventList.Contains(null))
- eventList.Remove(null); //Removes only one instance of null, we may have several. So continue remove until there is no more nulls.
- }
- }
- /// <summary>
- /// DO NOT USE for any isfs based event!
- /// Removes all pending events that matches the specified event type. .Equals method is
- /// used to compare.
- /// </summary>
- /// <param name="eventType"></param>
- public override void RemovePendingEventsOfType(object eventType)
- {
- RemovePendingEvents(MatchEventOnType, eventType);
- }
-
- /// <summary>
- /// Gets the pending envents from the StateMachine.
- /// </summary>
- /// <param name="eventType"></param>
- /// <returns>null or an IEnumberable</returns>
- public override IEnumerable<StateEngineEvent> GetPendingEventsOfType(object eventType)
- {
- return GetPendingEvents(MatchEventOnType, eventType);
- }
- /// <summary>
- /// Gets the pending StateEngineEvents of the type
- /// </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>
- /// <returns>null or an IEnumerable</returns>
- public override IEnumerable<StateEngineEvent> GetPendingEvents<TComparisonObject>(
- StateEngineEventPredicate<TComparisonObject> predicate, TComparisonObject comparisonObject)
- {
- var result = new List<StateEngineEvent>();
- lock (eventQueueSyncObj)
- {
- var tempQueue = new Queue<StateEngineEvent>(resendQueue);
- while (tempQueue.Count > 0)
- {
- var tempEvent = tempQueue.Dequeue();
- if (predicate(tempEvent, comparisonObject)) //Call the predicate delegate to evaluate the event.
- {
- result.Add(tempEvent);
- }
- }
- }
- return result;
- }
- /// <summary>
- /// Predicate method that is used to make the event type comparison in the RemovePendingEventsOfType method.
- /// </summary>
- /// <param name="stateEngineEvent"></param>
- /// <param name="eventType"></param>
- /// <returns></returns>
- private static bool MatchEventOnType(StateEngineEvent stateEngineEvent, object eventType)
- {
- if (stateEngineEvent != null)
- return stateEngineEvent.Type.Equals(eventType);
- else
- return false;
- }
- /// <summary>
- /// Clear all pending events.
- /// </summary>
- internal override void ClearPendingEvents()
- {
- lock (eventQueueSyncObj)
- {
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- {
- debugLogger.Add("ClearPendingEvents");
- foreach (StateEngineEvent tempEvent in resendQueue)
- debugLogger.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, " Removed event [{0}] from resend queue ", tempEvent), logCategory);
- }
- resendQueue.Clear();
- if ((debugLogger != null) && (debugLogger.IsActive(logCategory)))
- {
- foreach (StateEngineEvent tempEvent in eventList)
- debugLogger.Add(string.Format(System.Globalization.CultureInfo.InvariantCulture, " Removed event [{0}] from event list", tempEvent), logCategory);
- }
- eventList.Clear();
- }
- }
- }
- }
|