PageRenderTime 356ms CodeModel.GetById 141ms app.highlight 98ms RepoModel.GetById 113ms 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
  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
 27#include "linden_common.h"
 28
 29#include "llstatemachine.h"
 30#include "llapr.h"
 31
 32#define FSM_PRINT_STATE_TRANSITIONS (0)
 33
 34U32 LLUniqueID::sNextID = 0;
 35
 36bool	operator==(const LLUniqueID &a, const LLUniqueID &b)
 37{
 38	return (a.mId == b.mId);
 39}
 40
 41bool	operator!=(const LLUniqueID &a, const LLUniqueID &b)
 42{
 43	return (a.mId != b.mId);
 44}
 45
 46//-----------------------------------------------------------------------------
 47// LLStateDiagram
 48//-----------------------------------------------------------------------------
 49LLStateDiagram::LLStateDiagram()
 50{
 51	mDefaultState = NULL;
 52	mUseDefaultState = FALSE;
 53}
 54
 55LLStateDiagram::~LLStateDiagram()
 56{
 57
 58}
 59
 60// add a state to the state graph
 61BOOL LLStateDiagram::addState(LLFSMState *state)
 62{
 63	mStates[state] = Transitions();
 64	return TRUE;
 65}
 66
 67// add a directed transition between 2 states
 68BOOL LLStateDiagram::addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
 69{
 70	StateMap::iterator state_it;
 71	state_it = mStates.find(&start_state);
 72	Transitions* state_transitions = NULL;
 73	if (state_it == mStates.end() )
 74	{
 75		addState(&start_state);
 76		state_transitions = &mStates[&start_state];
 77	}
 78	else
 79	{
 80		state_transitions = &state_it->second;
 81	}
 82	state_it = mStates.find(&end_state);
 83	if (state_it == mStates.end() )
 84	{
 85		addState(&end_state);
 86	}
 87
 88	Transitions::iterator transition_it = state_transitions->find(&transition);
 89	if (transition_it != state_transitions->end())
 90	{
 91		llerrs << "LLStateTable::addDirectedTransition() : transition already exists" << llendl;
 92		return FALSE; // transition already exists
 93	}
 94
 95	(*state_transitions)[&transition] = &end_state;
 96	return TRUE;
 97}
 98
 99// add an undirected transition between 2 states
