예제 #1
0
        /// <summary>
        /// Verifies transitions in order of onEvent, onResult and if not found will return the
        /// default transition
        /// </summary>
        /// <param name="stateFunc">
        /// The function that is invoked that will return return message from the state processing
        /// and use it to check against the onResult queue.
        /// </param>
        /// <param name="eventMsg">The incoming message to validate against the onEvent list</param>
        /// <returns>The OnEvent, OnResult or default transition</returns>
        private ISpStateTransition GetTransition(bool onEntry, Func <ISpEventMessage, ISpEventMessage> stateFunc, ISpEventMessage msg)
        {
            return(WrapErr.ToErrorReportException(9999, () => {
                // Query the OnEvent queue for a transition BEFORE calling state function (OnEntry, OnTick)
                ISpStateTransition tr = this.GetOnEventTransition(msg);
                if (tr != null)
                {
                    tr.ReturnMessage = (tr.ReturnMessage == null)
                        ? this.MsgFactory.GetResponse(msg)
                        : this.MsgFactory.GetResponse(tr.ReturnMessage);
                    return tr;
                }

                // Only considered entered if you do not encounter a preemptive OnEvent transition on entry. In this way
                // you could get a situation where you cascade down several state depths based on higher up events
                if (onEntry)
                {
                    this.isEntered = true;
                }

                // Get the transition object from the 'OnResult' queue
                if ((tr = this.GetOnResultTransition(stateFunc.Invoke(msg))) != null)
                {
                    tr.ReturnMessage = this.MsgFactory.GetResponse(msg, tr.ReturnMessage);
                    return tr;
                }

                // If no transitions registered return SameState with default success message
                return new SpStateTransition(SpStateTransitionType.SameState, null, this.MsgFactory.GetDefaultResponse(msg));
            }));
        }
예제 #2
0
        /// <summary>
        /// Get a clone of the transition object from the store or null if not found
        /// </summary>
        /// <param name="store">The store to search</param>
        /// <param name="eventMsg">The message to insert in the transition object</param>
        /// <returns>The transition object from the store or null if not found</returns>
        public static ISpStateTransition GetTransitionCloneFromStore(Dictionary <int, ISpStateTransition> store, ISpEventMessage eventMsg)
        {
            WrapErr.ChkParam(store, "store", 51009);
            WrapErr.ChkParam(eventMsg, "eventMsg", 51010);

            return(WrapErr.ToErrorReportException(51011, () => {
                if (store.Keys.Contains(eventMsg.EventId))
                {
                    // Clone Transition object from store since its pointers get reset later
                    ISpStateTransition tr = (ISpStateTransition)store[eventMsg.EventId].Clone();

                    if (tr.ReturnMessage == null)
                    {
                        tr.ReturnMessage = eventMsg;
                    }
                    else
                    {
                        //Log.Info("SpTools", "GetTransitionCloneFromStore", "Found a transition AND - Held msg - transfering GUID");
                    }

                    // TODO - Look at transfering the GUID here
                    tr.ReturnMessage.Uid = eventMsg.Uid;


                    //tr.ReturnMessage = eventMsg;
                    return tr;
                }
                return null;
            }));
        }
예제 #3
0
        /// <summary>Tick current state to execute the action based on the inputed message</summary>
        /// <param name="msg">The inputed message</param>
        /// <returns>The return message from the action</returns>
        public ISpEventMessage Tick([NotNull] ISpEventMessage?msg)
        {
            WrapErr.ChkDisposed(this.disposed, 50176);
            WrapErr.ChkParam(msg, "msg", 50172);

            // First tick to drive it from entry to regular
            ISpStateTransition <TMsgId>?tr = null;
            bool tmpIsStarted = this.isStarted;

            //Log.Debug("SpMachine", "Tick", String.Format("isStarted:{0}", this.isStarted));

            if (!this.isStarted)
            {
                // The OnEntry must be called directly from the state machine for the first state. Subsequent
                // state transitions will insure that the OnEntry for the new state is called
                this.isStarted = true;
                tr             = this.state.OnEntry(msg);
            }
            else
            {
                tr = this.state.OnTick(msg);
            }
            WrapErr.ChkVar(tr, 50177, () => String.Format(
                               "The State '{0}' {1} Returned a Null Transition", this.state.FullName, tmpIsStarted ? "OnTick" : "OnEntry"));
            WrapErr.ChkVar(tr.ReturnMessage, 9999, "Null ReturnMessage");
            return(tr.ReturnMessage);
        }
