PageRenderTime 384ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llcharacter/llstatemachine.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 398 lines | 293 code | 56 blank | 49 comment | 53 complexity | 43ecef4d16e969ac069c886d0976ab9d MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llstatemachine.cpp
  3. * @brief LLStateMachine implementation file.
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llstatemachine.h"
  28. #include "llapr.h"
  29. #define FSM_PRINT_STATE_TRANSITIONS (0)
  30. U32 LLUniqueID::sNextID = 0;
  31. bool operator==(const LLUniqueID &a, const LLUniqueID &b)
  32. {
  33. return (a.mId == b.mId);
  34. }
  35. bool operator!=(const LLUniqueID &a, const LLUniqueID &b)
  36. {
  37. return (a.mId != b.mId);
  38. }
  39. //-----------------------------------------------------------------------------
  40. // LLStateDiagram
  41. //-----------------------------------------------------------------------------
  42. LLStateDiagram::LLStateDiagram()
  43. {
  44. mDefaultState = NULL;
  45. mUseDefaultState = FALSE;
  46. }
  47. LLStateDiagram::~LLStateDiagram()
  48. {
  49. }
  50. // add a state to the state graph
  51. BOOL LLStateDiagram::addState(LLFSMState *state)
  52. {
  53. mStates[state] = Transitions();
  54. return TRUE;
  55. }
  56. // add a directed transition between 2 states
  57. BOOL LLStateDiagram::addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
  58. {
  59. StateMap::iterator state_it;
  60. state_it = mStates.find(&start_state);
  61. Transitions* state_transitions = NULL;
  62. if (state_it == mStates.end() )
  63. {
  64. addState(&start_state);
  65. state_transitions = &mStates[&start_state];
  66. }
  67. else
  68. {
  69. state_transitions = &state_it->second;
  70. }
  71. state_it = mStates.find(&end_state);
  72. if (state_it == mStates.end() )
  73. {
  74. addState(&end_state);
  75. }
  76. Transitions::iterator transition_it = state_transitions->find(&transition);
  77. if (transition_it != state_transitions->end())
  78. {
  79. llerrs << "LLStateTable::addDirectedTransition() : transition already exists" << llendl;
  80. return FALSE; // transition already exists
  81. }
  82. (*state_transitions)[&transition] = &end_state;
  83. return TRUE;
  84. }
  85. // add an undirected transition between 2 states
  86. BOOL LLStateDiagram::addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
  87. {
  88. BOOL result;
  89. result = addTransition(start_state, end_state, transition);
  90. if (result)
  91. {
  92. result = addTransition(end_state, start_state, transition);
  93. }
  94. return result;
  95. }
  96. // add a transition that exists for every state
  97. void LLStateDiagram::addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition)
  98. {
  99. mDefaultTransitions[&transition] = &end_state;
  100. }
  101. // process a possible transition, and get the resulting state
  102. LLFSMState* LLStateDiagram::processTransition(LLFSMState& start_state, LLFSMTransition& transition)
  103. {
  104. // look up transition
  105. //LLFSMState** dest_state = (mStates.getValue(&start_state))->getValue(&transition);
  106. LLFSMState* dest_state = NULL;
  107. StateMap::iterator state_it = mStates.find(&start_state);
  108. if (state_it == mStates.end())
  109. {
  110. return NULL;
  111. }
  112. Transitions::iterator transition_it = state_it->second.find(&transition);
  113. // try default transitions if state-specific transition not found
  114. if (transition_it == state_it->second.end())
  115. {
  116. dest_state = mDefaultTransitions[&transition];
  117. }
  118. else
  119. {
  120. dest_state = transition_it->second;
  121. }
  122. // if we have a destination state...
  123. if (NULL != dest_state)
  124. {
  125. // ...return it...
  126. return dest_state;
  127. }
  128. // ... otherwise ...
  129. else
  130. {
  131. // ...look for default state...
  132. if (mUseDefaultState)
  133. {
  134. // ...return it if we have it...
  135. return mDefaultState;
  136. }
  137. else
  138. {
  139. // ...or else we're still in the same state.
  140. return &start_state;
  141. }
  142. }
  143. }
  144. void LLStateDiagram::setDefaultState(LLFSMState& default_state)
  145. {
  146. mUseDefaultState = TRUE;
  147. mDefaultState = &default_state;
  148. }
  149. S32 LLStateDiagram::numDeadendStates()
  150. {
  151. S32 numDeadends = 0;
  152. StateMap::iterator state_it;
  153. for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
  154. {
  155. if (state_it->second.size() == 0)
  156. {
  157. numDeadends++;
  158. }
  159. }
  160. return numDeadends;
  161. }
  162. BOOL LLStateDiagram::stateIsValid(LLFSMState& state)
  163. {
  164. if (mStates.find(&state) != mStates.end())
  165. {
  166. return TRUE;
  167. }
  168. return FALSE;
  169. }
  170. LLFSMState* LLStateDiagram::getState(U32 state_id)
  171. {
  172. StateMap::iterator state_it;
  173. for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
  174. {
  175. if (state_it->first->getID() == state_id)
  176. {
  177. return state_it->first;
  178. }
  179. }
  180. return NULL;
  181. }
  182. BOOL LLStateDiagram::saveDotFile(const std::string& filename)
  183. {
  184. LLAPRFile outfile ;
  185. outfile.open(filename, LL_APR_W);
  186. apr_file_t* dot_file = outfile.getFileHandle() ;
  187. if (!dot_file)
  188. {
  189. llwarns << "LLStateDiagram::saveDotFile() : Couldn't open " << filename << " to save state diagram." << llendl;
  190. return FALSE;
  191. }
  192. 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");
  193. StateMap::iterator state_it;
  194. for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
  195. {
  196. apr_file_printf(dot_file, "\t\"%s\" [fontsize=28,shape=box]\n", state_it->first->getName().c_str());
  197. }
  198. apr_file_printf(dot_file, "\t\"All States\" [fontsize=30,style=bold,shape=box]\n");
  199. Transitions::iterator transitions_it;
  200. for(transitions_it = mDefaultTransitions.begin(); transitions_it != mDefaultTransitions.end(); ++transitions_it)
  201. {
  202. apr_file_printf(dot_file, "\t\"All States\" -> \"%s\" [label = \"%s\",fontsize=24];\n", transitions_it->second->getName().c_str(),
  203. transitions_it->second->getName().c_str());
  204. }
  205. if (mDefaultState)
  206. {
  207. apr_file_printf(dot_file, "\t\"All States\" -> \"%s\";\n", mDefaultState->getName().c_str());
  208. }
  209. for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
  210. {
  211. LLFSMState *state = state_it->first;
  212. Transitions::iterator transitions_it;
  213. for(transitions_it = state_it->second.begin();
  214. transitions_it != state_it->second.end();
  215. ++transitions_it)
  216. {
  217. std::string state_name = state->getName();
  218. std::string target_name = transitions_it->second->getName();
  219. std::string transition_name = transitions_it->first->getName();
  220. apr_file_printf(dot_file, "\t\"%s\" -> \"%s\" [label = \"%s\",fontsize=24];\n", state->getName().c_str(),
  221. target_name.c_str(),
  222. transition_name.c_str());
  223. }
  224. }
  225. apr_file_printf(dot_file, "}\n");
  226. return TRUE;
  227. }
  228. std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM)
  229. {
  230. if (FSM.mDefaultState)
  231. {
  232. s << "Default State: " << FSM.mDefaultState->getName() << "\n";
  233. }
  234. LLStateDiagram::Transitions::iterator transitions_it;
  235. for(transitions_it = FSM.mDefaultTransitions.begin();
  236. transitions_it != FSM.mDefaultTransitions.end();
  237. ++transitions_it)
  238. {
  239. s << "Any State -- " << transitions_it->first->getName()
  240. << " --> " << transitions_it->second->getName() << "\n";
  241. }
  242. LLStateDiagram::StateMap::iterator state_it;
  243. for(state_it = FSM.mStates.begin(); state_it != FSM.mStates.end(); ++state_it)
  244. {
  245. LLStateDiagram::Transitions::iterator transitions_it;
  246. for(transitions_it = state_it->second.begin();
  247. transitions_it != state_it->second.end();
  248. ++transitions_it)
  249. {
  250. s << state_it->first->getName() << " -- " << transitions_it->first->getName()
  251. << " --> " << transitions_it->second->getName() << "\n";
  252. }
  253. s << "\n";
  254. }
  255. return s;
  256. }
  257. //-----------------------------------------------------------------------------
  258. // LLStateMachine
  259. //-----------------------------------------------------------------------------
  260. LLStateMachine::LLStateMachine()
  261. {
  262. // we haven't received a starting state yet
  263. mCurrentState = NULL;
  264. mLastState = NULL;
  265. mLastTransition = NULL;
  266. mStateDiagram = NULL;
  267. }
  268. LLStateMachine::~LLStateMachine()
  269. {
  270. }
  271. // returns current state
  272. LLFSMState* LLStateMachine::getCurrentState() const
  273. {
  274. return mCurrentState;
  275. }
  276. // executes current state
  277. void LLStateMachine::runCurrentState(void *data)
  278. {
  279. mCurrentState->execute(data);
  280. }
  281. // set current state
  282. BOOL LLStateMachine::setCurrentState(LLFSMState *initial_state, void* user_data, BOOL skip_entry)
  283. {
  284. llassert(mStateDiagram);
  285. if (mStateDiagram->stateIsValid(*initial_state))
  286. {
  287. mLastState = mCurrentState = initial_state;
  288. if (!skip_entry)
  289. {
  290. initial_state->onEntry(user_data);
  291. }
  292. return TRUE;
  293. }
  294. return FALSE;
  295. }
  296. BOOL LLStateMachine::setCurrentState(U32 state_id, void* user_data, BOOL skip_entry)
  297. {
  298. llassert(mStateDiagram);
  299. LLFSMState* state = mStateDiagram->getState(state_id);
  300. if (state)
  301. {
  302. mLastState = mCurrentState = state;
  303. if (!skip_entry)
  304. {
  305. state->onEntry(user_data);
  306. }
  307. return TRUE;
  308. }
  309. return FALSE;
  310. }
  311. void LLStateMachine::processTransition(LLFSMTransition& transition, void* user_data)
  312. {
  313. llassert(mStateDiagram);
  314. if (NULL == mCurrentState)
  315. {
  316. llwarns << "mCurrentState == NULL; aborting processTransition()" << llendl;
  317. return;
  318. }
  319. LLFSMState* new_state = mStateDiagram->processTransition(*mCurrentState, transition);
  320. if (NULL == new_state)
  321. {
  322. llwarns << "new_state == NULL; aborting processTransition()" << llendl;
  323. return;
  324. }
  325. mLastTransition = &transition;
  326. mLastState = mCurrentState;
  327. if (*mCurrentState != *new_state)
  328. {
  329. mCurrentState->onExit(user_data);
  330. mCurrentState = new_state;
  331. mCurrentState->onEntry(user_data);
  332. #if FSM_PRINT_STATE_TRANSITIONS
  333. llinfos << "Entering state " << mCurrentState->getName() <<
  334. " on transition " << transition.getName() << " from state " <<
  335. mLastState->getName() << llendl;
  336. #endif
  337. }
  338. }
  339. void LLStateMachine::setStateDiagram(LLStateDiagram* diagram)
  340. {
  341. mStateDiagram = diagram;
  342. }