/// <summary> /// Checks a state's starting state /// </summary> /// <param name="state">Specifies state</param> private void CheckStartingState(MartyState state) { // Check if state is null. if (state == null) { throw new ArgumentNullException("Cannot check the starting state of a null state."); } // Check if state's starting state is null. if (state.StartingState == null) { // Leave. return; } // Check if state has children. if (state.Children != null && state.Children.Count == 0) { throw new FormatException(string.Format("A state cannot have a starting state if it has no children; State = {0}, Invalid Starting State = {1}.", state.Name, this.GetState(state.StartingState).Name)); } // Check if starting state is one of the states children. if (!state.IsChild(state.StartingState)) { throw new FormatException(string.Format("A state cannot have a starting state that isn't one of its children; State = {0}, Invalid Starting State = {1}.", state.Name, this.GetState(state.StartingState).Name)); } }
/// <summary> /// Process exit event /// </summary> /// <param name="destinationState">Specifies destination state</param> internal void Exit(MartyState destinationState) { // Start event processing. this.ProcessingHandler(true); // Block transitioning. this.BlockTransitionHandler(); MartyState parentState = this.StateLookupHandler(this.ParentState); // Check if destination state is a descendant of this state. if (!this.IsDescendant(destinationState)) { // Process this state's exit event. this.EventHandler(MartyBase.Exit, null); // Check if this state's parent needs to process its exit event. if (!(this.ParentState == null || parentState.IsTopState || this.IsSibling(destinationState))) { // Process parent's exit event. parentState.EventHandler(MartyBase.Exit, null); } } // Stop event processing. this.ProcessingHandler(false); }
internal bool IsDescendant(MartyState state) { if (this.IsChild(state)) { return(true); } return(this.children.Any(child => this.StateLookupHandler(child).IsDescendant(state))); }
internal bool IsSibling(MartyState state) { if (this.Id == state.Id) { return(false); } return(this.ParentState == state.ParentState); }
/// <summary> /// Prepares Top state for use /// </summary> private void PrepareTopState() { // Initailize top state. this.Top = new MartyState(MartyConstants.TopStateName); // Register Top state. this.RegisterState(this.Top, this.TopHandler, null); // Throw an exception if starting state is null. if (this.TopStartingState == null) { throw new NullReferenceException(string.Format("The starting state for the {0} state cannot be null.", MartyConstants.TopStateName)); } // Set starting state for Top state. this.Top.StartingState = this.TopStartingState.Id; }
/// <summary> /// Changes states during state transition /// </summary> /// <param name="destinationState">Specifies destination state</param> private void ChangeState(MartyState destinationState) { // Mark that state transition is in progress. this.IsTransitioning = true; // Initialize old state as current state. MartyState oldState = this.CurrentState; // Check if current state is null. if (this.CurrentState != null) { this.CurrentState.Exit(destinationState); } // Set current state to be destination state. this.CurrentState = destinationState; // Check if starting state of current state is null. if (this.CurrentState.StartingState != null) { // Process start events until the actual starting state has been reached. do { // Process start event. this.CurrentState.Start(); // Set current state's starting state as current state. this.CurrentState = this.GetState(this.CurrentState.StartingState); } while (this.CurrentState.StartingState != null); } // Check if old state is null. if (oldState != null) { // Process entry event if it isn't null. this.CurrentState.Enter(oldState); } // Store that transitioning has ended. this.IsTransitioning = false; // Run next instruction. this.RunNextInstruction(); }
/// <summary> /// Transitions to a registered state /// </summary> /// <param name="destination">Specifies destination state</param> protected void TransitionTo(MartyState destination) { // Check if destination state is null. if (destination == null) { // Throw an exception if destination state is null. throw new InvalidOperationException(string.Format("Cannot transition to a null state.")); } // Throw an exception if a designation state is not registered. if (!this.states.Keys.Any(key => key == destination.Id)) { throw new InvalidOperationException(string.Format("The state {0} hasn't been registered.", destination.Name)); } // Throw an exception if transitioning is blocked in current state. if (!this.AllowTransitions) { throw new InvalidOperationException(string.Format("Transitions cannot be made during Start or Exit events. See state {0}.", this.CurrentState.Name)); } // Check if current state equals destination. if (this.CurrentState == destination) { // Leave. return; } // Check if state is busy. if (this.IsBusy) { // Queue instruction. this.instructions.Enqueue(new MartyInstruction(MartyInstructionTypes.Transition, destination.Id, null)); // Leave. return; } // Change state. this.ChangeState(destination); }
/// <summary> /// Performs entry action /// </summary> /// <param name="source">Specifies source state</param> internal void Enter(MartyState source) { // Mark that the event is being processed. this.ProcessingHandler(true); MartyState parentState = this.StateLookupHandler(this.ParentState); // Check if parent's entry event needs processing. if (!(null == parentState || parentState.IsTopState || source.IsAncestor(parentState) || this.IsDescendant(source))) { // Process parent's entry event. parentState.EventHandler(MartyBase.Entry, null); } // Check if source is NOT a descendant. if (!this.IsDescendant(source)) { // Process this.EventHandler(MartyBase.Entry, null); } // Release processing. this.ProcessingHandler(false); }
internal bool IsChild(MartyState state) { return(this.children.Any(child => child == state.Id)); }
internal bool IsAncestor(MartyState state) { return(!state.IsTopState && state.IsDescendant(this)); }
/// <summary> /// Registers a state /// </summary> /// <param name="state">Specifies state</param> /// <param name="eventHandler">Specifies event handler</param> /// <param name="parentState">Specifies parent</param> /// <param name="startingState">Specifies starting state</param> protected void RegisterState(MartyState state, EventHandler eventHandler, MartyState parentState = null, MartyState startingState = null) { // Check if state is null. if (state == null) { throw new ArgumentNullException("Cannot register a null state."); } // Check if state is being set as its own parent. if (state == parentState) { throw new ArgumentException(string.Format("State {0} cannot be its own parent.", state.Name)); } // Check if state is being set as its own starting state. if (state == startingState) { throw new ArgumentException(string.Format("State {0} cannot be its own starting state.", state.Name)); } // Try adding state to dictionary. try { // Add state to dictionary. this.states.Add(state.Id, state); } // Catch argument exceptions. catch (Exception exception) { // Throw exception with more detailed error message. throw new InvalidOperationException(string.Format("An exception occurred during state registration: {0}; Exception: {1}", state.Name, exception.Message)); } // Add state to parent state's children. if (parentState != null) { if (state.IsTopState) { throw new InvalidOperationException(string.Format("Cannot set '{0}' state as parent state. Use NULL instead.", MartyConstants.TopStateName)); } parentState.AddChild(state.Id); } else { if (!state.IsTopState) { parentState = this.Top; parentState.AddChild(state.Id); } } // Set state's properties. state.StartingState = startingState == null ? default(long?) : startingState.Id; state.ParentState = parentState == null ? default(long?) : parentState.Id; state.EventHandler = eventHandler; state.StateLookupHandler = this.GetState; state.ProcessingHandler = this.SetProcessing; state.BlockTransitionHandler = this.BlockTransitioning; }