100BOOL LLStateDiagram::addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
101{
102	BOOL result;
103	result = addTransition(start_state, end_state, transition);
104	if (result)
105	{
106		result = addTransition(end_state, start_state, transition);
107	}
108	return result;
109}
110
111// add a transition that exists for every state
112void LLStateDiagram::addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition)
113{
114	mDefaultTransitions[&transition] = &end_state;
115}
116
117// process a possible transition, and get the resulting state
118LLFSMState* LLStateDiagram::processTransition(LLFSMState& start_state, LLFSMTransition& transition)
119{
120	// look up transition
121	//LLFSMState** dest_state = (mStates.getValue(&start_state))->getValue(&transition);
122	LLFSMState* dest_state = NULL;
123	StateMap::iterator state_it = mStates.find(&start_state);
124	if (state_it == mStates.end())
125	{
126		return NULL;
127	}
128	Transitions::iterator transition_it = state_it->second.find(&transition);
129	
130	// try default transitions if state-specific transition not found
131	if (transition_it == state_it->second.end()) 
132	{
133		dest_state = mDefaultTransitions[&transition];
134	}
135	else
136	{
137		dest_state = transition_it->second;
138	}
139
140	// if we have a destination state...
141	if (NULL != dest_state)
142	{
143		// ...return it...
144		return dest_state;
145	}
146	// ... otherwise ...
147	else
148	{
149		// ...look for default state...
150		if (mUseDefaultState)
151		{
152			// ...return it if we have it...
153			return mDefaultState;
154		}
155		else
156		{
157			// ...or else we're still in the same state.
158			return &start_state;
159		}
160	}
161}
162
163void LLStateDiagram::setDefaultState(LLFSMState& default_state)
164{
165	mUseDefaultState = TRUE;
166	mDefaultState = &default_state;
167}
168
169S32 LLStateDiagram::numDeadendStates()
170{
171	S32 numDeadends = 0;
172	StateMap::iterator state_it;
173	for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
174	{
175		if (state_it->second.size() == 0)
176		{
177			numDeadends++;
178		}
179	}
180	return numDeadends;
181}
182
183BOOL LLStateDiagram::stateIsValid(LLFSMState& state)
184{
185	if (mStates.find(&state) != mStates.end())
186	{
187		return TRUE;
188	}
189	return FALSE;
190}
191
192LLFSMState* LLStateDiagram::getState(U32 state_id)
193{
194	StateMap::iterator state_it;
195	for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
196	{
197		if (state_it->first->getID() == state_id)
198		{
199			return state_it->first;
200		}
201	}
202	return NULL;
203}
204
205BOOL LLStateDiagram::saveDotFile(const std::string& filename)
206{
207	LLAPRFile outfile ;
208	outfile.open(filename, LL_APR_W);
209	apr_file_t* dot_file = outfile.getFileHandle() ;
210
211	if (!dot_file)
212	{
213		llwarns << "LLStateDiagram::saveDotFile() : Couldn't open " << filename << " to save state diagram." << llendl;
214		return FALSE;
215	}
216	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");
217	
218	StateMap::iterator state_it;
219	for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
220	{
221		apr_file_printf(dot_file, "\t\"%s\" [fontsize=28,shape=box]\n", state_it->first->getName().c_str());
222	}
223	apr_file_printf(dot_file, "\t\"All States\" [fontsize=30,style=bold,shape=box]\n");
224
225	Transitions::iterator transitions_it;
226	for(transitions_it = mDefaultTransitions.begin(); transitions_it != mDefaultTransitions.end(); ++transitions_it)
227	{
228		apr_file_printf(dot_file, "\t\"All States\" -> \"%s\" [label = \"%s\",fontsize=24];\n", transitions_it->second->getName().c_str(), 
229			transitions_it->second->getName().c_str());
230	}
231
232	if (mDefaultState)
233	{
234		apr_file_printf(dot_file, "\t\"All States\" -> \"%s\";\n", mDefaultState->getName().c_str());
235	}
236
237	
238	for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
239	{
240		LLFSMState *state = state_it->first;
241
242		Transitions::iterator transitions_it;
243		for(transitions_it = state_it->second.begin();
244			transitions_it != state_it->second.end();
245			++transitions_it)
246		{
247			std::string state_name = state->getName();
248			std::string target_name = transitions_it->second->getName();
249			std::string transition_name = transitions_it->first->getName();
250			apr_file_printf(dot_file, "\t\"%s\" -> \"%s\" [label = \"%s\",fontsize=24];\n", state->getName().c_str(), 
251				target_name.c_str(), 
252				transition_name.c_str());
253		}
254	}
255
256	apr_file_printf(dot_file, "}\n");
257
258	return TRUE;
259}
260
261std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM)
262{
263	if (FSM.mDefaultState)
264	{
265		s << "Default State: " << FSM.mDefaultState->getName() << "\n";
266	}
267
268	LLStateDiagram::Transitions::iterator transitions_it;
269	for(transitions_it = FSM.mDefaultTransitions.begin(); 
270		transitions_it != FSM.mDefaultTransitions.end(); 
271		++transitions_it)
272	{
273		s << "Any State -- " << transitions_it->first->getName()
274			<< " --> " << transitions_it->second->getName() << "\n";
275	}
276
277	LLStateDiagram::StateMap::iterator state_it;
278	for(state_it = FSM.mStates.begin(); state_it != FSM.mStates.end(); ++state_it)
279	{
280		LLStateDiagram::Transitions::iterator transitions_it;
281		for(transitions_it = state_it->second.begin();
282			transitions_it != state_it->second.end();
283			++transitions_it)
284		{
285			s << state_it->first->getName() << " -- " << transitions_it->first->getName() 
286				<< " --> " << transitions_it->second->getName() << "\n";
287		}
288		s << "\n";
289	}
290
291	return s;
292}
293
294//-----------------------------------------------------------------------------
295// LLStateMachine
296//-----------------------------------------------------------------------------
297
298LLStateMachine::LLStateMachine()
299{
300	// we haven't received a starting state yet
301	mCurrentState = NULL;
302	mLastState = NULL;
303	mLastTransition = NULL;
304	mStateDiagram = NULL;
305}
306
307LLStateMachine::~LLStateMachine()
308{
309
310}
311
312// returns current state
313LLFSMState*	LLStateMachine::getCurrentState() const
314{
315	return mCurrentState;
316}
317
318// executes current state
319void LLStateMachine::runCurrentState(void *data)
320{
321	mCurrentState->execute(data);
322}
323
324// set current state
325BOOL LLStateMachine::setCurrentState(LLFSMState *initial_state, void* user_data, BOOL skip_entry)
326{
327	llassert(mStateDiagram);
328
329	if (mStateDiagram->stateIsValid(*initial_state))
330	{
331		mLastState = mCurrentState = initial_state;
332		if (!skip_entry)
333		{
334			initial_state->onEntry(user_data);
335		}
336		return TRUE;
337	}
338
339	return FALSE;
340}
341
342BOOL LLStateMachine::setCurrentState(U32 state_id, void* user_data, BOOL skip_entry)
343{
344	llassert(mStateDiagram);
345
346	LLFSMState* state = mStateDiagram->getState(state_id);
347
348	if (state)
349	{
350		mLastState = mCurrentState = state;
351		if (!skip_entry)
352		{
353			state->onEntry(user_data);
354		}
355		return TRUE;
356	}
357
358	return FALSE;
359}
360
361void LLStateMachine::processTransition(LLFSMTransition& transition, void* user_data)
362{
363	llassert(mStateDiagram);
364	
365	if (NULL == mCurrentState)
366	{
367		llwarns << "mCurrentState == NULL; aborting processTransition()" << llendl;
368		return;
369	}
370
371	LLFSMState* new_state = mStateDiagram->processTransition(*mCurrentState, transition);
372
373	if (NULL == new_state)
374	{
375		llwarns << "new_state == NULL; aborting processTransition()" << llendl;
376		return;
377	}
378
379	mLastTransition = &transition;
380	mLastState = mCurrentState;
381
382	if (*mCurrentState != *new_state)
383	{
384		mCurrentState->onExit(user_data);
385		mCurrentState = new_state;
386		mCurrentState->onEntry(user_data);
387#if FSM_PRINT_STATE_TRANSITIONS
388		llinfos << "Entering state " << mCurrentState->getName() <<
389			" on transition " << transition.getName() << " from state " << 
390			mLastState->getName() << llendl;
391#endif
392	}
393}
394
395void LLStateMachine::setStateDiagram(LLStateDiagram* diagram)
396{
397	mStateDiagram = diagram;
398}