コード例 #1
0
        /// <summary>
        /// Enters the state.
        /// </summary>
        /// <param name="context">The event arguments pertaining to the transition into the state.</param>
        /// <param name="useHistory">Used by history states to reconstitute state history.</param>
        /// <remarks>
        /// This method is for use only by the North State Framework's internal logic.
        /// </remarks>
        protected internal virtual void enter(NSFStateMachineContext context, bool useHistory)
        {
            active = true;

            if (parentRegion != null)
            {
                parentRegion.setActiveSubstate(this);

                if (!parentRegion.isActive())
                {
                    parentRegion.enter(context, false);
                }
            }

            if (TopStateMachine.LoggingEnabled && LogEntry)
            {
                NSFTraceLog.PrimaryTraceLog.addTrace(NSFTraceTags.StateEnteredTag,
                                                     NSFTraceTags.StateMachineTag, TopStateMachine.Name,
                                                     NSFTraceTags.StateTag, Name);
            }

            // Update context to indicate entering this state
            context.EnteringState = this;
            context.ExitingState  = null;

            EntryActions.execute(context);

            NSFStateMachine parentStateMachine = ParentStateMachine;

            if (parentStateMachine != null)
            {
                parentStateMachine.executeStateChangeActions(context);
            }
        }
コード例 #2
0
        /// <summary>
        /// Handles exceptions caught by main event processing loop.
        /// </summary>
        /// <param name="exception">The exception caught.</param>
        protected void handleException(Exception exception)
        {
            NSFExceptionContext newContext = new NSFExceptionContext(this, new Exception(Name + " thread exception", exception));

            ExceptionActions.execute(newContext);
            NSFExceptionHandler.handleException(newContext);
        }
コード例 #3
0
        /// <summary>
        /// Handles exceptions raised during state machine processing.
        /// </summary>
        /// <param name="exception">The exception thrown.</param>
        /// <remarks>
        /// By default, any exception actions are executed first,
        /// then the exception is forwarded to the global exception handler, NSFExceptionHandler.handleException().
        /// </remarks>
        protected internal void handleException(Exception exception)
        {
            NSFExceptionContext newContext = new NSFExceptionContext(this, new Exception(Name + " state machine exception", exception));

            ExceptionActions.execute(newContext);
            NSFExceptionHandler.handleException(newContext);
        }
コード例 #4
0
        /// <summary>
        /// Event handler action that executes the scheduled action.
        /// </summary>
        /// <param name="context">Additional contextual information.</param>
        private void executeActions(NSFEventContext context)
        {
            if ((Name != null) && (Name != String.Empty))
            {
                NSFTraceLog.PrimaryTraceLog.addTrace(NSFTraceTags.ActionExecutedTag, NSFTraceTags.ActionTag, Name);
            }

            Actions.execute(context);
        }
コード例 #5
0
        /// <summary>
        /// Adds the specified event to the state machine's event queue.
        /// </summary>
        /// <param name="nsfEvent">The event to queue.</param>
        /// <param name="isPriorityEvent">Flag indicating if the event should be queued to the back of the queue (false) or the front of the queue (true).</param>
        /// <param name="logEventQueued">Flag indicating if an event queued trace should be added to the trace log.</param>
        private void queueEvent(NSFEvent nsfEvent, bool isPriorityEvent, bool logEventQueued)
        {
            if (!isTopStateMachine())
            {
                TopStateMachine.queueEvent(nsfEvent, isPriorityEvent, logEventQueued);
                return;
            }

            lock (stateMachineMutex)
            {
                // Do not allow events to be queued if terminating or terminated,
                // except for run to completion event, which may be queued if terminating to allow proper semantics to continue until terminated.
                if ((TerminationStatus == NSFEventHandlerTerminationStatus.EventHandlerTerminated) ||
                    ((TerminationStatus == NSFEventHandlerTerminationStatus.EventHandlerTerminating) && (nsfEvent != runToCompletionEvent)))
                {
                    return;
                }

                // Handle special case of terminate event by setting status and queuing a single terminate event.
                // Terminate event must be the last event queued to guarantee safe deletion when it is handled.
                if (nsfEvent == terminateEvent)
                {
                    if (TerminationStatus == NSFEventHandlerTerminationStatus.EventHandlerReady)
                    {
                        TerminationStatus = NSFEventHandlerTerminationStatus.EventHandlerTerminating;
                    }
                }

                // Event limit detection looks for too many events queued for the state machine.
                // If more than the specified number of events are queued, the state machine will remove
                // its queued events, call the event limit actions, and stop after executing any events queued
                // by the limit actions.
                if ((EventLimitDetectionEnabled) && (QueuedEvents == EventLimit))
                {
                    EventThread.removeEventsFor(this);
                    QueuedEvents = 0;

                    EventLimitActions.execute(new NSFStateMachineContext(this, null, null, null, nsfEvent));

                    // Stop the state machine so that no more event processing occurs until started again
                    stopStateMachine();

                    NSFTraceLog.PrimaryTraceLog.addTrace(NSFTraceTags.ErrorTag, NSFTraceTags.SourceTag, Name, NSFTraceTags.MessageTag, "EventLimit");

                    return;
                }

                nsfEvent.Destination = this;

                EventThread.queueEvent(nsfEvent, isPriorityEvent, logEventQueued);

                ++QueuedEvents;
            }
        }