예제 #4
0
 /// <summary>
 /// Get the transition object from the store or null if not found
 /// </summary>
 /// <param name="store">The store to search</param>
 /// <param name="eventMsg">The message to insert in the transition object</param>
 /// <returns>The transition object from the store or null if not found</returns>
 private ISpStateTransition GetTransitionFromStore(Dictionary <int, ISpStateTransition> store, ISpEventMessage eventMsg)
 {
     return(WrapErr.ToErrorReportException(50204, () => {
         ISpStateTransition t = SpTools.GetTransitionCloneFromStore(store, eventMsg);
         if (t != null)
         {
             this.LogTransition(t, eventMsg);
         }
         return t;
     }));
 }
예제 #5
0
        private ISpStateTransition GetSuperStateOnEventTransition(ISpEventMessage msg)
        {
            ISpStateTransition tr = this.GetOnEventTransition(msg);

            if (tr != null)
            {
                tr.ReturnMessage = (tr.ReturnMessage == null)
                    ? this.MsgFactory.GetResponse(msg)
                    : this.MsgFactory.GetResponse(tr.ReturnMessage);
            }
            return(tr);
        }
예제 #6
0
 /// <summary>
 /// Factor out the reporting of state transitions for clarity
 /// </summary>
 /// <param name="tr">The transition object</param>
 /// <param name="eventMsg">The event message which pushed this transition</param>
 private void LogTransition(ISpStateTransition tr, ISpEventMessage eventMsg)
 {
     WrapErr.ToErrorReportException(9999, () => {
         Log.Debug("SpState", "LogTransition",
                   String.Format(
                       "{0} OnMsg({1} - {2}) - From:{3} To:{4}",
                       tr.TransitionType,
                       this.GetCachedMsgTypeId(eventMsg.TypeId),
                       this.GetCachedEventId(eventMsg.EventId),
                       this.FullName,
                       tr.NextState == null ? "Unknown" : tr.NextState.FullName));
     });
 }
예제 #7
0
        /// <summary>
        /// Retrieve the transition object from the OnResults queue of the super state
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private ISpStateTransition GetSuperStateOnResultTransition(ISpEventMessage msg)
        {
            // Check super state registered result transitions against Sub State event id
            ISpStateTransition tr = this.GetOnResultTransition(msg);

            WrapErr.ChkVar(tr, 9999, () => {
                return(String.Format(
                           "State {0} Specified Exit but SuperState {1} has no handlers for that event id:{2}",
                           this.currentState.FullName, this.FullName, this.GetCachedEventId(msg.EventId)));
            });

            tr.ReturnMessage = this.MsgFactory.GetResponse(msg, tr.ReturnMessage);
            return(tr);
        }
예제 #8
0
        /// <summary>
        /// Handle Defered transition objects from both the current substate and this super state
        /// </summary>
        /// <param name="tr">The current transition object</param>
        /// <param name="msg"></param>
        /// <param name="fromSuperState">
        /// true if the Transition if from the super state, false if from the substate
        /// </param>
        /// <returns>The Transition</returns>
        private ISpStateTransition HandleDeferedStateTransitionType(ISpStateTransition tr, ISpEventMessage msg, bool fromSuperState)
        {
            // If the superstate iteself has a Defered transition it will return immediately to parent
            if (fromSuperState)
            {
                return(tr);
            }

            // Call super state override method that derived superstate will use to handle decision point and create a new message with event
            ISpEventMessage deferedMsg = this.OnRuntimeTransitionRequest(msg);

            // Process the transition type from the SubState level
            return(this.ReadTransitionType(
                       this.GetSuperStateOnResultTransition(deferedMsg), deferedMsg, true));
        }
예제 #9
0
        /// <summary>
        /// Register a state transition for an event
        /// </summary>
        /// <param name="eventId">The id converter of the event type</param>
        /// <param name="transition">The transition object</param>
        public static void RegisterTransition(string type, ISpToInt eventId, ISpStateTransition transition, Dictionary<int, ISpStateTransition> store)
        {
            WrapErr.ChkParam(eventId, "eventId", 51004);
            WrapErr.ChkParam(transition, "transition", 51005);
            WrapErr.ChkParam(store, "store", 51006);

            // Wrap the id converter separately
            int tmp = WrapErr.ToErrorReportException(51007,
                () => { return String.Format("Error on Event Id Converter for '{0}' Event Type", type); },
                () => { return eventId.ToInt(); });

            // Duplicate transitions on same Event is a no no.
            WrapErr.ChkFalse(store.Keys.Contains(tmp), 51008,
                () => { return String.Format("Already Contain a '{0}' Transition for Id:{1}", type, tmp); });
            store.Add(tmp, transition);
        }
