]> git.mxchange.org Git - simgear.git/blob - simgear/structure/StateMachine.cxx
Fix for libCurl pipelining and connection count.
[simgear.git] / simgear / structure / StateMachine.cxx
1 /* -*-c++-*-
2  *
3  * Copyright (C) 2013 James Turner
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  * MA 02110-1301, USA.
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include <simgear_config.h>
24 #endif
25      
26 #include "StateMachine.hxx"
27
28 #include <algorithm>
29 #include <cassert>
30 #include <set>
31 #include <boost/foreach.hpp>
32
33 #include <simgear/debug/logstream.hxx>
34 #include <simgear/structure/SGBinding.hxx>
35 #include <simgear/props/condition.hxx>
36 #include <simgear/timing/timestamp.hxx>
37 #include <simgear/structure/exception.hxx>
38                     
39 namespace simgear
40 {
41
42 typedef std::vector<StateMachine::State_ptr> StatePtrVec;
43
44 static void readBindingList(SGPropertyNode* desc, const std::string& name, 
45     SGPropertyNode* root, SGBindingList& result)
46 {
47     BOOST_FOREACH(SGPropertyNode* b, desc->getChildren(name)) {
48         SGBinding* bind = new SGBinding;
49         bind->read(b, root);
50         result.push_back(bind);
51     }
52 }
53
54 ///////////////////////////////////////////////////////////////////////////
55
56 class StateMachine::State::StatePrivate
57 {
58 public:
59     std::string _name;
60     SGBindingList _updateBindings, 
61         _entryBindings, 
62         _exitBindings;
63 };
64
65 ///////////////////////////////////////////////////////////////////////////
66
67 class StateMachine::Transition::TransitionPrivate
68 {
69 public:
70     std::string _name;
71     SGBindingList _bindings;
72     std::set<State*> _sourceStates; ///< weak refs to source states
73     State* _target;
74     bool _excludeTarget;
75     SGSharedPtr<SGCondition> _condition;
76 };
77     
78 ///////////////////////////////////////////////////////////////////////////
79
80 class StateMachine::StateMachinePrivate : public SGPropertyChangeListener
81 {
82 public:
83     StateMachinePrivate(StateMachine* p) : _p(p) { }
84     
85     void computeEligibleTransitions()
86     {
87         _eligible.clear();
88         BOOST_FOREACH(Transition_ptr t, _transitions) {
89             if (t->applicableForState(_currentState)) {
90                 _eligible.push_back(t.ptr());
91             }
92         }
93     }
94     
95     StateMachine* _p;
96     bool _initialised;
97     State_ptr _currentState;
98     StatePtrVec _states;
99     std::vector<Transition_ptr> _transitions;
100     std::vector<Transition*> _eligible;
101     SGTimeStamp _timeInState;
102     
103     bool _listenerLockout; ///< block our listener when self-updating props
104     virtual void valueChanged(SGPropertyNode* changed)
105     {
106         if (_listenerLockout) {
107             return;
108         }
109         
110         if (changed == _currentStateIndex) {
111             State_ptr s = _p->stateByIndex(changed->getIntValue());
112             _p->changeToState(s);
113         } else if (changed == _currentStateName) {
114             _p->changeToStateName(changed->getStringValue());
115         }
116     }
117     
118 // exposed properties
119     SGPropertyNode_ptr _root;
120     SGPropertyNode_ptr _currentStateIndex;
121     SGPropertyNode_ptr _currentStateName;
122     SGPropertyNode_ptr _timeInStateProp;
123 };
124
125 ///////////////////////////////////////////////////////////////////////////
126
127 StateMachine::State::State(const std::string& aName) :
128     d(new StatePrivate)
129 {
130     d->_name = aName;
131 }
132
133 StateMachine::State::~State()
134 {
135 }
136
137 std::string StateMachine::State::name() const
138 {
139     return d->_name;
140 }
141
142 void StateMachine::State::update()
143 {
144     fireBindingList(d->_updateBindings);
145 }
146
147 void StateMachine::State::fireEntryBindings()
148 {
149     fireBindingList(d->_entryBindings);
150 }   
151
152 void StateMachine::State::fireExitBindings()
153 {
154     fireBindingList(d->_exitBindings);
155 }   
156
157 void StateMachine::State::addUpdateBinding(SGBinding* aBinding)
158 {
159     d->_updateBindings.push_back(aBinding);
160 }
161
162 void StateMachine::State::addEntryBinding(SGBinding* aBinding)
163 {
164     d->_entryBindings.push_back(aBinding);
165 }
166
167 void StateMachine::State::addExitBinding(SGBinding* aBinding)
168 {
169     d->_exitBindings.push_back(aBinding);
170 }
171
172 ///////////////////////////////////////////////////////////////////////////
173
174 StateMachine::Transition::Transition(const std::string& aName, State* aTarget) :
175     d(new TransitionPrivate)
176 {
177     assert(aTarget);
178     d->_name = aName;
179     d->_target = aTarget;
180     d->_excludeTarget = true;
181 }
182
183 StateMachine::Transition::~Transition()
184 {
185 }
186
187 StateMachine::State* StateMachine::Transition::target() const
188 {
189     return d->_target;
190 }    
191
192 void StateMachine::Transition::addSourceState(State* aSource)
193 {
194     if (aSource == d->_target) { // should this be disallowed outright?
195         SG_LOG(SG_GENERAL, SG_WARN, d->_name << ": adding target state as source");
196     }
197     
198     d->_sourceStates.insert(aSource);
199 }    
200
201 bool StateMachine::Transition::applicableForState(State* aCurrent) const
202 {
203     if (d->_excludeTarget && (aCurrent == d->_target)) {
204         return false;
205     }
206     
207     if (d->_sourceStates.empty()) {
208         return true;
209     }
210     return d->_sourceStates.count(aCurrent);
211
212     
213 bool StateMachine::Transition::evaluate() const
214 {
215     return d->_condition->test();
216 }    
217
218 void StateMachine::Transition::fireBindings()
219 {
220     fireBindingList(d->_bindings);
221 }   
222
223 std::string StateMachine::Transition::name() const
224 {
225     return d->_name;
226 }
227
228 void StateMachine::Transition::setTriggerCondition(SGCondition* aCondition)
229 {
230     d->_condition = aCondition;
231 }
232
233 void StateMachine::Transition::addBinding(SGBinding* aBinding)
234 {
235     d->_bindings.push_back(aBinding);
236 }
237     
238 void StateMachine::Transition::setExcludeTarget(bool aExclude)
239 {
240     d->_excludeTarget = aExclude;
241 }
242     
243 ///////////////////////////////////////////////////////////////////////////
244
245 StateMachine::StateMachine() :
246     d(new StateMachinePrivate(this))
247 {
248     d->_root = new SGPropertyNode();
249     d->_listenerLockout = false;
250     d->_initialised = false;
251 }
252
253 StateMachine::~StateMachine()
254 {
255     
256 }
257
258 void StateMachine::init()
259 {
260     if (d->_initialised) {
261             return;
262     }
263     
264     if (d->_states.empty()) {
265         throw sg_range_exception("StateMachine::init: no states defined");
266     }
267     
268     d->_currentStateIndex = d->_root->getChild("current-index", 0, true);
269     d->_currentStateIndex->setIntValue(0);
270     
271     d->_currentStateName = d->_root->getChild("current-name", 0, true);
272     d->_currentStateName->setStringValue("");
273     
274     d->_currentStateIndex->addChangeListener(d.get());
275     d->_currentStateName->addChangeListener(d.get());
276     
277     d->_timeInStateProp = d->_root->getChild("elapsed-time-msec", 0, true);
278     d->_timeInStateProp->setIntValue(0);
279     
280     // TODO go to default state if found
281     innerChangeState(d->_states[0], NULL);
282     d->_initialised = true;
283 }
284
285 void StateMachine::shutdown()
286 {
287     d->_currentStateIndex->removeChangeListener(d.get());
288     d->_currentStateName->removeChangeListener(d.get());
289     
290 }
291
292 void StateMachine::innerChangeState(State_ptr aState, Transition_ptr aTrans)
293 {
294     if (d->_currentState) {
295         d->_currentState->fireExitBindings();
296     }
297         
298 // fire bindings before we change the state, hmmmm    
299     if (aTrans) {
300         aTrans->fireBindings();
301     }
302     
303     // update our private state and properties
304     d->_listenerLockout = true;
305     d->_currentState = aState;
306     d->_timeInState.stamp();
307     d->_currentStateName->setStringValue(d->_currentState->name());
308     d->_currentStateIndex->setIntValue(indexOfState(aState));
309     d->_timeInStateProp->setIntValue(0);
310     d->_listenerLockout = false;
311     
312     // fire bindings
313     d->_currentState->fireEntryBindings();
314     d->_currentState->update();
315         
316     d->computeEligibleTransitions();
317 }
318
319 void StateMachine::changeToState(State_ptr aState, bool aOnlyIfDifferent)
320 {
321     assert(aState != NULL);
322     if (std::find(d->_states.begin(), d->_states.end(), aState) == d->_states.end()) {
323         throw sg_exception("Requested change to state not in machine");
324     }
325     
326     if (aOnlyIfDifferent && (aState == d->_currentState)) {
327         return;
328     }
329     
330     innerChangeState(aState, NULL);
331 }
332
333 void StateMachine::changeToStateName(const std::string& aName, bool aOnlyIfDifferent)
334 {
335     State_ptr st = findStateByName(aName);
336     if (!st) {
337         throw sg_range_exception("unknown state:" + aName);
338     }
339     
340     changeToState(st, aOnlyIfDifferent);
341 }
342
343 StateMachine::State_ptr StateMachine::state() const
344 {
345     return d->_currentState;
346 }
347     
348 SGPropertyNode* StateMachine::root()
349 {
350     return d->_root;
351 }
352
353 void StateMachine::update(double aDt)
354 {
355     // do this first, for triggers which depend on time in current state
356     // (spring-loaded transitions)
357     d->_timeInStateProp->setIntValue(d->_timeInState.elapsedMSec());
358     
359     Transition_ptr trigger;
360     
361     BOOST_FOREACH(Transition* trans, d->_eligible) {
362         if (trans->evaluate()) {
363             if (trigger != Transition_ptr()) {
364                 SG_LOG(SG_GENERAL, SG_WARN, "ambiguous transitions! " 
365                     << trans->name() << " or "  << trigger->name());
366             }
367             
368             trigger = trans;
369         }
370     }
371     
372     if (trigger != Transition_ptr()) {
373         SG_LOG(SG_GENERAL, SG_DEBUG, "firing transition:" << trigger->name());
374         innerChangeState(trigger->target(), trigger);
375     }
376     
377     d->_currentState->update();
378 }    
379
380 StateMachine::State_ptr StateMachine::findStateByName(const std::string& aName) const
381 {
382     BOOST_FOREACH(State_ptr sp, d->_states) {
383         if (sp->name() == aName) {
384             return sp;
385         }
386     }
387     
388     SG_LOG(SG_GENERAL, SG_WARN, "unknown state:" << aName);
389     return State_ptr();
390 }
391
392 StateMachine::State_ptr StateMachine::stateByIndex(unsigned int aIndex) const
393 {
394     if (aIndex >= d->_states.size()) {
395         throw sg_range_exception("invalid state index, out of bounds");
396     }
397     
398     return d->_states[aIndex];
399 }
400     
401 int StateMachine::indexOfState(State_ptr aState) const
402 {
403     StatePtrVec::const_iterator it = std::find(d->_states.begin(), d->_states.end(), aState);
404     if (it == d->_states.end()) {
405         return -1;
406     }
407     
408     return it - d->_states.begin();
409 }
410
411 StateMachine::State_ptr StateMachine::createState(const std::string& aName)
412 {
413     if (findStateByName(aName) != NULL) {
414         throw sg_range_exception("duplicate state name");
415     }
416     
417     State_ptr st = new State(aName);
418     addState(st);
419     return st;
420 }
421
422 StateMachine::Transition_ptr
423 StateMachine::createTransition(const std::string& aName, State_ptr aTarget)
424 {
425     Transition_ptr t = new Transition(aName, aTarget);
426     addTransition(t);
427     return t;
428 }
429
430 void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
431 {
432     std::string path = desc->getStringValue("branch");
433     if (!path.empty()) {
434         d->_root = root->getNode(path, 0, true);
435         assert(d->_root);
436     }
437     
438     BOOST_FOREACH(SGPropertyNode* stateDesc, desc->getChildren("state")) {
439         std::string nm = stateDesc->getStringValue("name");
440         State_ptr st(new State(nm));
441         
442         readBindingList(stateDesc, "enter", root, st->d->_entryBindings);
443         readBindingList(stateDesc, "update", root, st->d->_updateBindings);
444         readBindingList(stateDesc, "exit", root, st->d->_exitBindings);
445         
446         addState(st);
447     } // of states iteration
448     
449     BOOST_FOREACH(SGPropertyNode* tDesc, desc->getChildren("transition")) {
450         std::string nm = tDesc->getStringValue("name");
451         State_ptr target = findStateByName(tDesc->getStringValue("target"));
452         
453         SGCondition* cond = sgReadCondition(root, tDesc->getChild("condition"));
454         
455         Transition_ptr t(new Transition(nm, target));
456         t->setTriggerCondition(cond);
457         
458         t->setExcludeTarget(tDesc->getBoolValue("exclude-target", true));
459         BOOST_FOREACH(SGPropertyNode* src, tDesc->getChildren("source")) {
460             State_ptr srcState = findStateByName(src->getStringValue());
461             t->addSourceState(srcState);
462         }
463         
464         readBindingList(tDesc, "binding", root, t->d->_bindings);
465         
466         addTransition(t);
467     } // of states iteration
468     
469     init();
470 }
471
472 StateMachine* StateMachine::createFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
473 {
474     StateMachine* sm = new StateMachine;
475     sm->initFromPlist(desc, root);
476     return sm;
477 }
478
479 void StateMachine::addState(State_ptr aState)
480 {
481     bool wasEmpty = d->_states.empty();
482     d->_states.push_back(aState);
483     if (wasEmpty) {
484         d->_currentState = aState;
485     }
486 }
487
488 void StateMachine::addTransition(Transition_ptr aTrans)
489 {
490     d->_transitions.push_back(aTrans);
491 }
492
493 } // of namespace simgear