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 
22 #ifndef __SUBSYSTEM_MGR_HXX
23 #define __SUBSYSTEM_MGR_HXX 1
24 
25 
26 #include <simgear/compiler.h>
27 
28 #include <string>
29 #include <map>
30 #include <vector>
31 #include <functional>
32 
33 #include <simgear/timing/timestamp.hxx>
34 #include <simgear/structure/SGSharedPtr.hxx>
35 #include <simgear/misc/strutils.hxx>
36 #include <simgear/props/propsfwd.hxx>
37 
38 class TimingInfo
39 {
40 private:
41     std::string eventName;
42     SGTimeStamp time;
43 
44 public:
TimingInfo(const std::string & name,const SGTimeStamp & t)45     TimingInfo(const std::string& name, const SGTimeStamp &t) :
46         eventName(name), time(t)
47     { }
getName() const48     const std::string& getName() const { return eventName; }
getTime() const49     const SGTimeStamp& getTime() const { return time; }
50 };
51 
52 // forward decls
53 class SampleStatistic;
54 class SGSubsystemGroup;
55 class SGSubsystemMgr;
56 
57 typedef std::vector<TimingInfo> eventTimeVec;
58 typedef std::vector<TimingInfo>::iterator eventTimeVecIterator;
59 
60 typedef void (*SGSubsystemTimingCb)(void* userData, const std::string& name, SampleStatistic* pStatistic);
61 
62 /**
63  * Basic interface for all FlightGear subsystems.
64  *
65  * <p>This is an abstract interface that all FlightGear subsystems
66  * will eventually implement.  It defines the basic operations for
67  * each subsystem: initialization, property binding and unbinding, and
68  * updating.  Interfaces may define additional methods, but the
69  * preferred way of exchanging information with other subsystems is
70  * through the property tree.</p>
71  *
72  * <p>To publish information through a property, a subsystem should
73  * bind it to a variable or (if necessary) a getter/setter pair in the
74  * bind() method, and release the property in the unbind() method:</p>
75  *
76  * <pre>
77  * void MySubsystem::bind ()
78  * {
79  *   fgTie("/controls/flight/elevator", &_elevator);
80  *   fgSetArchivable("/controls/flight/elevator");
81  * }
82  *
83  * void MySubsystem::unbind ()
84  * {
85  *   fgUntie("/controls/flight/elevator");
86  * }
87  * </pre>
88  *
89  * <p>To reference a property (possibly) from another subsystem, there
90  * are two alternatives.  If the property will be referenced only
91  * infrequently (say, in the init() method), then the fgGet* methods
92  * declared in fg_props.hxx are the simplest:</p>
93  *
94  * <pre>
95  * void MySubsystem::init ()
96  * {
97  *   _errorMargin = fgGetFloat("/display/error-margin-pct");
98  * }
99  * </pre>
100  *
101  * <p>On the other hand, if the property will be referenced frequently
102  * (say, in the update() method), then the hash-table lookup required
103  * by the fgGet* methods might be too expensive; instead, the
104  * subsystem should obtain a reference to the actual property node in
105  * its init() function and use that reference in the main loop:</p>
106  *
107  * <pre>
108  * void MySubsystem::init ()
109  * {
110  *   _errorNode = fgGetNode("/display/error-margin-pct", true);
111  * }
112  *
113  * void MySubsystem::update (double delta_time_sec)
114  * {
115  *   do_something(_errorNode.getFloatValue());
116  * }
117  * </pre>
118  *
119  * <p>The node returned will always be a pointer to SGPropertyNode,
120  * and the subsystem should <em>not</em> delete it in its destructor
121  * (the pointer belongs to the property tree, not the subsystem).</p>
122  *
123  * <p>The program may ask the subsystem to suspend or resume
124  * sim-time-dependent operations; by default, the suspend() and
125  * resume() methods set the protected variable <var>_suspended</var>,
126  * which the subsystem can reference in its update() method, but
127  * subsystems may also override the suspend() and resume() methods to
128  * take different actions.</p>
129  */
130 
131 class SGSubsystem : public SGReferenced
132 {
133 public:
134     using TimerStats = std::map<std::string, double>;
135     /**
136    * Default constructor.
137    */
138   SGSubsystem ();
139 
140   /**
141    * Virtual destructor to ensure that subclass destructors are called.
142    */
143   virtual ~SGSubsystem ();
144 
145 
146   /**
147    * Initialize the subsystem.
148    *
149    * <p>This method should set up the state of the subsystem, but
150    * should not bind any properties.  Note that any dependencies on
151    * the state of other subsystems should be placed here rather than
152    * in the constructor, so that FlightGear can control the
153    * initialization order.</p>
154    */
155   virtual void init ();
156 
157   typedef enum
158   {
159     INIT_DONE,      ///< subsystem is fully initialised
160     INIT_CONTINUE   ///< init should be called again
161   } InitStatus;
162 
163   virtual InitStatus incrementalInit ();
164 
165   /**
166    * Initialize parts that depend on other subsystems having been initialized.
167    *
168    * <p>This method should set up all parts that depend on other
169    * subsystems. One example is the scripting/Nasal subsystem, which
170    * is initialized last. So, if a subsystem wants to execute Nasal
171    * code in subsystem-specific configuration files, it has to do that
172    * in its postinit() method.</p>
173    */
174   virtual void postinit ();
175 
176 
177   /**
178    * Reinitialize the subsystem.
179    *
180    * <p>This method should cause the subsystem to reinitialize itself,
181    * and (normally) to reload any configuration files.</p>
182    */
183   virtual void reinit ();
184 
185 
186   /**
187    * Shutdown the subsystem.
188    *
189    * <p>Release any state associated with subsystem. Shutdown happens in
190    * the reverse order to init(), so this is the correct place to do
191    * shutdown that depends on other subsystems.
192    * </p>
193    */
194   virtual void shutdown ();
195 
196   /**
197    * Acquire the subsystem's property bindings.
198    *
199    * <p>This method should bind all properties that the subsystem
200    * publishes.  It will be invoked after init, but before any
201    * invocations of update.</p>
202    */
203   virtual void bind ();
204 
205 
206   /**
207    * Release the subsystem's property bindings.
208    *
209    * <p>This method should release all properties that the subsystem
210    * publishes.  It will be invoked by FlightGear (not the destructor)
211    * just before the subsystem is removed.</p>
212    */
213   virtual void unbind ();
214 
215 
216   /**
217    * Update the subsystem.
218    *
219    * <p>FlightGear invokes this method every time the subsystem should
220    * update its state.</p>
221    *
222    * @param delta_time_sec The delta time, in seconds, since the last
223    * update.  On first update, delta time will be 0.
224    */
225   virtual void update (double delta_time_sec) = 0;
226 
227 
228   /**
229    * Suspend operation of this subsystem.
230    *
231    * <p>This method instructs the subsystem to suspend
232    * sim-time-dependent operations until asked to resume.  The update
233    * method will still be invoked so that the subsystem can take any
234    * non-time-dependent actions, such as updating the display.</p>
235    *
236    * <p>It is not an error for the suspend method to be invoked when
237    * the subsystem is already suspended; the invocation should simply
238    * be ignored.</p>
239    */
240   virtual void suspend ();
241 
242 
243   /**
244    * Suspend or resume operation of this subsystem.
245    *
246    * @param suspended true if the subsystem should be suspended, false
247    * otherwise.
248    */
249   virtual void suspend (bool suspended);
250 
251 
252   /**
253    * Resume operation of this subsystem.
254    *
255    * <p>This method instructs the subsystem to resume
256    * sim-time-depended operations.  It is not an error for the resume
257    * method to be invoked when the subsystem is not suspended; the
258    * invocation should simply be ignored.</p>
259    */
260   virtual void resume ();
261 
262 
263   /**
264    * Test whether this subsystem is suspended.
265    *
266    * @return true if the subsystem is suspended, false if it is not.
267    */
268   virtual bool is_suspended () const;
269 
270   /**
271    * Trigger the callback to report timing information for all subsystems.
272    */
273   void reportTiming(void);
274 
275   virtual void reportTimingStats(TimerStats *_lastValues);
276   /**
277    * Place time stamps at strategic points in the execution of subsystems
278    * update() member functions. Predominantly for debugging purposes.
279    */
280   void stamp(const std::string& name);
281 
282     /**
283      * composite name for this subsystem (type name & optional instance name)
284      */
subsystemId() const285     std::string subsystemId() const
286     { return _subsystemId; }
287 
288     /**
289      * @brief the type (class)-specific part of the subsystem name.
290      */
291     std::string subsystemClassId() const;
292 
293     /**
294      * @brief the instance part of the subsystem name. Empty if this
295      * subsystem is not instanced
296      */
297     std::string subsystemInstanceId() const;
298 
is_group() const299     virtual bool is_group() const
300     { return false; }
301 
302     virtual SGSubsystemMgr* get_manager() const;
303 
304     /// get the parent group of this subsystem
305     SGSubsystemGroup* get_group() const;
306 
307     // ordering here is exceptionally important, due to
308     // liveness of ranges. If you're extending this consider
309     // carefully where the new state lies and position it correctly.
310     // Also extend the tests to ensure you handle all cases.
311     enum class State {
312         INVALID = -1,
313         ADD = 0,
314         BIND,
315         INIT,
316         POSTINIT,
317         REINIT,
318         SUSPEND,
319         RESUME,
320         SHUTDOWN,
321         UNBIND,
322         REMOVE
323     };
324 
325     /**
326      * debug helper, print a state as a string
327      */
328     static std::string nameForState(State s);
329 
330     /**
331     * gets fine grained stats of time elapsed since last clear
332     * returns map of ident and time
333     */
getTimerStats()334     virtual const TimerStats  &getTimerStats() {
335         return _timerStats;
336     }
337 
338     /**
339     * clear fine grained stats that are over the specified value.
340     */
resetTimerStats(double val=0)341     virtual void resetTimerStats(double val = 0) { }
342 
343 protected:
344     friend class SGSubsystemMgr;
345     friend class SGSubsystemGroup;
346 
347     void set_name(const std::string& n);
348 
349     void set_group(SGSubsystemGroup* group);
350 
351     /// composite name for the subsystem (type name and instance name if this
352     /// is an instanced subsystem. (Since this member was originally defined as
353     /// protected, not private, we can't rename it easily)
354     std::string _name;
355 
356     bool _suspended = false;
357 
358     eventTimeVec timingInfo;
359 
360     static SGSubsystemTimingCb reportTimingCb;
361     static void* reportTimingUserData;
362     static bool reportTimingStatsRequest;
363     static int maxTimePerFrame_ms;
364 
365 private:
366     /// composite name for the subsystem (type name and instance name if this
367     /// is an instanced subsystem. (Since this member was originally defined as
368     /// protected, not private, we can't rename it easily)
369     std::string _subsystemId;
370 
371     SGSubsystemGroup* _group = nullptr;
372 protected:
373     TimerStats _timerStats, _lastTimerStats;
374     double _executionTime;
375     double _lastExecutionTime;
376 
377 };
378 
379 typedef SGSharedPtr<SGSubsystem> SGSubsystemRef;
380 
381 /**
382  * A group of FlightGear subsystems.
383  */
384 class SGSubsystemGroup : public SGSubsystem
385 {
386 public:
387     SGSubsystemGroup ();
388     virtual ~SGSubsystemGroup ();
389 
390     // Subsystem API.
391     void bind() override;
392     InitStatus incrementalInit() override;
393     void init() override;
394     void postinit() override;
395     void reinit() override;
396     void resume() override;
397     void shutdown() override;
398     void suspend() override;
399     void unbind() override;
400     void update(double delta_time_sec) override;
401     bool is_suspended() const override;
402 
403     virtual void set_subsystem (const std::string &name,
404                                 SGSubsystem * subsystem,
405                                 double min_step_sec = 0);
406 
407     void set_subsystem (SGSubsystem * subsystem, double min_step_sec = 0);
408 
409     virtual SGSubsystem * get_subsystem (const std::string &name);
410     bool remove_subsystem (const std::string &name);
411     virtual bool has_subsystem (const std::string &name) const;
412 
413     void reportTimingStats(TimerStats *_lastValues) override;
414     /**
415      * Remove all subsystems.
416      */
417     virtual void clearSubsystems();
418 
419     void reportTiming(void);
420 
421     /**
422      *
423      */
424     void set_fixed_update_time(double fixed_dt);
425 
426     /**
427      * retrive list of member subsystem names
428      */
429     string_list member_names() const;
430 
431     template<class T>
get_subsystem()432     T* get_subsystem()
433     {
434         return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId()));
435     }
436 
is_group() const437     bool is_group() const override
438     { return true; }
439 
440     SGSubsystemMgr* get_manager() const override;
441 
442 private:
443     void forEach(std::function<void(SGSubsystem*)> f);
444     void reverseForEach(std::function<void(SGSubsystem*)> f);
445 
446     void notifyWillChange(SGSubsystem* sub, SGSubsystem::State s);
447     void notifyDidChange(SGSubsystem* sub, SGSubsystem::State s);
448 
449     friend class SGSubsystemMgr;
450 
451     void set_manager(SGSubsystemMgr* manager);
452 
453     class Member;
454     Member* get_member (const std::string &name, bool create = false);
455 
456     using MemberVec = std::vector<Member*>;
457     MemberVec _members;
458 
459     // track the state of this group, so we can transition added/removed
460     // members correctly
461     SGSubsystem::State _state = SGSubsystem::State::INVALID;
462     double _fixedUpdateTime;
463     double _updateTimeRemainder;
464 
465   /// index of the member we are currently init-ing
466     int _initPosition;
467 
468     /// back-pointer to the manager, for the root groups. (sub-groups
469     /// will have this as null, and chain via their parent)
470     SGSubsystemMgr* _manager = nullptr;
471 };
472 
473 typedef SGSharedPtr<SGSubsystemGroup> SGSubsystemGroupRef;
474 
475 /**
476  * Manage subsystems for FlightGear.
477  *
478  * This top-level subsystem will eventually manage all of the
479  * subsystems in FlightGear: it broadcasts its life-cycle events
480  * (init, bind, etc.) to all of the subsystems it manages.  Subsystems
481  * are grouped to guarantee order of initialization and execution --
482  * currently, the only two groups are INIT and GENERAL, but others
483  * will appear in the future.
484  *
485  * All subsystems are named as well as grouped, and subsystems can be
486  * looked up by name and cast to the appropriate subtype when another
487  * subsystem needs to invoke specialized methods.
488  *
489  * The subsystem manager owns the pointers to all the subsystems in
490  * it.
491  */
492 class SGSubsystemMgr : public SGSubsystem
493 {
494 public:
495     SGSubsystemMgr(const SGSubsystemMgr&) = delete;
496     SGSubsystemMgr& operator=(const SGSubsystemMgr&) = delete;
497 
498     /**
499      * Types of subsystem groups.
500      */
501     enum GroupType {
502         INVALID = -1,
503         INIT = 0,
504         GENERAL,
505         FDM,        ///< flight model, autopilot, instruments that run coupled
506         POST_FDM,   ///< certain subsystems depend on FDM data
507         DISPLAY,    ///< view, camera, rendering updates
508         SOUND/*I want to be last!*/,  ///< needs to run AFTER display, to allow concurrent GPU/sound processing
509         MAX_GROUPS
510     };
511 
512     SGSubsystemMgr ();
513     virtual ~SGSubsystemMgr ();
514 
515     // Subsystem API.
516     void bind() override;
517     void init() override;
518     InitStatus incrementalInit() override;
519     void postinit() override;
520     void reinit() override;
521     void resume() override;
522     void shutdown() override;
523     void suspend() override;
524     void unbind() override;
525     void update(double delta_time_sec) override;
526     bool is_suspended() const override;
527 
528     // Subsystem identification.
staticSubsystemClassId()529     static const char* staticSubsystemClassId() { return "subsystem-mgr"; }
530 
531     virtual void add (const char * name,
532                       SGSubsystem * subsystem,
533                       GroupType group = GENERAL,
534                       double min_time_sec = 0);
535 
536     /**
537      * remove a subsystem, and return true on success
538      * returns false if the subsystem was not found
539      */
540     bool remove(const char* name);
541 
542     virtual SGSubsystemGroup * get_group (GroupType group);
543 
544     SGSubsystem* get_subsystem(const std::string &name) const;
545 
546     SGSubsystem* get_subsystem(const std::string &name, const std::string& subsystemInstanceId) const;
547 
548     void reportTiming();
setReportTimingCb(void * userData,SGSubsystemTimingCb cb)549     void setReportTimingCb(void* userData, SGSubsystemTimingCb cb) { reportTimingCb = cb; reportTimingUserData = userData; }
setReportTimingStats(bool v)550     void setReportTimingStats(bool v) { reportTimingStatsRequest = v; }
551 
552     /**
553      * @brief set the root property node for this subsystem manager
554      * subsystems can retrieve this value during init/bind (or at any time)
555      * to base their own properties trees off of.
556      */
557     void set_root_node(SGPropertyNode_ptr node);
558 
559     /**
560      * @brief retrieve the root property node for this subsystem manager
561      */
562     SGPropertyNode_ptr root_node() const;
563 
564     template<class T>
get_subsystem() const565     T* get_subsystem() const
566     {
567         return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId()));
568     }
569 
570 // instanced overloads, for both raw char* and std::string
571 // these concatenate the subsystem type name with the instance name
572     template<class T>
get_subsystem(const char * subsystemInstanceId) const573     T* get_subsystem(const char* subsystemInstanceId) const
574     {
575         return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId));
576     }
577 
578     template<class T>
get_subsystem(const std::string & subsystemInstanceId) const579     T* get_subsystem(const std::string& subsystemInstanceId) const
580     {
581         return dynamic_cast<T*>(get_subsystem(T::staticSubsystemClassId(), subsystemInstanceId));
582     }
583 
584     /**
585      * @brief Subsystem dependency structure.
586      */
587     struct Dependency {
588         enum Type {
589             HARD,               ///< The subsystem cannot run without this subsystem dependency.
590             SOFT,               ///< The subsystem uses this subsystem dependency, but can run without it.
591             SEQUENCE,           ///< Used for ordering subsystem initialisation.
592             NONSUBSYSTEM_HARD,  ///< The subsystem cannot run without this non-subsystem dependency.
593             NONSUBSYSTEM_SOFT,  ///< The subsystem uses this non-subsystem dependency, but can run without it.
594             PROPERTY            ///< The subsystem requires this property to exist to run.
595         };
596 
597         std::string name;
598         Type type;
599     };
600 
601     using DependencyVec = std::vector<SGSubsystemMgr::Dependency>;
602     using SubsystemFactoryFunctor = std::function<SGSubsystemRef()>;
603 
604     /**
605      * @brief register a subsytem with the manager
606      *
607      */
608     static void registerSubsystem(const std::string& name,
609                                   SubsystemFactoryFunctor f,
610                                   GroupType group,
611                                   bool isInstanced = false,
612                                   double updateInterval = 0.0,
613                                   std::initializer_list<Dependency> deps = {});
614 
615     template<class T>
616     class Registrant
617     {
618     public:
Registrant(GroupType group=GENERAL,std::initializer_list<Dependency> deps={},double updateInterval=0.0)619         Registrant(GroupType group = GENERAL,
620                    std::initializer_list<Dependency> deps = {},
621                    double updateInterval = 0.0)
622         {
__anon74f1a66a0202()623             SubsystemFactoryFunctor factory = [](){ return new T; };
624             SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(),
625                                               factory, group,
626                                               false, updateInterval,
627                                               deps);
628         }
629 
630         // could implement a dtor to unregister but not needed at the moment
631     };
632 
633     template<class T>
634     class InstancedRegistrant
635     {
636     public:
InstancedRegistrant(GroupType group=GENERAL,std::initializer_list<Dependency> deps={},double updateInterval=0.0)637         InstancedRegistrant(GroupType group = GENERAL,
638                             std::initializer_list<Dependency> deps = {},
639                             double updateInterval = 0.0)
640         {
__anon74f1a66a0302()641             SubsystemFactoryFunctor factory = [](){ return new T; };
642             SGSubsystemMgr::registerSubsystem(T::staticSubsystemClassId(),
643                                               factory, group,
644                                               true, updateInterval,
645                                               deps);
646         }
647 
648         // could implement a dtor to unregister but not needed at the moment
649     };
650 
651     /**
652      * @brief templated add function, subsystem is deduced automatically
653      *
654      */
655     template <class T>
add(GroupType customGroup=INVALID,double customInterval=0.0)656     SGSharedPtr<T> add(GroupType customGroup = INVALID, double customInterval = 0.0)
657     {
658         auto ref = create(T::staticSubsystemClassId());
659 
660 
661         const GroupType group = (customGroup == INVALID) ?
662             defaultGroupFor(T::staticSubsystemClassId()) : customGroup;
663         const double interval = (customInterval == 0.0) ?
664         defaultUpdateIntervalFor(T::staticSubsystemClassId()) : customInterval;
665         add(ref->subsystemId().c_str(), ref.ptr(), group, interval);
666         return dynamic_cast<T*>(ref.ptr());
667     }
668 
669     /**
670      * @brief templated creation function, only makes the instance but
671      * doesn't add to the manager or group heirarchy
672      */
673     template <class T>
create()674     SGSharedPtr<T> create()
675     {
676         auto ref = create(T::staticSubsystemClassId());
677         return dynamic_cast<T*>(ref.ptr());
678     }
679 
680     SGSubsystemRef create(const std::string& name);
681 
682     template <class T>
createInstance(const std::string & subsystemInstanceId)683     SGSharedPtr<T> createInstance(const std::string& subsystemInstanceId)
684     {
685         auto ref = createInstance(T::staticSubsystemClassId(), subsystemInstanceId);
686         return dynamic_cast<T*>(ref.ptr());
687     }
688 
689     SGSubsystemRef createInstance(const std::string& name, const std::string& subsystemInstanceId);
690 
691     static GroupType defaultGroupFor(const char* name);
692     static double defaultUpdateIntervalFor(const char* name);
693     static const DependencyVec& dependsFor(const char* name);
694 
695     /**
696      * @brief delegate to recieve notifications when the subsystem
697      * configuration changes. For any event/state change, a before (will)
698      * and after (did) change notifications are sent.
699      */
700     class Delegate
701     {
702     public:
703         virtual void willChange(SGSubsystem* sub, SGSubsystem::State newState);
704         virtual void didChange(SGSubsystem* sub, SGSubsystem::State currentState);
705     };
706 
707     void addDelegate(Delegate * d);
708     void removeDelegate(Delegate * d);
709 
710     /**
711      * @brief return a particular subsystem manager by name. Passing an
712      * empty string retrived the default/global subsystem manager, assuming it
713      * has been created.
714      */
715     static SGSubsystemMgr* getManager(const std::string& id);
716 private:
717     friend class SGSubsystem;
718     friend class SGSubsystemGroup;
719 
720     void notifyDelegatesWillChange(SGSubsystem* sub, State newState);
721     void notifyDelegatesDidChange(SGSubsystem* sub, State statee);
722 
723     std::vector<SGSubsystemGroupRef> _groups;
724     unsigned int _initPosition = 0;
725     bool _destructorActive = false;
726     SGPropertyNode_ptr _rootNode;
727 
728     // non-owning reference, this is to accelerate lookup
729     // by name which otherwise needs a full walk of the entire tree
730     using SubsystemDict = std::map<std::string, SGSubsystem*>;
731     mutable SubsystemDict _subsystemNameCache;
732 
733     using DelegateVec = std::vector<Delegate*>;
734     DelegateVec _delegates;
735 };
736 
737 #endif // __SUBSYSTEM_MGR_HXX
738