コード例 #6
0
        /// <summary>
        /// Executes the actions in the state change actions list.
        /// </summary>
        internal void executeStateChangeActions(NSFStateMachineContext context)
        {
            StateChangeActions.execute(context);

            NSFStateMachine parentStateMachine = ParentStateMachine;

            if (parentStateMachine != null)
            {
                parentStateMachine.executeStateChangeActions(context);
            }
        }
        /// <summary>
        /// Global exception handling method.
        /// </summary>
        /// <param name="context">Additional contextual information.</param>
        /// <remarks>
        /// All exceptions are ultimately routed to this method. It logs the exception to the trace log,
        /// saves the trace log if a file name has been set, and executes any exception actions.
        /// </remarks>
        public static void handleException(NSFExceptionContext context)
        {
            try
            {
                NSFTraceLog.PrimaryTraceLog.addTrace(NSFTraceTags.ExceptionTag, NSFTraceTags.MessageTag, context.Exception.ToString());
            }
            catch (Exception)
            {
                // Nothing to do
            }

            ExceptionActions.execute(context);
        }
コード例 #8
0
        /// <summary>
        /// Exits the state.
        /// </summary>
        /// <param name="context">The event arguments pertaining to the transition out of the state.</param>
        /// <remarks>
        /// This method is for use only by the North State Framework's internal logic.
        /// </remarks>
        protected internal virtual void exit(NSFStateMachineContext context)
        {
            active = false;

            // Update context to indicate exiting this state
            context.ExitingState  = this;
            context.EnteringState = null;

            ExitActions.execute(context);

            if (parentRegion != null)
            {
                parentRegion.setActiveSubstate(NSFState.NullState);
            }
        }
コード例 #9
0
        /// <summary>
        /// Implements the main timer processing loop.
        /// </summary>
        protected override void threadLoop()
        {
            NSFTime currentTime = CurrentTime;
            List <NSFTimerAction> readyActions = new List <NSFTimerAction>();

            while (true)
            {
                // Set up next timeout
                lock (threadMutex)
                {
                    if (actions.Count != 0)
                    {
                        timer.setNextTimeout(actions.First.Value.ExecutionTime);
                    }
                    else
                    {
                        timer.setNextTimeout(Int32.MaxValue);
                    }
                }

                timer.waitForNextTimeout();

                // Clean up and return if terminating
                if (TerminationStatus != NSFThreadTerminationStatus.ThreadReady)
                {
                    actions.Clear();
                    return;
                }

                currentTime = CurrentTime;

                lock (threadMutex)
                {
                    // Create list of actions ready to execute
                    foreach (NSFTimerAction action in actions)
                    {
                        if (action.ExecutionTime <= currentTime)
                        {
                            readyActions.Add(action);
                        }
                        else
                        {
                            break;
                        }
                    }

                    // Reschedule repetitive actions
                    foreach (NSFTimerAction action in readyActions)
                    {
                        if (action.RepeatTime != 0)
                        {
                            action.ExecutionTime = action.ExecutionTime + action.RepeatTime;
                            insertAction(action);
                        }
                        else
                        {
                            actions.Remove(action);
                        }
                    }
                }

                // Check for excessive gap between current time and execution time
                if (readyActions.Count != 0)
                {
                    NSFTime timeGap = currentTime - readyActions[0].ExecutionTime;

                    if ((MaxAllowableTimeGap > 0) && (timeGap > MaxAllowableTimeGap) && (currentTime > nextTimeGapInterval))
                    {
                        NSFTraceLog.PrimaryTraceLog.addTrace(NSFTraceTags.ErrorTag, NSFTraceTags.SourceTag, Name, NSFTraceTags.MessageTag, "TimeGap", NSFTraceTags.ValueTag, timeGap.ToString());
                        TimeGapActions.execute(new NSFContext(this));

                        // Set time when next time gap can be recorded
                        // This prevents all gapped timer events from recording time gap trace
                        nextTimeGapInterval = currentTime + MaxAllowableTimeGap;
                    }

                    if (timeGap > MaxObservedTimeGap)
                    {
                        MaxObservedTimeGap = timeGap;
                    }
                }

                // Execute all ready actions
                while (readyActions.Count != 0)
                {
                    executeAction(readyActions[0]);
                    readyActions.RemoveAt(0);
                }
            }
        }
