State Machine Editor The State Machine EditorThe State Machine Editor is a tool that converts an XML description of a 'state machine' to source code in c# or other languages. A tool to convert user input to the XML is included. An example xml script is loaded from a button under the help selection, it shows the format and can be used to test the different kinds of code generation. A state machine is a set of Transitions where a starting 'State' receives an 'Event', performs an 'Action' and arrives at a final 'State'. A set of these transitions can model complex processes. The State Machine Editor can translate xml state machine models into c#, c++ or vb code, some work is needed to integrate the code into c++ or vb project however complete projects are generated for c#. Features:
The 2nd generated API for the state machines uses the State Machine PatternIn the generated c# and WPF project a button is created to trigger processing each event. Each state class has StateEntry, StateExit and Action events. The current state changes when an Action returns true. The Test Project generated maps each state's events to show the machine status. ... in MainWindow.xmal.cs public void OnLoaded(object obj, System.EventArgs e) { this.SetUpEvents(); this._stateMachineMonster.StateChanged += this.OnStateChanged; ... } ... public void SetUpEvents() { IState baseState = this._stateMachineMonster.CurrentState; if (("Moving" == baseState.StateName)) { Moving stateobj = ((Moving)(baseState)); stateobj.LookAbout += this.AnyAction; stateobj.ChompEnemy += this.AnyAction; stateobj.ChargeEnemy += this.AnyAction; stateobj.Nothing += this.AnyAction; stateobj.GoToFriend += this.AnyAction; stateobj.StateEntry += this.AnyEntry; stateobj.StateExit += this.AnyExit; return; } if (("Chasing" == baseState.StateName)) { In the statemachine class the current state is checked for an Action for the stateevent and either a new state name or null is returned. The StateChanged event is for use when the new CurrentState needs further initialization after it is changed. ... in the statemachine class events change the State class instance in an IState interface object 'CurrentState' public void MachineEventArrival(string stateevent) { // test if the stateevent runs an action if so run it and return a new statename or null string state = this.CurrentState.StateEventCheck(stateevent); // if the state remains the current state no StateEntry, StateChange or StateExit events need to be run if ((state == this.CurrentState.StateName)) { return; } if ((state != null)) { // runs the old state class's StateExit event this.CurrentState.OnExit(stateevent); // puts the new state class in CurrentState this.StateFromStateName(state); // if it is being used by someone run the StateChange event if ((this.StateChanged != null)) { this.StateChanged(this, new EventArgs()); } // run the new state class's StateEntry event this.CurrentState.OnEntry(stateevent); } } The first API between the real world and the State Machines was designed as a multicasting black boxA button is created to trigger processing each event. The stack traces shown below show how the event arrives, is tested, any Action run, and the state change if one occures. private void EventButtonOnClick(object sender, RoutedEventArgs routedEventArgs) {... case "EnemySuspected": this.chevnt = "EnemySuspected"; // check if 'EnemySuspected' event is valid evtst = _monster.MonsterInputReady.EventsReadyFromState(_monster.GetCurrentState()); if(evtst != null) { // tell the state machine to run the event 'EnemySuspected' retv = _monster.RunMonsterEventEnemySuspected(DateTime.Now.ToString()); } break; For this to work, methods are attached at start up to events that come from the state machine. public void StartTest() { _monster.MonsterEventEnemyClose += this.TestInputEventEnemyClose; _monster.MonsterEventEnemyFound += this.TestInputEventEnemyFound; _monster.MonsterEventEnemySuspected += this.TestInputEventEnemySuspected; //...missing _monster.MonsterActions.MonsterActionLookAbout += this.TestActionEventLookAbout; _monster.MonsterActions.MonsterActionChompEnemy += this.TestActionEventChompEnemy; _monster.MonsterActions.MonsterActionChargeEnemy += this.TestActionEventChargeEnemy;//... _monster.MonsterInputReady.MovingCheckEventReady += this.CheckForEventInStateMoving; _monster.MonsterInputReady.ChasingCheckEventReady += this.CheckForEventInStateChasing; _monster.MonsterInputReady.FleeingCheckEventReady += this.CheckForEventInStateFleeing; //... } The state machine tests if an event is valid for the state named 'Moving' The test code logs that it's checking Call Stack
public string CheckForEventInStateMoving(object sender, MonsterTestEventsReadyArgs e) { string nowstate = this._monster.GetCurrentState(); string runs = " running CheckForEventInStateMoving"; string message = string.Format("{0} In state {1} with id = {2} inst = {3}", runs, nowstate, e.Sm_ID, e.Sm_Instance); this.Writer(message); System.Collections.Generic.Dictionary validevnts = this._monster.MonsterInputReady.ListEventsFromState("Moving"); if (validevnts.ContainsKey(this.chevnt)) { return this.chevnt; } return null; } Called by the state machine when an event is known to be valid for the current state. The test code logs that the event arived Call Stack
public bool TestInputEventEnemySuspected(object sender, MonsterInputEventArgs e) { EventTextBlock.Text = "EnemySuspected"; string nowstate = this._monster.GetCurrentState(); string runs = " running MonsterEventEnemySuspected"; string message = string.Format("{0} In state {1} [{2}] for id = {3} instance = {4}", runs, nowstate, e.Comment, e.Sm_ID, e.Sm_Instance); this.Writer(message); return true; } Called by the state machine to perform the the action 'LookAbout' Call Stack
public bool TestActionEventLookAbout(object sender, MonsterActionsEventArgs e) { ActionTextBlock.Text = "LookAbout"; string nowstate = this._monster.GetCurrentState(); string runs = " running Action MonsterEvent_LookAbout"; string message = string.Format("{0} In state {1} with id = {2} inst = {3}", runs, nowstate, e.Sm_ID, e.Sm_Instance); this.Writer(message); return true; } |