diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llcharacter/llstatemachine.cpp |
Print done when done.
Diffstat (limited to 'indra/llcharacter/llstatemachine.cpp')
-rw-r--r-- | indra/llcharacter/llstatemachine.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/indra/llcharacter/llstatemachine.cpp b/indra/llcharacter/llstatemachine.cpp new file mode 100644 index 0000000000..d51b2f75aa --- /dev/null +++ b/indra/llcharacter/llstatemachine.cpp @@ -0,0 +1,378 @@ +/** + * @file llstatemachine.cpp + * @brief LLStateMachine implementation file. + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "llstatemachine.h" +#include "llapr.h" + +#define FSM_PRINT_STATE_TRANSITIONS (0) + +U32 LLUniqueID::sNextID = 0; + +bool operator==(const LLUniqueID &a, const LLUniqueID &b) +{ + return (a.mId == b.mId); +} + +bool operator!=(const LLUniqueID &a, const LLUniqueID &b) +{ + return (a.mId != b.mId); +} + +//----------------------------------------------------------------------------- +// LLStateDiagram +//----------------------------------------------------------------------------- +LLStateDiagram::LLStateDiagram() +{ + mUseDefaultState = FALSE; +} + +LLStateDiagram::~LLStateDiagram() +{ + +} + +// add a state to the state graph +BOOL LLStateDiagram::addState(LLFSMState *state) +{ + mStates[state] = Transitions(); + return TRUE; +} + +// add a directed transition between 2 states +BOOL LLStateDiagram::addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition) +{ + StateMap::iterator state_it; + state_it = mStates.find(&start_state); + Transitions* state_transitions = NULL; + if (state_it == mStates.end() ) + { + addState(&start_state); + state_transitions = &mStates[&start_state]; + } + else + { + state_transitions = &state_it->second; + } + state_it = mStates.find(&end_state); + if (state_it == mStates.end() ) + { + addState(&end_state); + } + + Transitions::iterator transition_it = state_transitions->find(&transition); + if (transition_it != state_transitions->end()) + { + llerrs << "LLStateTable::addDirectedTransition() : transition already exists" << llendl; + return FALSE; // transition already exists + } + + (*state_transitions)[&transition] = &end_state; + return TRUE; +} + +// add an undirected transition between 2 states +BOOL LLStateDiagram::addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition) +{ + BOOL result; + result = addTransition(start_state, end_state, transition); + if (result) + { + result = addTransition(end_state, start_state, transition); + } + return result; +} + +// add a transition that exists for every state +void LLStateDiagram::addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition) +{ + mDefaultTransitions[&transition] = &end_state; +} + +// process a possible transition, and get the resulting state +LLFSMState* LLStateDiagram::processTransition(LLFSMState& start_state, LLFSMTransition& transition) +{ + // look up transition + //LLFSMState** dest_state = (mStates.getValue(&start_state))->getValue(&transition); + LLFSMState* dest_state = NULL; + StateMap::iterator state_it = mStates.find(&start_state); + if (state_it == mStates.end()) + { + return NULL; + } + Transitions::iterator transition_it = state_it->second.find(&transition); + + // try default transitions if state-specific transition not found + if (transition_it == state_it->second.end()) + { + dest_state = mDefaultTransitions[&transition]; + } + else + { + dest_state = transition_it->second; + } + + // if we have a destination state... + if (NULL != dest_state) + { + // ...return it... + return dest_state; + } + // ... otherwise ... + else + { + // ...look for default state... + if (mUseDefaultState) + { + // ...return it if we have it... + return mDefaultState; + } + else + { + // ...or else we're still in the same state. + return &start_state; + } + } +} + +void LLStateDiagram::setDefaultState(LLFSMState& default_state) +{ + mUseDefaultState = TRUE; + mDefaultState = &default_state; +} + +S32 LLStateDiagram::numDeadendStates() +{ + S32 numDeadends = 0; + StateMap::iterator state_it; + for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it) + { + if (state_it->second.size() == 0) + { + numDeadends++; + } + } + return numDeadends; +} + +BOOL LLStateDiagram::stateIsValid(LLFSMState& state) +{ + if (mStates.find(&state) != mStates.end()) + { + return TRUE; + } + return FALSE; +} + +LLFSMState* LLStateDiagram::getState(U32 state_id) +{ + StateMap::iterator state_it; + for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it) + { + if (state_it->first->getID() == state_id) + { + return state_it->first; + } + } + return NULL; +} + +BOOL LLStateDiagram::saveDotFile(const char* filename) +{ + apr_file_t* dot_file = ll_apr_file_open(filename, LL_APR_W); + + if (!dot_file) + { + llwarns << "LLStateDiagram::saveDotFile() : Couldn't open " << filename << " to save state diagram." << llendl; + return FALSE; + } + apr_file_printf(dot_file, "digraph StateMachine {\n\tsize=\"100,100\";\n\tfontsize=40;\n\tlabel=\"Finite State Machine\";\n\torientation=landscape\n\tratio=.77\n"); + + StateMap::iterator state_it; + for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it) + { + apr_file_printf(dot_file, "\t\"%s\" [fontsize=28,shape=box]\n", state_it->first->getName().c_str()); + } + apr_file_printf(dot_file, "\t\"All States\" [fontsize=30,style=bold,shape=box]\n"); + + Transitions::iterator transitions_it; + for(transitions_it = mDefaultTransitions.begin(); transitions_it != mDefaultTransitions.end(); ++transitions_it) + { + apr_file_printf(dot_file, "\t\"All States\" -> \"%s\" [label = \"%s\",fontsize=24];\n", transitions_it->second->getName().c_str(), + transitions_it->second->getName().c_str()); + } + + if (mDefaultState) + { + apr_file_printf(dot_file, "\t\"All States\" -> \"%s\";\n", mDefaultState->getName().c_str()); + } + + + for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it) + { + LLFSMState *state = state_it->first; + + Transitions::iterator transitions_it; + for(transitions_it = state_it->second.begin(); + transitions_it != state_it->second.end(); + ++transitions_it) + { + std::string state_name = state->getName(); + std::string target_name = transitions_it->second->getName(); + std::string transition_name = transitions_it->first->getName(); + apr_file_printf(dot_file, "\t\"%s\" -> \"%s\" [label = \"%s\",fontsize=24];\n", state->getName().c_str(), + target_name.c_str(), + transition_name.c_str()); + } + } + + apr_file_printf(dot_file, "}\n"); + + apr_file_close(dot_file); + + return TRUE; +} + +std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM) +{ + if (FSM.mDefaultState) + { + s << "Default State: " << FSM.mDefaultState->getName() << "\n"; + } + + LLStateDiagram::Transitions::iterator transitions_it; + for(transitions_it = FSM.mDefaultTransitions.begin(); + transitions_it != FSM.mDefaultTransitions.end(); + ++transitions_it) + { + s << "Any State -- " << transitions_it->first->getName() + << " --> " << transitions_it->second->getName() << "\n"; + } + + LLStateDiagram::StateMap::iterator state_it; + for(state_it = FSM.mStates.begin(); state_it != FSM.mStates.end(); ++state_it) + { + LLStateDiagram::Transitions::iterator transitions_it; + for(transitions_it = state_it->second.begin(); + transitions_it != state_it->second.end(); + ++transitions_it) + { + s << state_it->first->getName() << " -- " << transitions_it->first->getName() + << " --> " << transitions_it->second->getName() << "\n"; + } + s << "\n"; + } + + return s; +} + +//----------------------------------------------------------------------------- +// LLStateMachine +//----------------------------------------------------------------------------- + +LLStateMachine::LLStateMachine() +{ + // we haven't received a starting state yet + mCurrentState = NULL; + mLastState = NULL; + mStateDiagram = NULL; +} + +LLStateMachine::~LLStateMachine() +{ + +} + +// returns current state +LLFSMState* LLStateMachine::getCurrentState() const +{ + return mCurrentState; +} + +// executes current state +void LLStateMachine::runCurrentState(void *data) +{ + mCurrentState->execute(data); +} + +// set current state +BOOL LLStateMachine::setCurrentState(LLFSMState *initial_state, void* user_data, BOOL skip_entry) +{ + llassert(mStateDiagram); + + if (mStateDiagram->stateIsValid(*initial_state)) + { + mLastState = mCurrentState = initial_state; + if (!skip_entry) + { + initial_state->onEntry(user_data); + } + return TRUE; + } + + return FALSE; +} + +BOOL LLStateMachine::setCurrentState(U32 state_id, void* user_data, BOOL skip_entry) +{ + llassert(mStateDiagram); + + LLFSMState* state = mStateDiagram->getState(state_id); + + if (state) + { + mLastState = mCurrentState = state; + if (!skip_entry) + { + state->onEntry(user_data); + } + return TRUE; + } + + return FALSE; +} + +void LLStateMachine::processTransition(LLFSMTransition& transition, void* user_data) +{ + llassert(mStateDiagram); + + LLFSMState* new_state = mStateDiagram->processTransition(*mCurrentState, transition); + + mLastTransition = &transition; + mLastState = mCurrentState; + + if (*mCurrentState != *new_state) + { + if (mCurrentState && new_state && *mCurrentState != *new_state) + { + mCurrentState->onExit(user_data); + } + if (new_state) + { + if (!mCurrentState || *mCurrentState != *new_state) + { + mCurrentState = new_state; + if (mCurrentState) + { + mCurrentState->onEntry(user_data); + } + } + } +#if FSM_PRINT_STATE_TRANSITIONS + llinfos << "Entering state " << mCurrentState->getName() << + " on transition " << transition.getName() << " from state " << + mLastState->getName() << llendl; +#endif + } +} + +void LLStateMachine::setStateDiagram(LLStateDiagram* diagram) +{ + mStateDiagram = diagram; +} |