]> git.mxchange.org Git - simgear.git/blob - simgear/structure/subsystem_mgr.cxx
a59d6ef086f9ac4291c878187c38e14deede6f85
[simgear.git] / simgear / structure / subsystem_mgr.cxx
1 // Written by David Megginson, started 2000-12
2 //
3 // Copyright (C) 2000  David Megginson, david@megginson.com
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, MA  02110-1301, USA.
18 //
19 // $Id$
20
21 #ifdef HAVE_CONFIG_H
22 #  include <simgear_config.h>
23 #endif
24
25 #include <simgear/debug/logstream.hxx>
26 #include <simgear/timing/timestamp.hxx>
27
28 #include "exception.hxx"
29 #include "subsystem_mgr.hxx"
30
31 #include <simgear/math/SGMath.hxx>
32
33
34 const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;\f
35 ////////////////////////////////////////////////////////////////////////
36 // Implementation of SGSubsystem
37 ////////////////////////////////////////////////////////////////////////
38
39
40 SGSubsystem::SGSubsystem ()
41   : _suspended(false)
42 {
43 }
44
45 SGSubsystem::~SGSubsystem ()
46 {
47 }
48
49 void
50 SGSubsystem::init ()
51 {
52 }
53
54 void
55 SGSubsystem::postinit ()
56 {
57 }
58
59 void
60 SGSubsystem::reinit ()
61 {
62 }
63
64 void
65 SGSubsystem::shutdown ()
66 {
67 }
68
69 void
70 SGSubsystem::bind ()
71 {
72 }
73
74 void
75 SGSubsystem::unbind ()
76 {
77 }
78
79 void
80 SGSubsystem::suspend ()
81 {
82   _suspended = true;
83 }
84
85 void
86 SGSubsystem::suspend (bool suspended)
87 {
88   _suspended = suspended;
89 }
90
91 void
92 SGSubsystem::resume ()
93 {
94   _suspended = false;
95 }
96
97 bool
98 SGSubsystem::is_suspended () const
99 {
100   return _suspended;
101 }
102
103
104 void
105 SGSubsystem::printTimingInformation ()
106 {
107    SGTimeStamp startTime;
108    for ( eventTimeVecIterator i = timingInfo.begin();
109           i != timingInfo.end();
110           ++i) {
111        if (i == timingInfo.begin()) {
112            startTime = i->getTime();
113        } else {
114            SGTimeStamp endTime = i->getTime();
115            SG_LOG(SG_GENERAL, SG_ALERT, "- Getting to timestamp :   "
116                   << i->getName() << " takes " << endTime - startTime
117                   << " sec.");
118            startTime = endTime;
119        }
120    }
121 }
122
123
124
125 void SGSubsystem::stamp(const string& name)
126 {
127     timingInfo.push_back(TimingInfo(name, SGTimeStamp::now()));
128 }
129
130 \f
131 ////////////////////////////////////////////////////////////////////////
132 // Implementation of SGSubsystemGroup.
133 ////////////////////////////////////////////////////////////////////////
134
135 SGSubsystemGroup::SGSubsystemGroup () :
136   _fixedUpdateTime(-1.0),
137   _updateTimeRemainder(0.0)
138 {
139 }
140
141 SGSubsystemGroup::~SGSubsystemGroup ()
142 {
143     printTimingStatistics();
144
145     // reverse order to prevent order dependency problems
146     for (unsigned int i = _members.size(); i > 0; i--)
147     {
148         delete _members[i-1];
149     }
150 }
151
152 void
153 SGSubsystemGroup::init ()
154 {
155     for (unsigned int i = 0; i < _members.size(); i++)
156         _members[i]->subsystem->init();
157 }
158
159 void
160 SGSubsystemGroup::postinit ()
161 {
162     for (unsigned int i = 0; i < _members.size(); i++)
163         _members[i]->subsystem->postinit();
164 }
165
166 void
167 SGSubsystemGroup::reinit ()
168 {
169     for (unsigned int i = 0; i < _members.size(); i++)
170         _members[i]->subsystem->reinit();
171 }
172
173 void
174 SGSubsystemGroup::shutdown ()
175 {
176     // reverse order to prevent order dependency problems
177     for (unsigned int i = _members.size(); i > 0; i--)
178         _members[i-1]->subsystem->shutdown();
179 }
180
181 void
182 SGSubsystemGroup::bind ()
183 {
184     for (unsigned int i = 0; i < _members.size(); i++)
185         _members[i]->subsystem->bind();
186 }
187
188 void
189 SGSubsystemGroup::unbind ()
190 {
191     // reverse order to prevent order dependency problems
192     for (unsigned int i = _members.size(); i > 0; i--)
193        _members[i-1]->subsystem->unbind();
194 }
195
196 void
197 SGSubsystemGroup::update (double delta_time_sec)
198 {
199     int loopCount = 1;
200     // if dt == 0.0, we are paused, so we need to run one iteration
201     // of our members; if we have a fixed update time, we compute a
202     // loop count, and locally adjust dt
203     if ((delta_time_sec > 0.0) && (_fixedUpdateTime > 0.0)) {
204       double localDelta = delta_time_sec + _updateTimeRemainder;
205       loopCount = SGMiscd::roundToInt(localDelta / _fixedUpdateTime);
206       _updateTimeRemainder = delta_time_sec - (loopCount * _fixedUpdateTime);
207       delta_time_sec = _fixedUpdateTime;
208     }
209
210     while (loopCount-- > 0) {
211       for (unsigned int i = 0; i < _members.size(); i++)
212       {
213            SGTimeStamp timeStamp = SGTimeStamp::now();
214            _members[i]->update(delta_time_sec); // indirect call
215            timeStamp = SGTimeStamp::now() - timeStamp;
216            double b = timeStamp.toUSecs();
217            _members[i]->updateExecutionTime(b);
218            double threshold = _members[i]->getTimeWarningThreshold();
219            if (( b > threshold ) && (b > 10000)) {
220                _members[i]->printTimingInformation(b);
221            }
222       }
223     } // of multiple update loop
224 }
225
226 void 
227 SGSubsystemGroup::collectDebugTiming(bool collect)
228 {
229     for (unsigned int i = 0; i < _members.size(); i++)
230     {
231         _members[i]->collectDebugTiming(collect);
232     }
233 }
234
235 void 
236 SGSubsystemGroup::printTimingStatistics(double minMaxTime,double minJitter)
237 {
238     for (unsigned int i = _members.size(); i > 0; i--)
239     {
240         _members[i-1]->printTimingStatistics(minMaxTime, minJitter);
241         _members[i-1]->timeStat.reset();
242     }
243 }
244
245 void
246 SGSubsystemGroup::suspend ()
247 {
248     for (unsigned int i = 0; i < _members.size(); i++)
249         _members[i]->subsystem->suspend();
250 }
251
252 void
253 SGSubsystemGroup::resume ()
254 {
255     for (unsigned int i = 0; i < _members.size(); i++)
256         _members[i]->subsystem->resume();
257 }
258
259 bool
260 SGSubsystemGroup::is_suspended () const
261 {
262     return false;
263 }
264
265 void
266 SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
267                                  double min_step_sec)
268 {
269     Member * member = get_member(name, true);
270     if (member->subsystem != 0)
271         delete member->subsystem;
272     member->name = name;
273     member->subsystem = subsystem;
274     member->min_step_sec = min_step_sec;
275 }
276
277 SGSubsystem *
278 SGSubsystemGroup::get_subsystem (const string &name)
279 {
280     Member * member = get_member(name);
281     if (member != 0)
282         return member->subsystem;
283     else
284         return 0;
285 }
286
287 void
288 SGSubsystemGroup::remove_subsystem (const string &name)
289 {
290     for (unsigned int i = 0; i < _members.size(); i++) {
291         if (name == _members[i]->name) {
292             _members.erase(_members.begin() + i);
293             return;
294         }
295     }
296 }
297
298 void
299 SGSubsystemGroup::set_fixed_update_time(double dt)
300 {
301   _fixedUpdateTime = dt;
302 }
303
304 /**
305  * Print timing statistics.
306  * Only show data if jitter exceeds minJitter or
307  * maximum time exceeds minMaxTime. 
308  */
309 void
310 SGSubsystemGroup::Member::printTimingStatistics(double minMaxTime,double minJitter)
311 {
312     if (collectTimeStats) {
313         double minTime = timeStat.min()   / 1000;
314         double maxTime = timeStat.max()   / 1000;
315         double meanTime = timeStat.mean() / 1000;
316         double stddev   = timeStat.stdDev()   / 1000;
317
318         if ((maxTime - minTime >= minJitter)||
319             (maxTime >= minMaxTime))
320         {
321             char buffer[256];
322             snprintf(buffer, 256, "Timing summary for %20s.\n"
323                                   "-  mean time: %04.2f ms.\n"
324                                   "-  min time : %04.2f ms.\n"
325                                   "-  max time : %04.2f ms.\n"
326                                   "-  stddev   : %04.2f ms.\n", name.c_str(), meanTime, minTime, maxTime, stddev);
327             SG_LOG(SG_GENERAL, SG_ALERT, buffer);
328         }
329     }
330 }
331
332
333 bool
334 SGSubsystemGroup::has_subsystem (const string &name) const
335 {
336     return (((SGSubsystemGroup *)this)->get_member(name) != 0);
337 }
338
339 SGSubsystemGroup::Member *
340 SGSubsystemGroup::get_member (const string &name, bool create)
341 {
342     for (unsigned int i = 0; i < _members.size(); i++) {
343         if (_members[i]->name == name)
344             return _members[i];
345     }
346     if (create) {
347         Member * member = new Member;
348         _members.push_back(member);
349         return member;
350     } else {
351         return 0;
352     }
353 }
354
355
356 \f
357 ////////////////////////////////////////////////////////////////////////
358 // Implementation of SGSubsystemGroup::Member
359 ////////////////////////////////////////////////////////////////////////
360
361
362 SGSubsystemGroup::Member::Member ()
363     : name(""),
364       subsystem(0),
365       min_step_sec(0),
366       elapsed_sec(0),
367       collectTimeStats(false),
368       exceptionCount(0)
369 {
370 }
371
372 // This shouldn't be called due to subsystem pointer ownership issues.
373 SGSubsystemGroup::Member::Member (const Member &)
374 {
375 }
376
377 SGSubsystemGroup::Member::~Member ()
378 {
379     delete subsystem;
380 }
381
382 void
383 SGSubsystemGroup::Member::update (double delta_time_sec)
384 {
385     elapsed_sec += delta_time_sec;
386     if (elapsed_sec < min_step_sec) {
387         return;
388     }
389     
390     if (subsystem->is_suspended()) {
391         return;
392     }
393     
394     try {
395       subsystem->update(elapsed_sec);
396       elapsed_sec = 0;
397     } catch (sg_exception& e) {
398       SG_LOG(SG_GENERAL, SG_ALERT, "caught exception processing subsystem:" << name
399         << "\nmessage:" << e.getMessage());
400       
401       if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
402         SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount <<
403           ", suspending)");
404         subsystem->suspend();
405       }
406     }
407 }
408
409
410 void 
411 SGSubsystemGroup::Member::printTimingInformation(double time)
412 {
413      if (collectTimeStats) {
414          SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem Timing Alert, subsystem \"" << name << "\": " << time/1000.0 << "ms");
415          subsystem->printTimingInformation();
416      }
417 }
418
419 double SGSubsystemGroup::Member::getTimeWarningThreshold()
420 {
421     return (timeStat.mean() + 3 * timeStat.stdDev());
422 }
423
424 void SGSubsystemGroup::Member::updateExecutionTime(double time)
425 {
426     if (collectTimeStats) {
427         timeStat += time;
428     }
429 }
430
431
432
433
434 \f
435 ////////////////////////////////////////////////////////////////////////
436 // Implementation of SGSubsystemMgr.
437 ////////////////////////////////////////////////////////////////////////
438
439
440 SGSubsystemMgr::SGSubsystemMgr ()
441 {
442   for (int i = 0; i < MAX_GROUPS; i++) {
443     _groups[i] = new SGSubsystemGroup;
444   }
445 }
446
447 SGSubsystemMgr::~SGSubsystemMgr ()
448 {
449   // ensure get_subsystem returns NULL from now onwards,
450   // before the SGSubsystemGroup destructors are run
451   _subsystem_map.clear();
452   
453   for (int i = 0; i < MAX_GROUPS; i++) {
454     delete _groups[i];
455   }
456 }
457
458 void
459 SGSubsystemMgr::init ()
460 {
461     for (int i = 0; i < MAX_GROUPS; i++)
462             _groups[i]->init();
463 }
464
465 void
466 SGSubsystemMgr::postinit ()
467 {
468     for (int i = 0; i < MAX_GROUPS; i++)
469             _groups[i]->postinit();
470 }
471
472 void
473 SGSubsystemMgr::reinit ()
474 {
475     for (int i = 0; i < MAX_GROUPS; i++)
476             _groups[i]->reinit();
477 }
478
479 void
480 SGSubsystemMgr::shutdown ()
481 {
482     // reverse order to prevent order dependency problems
483     for (int i = MAX_GROUPS-1; i >= 0; i--)
484         _groups[i]->shutdown();
485 }
486
487
488 void
489 SGSubsystemMgr::bind ()
490 {
491     for (int i = 0; i < MAX_GROUPS; i++)
492         _groups[i]->bind();
493 }
494
495 void
496 SGSubsystemMgr::unbind ()
497 {
498     // reverse order to prevent order dependency problems
499     for (int i = MAX_GROUPS-1; i >= 0; i--)
500         _groups[i]->unbind();
501 }
502
503 void
504 SGSubsystemMgr::update (double delta_time_sec)
505 {
506     for (int i = 0; i < MAX_GROUPS; i++) {
507         _groups[i]->update(delta_time_sec);
508     }
509 }
510
511 void 
512 SGSubsystemMgr::collectDebugTiming(bool collect)
513 {
514     for (int i = 0; i < MAX_GROUPS; i++) {
515         _groups[i]->collectDebugTiming(collect);
516     }
517 }
518
519 void
520 SGSubsystemMgr::suspend ()
521 {
522     for (int i = 0; i < MAX_GROUPS; i++)
523         _groups[i]->suspend();
524 }
525
526 void
527 SGSubsystemMgr::resume ()
528 {
529     for (int i = 0; i < MAX_GROUPS; i++)
530         _groups[i]->resume();
531 }
532
533 bool
534 SGSubsystemMgr::is_suspended () const
535 {
536     return false;
537 }
538
539 void
540 SGSubsystemMgr::add (const char * name, SGSubsystem * subsystem,
541                      GroupType group, double min_time_sec)
542 {
543     SG_LOG(SG_GENERAL, SG_INFO, "Adding subsystem " << name);
544     get_group(group)->set_subsystem(name, subsystem, min_time_sec);
545
546     if (_subsystem_map.find(name) != _subsystem_map.end()) {
547         SG_LOG(SG_GENERAL, SG_ALERT, "Adding duplicate subsystem " << name);
548         throw sg_exception("duplicate subsystem");
549     }
550     _subsystem_map[name] = subsystem;
551 }
552
553 SGSubsystem* 
554 SGSubsystemMgr::remove(const char* name)
555 {
556   SubsystemDict::iterator s =_subsystem_map.find(name);
557   if (s == _subsystem_map.end()) {
558     return NULL;
559   }
560   
561   SGSubsystem* sub = s->second;
562   _subsystem_map.erase(s);
563   
564 // tedious part - we don't know which group the subsystem belongs too
565   for (int i = 0; i < MAX_GROUPS; i++) {
566     if (_groups[i]->get_subsystem(name) == sub) {
567       _groups[i]->remove_subsystem(name);
568       break;
569     }
570   } // of groups iteration
571   
572   return sub;
573 }
574
575
576 SGSubsystemGroup *
577 SGSubsystemMgr::get_group (GroupType group)
578 {
579     return _groups[group];
580 }
581
582 SGSubsystem *
583 SGSubsystemMgr::get_subsystem (const string &name) const
584 {
585     SubsystemDict::const_iterator s =_subsystem_map.find(name);
586
587     if (s == _subsystem_map.end())
588         return 0;
589     else
590         return s->second;
591 }
592
593 void
594 SGSubsystemMgr::printTimingStatistics(double minMaxTime,double minJitter)
595 {
596     for (int i = 0; i < MAX_GROUPS; i++) {
597         _groups[i]->printTimingStatistics(minMaxTime, minJitter);
598     } // of groups iteration
599 }
600
601 // end of subsystem_mgr.cxx