llstatemachine.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 
00034 #include "llstatemachine.h"
00035 #include "llapr.h"
00036 
00037 #define FSM_PRINT_STATE_TRANSITIONS (0)
00038 
00039 U32 LLUniqueID::sNextID = 0;
00040 
00041 bool    operator==(const LLUniqueID &a, const LLUniqueID &b)
00042 {
00043         return (a.mId == b.mId);
00044 }
00045 
00046 bool    operator!=(const LLUniqueID &a, const LLUniqueID &b)
00047 {
00048         return (a.mId != b.mId);
00049 }
00050 
00051 //-----------------------------------------------------------------------------
00052 // LLStateDiagram
00053 //-----------------------------------------------------------------------------
00054 LLStateDiagram::LLStateDiagram()
00055 {
00056         mUseDefaultState = FALSE;
00057 }
00058 
00059 LLStateDiagram::~LLStateDiagram()
00060 {
00061 
00062 }
00063 
00064 // add a state to the state graph
00065 BOOL LLStateDiagram::addState(LLFSMState *state)
00066 {
00067         mStates[state] = Transitions();
00068         return TRUE;
00069 }
00070 
00071 // add a directed transition between 2 states
00072 BOOL LLStateDiagram::addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
00073 {
00074         StateMap::iterator state_it;
00075         state_it = mStates.find(&start_state);
00076         Transitions* state_transitions = NULL;
00077         if (state_it == mStates.end() )
00078         {
00079                 addState(&start_state);
00080                 state_transitions = &mStates[&start_state];
00081         }
00082         else
00083         {
00084                 state_transitions = &state_it->second;
00085         }
00086         state_it = mStates.find(&end_state);
00087         if (state_it == mStates.end() )
00088         {
00089                 addState(&end_state);
00090         }
00091 
00092         Transitions::iterator transition_it = state_transitions->find(&transition);
00093         if (transition_it != state_transitions->end())
00094         {
00095                 llerrs << "LLStateTable::addDirectedTransition() : transition already exists" << llendl;
00096                 return FALSE; // transition already exists
00097         }
00098 
00099         (*state_transitions)[&transition] = &end_state;
00100         return TRUE;
00101 }
00102 
00103 // add an undirected transition between 2 states
00104 BOOL LLStateDiagram::addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
00105 {
00106         BOOL result;
00107         result = addTransition(start_state, end_state, transition);
00108         if (result)
00109         {
00110                 result = addTransition(end_state, start_state, transition);
00111         }
00112         return result;
00113 }
00114 
00115 // add a transition that exists for every state
00116 void LLStateDiagram::addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition)
00117 {
00118         mDefaultTransitions[&transition] = &end_state;
00119 }
00120 
00121 // process a possible transition, and get the resulting state
00122 LLFSMState* LLStateDiagram::processTransition(LLFSMState& start_state, LLFSMTransition& transition)
00123 {
00124         // look up transition
00125         //LLFSMState** dest_state = (mStates.getValue(&start_state))->getValue(&transition);
00126         LLFSMState* dest_state = NULL;
00127         StateMap::iterator state_it = mStates.find(&start_state);
00128         if (state_it == mStates.end())
00129         {
00130                 return NULL;
00131         }
00132         Transitions::iterator transition_it = state_it->second.find(&transition);
00133         
00134         // try default transitions if state-specific transition not found
00135         if (transition_it == state_it->second.end()) 
00136         {
00137                 dest_state = mDefaultTransitions[&transition];
00138         }
00139         else
00140         {
00141                 dest_state = transition_it->second;
00142         }
00143 
00144         // if we have a destination state...
00145         if (NULL != dest_state)
00146         {
00147                 // ...return it...
00148                 return dest_state;
00149         }
00150         // ... otherwise ...
00151         else
00152         {
00153                 // ...look for default state...
00154                 if (mUseDefaultState)
00155                 {
00156                         // ...return it if we have it...
00157                         return mDefaultState;
00158                 }
00159                 else
00160                 {
00161                         // ...or else we're still in the same state.
00162                         return &start_state;
00163                 }
00164         }
00165 }
00166 
00167 void LLStateDiagram::setDefaultState(LLFSMState& default_state)
00168 {
00169         mUseDefaultState = TRUE;
00170         mDefaultState = &default_state;
00171 }
00172 
00173 S32 LLStateDiagram::numDeadendStates()
00174 {
00175         S32 numDeadends = 0;
00176         StateMap::iterator state_it;
00177         for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
00178         {
00179                 if (state_it->second.size() == 0)
00180                 {
00181                         numDeadends++;
00182                 }
00183         }
00184         return numDeadends;
00185 }
00186 
00187 BOOL LLStateDiagram::stateIsValid(LLFSMState& state)
00188 {
00189         if (mStates.find(&state) != mStates.end())
00190         {
00191                 return TRUE;
00192         }
00193         return FALSE;
00194 }
00195 
00196 LLFSMState* LLStateDiagram::getState(U32 state_id)
00197 {
00198         StateMap::iterator state_it;
00199         for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
00200         {
00201                 if (state_it->first->getID() == state_id)
00202                 {
00203                         return state_it->first;
00204                 }
00205         }
00206         return NULL;
00207 }
00208 
00209 BOOL LLStateDiagram::saveDotFile(const char* filename)
00210 {
00211         apr_file_t* dot_file = ll_apr_file_open(filename, LL_APR_W);
00212 
00213         if (!dot_file)
00214         {
00215                 llwarns << "LLStateDiagram::saveDotFile() : Couldn't open " << filename << " to save state diagram." << llendl;
00216                 return FALSE;
00217         }
00218         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");
00219         
00220         StateMap::iterator state_it;
00221         for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
00222         {
00223                 apr_file_printf(dot_file, "\t\"%s\" [fontsize=28,shape=box]\n", state_it->first->getName().c_str());
00224         }
00225         apr_file_printf(dot_file, "\t\"All States\" [fontsize=30,style=bold,shape=box]\n");
00226 
00227         Transitions::iterator transitions_it;
00228         for(transitions_it = mDefaultTransitions.begin(); transitions_it != mDefaultTransitions.end(); ++transitions_it)
00229         {
00230                 apr_file_printf(dot_file, "\t\"All States\" -> \"%s\" [label = \"%s\",fontsize=24];\n", transitions_it->second->getName().c_str(), 
00231                         transitions_it->second->getName().c_str());
00232         }
00233 
00234         if (mDefaultState)
00235         {
00236                 apr_file_printf(dot_file, "\t\"All States\" -> \"%s\";\n", mDefaultState->getName().c_str());
00237         }
00238 
00239         
00240         for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
00241         {
00242                 LLFSMState *state = state_it->first;
00243 
00244                 Transitions::iterator transitions_it;
00245                 for(transitions_it = state_it->second.begin();
00246                         transitions_it != state_it->second.end();
00247                         ++transitions_it)
00248                 {
00249                         std::string state_name = state->getName();
00250                         std::string target_name = transitions_it->second->getName();
00251                         std::string transition_name = transitions_it->first->getName();
00252                         apr_file_printf(dot_file, "\t\"%s\" -> \"%s\" [label = \"%s\",fontsize=24];\n", state->getName().c_str(), 
00253                                 target_name.c_str(), 
00254                                 transition_name.c_str());
00255                 }
00256         }
00257 
00258         apr_file_printf(dot_file, "}\n");
00259 
00260         apr_file_close(dot_file);
00261 
00262         return TRUE;
00263 }
00264 
00265 std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM)
00266 {
00267         if (FSM.mDefaultState)
00268         {
00269                 s << "Default State: " << FSM.mDefaultState->getName() << "\n";
00270         }
00271 
00272         LLStateDiagram::Transitions::iterator transitions_it;
00273         for(transitions_it = FSM.mDefaultTransitions.begin(); 
00274                 transitions_it != FSM.mDefaultTransitions.end(); 
00275                 ++transitions_it)
00276         {
00277                 s << "Any State -- " << transitions_it->first->getName()
00278                         << " --> " << transitions_it->second->getName() << "\n";
00279         }
00280 
00281         LLStateDiagram::StateMap::iterator state_it;
00282         for(state_it = FSM.mStates.begin(); state_it != FSM.mStates.end(); ++state_it)
00283         {
00284                 LLStateDiagram::Transitions::iterator transitions_it;
00285                 for(transitions_it = state_it->second.begin();
00286                         transitions_it != state_it->second.end();
00287                         ++transitions_it)
00288                 {
00289                         s << state_it->first->getName() << " -- " << transitions_it->first->getName() 
00290                                 << " --> " << transitions_it->second->getName() << "\n";
00291                 }
00292                 s << "\n";
00293         }
00294 
00295         return s;
00296 }
00297 
00298 //-----------------------------------------------------------------------------
00299 // LLStateMachine
00300 //-----------------------------------------------------------------------------
00301 
00302 LLStateMachine::LLStateMachine()
00303 {
00304         // we haven't received a starting state yet
00305         mCurrentState = NULL;
00306         mLastState = NULL;
00307         mStateDiagram = NULL;
00308 }
00309 
00310 LLStateMachine::~LLStateMachine()
00311 {
00312 
00313 }
00314 
00315 // returns current state
00316 LLFSMState*     LLStateMachine::getCurrentState() const
00317 {
00318         return mCurrentState;
00319 }
00320 
00321 // executes current state
00322 void LLStateMachine::runCurrentState(void *data)
00323 {
00324         mCurrentState->execute(data);
00325 }
00326 
00327 // set current state
00328 BOOL LLStateMachine::setCurrentState(LLFSMState *initial_state, void* user_data, BOOL skip_entry)
00329 {
00330         llassert(mStateDiagram);
00331 
00332         if (mStateDiagram->stateIsValid(*initial_state))
00333         {
00334                 mLastState = mCurrentState = initial_state;
00335                 if (!skip_entry)
00336                 {
00337                         initial_state->onEntry(user_data);
00338                 }
00339                 return TRUE;
00340         }
00341 
00342         return FALSE;
00343 }
00344 
00345 BOOL LLStateMachine::setCurrentState(U32 state_id, void* user_data, BOOL skip_entry)
00346 {
00347         llassert(mStateDiagram);
00348 
00349         LLFSMState* state = mStateDiagram->getState(state_id);
00350 
00351         if (state)
00352         {
00353                 mLastState = mCurrentState = state;
00354                 if (!skip_entry)
00355                 {
00356                         state->onEntry(user_data);
00357                 }
00358                 return TRUE;
00359         }
00360 
00361         return FALSE;
00362 }
00363 
00364 void LLStateMachine::processTransition(LLFSMTransition& transition, void* user_data)
00365 {
00366         llassert(mStateDiagram);
00367         
00368         if (NULL == mCurrentState)
00369         {
00370                 llwarns << "mCurrentState == NULL; aborting processTransition()" << llendl;
00371                 return;
00372         }
00373 
00374         LLFSMState* new_state = mStateDiagram->processTransition(*mCurrentState, transition);
00375 
00376         if (NULL == new_state)
00377         {
00378                 llwarns << "new_state == NULL; aborting processTransition()" << llendl;
00379                 return;
00380         }
00381 
00382         mLastTransition = &transition;
00383         mLastState = mCurrentState;
00384 
00385         if (*mCurrentState != *new_state)
00386         {
00387                 mCurrentState->onExit(user_data);
00388                 mCurrentState = new_state;
00389                 mCurrentState->onEntry(user_data);
00390 #if FSM_PRINT_STATE_TRANSITIONS
00391                 llinfos << "Entering state " << mCurrentState->getName() <<
00392                         " on transition " << transition.getName() << " from state " << 
00393                         mLastState->getName() << llendl;
00394 #endif
00395         }
00396 }
00397 
00398 void LLStateMachine::setStateDiagram(LLStateDiagram* diagram)
00399 {
00400         mStateDiagram = diagram;
00401 }

Generated on Thu Jul 1 06:09:13 2010 for Second Life Viewer by  doxygen 1.4.7