public StateMachineContainer()
            {
                //
                //  The underlying finite state machine engine needs a list of all
                //  the states. It gets them as we "Add" them here.
                //
                OpeningDoor = engine.AddState("OpeningDoor");
                ClosingDoor = engine.AddState("ClosingDoor");
                Stopping    = engine.AddState("Stopping");
                //
                //  Alternatively
                //OpeningDoor = engine.AddState(new FiniteState("OpeningDoor"));
                //ClosingDoor = engine.AddState(new FiniteState("ClosingDoor"));
                //  Alternatively (more)
                //Stopping = new FiniteState("Stopping");
                //engine.AddState(Stopping);

                //
                //  Events are not "added" to the state machine directly.
                //  However, they are effectively added to each state when state transitions are defined.
                //
                DoorOpened   = new FiniteStateEvent("DoorOpened");
                DoorClosed   = new FiniteStateEvent("DoorClosed");
                ActionFailed = new FiniteStateEvent("ActionFailed");
            }
Exemple #2
0
        ///// <summary>
        ///// RunStateMachine is the heart of the engine that runs the finite state machine. It is executed in a
        ///// separate thread that is started when you <see cref="Start(FiniteState)"/> the state machine.
        ///// All events that have been queued by <see cref="RaiseEvent(FiniteStateEvent)"/>, are processed here.
        ///// The method runs in an infinite loop that only exits when <see cref="Stop()"/> is called.
        ///// When it has processed all of the events in the queue, it will sleep until it is pulsed
        ///// by either <see cref="RaiseEvent(FiniteStateEvent)"/> or <see cref="Stop()"/>
        ///// </summary>
        //public void RunStateMachine()
        //{
        //    string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
        //    if (log.IsDebugEnabled)
        //        log.Debug($"{methodName}: enter");
        //
        //    try
        //    {
        //        while (!stopRequested)
        //        {
        //            HandlePendingEvents();
        //            if (!stopRequested)
        //            {
        //                lock (locker)
        //                {
        //                    if (log.IsDebugEnabled)
        //                        log.Debug($"{methodName}: Putting to sleep the state machine thread.");
        //
        //                    Monitor.Wait(locker); // Nothing to do, so go to sleep until we are pulsed by RaiseEvent or Stop
        //
        //                    if (log.IsDebugEnabled)
        //                        log.Debug($"{methodName}: Just woke up :-)");
        //                }
        //            }
        //        }
        //    }
        //    catch(Exception e)
        //    {
        //        log.Error($"{e.Message}");
        //        //OnExceptionAction?.Invoke(e);
        //    }
        //    finally
        //    {
        //        if (log.IsDebugEnabled)
        //            log.Debug($"{methodName}: exit");
        //    }
        //}
        //

        /// <summary>
        /// This function is called during the first call to RaiseEvent() in Start() because at that point,
        /// we are not handling a previous event. From then on, every event handler raises an event.
        /// </summary>
        public void HandlePendingEvents()
        {
            string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;

            if (log.IsDebugEnabled)
            {
                log.Debug($"{methodName}: enter");
            }

            try
            {
                while (PendingEvents.Count > 0)
                {
                    if (log.IsDebugEnabled)
                    {
                        log.Debug($"{methodName}: PendingEvents.Count={PendingEvents.Count}");
                    }
                    FiniteStateEvent ev = null;
                    lock (PendingEvents)
                    {
                        ev = PendingEvents.Dequeue();
                    }
                    if (CurrentState.Transitions.TryGetValue(ev.Name, out FiniteStateTransition transition))
                    {
                        if (log.IsDebugEnabled)
                        {
                            log.Debug($"{methodName}: transition {CurrentState.Name} -> {ev.Name} ->{transition.ToState.Name}");
                        }
                        SetCurrentState(transition.ToState);
                        if (log.IsDebugEnabled)
                        {
                            log.Debug($"{methodName}: before invoke, PendingEvents.Count={PendingEvents.Count}");
                        }
                        CurrentState.OnEnterAction?.Invoke();
                        if (log.IsDebugEnabled)
                        {
                            log.Debug($"{methodName}: after invoke, PendingEvents.Count={PendingEvents.Count}");
                        }
                    }
                    else
                    {
                        throw new ArgumentException($"Failed to find a transition for event '{ev.Name}' from state '{CurrentState.Name}'.");
                    }
                }
            }
            finally
            {
                if (log.IsDebugEnabled)
                {
                    log.Debug($"{methodName}: exit");
                }
            }
        }
        /// <summary>
        /// Raises an event in the finite state machine.
        /// </summary>
        /// <param name="e">The instance of the event to raise</param>
        public void RaiseEvent(FiniteStateEvent e)
        {
            string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;

            if (log.IsDebugEnabled)
            {
                log.Debug($"{methodName}: enter");
            }

            engine.RaiseEvent(e);

            if (log.IsDebugEnabled)
            {
                log.Debug($"{methodName}: exit");
            }
        }