예제 #10
0
        /// <summary>
        /// Register a state transition for an event
        /// </summary>
        /// <param name="eventId">The id converter of the event type</param>
        /// <param name="transition">The transition object</param>
        public static void RegisterTransition(string type, ISpToInt eventId, ISpStateTransition transition, Dictionary <int, ISpStateTransition> store)
        {
            WrapErr.ChkParam(eventId, "eventId", 51004);
            WrapErr.ChkParam(transition, "transition", 51005);
            WrapErr.ChkParam(store, "store", 51006);

            // Wrap the id converter separately
            int tmp = WrapErr.ToErrorReportException(51007,
                                                     () => { return(String.Format("Error on Event Id Converter for '{0}' Event Type", type)); },
                                                     () => { return(eventId.ToInt()); });

            // Duplicate transitions on same Event is a no no.
            WrapErr.ChkFalse(store.Keys.Contains(tmp), 51008,
                             () => { return(String.Format("Already Contain a '{0}' Transition for Id:{1}", type, tmp)); });
            store.Add(tmp, transition);
        }
예제 #11
0
        /// <summary>
        /// Execute on each tick period
        /// </summary>
        /// <param name="msg">The incoming message with event</param>
        /// <returns>The return transition object with result information</returns>
        public sealed override ISpStateTransition OnTick(ISpEventMessage msg)
        {
            //Log.Info(this.className, "OnTick", String.Format("'{0}' State", this.FullName));
            WrapErr.ChkVar(this.entryState, 9999, "The 'SetEntryState() Must be Called in the Constructor");
            WrapErr.ChkVar(this.currentState, 9999, "Current state is not set");
            WrapErr.ChkTrue(this.IsEntryExcecuted, 9999, "Tick Being Called before OnEntry");

            // If there are OnEvent transitions registered at the superstate level return immediately
            ISpStateTransition tr = GetSuperStateOnEventTransition(msg);

            if (tr != null)
            {
                return(tr);
            }
            return(this.GetTransition(this.currentState.OnTick, msg));
        }
예제 #12
0
        /// <summary>
        /// Handle the NextState Transition type by setting the next state as
        /// </summary>
        /// <param name="tr"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        private ISpStateTransition HandleNextStateTransitionType(ISpStateTransition tr, ISpEventMessage msg)
        {
            Log.Info(this.className, "HandleNextStateTransitionType", String.Format("'{0}' State", this.FullName));

            WrapErr.ChkTrue(tr.TransitionType == SpStateTransitionType.NextState, 9999,
                            () => { return(String.Format("{0} is not NextState", tr.TransitionType)); });

            WrapErr.ChkVar(tr.NextState, 9999, () => { return
                                                       (String.Format(
                                                            "State {0} Specified Next State on Event {1} but Next State Null",
                                                            this.currentState.FullName, this.GetCachedEventId(msg.EventId))); });

            this.currentState.OnExit();
            this.currentState = tr.NextState;
            return(this.currentState.OnEntry(this.MsgFactory.GetDefaultResponse(msg)));
        }
예제 #13
0
        /// <summary>
        /// Execute logic on entry into this superstate
        /// </summary>
        /// <param name="msg">The incoming message with event</param>
        /// <returns>The return transition object with result information</returns>
        public sealed override ISpStateTransition OnEntry(ISpEventMessage msg)
        {
            Log.Info(this.className, "OnEntry", String.Format("'{0}' State Event {1}", this.FullName, this.GetCachedEventId(msg.EventId)));
            WrapErr.ChkVar(this.entryState, 9999, "The 'SentEntryState() Must be Called in the Constructor");

            // Find if there are exit conditions OnEntry at the SuperState level and excecute them first
            // This will check OnEvent transitions queue and transitions from the overriden ExecOnEntry
            ISpStateTransition t = base.OnEntry(msg);

            if (t.TransitionType != SpStateTransitionType.SameState)
            {
                return(t);
            }

            // return transition
            this.currentState = this.entryState;
            return(this.currentState.OnEntry(msg));
        }
