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
00053
00054 LLStateDiagram::LLStateDiagram()
00055 {
00056 mUseDefaultState = FALSE;
00057 }
00058
00059 LLStateDiagram::~LLStateDiagram()
00060 {
00061
00062 }
00063
00064
00065 BOOL LLStateDiagram::addState(LLFSMState *state)
00066 {
00067 mStates[state] = Transitions();
00068 return TRUE;
00069 }
00070
00071
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;
00097 }
00098
00099 (*state_transitions)[&transition] = &end_state;
00100 return TRUE;
00101 }
00102
00103
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
00116 void LLStateDiagram::addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition)
00117 {
00118 mDefaultTransitions[&transition] = &end_state;
00119 }
00120
00121
00122 LLFSMState* LLStateDiagram::processTransition(LLFSMState& start_state, LLFSMTransition& transition)
00123 {
00124
00125
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
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
00145 if (NULL != dest_state)
00146 {
00147
00148 return dest_state;
00149 }
00150
00151 else
00152 {
00153
00154 if (mUseDefaultState)
00155 {
00156
00157 return mDefaultState;
00158 }
00159 else
00160 {
00161
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
00300
00301
00302 LLStateMachine::LLStateMachine()
00303 {
00304
00305 mCurrentState = NULL;
00306 mLastState = NULL;
00307 mStateDiagram = NULL;
00308 }
00309
00310 LLStateMachine::~LLStateMachine()
00311 {
00312
00313 }
00314
00315
00316 LLFSMState* LLStateMachine::getCurrentState() const
00317 {
00318 return mCurrentState;
00319 }
00320
00321
00322 void LLStateMachine::runCurrentState(void *data)
00323 {
00324 mCurrentState->execute(data);
00325 }
00326
00327
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 }