/// <summary> /// Handles the given event. /// </summary> /// <param name="e">Event to handle</param> private void HandleEvent(Event e) { // Do not process an ignored event. if (this.IgnoredEvents.Contains(e.GetType())) { Output.Log("<IgnoreLog> Monitor '{0}' ignored event '{1}'.", this.GetType().Name, e.GetType()); return; } // Assign trigger and payload. this.Trigger = e.GetType(); this.Payload = e.Payload; while (true) { if (this.State == null) { // If the event cannot be handled then report an error and exit. this.Assert(false, "Monitor '{0}' received event '{1}' that cannot be handled.", this.GetType().Name, e.GetType().Name); } // If current state cannot handle the event then null the state. if (!this.CanHandleEvent(e.GetType())) { Output.Debug(DebugType.Runtime, "<ExitLog> Monitor '{0}' exiting state '{1}'.", this, this.State.GetType().Name); this.State = null; continue; } // Checks if the event can trigger a goto state transition. if (this.GotoTransitions.ContainsKey(e.GetType())) { var transition = this.GotoTransitions[e.GetType()]; Type targetState = transition.Item1; Action onExitAction = transition.Item2; this.GotoState(targetState, onExitAction); } // Checks if the event can trigger an action. else if (this.ActionBindings.ContainsKey(e.GetType())) { Action action = this.ActionBindings[e.GetType()]; this.Do(action); } break; } }
/// <summary> /// Initializes information about the states of the monitor. /// </summary> private void InitializeStateInformation() { this.StateTypes = new HashSet<Type>(); this.States = new HashSet<MonitorState>(); Type monitorType = this.GetType(); Type initialStateType = null; while (monitorType != typeof(Monitor)) { foreach (var s in monitorType.GetNestedTypes(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)) { if (s.IsClass && s.IsSubclassOf(typeof(MonitorState))) { if (s.IsDefined(typeof(Start), false)) { this.Assert(initialStateType == null, "Monitor '{0}' can not have " + "more than one start states.\n", this.GetType().Name); initialStateType = s; } this.Assert(s.BaseType == typeof(MonitorState), "State '{0}' is " + "not of the correct type.\n", s.Name); this.StateTypes.Add(s); } } monitorType = monitorType.BaseType; } foreach (var type in this.StateTypes) { var isHot = false; var isCold = false; var hotAttribute = type.GetCustomAttribute(typeof(Hot), false) as Hot; if (hotAttribute != null) { isHot = true; } var coldAttribute = type.GetCustomAttribute(typeof(Cold), false) as Cold; if (coldAttribute != null) { isCold = true; } MonitorState state = Activator.CreateInstance(type) as MonitorState; state.InitializeState(this, isHot, isCold); this.States.Add(state); } var initialState = this.States.First(val => val.GetType().Equals(initialStateType)); this.ConfigureStateTransitions(initialState); this.State = initialState; }
/// <summary> /// Configures the state transitions of the monitor. /// </summary> /// <param name="state">State</param> private void ConfigureStateTransitions(MonitorState state) { this.GotoTransitions = state.GotoTransitions; this.ActionBindings = state.ActionBindings; this.IgnoredEvents = state.IgnoredEvents; }
/// <summary> /// Performs a goto transition to the given state. /// </summary> /// <param name="s">Type of the state</param> /// <param name="onExit">Goto on exit action</param> private void GotoState(Type s, Action onExit) { // The monitor performs the on exit statements of the current state. this.ExecuteCurrentStateOnExit(onExit); var nextState = this.States.First(val => val.GetType().Equals(s)); this.ConfigureStateTransitions(nextState); // The monitor transitions to the new state. this.State = nextState; // The monitor performs the on entry statements of the new state. this.ExecuteCurrentStateOnEntry(); }
/// <summary> /// Handles the given event. /// </summary> /// <param name="e">Event to handle</param> private void HandleEvent(Event e) { // Do not process an ignored event. if (this.IgnoredEvents.Contains(e.GetType())) { return; } // Assigns the receieved event. this.ReceivedEvent = e; while (true) { if (this.State == null) { // If the event cannot be handled, then report an error and exit. this.Assert(false, $"Monitor '{this.GetType().Name}' received event " + $"'{e.GetType().FullName}' that cannot be handled."); } // If current state cannot handle the event then null the state. if (!this.CanHandleEvent(e.GetType())) { base.Runtime.NotifyExitedState(this); this.State = null; continue; } // Checks if the event is a goto state event. if (e.GetType() == typeof(GotoStateEvent)) { Type targetState = (e as GotoStateEvent).State; this.GotoState(targetState, null); } // Checks if the event can trigger a goto state transition. else if (this.GotoTransitions.ContainsKey(e.GetType())) { var transition = this.GotoTransitions[e.GetType()]; this.GotoState(transition.Item1, transition.Item2); } // Checks if the event can trigger an action. else if (this.ActionBindings.ContainsKey(e.GetType())) { string actionName = this.ActionBindings[e.GetType()]; this.Do(actionName); } break; } }
/// <summary> /// Performs a goto transition to the given state. /// </summary> /// <param name="s">Type of the state</param> /// <param name="onExitActionName">Action name</param> private void GotoState(Type s, string onExitActionName) { // The monitor performs the on exit statements of the current state. this.ExecuteCurrentStateOnExit(onExitActionName); var nextState = StateMap[this.GetType()].First(val => val.GetType().Equals(s)); this.ConfigureStateTransitions(nextState); // The monitor transitions to the new state. this.State = nextState; if (nextState.IsCold) { this.LivenessTemperature = 0; } // The monitor performs the on entry statements of the new state. this.ExecuteCurrentStateOnEntry(); }
/// <summary> /// Initializes information about the states of the monitor. /// </summary> internal void InitializeStateInformation() { Type monitorType = this.GetType(); // Caches the available state types for this monitor type. if (StateTypeMap.TryAdd(monitorType, new HashSet<Type>())) { Type baseType = monitorType; while (baseType != typeof(Monitor)) { foreach (var s in baseType.GetNestedTypes(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)) { this.ExtractStateTypes(s); } baseType = baseType.BaseType; } } // Caches the available state instances for this monitor type. if (StateMap.TryAdd(monitorType, new HashSet<MonitorState>())) { foreach (var type in StateTypeMap[monitorType]) { var state = Activator.CreateInstance(type) as MonitorState; state.InitializeState(); this.Assert((state.IsCold && !state.IsHot) || (!state.IsCold && state.IsHot) || (!state.IsCold && !state.IsHot), $"State '{type.FullName}' of monitor '{base.Id}' " + "cannot be both cold and hot."); StateMap[monitorType].Add(state); } } // Caches the actions declarations for this monitor type. if (MonitorActionMap.TryAdd(monitorType, new Dictionary<string, MethodInfo>())) { foreach (var state in StateMap[monitorType]) { if (state.EntryAction != null && !MonitorActionMap[monitorType].ContainsKey(state.EntryAction)) { MonitorActionMap[monitorType].Add(state.EntryAction, this.GetActionWithName(state.EntryAction)); } if (state.ExitAction != null && !MonitorActionMap[monitorType].ContainsKey(state.ExitAction)) { MonitorActionMap[monitorType].Add(state.ExitAction, this.GetActionWithName(state.ExitAction)); } foreach (var transition in state.GotoTransitions) { if (transition.Value.Item2 != null && !MonitorActionMap[monitorType].ContainsKey(transition.Value.Item2)) { MonitorActionMap[monitorType].Add(transition.Value.Item2, this.GetActionWithName(transition.Value.Item2)); } } foreach (var action in state.ActionBindings) { if (!MonitorActionMap[monitorType].ContainsKey(action.Value)) { MonitorActionMap[monitorType].Add(action.Value, this.GetActionWithName(action.Value)); } } } } // Populates the map of actions for this monitor instance. foreach (var kvp in MonitorActionMap[monitorType]) { this.ActionMap.Add(kvp.Key, kvp.Value); } var initialStates = StateMap[monitorType].Where(state => state.IsStart).ToList(); this.Assert(initialStates.Count != 0, $"Monitor '{base.Id}' must declare a start state."); this.Assert(initialStates.Count == 1, $"Monitor '{base.Id}' " + "can not declare more than one start states."); this.ConfigureStateTransitions(initialStates.Single()); this.State = initialStates.Single(); this.AssertStateValidity(); }
/// <summary> /// Initializes information about the states of the monitor. /// </summary> internal void InitializeStateInformation() { Type monitorType = this.GetType(); // Caches the available state types for this monitor type. if (StateTypeMap.TryAdd(monitorType, new HashSet <Type>())) { Type baseType = monitorType; while (baseType != typeof(Monitor)) { foreach (var s in baseType.GetNestedTypes(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)) { this.ExtractStateTypes(s); } baseType = baseType.BaseType; } } // Caches the available state instances for this monitor type. if (StateMap.TryAdd(monitorType, new HashSet <MonitorState>())) { foreach (var type in StateTypeMap[monitorType]) { Type stateType = type; if (type.IsAbstract) { continue; } if (type.IsGenericType) { // If the state type is generic (only possible if inherited by a // generic monitor declaration), then iterate through the base // monitor classes to identify the runtime generic type, and use // it to instantiate the runtime state type. This type can be // then used to create the state constructor. Type declaringType = this.GetType(); while (!declaringType.IsGenericType || !type.DeclaringType.FullName.Equals(declaringType.FullName.Substring( 0, declaringType.FullName.IndexOf('[')))) { declaringType = declaringType.BaseType; } if (declaringType.IsGenericType) { stateType = type.MakeGenericType(declaringType.GetGenericArguments()); } } ConstructorInfo constructor = stateType.GetConstructor(Type.EmptyTypes); var lambda = Expression.Lambda <Func <MonitorState> >( Expression.New(constructor)).Compile(); MonitorState state = lambda(); state.InitializeState(); this.Assert((state.IsCold && !state.IsHot) || (!state.IsCold && state.IsHot) || (!state.IsCold && !state.IsHot), $"State '{type.FullName}' of monitor '{this.Id}' " + "cannot be both cold and hot."); StateMap[monitorType].Add(state); } } // Caches the actions declarations for this monitor type. if (MonitorActionMap.TryAdd(monitorType, new Dictionary <string, MethodInfo>())) { foreach (var state in StateMap[monitorType]) { if (state.EntryAction != null && !MonitorActionMap[monitorType].ContainsKey(state.EntryAction)) { MonitorActionMap[monitorType].Add(state.EntryAction, this.GetActionWithName(state.EntryAction)); } if (state.ExitAction != null && !MonitorActionMap[monitorType].ContainsKey(state.ExitAction)) { MonitorActionMap[monitorType].Add(state.ExitAction, this.GetActionWithName(state.ExitAction)); } foreach (var transition in state.GotoTransitions) { if (transition.Value.Lambda != null && !MonitorActionMap[monitorType].ContainsKey(transition.Value.Lambda)) { MonitorActionMap[monitorType].Add(transition.Value.Lambda, this.GetActionWithName(transition.Value.Lambda)); } } foreach (var action in state.ActionBindings) { if (!MonitorActionMap[monitorType].ContainsKey(action.Value.Name)) { MonitorActionMap[monitorType].Add(action.Value.Name, this.GetActionWithName(action.Value.Name)); } } } } // Populates the map of actions for this monitor instance. foreach (var kvp in MonitorActionMap[monitorType]) { this.ActionMap.Add(kvp.Key, kvp.Value); } var initialStates = StateMap[monitorType].Where(state => state.IsStart).ToList(); this.Assert(initialStates.Count != 0, $"Monitor '{this.Id}' must declare a start state."); this.Assert(initialStates.Count == 1, $"Monitor '{this.Id}' " + "can not declare more than one start states."); this.ConfigureStateTransitions(initialStates.Single()); this.State = initialStates.Single(); this.AssertStateValidity(); }