Exemple #4
0
        /// <summary>
        /// Raises an event in the finite state machine.
        /// </summary>
        /// <param name="e">The instance of the event to raise</param>
        public void RaiseEvent(FiniteStateEvent e)
        {
            string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;

            if (log.IsDebugEnabled)
            {
                log.Debug($"{methodName}: enter");
            }
            try
            {
                if (log.IsInfoEnabled)
                {
                    log.Info($"{methodName}: [{e.Name}]");
                }
                //
                //  Use a queue so that event handlers can raise events.
                //
                lock (PendingEvents)
                {
                    PendingEvents.Enqueue(e);

                    if (log.IsDebugEnabled)
                    {
                        log.Debug($"{methodName}: handlingEvent = {handlingEvent}");
                    }

                    if (handlingEvent)
                    {
                        return;
                    }
                    handlingEvent = true;
                }
                HandlePendingEvents();
                // eliminate race for the flag by using the same lock object
                lock (PendingEvents)
                {
                    handlingEvent = false;
                }
            }
            finally
            {
                if (log.IsDebugEnabled)
                {
                    log.Debug($"{methodName}: exit");
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Starts the state machine.
        ///
        /// Every state diagram has a start state with a single transition to some initial state in the diagram via a start event.
        /// This method performs the following actions:
        ///  * creates the start state
        ///  * creates the start event
        ///  * adds a transition from the start state, via the start event to InitialState, the finite state passed as a parameter
        ///  * sets the current state to be the start state
        ///  * raises the start event
        ///    The OnEntry event handler for InitialState will be executed.
        ///    The state machine is now sitting in it's initial state and waiting for an event to occur.
        ///
        /// On a technical note, the underlying Thread, in which the event handlers are run, is also created and started here.
        /// </summary>
        /// <param name="InitialState">The finite state in which this state machine starts.</param>
        /// <exception cref="ArgumentException">Thrown if the parameter InitialState is a finite state that has not been added by <see cref="AddState(FiniteState)"/></exception>
        public void Start(FiniteState InitialState)
        {
            string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;

            if (log.IsDebugEnabled)
            {
                log.Debug($"{methodName}: enter");
            }

            //if (OnExceptionAction == null)
            //{
            //    throw new ArgumentException($"The state machine must have an OnExceptionAction before you can start it.");
            //}

            //
            //  Perform sanity checks
            //
            foreach (KeyValuePair <string, FiniteState> kvp in ManagedStates)
            {
                //
                //  1. Make sure each finite state has an OnEnterAction
                //
                FiniteState finiteState = kvp.Value;
                if (finiteState.OnEnterAction == null)
                {
                    throw new Exception($"The finite state, '{finiteState.Name}' does not have an OnEnterAction.");
                }
                //
                //  2. Make sure the target state in every transition has been added to the state machine.
                //
                foreach (KeyValuePair <string, FiniteStateTransition> transitionKvp in finiteState.Transitions)
                {
                    FiniteStateTransition fst = transitionKvp.Value;
                    string stateName          = fst.ToState.Name;
                    if (!ManagedStates.ContainsKey(stateName))
                    {
                        throw new Exception($"The finite state, '{stateName}' has not been added to the state machine.");
                    }
                }
            }

            FiniteState      startState = new FiniteState(START_STATE_NAME);
            FiniteStateEvent startEvent = new FiniteStateEvent(START_EVENT_NAME);

            startState.OnEnterAction = () => { }; // needs an empty handler
            AddState(startState);
            startState.AddTransition(startEvent, InitialState);
            SetCurrentState(startState);
            RaiseEvent(startEvent);

            //Thread runner = new Thread(RunStateMachine)
            //{
            //    Name = Name
            //};
            //runner.Start();

            if (log.IsDebugEnabled)
            {
                log.Debug($"{methodName}: exit");
            }
        }
 /// <summary>
 /// Add a new transition to the collection of transitions from this state that is created from an event and a state.
 /// </summary>
 /// <param name="ViaEvent">The event that will trigger this transition.</param>
 /// <param name="ToState">The state that is the destination of the transition.</param>
 /// <exception cref="ArgumentException">Thrown if the event in TransitionToAdd has already been used in a transition from this state.</exception>
 public void AddTransition(FiniteStateEvent ViaEvent, FiniteState ToState)
 {
     AddTransition(new FiniteStateTransition(ViaEvent, ToState));
 }
 public void despatch(FiniteStateEvent e)
 {
     engine.RaiseEvent(e);
 }