private void TransitionToSynchronized(QState targetState, ref TransitionChain transitionChain) { //The entire method had been embedded within [MethodImpl(MethodImplOptions.Synchronized)], //which is not available in .NET Core, and so a lock is used instead... lock (this) { if (transitionChain != null) { // We encountered a race condition. The first (non-synchronized) check indicated that the transition chain // is null. However, a second threat beat us in getting into this synchronized method and populated // the transition chain in the meantime. We can execute the regular method again now. TransitionTo(targetState, ref transitionChain); } else { // The transition chain is not initialized yet, we need to dynamically retrieve // the required transition steps and record them so that we can subsequently simply // play them back. TransitionChainRecorder recorder = new TransitionChainRecorder(); TransitionFromSourceToTarget(targetState.GetMethodInfo(), recorder); // We pass the recorded transition steps back to the caller: transitionChain = recorder.GetRecordedTransitionChain(); } } }
private void TransitionDownToTargetState( QState targetState, ArrayList statesTargetToLCA, int indexFirstStateToEnter, TransitionChainRecorder recorder) { // we enter the states in the passed in array in reverse order for (int stateIndex = indexFirstStateToEnter; stateIndex >= 0; stateIndex--) { Trigger((QState)statesTargetToLCA[stateIndex], QSignals.Entry, recorder); } m_MyState = targetState; // At last we are ready to initialize the target state. // If the specified target state handles init then the effective // target state is deeper than the target state specified in // the transition. while (Trigger(targetState, QSignals.Init, recorder) == null) { // Initial transition must be one level deep Debug.Assert(targetState == GetSuperState(m_MyState)); targetState = m_MyState; Trigger(targetState, QSignals.Entry, recorder); } if (recorder != null) { // We always make sure that the last entry in the recorder represents the entry to the target state. EnsureLastTransistionStepIsEntryIntoTargetState(targetState, recorder); Debug.Assert(recorder.GetRecordedTransitionChain().Length > 0); } }
/// <summary> /// Handles the transition from the source state to the target state without the help of a previously /// recorded transition chain. /// </summary> /// <param name="targetState">The <see cref="QState"/> representing the state to transition to.</param> /// <param name="recorder">An instance of <see cref="TransitionChainRecorder"/> or <see langword="null"/></param> /// <remarks> /// Passing in <see langword="null"/> as the recorder means that we deal with a dynamic transition. /// If an actual instance of <see cref="TransitionChainRecorder"/> is passed in then we deal with a static /// transition that was not recorded yet. In this case the function will record the transition steps /// as they are determined. /// </remarks> private void TransitionFromSourceToTarget(QState targetState, TransitionChainRecorder recorder) { ArrayList statesTargetToLCA; int indexFirstStateToEnter; ExitUpToLCA(targetState, out statesTargetToLCA, out indexFirstStateToEnter, recorder); TransitionDownToTargetState(targetState, statesTargetToLCA, indexFirstStateToEnter, recorder); }
/// <summary> /// Sends the specified signal to the specified state and (optionally) records the transition /// </summary> /// <param name="receiverState">The <see cref="QState"/> that represents the state method /// to which to send the signal.</param> /// <param name="qSignal">The <see cref="QSignals"/> to send.</param> /// <param name="recorder">An instance of <see cref="TransitionChainRecorder"/> if the transition /// is to be recorded; <see langword="null"/> otherwise.</param> /// <returns>The <see cref="QState"/> returned by the state that recieved the signal.</returns> /// <remarks> /// Even if a recorder is specified, the transition will only be recorded if the state /// <see paramref="receiverState"/> actually handled it. /// This function is used to record the transition chain for a static transition that is executed /// the first time. /// </remarks> private QState Trigger(QState receiverState, string qSignal, TransitionChainRecorder recorder) { QState state = Trigger(receiverState, qSignal); if ((state == null) && (recorder != null)) { // The receiverState handled the event recorder.Record(receiverState, qSignal); } return(state); }
/// <summary> /// Sends the specified signal to the specified state and (optionally) records the transition /// </summary> /// <param name="receiverStateMethod">The <see cref="MethodInfo"/> that represents the state method /// to which to send the signal.</param> /// <param name="qSignal">The <see cref="QSignals"/> to send.</param> /// <param name="recorder">An instance of <see cref="TransitionChainRecorder"/> if the transition /// is to be recorded; <see langword="null"/> otherwise.</param> /// <returns>The <see cref="QState"/> returned by the state that recieved the signal.</returns> /// <remarks> /// Even if a recorder is specified, the transition will only be recorded if the state /// <see paramref="receiverStateMethod"/> actually handled it. /// This function is used to record the transition chain for a static transition that is executed /// the first time. /// </remarks> private MethodInfo Trigger(MethodInfo receiverStateMethod, QSignals qSignal, TransitionChainRecorder recorder) { MethodInfo stateMethod = Trigger(receiverStateMethod, qSignal); if ((stateMethod == null) && (recorder != null)) { // The receiverState handled the event recorder.Record(receiverStateMethod, qSignal); } return(stateMethod); }
private void TransitionToSynchronized(QState targetState, ref TransitionChain transitionChain) { if (transitionChain != null) { // We encountered a race condition. The first (non-synchronized) check indicated that the transition chain // is null. However, a second threat beat us in getting into this synchronized method and populated // the transition chain in the meantime. We can execute the regular method again now. TransitionTo(targetState, ref transitionChain); } else { // The transition chain is not initialized yet, we need to dynamically retrieve // the required transition steps and record them so that we can subsequently simply // play them back. TransitionChainRecorder recorder = new TransitionChainRecorder(); TransitionFromSourceToTarget(targetState, recorder); // We pass the recorded transition steps back to the caller: transitionChain = recorder.GetRecordedTransitionChain(); } }
private void EnsureLastTransistionStepIsEntryIntoTargetState( QState targetState, TransitionChainRecorder recorder) { if (recorder.GetRecordedTransitionChain().Length == 0) { // Nothing recorded so far RecordEntryIntoTargetState(targetState, recorder); return; } else { // We need to test whether the last recorded transition step is the entry into the target state TransitionChain transitionChain = recorder.GetRecordedTransitionChain(); TransitionStep lastTransitionStep = transitionChain[transitionChain.Length - 1]; if (lastTransitionStep.State != targetState || lastTransitionStep.QSignal != QSignals.Entry) { RecordEntryIntoTargetState(targetState, recorder); return; } } }
/// <summary> /// Determines the transition chain between the target state and the LCA (Least Common Ancestor) /// and exits up to LCA while doing so. /// </summary> /// <param name="targetStateMethod">The target state method of the transition.</param> /// <param name="statesTargetToLCA">An <see cref="ArrayList"/> that holds (in reverse order) the states /// that need to be entered on the way down to the target state. /// Note: The index of the first state that needs to be entered is returned in /// <see paramref="indexFirstStateToEnter"/>.</param> /// <param name="indexFirstStateToEnter">Returns the index in the array <see cparamref="statesTargetToLCA"/> /// that specifies the first state that needs to be entered on the way down to the target state.</param> /// <param name="recorder">An instance of <see cref="TransitionChainRecorder"/> if the transition chain /// should be recorded; <see langword="null"/> otherwise.</param> private void ExitUpToLCA( QState targetState, out ArrayList statesTargetToLCA, out int indexFirstStateToEnter, TransitionChainRecorder recorder) { statesTargetToLCA = new ArrayList(); statesTargetToLCA.Add(targetState); indexFirstStateToEnter = 0; // (a) check my source state == target state (transition to self) if (m_MySourceState == targetState) { Trigger(m_MySourceState, QSignals.Exit, recorder); return; } // (b) check my source state == super state of the target state QState targetSuperState = GetSuperState(targetState); //Debug.WriteLine(targetSuperState.Name); if (m_MySourceState == targetSuperState) { return; } // (c) check super state of my source state == super state of target state // (most common) QState sourceSuperState = GetSuperState(m_MySourceState); if (sourceSuperState == targetSuperState) { Trigger(m_MySourceState, QSignals.Exit, recorder); return; } // (d) check super state of my source state == target if (sourceSuperState == targetState) { Trigger(m_MySourceState, QSignals.Exit, recorder); indexFirstStateToEnter = -1; // we don't enter the LCA return; } // (e) check rest of my source = super state of super state ... of target state hierarchy statesTargetToLCA.Add(targetSuperState); indexFirstStateToEnter++; for (QState state = GetSuperState(targetSuperState); state != null; state = GetSuperState(state)) { if (m_MySourceState == state) { return; } statesTargetToLCA.Add(state); indexFirstStateToEnter++; } // For both remaining cases we need to exit the source state Trigger(m_MySourceState, QSignals.Exit, recorder); // (f) check rest of super state of my source state == // super state of super state of ... target state // The array list is currently filled with all the states // from the target state up to the top state for (int stateIndex = indexFirstStateToEnter; stateIndex >= 0; stateIndex--) { if (sourceSuperState == (QState)statesTargetToLCA[stateIndex]) { indexFirstStateToEnter = stateIndex - 1; // Note that we do not include the LCA state itself; // i.e., we do not enter the LCA return; } } // (g) check each super state of super state ... of my source state == // super state of super state of ... target state for (QState state = sourceSuperState; state != null; state = GetSuperState(state)) { for (int stateIndex = indexFirstStateToEnter; stateIndex >= 0; stateIndex--) { if (state == (QState)statesTargetToLCA[stateIndex]) { indexFirstStateToEnter = stateIndex - 1; // Note that we do not include the LCA state itself; // i.e., we do not enter the LCA return; } } Trigger(state, QSignals.Exit, recorder); } // We should never get here throw new ApplicationException("Mal formed Hierarchical State Machine"); }
private void RecordEntryIntoTargetState( MethodInfo targetStateMethod, TransitionChainRecorder recorder) { recorder.Record(targetStateMethod, QSignals.Entry); }
/// <summary> /// Determines the transition chain between the target state and the LCA (Least Common Ancestor) /// and exits up to LCA while doing so. /// </summary> /// <param name="targetStateMethod">The target state method of the transition.</param> /// <param name="statesTargetToLCA">An <see cref="ArrayList"/> that holds (in reverse order) the states /// that need to be entered on the way down to the target state. /// Note: The index of the first state that needs to be entered is returned in /// <see paramref="indexFirstStateToEnter"/>.</param> /// <param name="indexFirstStateToEnter">Returns the index in the array <see cparamref="statesTargetToLCA"/> /// that specifies the first state that needs to be entered on the way down to the target state.</param> /// <param name="recorder">An instance of <see cref="TransitionChainRecorder"/> if the transition chain /// should be recorded; <see langword="null"/> otherwise.</param> private void ExitUpToLCA( MethodInfo targetStateMethod, out ArrayList statesTargetToLCA, out int indexFirstStateToEnter, TransitionChainRecorder recorder) { statesTargetToLCA = new ArrayList(); statesTargetToLCA.Add(targetStateMethod); indexFirstStateToEnter = 0; // (a) check my source state == target state (transition to self) if(m_MySourceStateMethod == targetStateMethod) { Trigger(m_MySourceStateMethod, QSignals.Exit, recorder); return; } // (b) check my source state == super state of the target state MethodInfo targetSuperStateMethod = GetSuperStateMethod(targetStateMethod); //Debug.WriteLine(targetSuperStateMethod.Name); if(m_MySourceStateMethod == targetSuperStateMethod) { return; } // (c) check super state of my source state == super state of target state // (most common) MethodInfo sourceSuperStateMethod = GetSuperStateMethod(m_MySourceStateMethod); if(sourceSuperStateMethod == targetSuperStateMethod) { Trigger(m_MySourceStateMethod, QSignals.Exit, recorder); return; } // (d) check super state of my source state == target if (sourceSuperStateMethod == targetStateMethod) { Trigger(m_MySourceStateMethod, QSignals.Exit, recorder); indexFirstStateToEnter = -1; // we don't enter the LCA return; } // (e) check rest of my source = super state of super state ... of target state hierarchy statesTargetToLCA.Add(targetSuperStateMethod); indexFirstStateToEnter++; for (MethodInfo stateMethod = GetSuperStateMethod(targetSuperStateMethod); stateMethod != null; stateMethod = GetSuperStateMethod(stateMethod)) { if (m_MySourceStateMethod == stateMethod) { return; } statesTargetToLCA.Add(stateMethod); indexFirstStateToEnter++; } // For both remaining cases we need to exit the source state Trigger(m_MySourceStateMethod, QSignals.Exit, recorder); // (f) check rest of super state of my source state == // super state of super state of ... target state // The array list is currently filled with all the states // from the target state up to the top state for (int stateIndex = indexFirstStateToEnter; stateIndex >= 0; stateIndex--) { if (sourceSuperStateMethod == (MethodInfo)statesTargetToLCA[stateIndex]) { indexFirstStateToEnter = stateIndex - 1; // Note that we do not include the LCA state itself; // i.e., we do not enter the LCA return; } } // (g) check each super state of super state ... of my source state == // super state of super state of ... target state for (MethodInfo stateMethod = sourceSuperStateMethod; stateMethod != null; stateMethod = GetSuperStateMethod(stateMethod)) { for (int stateIndex = indexFirstStateToEnter; stateIndex >= 0; stateIndex--) { if (stateMethod == (MethodInfo)statesTargetToLCA[stateIndex]) { indexFirstStateToEnter = stateIndex - 1; // Note that we do not include the LCA state itself; // i.e., we do not enter the LCA return; } } Trigger(stateMethod, QSignals.Exit, recorder); } // We should never get here throw new ApplicationException("Mal formed Hierarchical State Machine"); }
/// <summary> /// Sends the specified signal to the specified state and (optionally) records the transition /// </summary> /// <param name="receiverStateMethod">The <see cref="MethodInfo"/> that represents the state method /// to which to send the signal.</param> /// <param name="qSignal">The <see cref="QSignals"/> to send.</param> /// <param name="recorder">An instance of <see cref="TransitionChainRecorder"/> if the transition /// is to be recorded; <see langword="null"/> otherwise.</param> /// <returns>The <see cref="QState"/> returned by the state that recieved the signal.</returns> /// <remarks> /// Even if a recorder is specified, the transition will only be recorded if the state /// <see paramref="receiverStateMethod"/> actually handled it. /// This function is used to record the transition chain for a static transition that is executed /// the first time. /// </remarks> private MethodInfo Trigger(MethodInfo receiverStateMethod, string qSignal, TransitionChainRecorder recorder) { MethodInfo stateMethod = Trigger(receiverStateMethod, qSignal); if ((stateMethod == null) && (recorder != null)) { // The receiverState handled the event recorder.Record(receiverStateMethod, qSignal); } return stateMethod; }
private void TransitionToSynchronized(QState targetState, ref TransitionChain transitionChain) { if (transitionChain != null) { // We encountered a race condition. The first (non-synchronized) check indicated that the transition chain // is null. However, a second threat beat us in getting into this synchronized method and populated // the transition chain in the meantime. We can execute the regular method again now. TransitionTo(targetState, ref transitionChain); } else { // The transition chain is not initialized yet, we need to dynamically retrieve // the required transition steps and record them so that we can subsequently simply // play them back. TransitionChainRecorder recorder = new TransitionChainRecorder(); TransitionFromSourceToTarget(targetState.Method, recorder); // We pass the recorded transition steps back to the caller: transitionChain = recorder.GetRecordedTransitionChain(); } }
/// <summary> /// Handles the transition from the source state to the target state without the help of a previously /// recorded transition chain. /// </summary> /// <param name="targetStateMethod">The <see cref="MethodInfo"/> representing the state method to transition to.</param> /// <param name="recorder">An instance of <see cref="TransitionChainRecorder"/> or <see langword="null"/></param> /// <remarks> /// Passing in <see langword="null"/> as the recorder means that we deal with a dynamic transition. /// If an actual instance of <see cref="TransitionChainRecorder"/> is passed in then we deal with a static /// transition that was not recorded yet. In this case the function will record the transition steps /// as they are determined. /// </remarks> private void TransitionFromSourceToTarget(MethodInfo targetStateMethod, TransitionChainRecorder recorder) { ArrayList statesTargetToLCA; int indexFirstStateToEnter; ExitUpToLCA(targetStateMethod, out statesTargetToLCA, out indexFirstStateToEnter, recorder); TransitionDownToTargetState(targetStateMethod, statesTargetToLCA, indexFirstStateToEnter, recorder); }
private void TransitionDownToTargetState( MethodInfo targetStateMethod, ArrayList statesTargetToLCA, int indexFirstStateToEnter, TransitionChainRecorder recorder) { // we enter the states in the passed in array in reverse order for (int stateIndex = indexFirstStateToEnter; stateIndex >= 0; stateIndex--) { Trigger((MethodInfo)statesTargetToLCA[stateIndex], QSignals.Entry, recorder); } m_MyStateMethod = targetStateMethod; // At last we are ready to initialize the target state. // If the specified target state handles init then the effective // target state is deeper than the target state specified in // the transition. while (Trigger(targetStateMethod, QSignals.Init, recorder) == null) { // Initial transition must be one level deep Debug.Assert(targetStateMethod == GetSuperStateMethod(m_MyStateMethod)); targetStateMethod = m_MyStateMethod; Trigger(targetStateMethod, QSignals.Entry, recorder); } if (recorder != null) { // We always make sure that the last entry in the recorder represents the entry to the target state. EnsureLastTransistionStepIsEntryIntoTargetState(targetStateMethod, recorder); Debug.Assert(recorder.GetRecordedTransitionChain().Length > 0); } }
private void RecordEntryIntoTargetState( QState targetState, TransitionChainRecorder recorder) { recorder.Record(targetState, QSignals.Entry); }
private void EnsureLastTransistionStepIsEntryIntoTargetState( MethodInfo targetStateMethod, TransitionChainRecorder recorder) { if (recorder.GetRecordedTransitionChain().Length == 0) { // Nothing recorded so far RecordEntryIntoTargetState(targetStateMethod, recorder); return; } else { // We need to test whether the last recorded transition step is the entry into the target state TransitionChain transitionChain = recorder.GetRecordedTransitionChain(); TransitionStep lastTransitionStep = transitionChain[transitionChain.Length - 1]; if (lastTransitionStep.StateMethod != targetStateMethod || lastTransitionStep.QSignal != QSignals.Entry) { RecordEntryIntoTargetState(targetStateMethod, recorder); return; } } }