コード例 #10
0
        /// <summary>
        /// Handles an event.
        /// </summary>
        /// <param name="nsfEvent">The event to handle.</param>
        /// <returns>Status indicating if the event was handled or not.</returns>
        /// <remarks>
        /// This method is for use only by the North State Framework's internal logic.
        /// It processes the event using UML defined behavior, including run to completion.
        /// </remarks>
        public NSFEventStatus handleEvent(NSFEvent nsfEvent)
        {
            lock (stateMachineMutex)
            {
                --QueuedEvents;

                // This should only happen if events are queued without using the queueEvent method
                if (QueuedEvents < 0)
                {
                    QueuedEvents = 0;
                }
            }

            // Handle status changing events
            if ((nsfEvent == startEvent))
            {
                RunStatus = NSFEventHandlerRunStatus.EventHandlerStarted;
            }
            else if (nsfEvent == stopEvent)
            {
                RunStatus = NSFEventHandlerRunStatus.EventHandlerStopped;
            }
            else if (nsfEvent == terminateEvent)
            {
                TerminationStatus = NSFEventHandlerTerminationStatus.EventHandlerTerminated;
                EventThread.removeEventHandler(this);
                return(NSFEventStatus.NSFEventHandled);
            }
            else if (nsfEvent == resetEvent)
            {
                reset();
            }

            // Don't process events if stopped
            if (RunStatus == NSFEventHandlerRunStatus.EventHandlerStopped)
            {
                return(NSFEventStatus.NSFEventUnhandled);
            }

            // If not already active, enter state machine at the root
            if (!active)
            {
                enter(new NSFStateMachineContext(this, this, null, null, startEvent), false);
            }

            // Process the event
            NSFEventStatus eventStatus = NSFEventStatus.NSFEventUnhandled;

            try
            {
                eventStatus = processEvent(nsfEvent);

                if (eventStatus == NSFEventStatus.NSFEventHandled)
                {
                    runToCompletion();
                }

                // Consecutive loop detection looks for too many events without the state machine pausing.
                // If more than the specified number of transitions occur without a pause, the state machine will remove
                // its queued events, call the consecutive loop limit actions, and stop after executing any events queued
                // by the actions.
                if (ConsecutiveLoopDetectionEnabled)
                {
                    ++consecutiveLoopCount;

                    if (consecutiveLoopCount == ConsecutiveLoopLimit)
                    {
                        lock (stateMachineMutex)
                        {
                            EventThread.removeEventsFor(this);
                            QueuedEvents = 0;
                        }

                        ConsecutiveLoopLimitActions.execute(new NSFStateMachineContext(this, null, null, null, nsfEvent));

                        // Stop the state machine so that no more event processing occurs until started again
                        stopStateMachine();

                        // Reset consecutive loop count in case state machine is started again
                        consecutiveLoopCount = 0;

                        NSFTraceLog.PrimaryTraceLog.addTrace(NSFTraceTags.ErrorTag, NSFTraceTags.SourceTag, Name, NSFTraceTags.MessageTag, "ConsecutiveLoopLimit");
                    }
                    else if (QueuedEvents == 0)
                    {
                        // If no events are queued for this state machine, then it has paused, indicating it's not in an infinite loop.
                        consecutiveLoopCount = 0;
                    }
                }
            }
            catch (Exception exception)
            {
                handleException(new Exception(nsfEvent.Name + " event handling exception", exception));
            }

            return(eventStatus);
        }