예제 #14
0
        /// <summary>Register a transition for a state</summary>
        /// <param name="type">string of transition type</param>
        /// <typeparam name="TMsgId">Event id</typeparam>
        /// <param name="msgId">The event message id</param>
        /// <param name="transition">Transition object</param>
        /// <param name="store">Transition store</param>
        public static void RegisterTransition <TMsgId>(string type, TMsgId msgId, ISpStateTransition <TMsgId>?transition, Dictionary <int, ISpStateTransition <TMsgId> >?store) where TMsgId : struct
        {
            //WrapErr.ChkParam(eventId, "msgId", 51004);
            WrapErr.ChkParam(transition, "transition", 51005);
            WrapErr.ChkParam(store, "store", 51006);

            WrapErr.ChkTrue(typeof(TMsgId).IsEnum, 9999, () => string.Format("Transition type {0} must be Enum", msgId.GetType().Name));
            WrapErr.ChkTrue(typeof(TMsgId).GetEnumUnderlyingType() == typeof(Int32), 9999,
                            () => string.Format("Transition type enum {0} must be derived from int", msgId.GetType().Name));

            int tmp = Convert.ToInt32(msgId);

            // 51007 - failure of conversion number

            // Duplicate transitions on same Event is a no no.
            WrapErr.ChkFalse(store.Keys.Contains(tmp), 51008,
                             () => { return(String.Format("Already Contain a '{0}' Transition for Id:{1}", type, tmp)); });
            store.Add(tmp, transition);
        }
예제 #15
0
        /// <summary>
        /// Read the transition object to determine behavior
        /// </summary>
        /// <param name="tr">The transition object</param>
        /// <param name="msg">the current event message</param>
        /// <param name="superStateLevelEvent">
        ///  true if the transition object is from the current substate, false if the transition was generated
        ///  by the superstate based on a previous Defered Transition type generated from the substate. This
        ///  prevents infinite recursion.
        /// </param>
        /// <returns>A Transtion object with the results of the state processing</returns>
        ISpStateTransition ReadTransitionType(ISpStateTransition tr, ISpEventMessage msg, bool superStateLevelEvent)
        {
            WrapErr.ChkVar(tr, 9999, "The transition is null");
            switch (tr.TransitionType)
            {
            case SpStateTransitionType.SameState:
                return(tr);

            case SpStateTransitionType.NextState:
                return(this.HandleNextStateTransitionType(tr, msg));

            case SpStateTransitionType.ExitState:
                return(this.HandleExitStateTransitionType(msg));

            case SpStateTransitionType.Defered:
                return(this.HandleDeferedStateTransitionType(tr, msg, superStateLevelEvent));

            default:
                WrapErr.ChkTrue(false, 9999, String.Format("Transition Type {0} not Handled", tr.TransitionType));
                return(tr);
            }
        }
예제 #16
0
        /// <summary>
        /// Handle the substate ExitState Transition Type by using the event msg passed to it to do a lookup
        /// of this super state's transitions. The super state should have a transition that has been
        /// registered to the same id
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private ISpStateTransition HandleExitStateTransitionType(ISpEventMessage msg)
        {
            Log.Info(this.className, "HandleExitStateTransitionType", String.Format("'{0}' State", this.FullName));

            // TODO - this is really only another kind of defered. The difference is that the superstate does not
            // TODO     called at runtime to handle the event. Rather the event is passed to the super state's
            // TODO     registered events. In this scenario it may not actually exit the super state but process
            //          the event id to something else. The difference is that the results are being determined
            //          by the registrations at the superstate level rather than the sub state level

            // Check super state registered result transitions against Sub State event id
            ISpStateTransition tr = this.GetSuperStateOnResultTransition(msg);

            WrapErr.ChkVar(tr, 9999, () => {
                return(String.Format(
                           "State {0} Specified Exit but SuperState {1} has no handlers for that event id:{2}",
                           this.currentState.FullName, this.FullName, this.GetCachedEventId(msg.EventId)));
            });

            // At this point, the transition registered to the superstate should have everything set in it
            return(tr);
        }
예제 #17
0
 /// <summary>
 /// Register a state transition from the result of state processing.
 /// </summary>
 /// <param name="eventId">The id converter of the event as the result of state processing</param>
 /// <param name="transition">The transition object</param>
 public void RegisterOnResultTransition(ISpToInt eventId, ISpStateTransition transition)
 {
     SpTools.RegisterTransition("OnResult", eventId, transition, this.onResultTransitions);
 }