/// <summary> /// Creates a state machine that references a Model. Many of the event /// delegates send a model reference with the notification. /// </summary> /// <param name="model">The model to which this State Machine belongs.</param> /// <param name="transitionMatrix">A matrix of booleans. 'From' states are the /// row indices, and 'To' states are the column indices. The contents of a given /// cell in the matrix indicates whether that transition is permissible.</param> /// <param name="followOnStates">An array of enumerations of states, indicating /// which transition should occur automatically, if any, after transition into /// a given state has completed successfully.</param> /// <param name="initialState">Specifies the state in the state machine that is /// to be the initial state.</param> public StateMachine(IModel model, bool[,] transitionMatrix, Enum[] followOnStates, Enum initialState) { m_model = model; m_enumValues = Enum.GetValues(initialState.GetType()); InitializeStateTranslationTable(initialState); if (transitionMatrix.GetLength(0) != transitionMatrix.GetLength(1)) { throw new ApplicationException("Transition matrix must be square."); } m_currentState = GetStateNumber(initialState); m_2DTransitions = new ITransitionHandler[m_numStates, m_numStates]; m_transitionsFrom = new ITransitionHandler[m_numStates]; m_transitionsTo = new ITransitionHandler[m_numStates]; m_stateMethods = new StateMethod[m_numStates]; m_followOnStates = followOnStates; for (int i = 0; i < m_numStates; i++) { m_transitionsFrom[i] = new TransitionHandler(); m_transitionsTo[i] = new TransitionHandler(); for (int j = 0; j < m_numStates; j++) { if (transitionMatrix[i, j]) { m_2DTransitions[i, j] = new TransitionHandler(); } else { m_2DTransitions[i, j] = new InvalidTransitionHandler(); } } } m_universalTransition = new TransitionHandler(); }
/// <summary> /// Commands the state machine to attempt transition to the indicated state. /// Returns a list of ITransitionFailureReasons. If this list is empty, the /// transition was successful. /// </summary> /// <param name="toWhatState">The desired new state of the State Machine.</param> /// <param name="userData">The user data to pass into this transition request - it will be sent out of each state change notification and state method.</param> /// <returns> /// A list of ITransitionFailureReasons. (Empty if successful.) /// </returns> public IList DoTransition(Enum toWhatState, object userData) { Debug.Assert(m_model != null, "Did you forget to set the model on the State Machine?"); try { m_transitionInProgress = true; m_nextState = GetStateNumber(toWhatState); if (s_diagnostics) { Trace.Write("State machine in model \"" + m_model.Name + "\" servicing request to transition "); _Debug.WriteLine("from \"" + State + "\" into \"" + toWhatState + "\"."); StackTrace st = new StackTrace(); _Debug.WriteLine(st.ToString()); } // TODO: Determine if this is a good policy - it prohibits self-transitions. if (m_nextState == m_currentState) { return(new ArrayList()); } MergedTransitionHandler mth = null; if (m_stateMachineStructureLocked) { if (m_mergedTransitionHandlers == null) { m_mergedTransitionHandlers = new MergedTransitionHandler[m_numStates][]; for (int i = 0; i < m_numStates; i++) { m_mergedTransitionHandlers[i] = new MergedTransitionHandler[m_numStates]; } } mth = m_mergedTransitionHandlers[m_currentState][m_nextState]; } if (mth == null) { TransitionHandler outbound = (TransitionHandler)m_transitionsFrom[m_currentState]; ITransitionHandler across = (ITransitionHandler)m_2DTransitions[m_currentState, m_nextState]; TransitionHandler inbound = (TransitionHandler)m_transitionsTo[m_nextState]; mth = new MergedTransitionHandler(outbound, (TransitionHandler)across, inbound, (TransitionHandler)m_universalTransition); if (m_stateMachineStructureLocked) { m_mergedTransitionHandlers[m_currentState][m_nextState] = mth; } if (across is InvalidTransitionHandler) { string reason = "Illegal State Transition requested from " + State + " to " + toWhatState; SimpleTransitionFailureReason stfr = new SimpleTransitionFailureReason(reason, this); throw new TransitionFailureException(stfr); } } if (s_diagnostics) { _Debug.WriteLine(mth.Dump()); } IList failureReasons = mth.DoPrepare(m_model, userData); if (failureReasons.Count != 0) { mth.DoRollback(m_model, userData, failureReasons); return(failureReasons); } mth.DoCommit(m_model, userData); if (s_diagnostics) { _Debug.WriteLine("Exiting " + State); } m_currentState = m_nextState; TransitionCompletedSuccessfully?.Invoke(m_model, userData); if (s_diagnostics) { _Debug.WriteLine("Entering " + State); } m_stateMethods[m_currentState]?.Invoke(m_model, userData); // After running the state method, see if there are any follow-on states to be processed. if (m_followOnStates == null || m_followOnStates[m_currentState] == null || m_followOnStates[m_currentState].Equals(State)) { return(null); } else { return(DoTransition(m_followOnStates[m_currentState], userData)); } } finally { if (s_diagnostics) { _Debug.WriteLine("Coming to a rest in state " + State); } m_transitionInProgress = false; m_nextState = m_currentState; } }
protected TransitionBase() { _handler = DependencyService.Get <ITransitionProvider>()?.Resolve(GetType()); }