Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next
This commit is contained in:
commit
b01718aae7
@ -397,7 +397,12 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
message(WARNING "GCC 4.4 will be required soon, please upgrade")
|
||||
endif()
|
||||
|
||||
if(ENABLE_SIMD)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_C_FLAGS
|
||||
"${CMAKE_C_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
|
||||
elseif (ENABLE_SIMD)
|
||||
if (X86 OR X86_64)
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
@ -420,7 +425,12 @@ if (CLANG)
|
||||
# fix Boost compilation :(
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
|
||||
if(ENABLE_SIMD)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_C_FLAGS
|
||||
"${CMAKE_C_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline-functions")
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline-functions")
|
||||
elseif (ENABLE_SIMD)
|
||||
if (X86 OR X86_64)
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
|
@ -861,6 +861,36 @@ void updateValidToInvalid(HTTP::Client* cl)
|
||||
SG_VERIFY(!vectorContains(root->catalogs(), c));
|
||||
}
|
||||
|
||||
void updateInvalidToInvalid(HTTP::Client* cl)
|
||||
{
|
||||
global_catalogVersion = 0;
|
||||
SGPath rootPath(simgear::Dir::current().path());
|
||||
rootPath.append("cat_update_invalid_to_inalid");
|
||||
simgear::Dir pd(rootPath);
|
||||
pd.removeChildren();
|
||||
|
||||
// first, sync the invalid version
|
||||
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
|
||||
root->setHTTPClient(cl);
|
||||
|
||||
pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTestInvalid/catalog.xml");
|
||||
waitForUpdateComplete(cl, root);
|
||||
SG_VERIFY(!c->isEnabled());
|
||||
|
||||
SG_VERIFY(c->status() == pkg::Delegate::FAIL_VALIDATION);
|
||||
SG_VERIFY(!vectorContains(root->catalogs(), c));
|
||||
SG_VERIFY(vectorContains(root->allCatalogs(), c));
|
||||
|
||||
// now refresh to a different, but still bad one
|
||||
global_catalogVersion = 3;
|
||||
c->refresh();
|
||||
waitForUpdateComplete(cl, root);
|
||||
SG_VERIFY(!c->isEnabled());
|
||||
SG_VERIFY(c->status() == pkg::Delegate::FAIL_VALIDATION);
|
||||
SG_VERIFY(!vectorContains(root->catalogs(), c));
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
sglog().setLogLevels( SG_ALL, SG_DEBUG );
|
||||
@ -894,6 +924,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
updateInvalidToValid(&cl);
|
||||
updateValidToInvalid(&cl);
|
||||
updateInvalidToInvalid(&cl);
|
||||
|
||||
removeInvalidCatalog(&cl);
|
||||
|
||||
|
@ -671,7 +671,7 @@ void Root::cancelDownload(InstallRef aInstall)
|
||||
|
||||
void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
|
||||
{
|
||||
CatalogDict::iterator catIt = d->catalogs.find(aCat->id());
|
||||
auto catIt = d->catalogs.find(aCat->id());
|
||||
d->fireRefreshStatus(aCat, aReason);
|
||||
|
||||
if (aReason == Delegate::STATUS_IN_PROGRESS) {
|
||||
|
@ -47,6 +47,10 @@ simgear_component(structure structure "${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
add_executable(test_subsystems subsystem_test.cxx)
|
||||
target_link_libraries(test_subsystems ${TEST_LIBS})
|
||||
add_test(subsystems ${EXECUTABLE_OUTPUT_PATH}/test_subsystems)
|
||||
|
||||
add_executable(test_state_machine state_machine_test.cxx)
|
||||
target_link_libraries(test_state_machine ${TEST_LIBS})
|
||||
add_test(state_machine ${EXECUTABLE_OUTPUT_PATH}/test_state_machine)
|
||||
|
@ -18,9 +18,7 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -36,6 +34,7 @@
|
||||
const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
|
||||
|
||||
using std::string;
|
||||
using State = SGSubsystem::State;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of SGSubsystem
|
||||
@ -45,7 +44,6 @@ SGSubsystemTimingCb SGSubsystem::reportTimingCb = NULL;
|
||||
void* SGSubsystem::reportTimingUserData = NULL;
|
||||
|
||||
SGSubsystem::SGSubsystem ()
|
||||
: _suspended(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -93,19 +91,32 @@ SGSubsystem::unbind ()
|
||||
void
|
||||
SGSubsystem::suspend ()
|
||||
{
|
||||
_suspended = true;
|
||||
suspend(true);
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystem::suspend (bool suspended)
|
||||
{
|
||||
_suspended = suspended;
|
||||
// important we don't use is_suspended() here since SGSubsystemGroup
|
||||
// overries. We record the actual state correctly here, even for groups,
|
||||
// so this works out, whereas is_suspended() is always false for groups
|
||||
if (_suspended == suspended)
|
||||
return;
|
||||
|
||||
const auto newState = suspended ? State::SUSPEND : State::RESUME;
|
||||
const auto manager = get_manager();
|
||||
|
||||
if (manager)
|
||||
manager->notifyDelegatesWillChange(this, newState);
|
||||
_suspended = suspended;
|
||||
if (manager)
|
||||
manager->notifyDelegatesDidChange(this, newState);
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystem::resume ()
|
||||
{
|
||||
_suspended = false;
|
||||
suspend(false);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -119,6 +130,47 @@ void SGSubsystem::stamp(const string& name)
|
||||
timingInfo.push_back(TimingInfo(name, SGTimeStamp::now()));
|
||||
}
|
||||
|
||||
void SGSubsystem::set_name(const std::string &n)
|
||||
{
|
||||
assert(_name.empty());
|
||||
_name = n;
|
||||
}
|
||||
|
||||
void SGSubsystem::set_group(SGSubsystemGroup* group)
|
||||
{
|
||||
_group = group;
|
||||
}
|
||||
|
||||
SGSubsystemGroup* SGSubsystem::get_group() const
|
||||
{
|
||||
return _group;
|
||||
}
|
||||
|
||||
SGSubsystemMgr* SGSubsystem::get_manager() const
|
||||
{
|
||||
if (!get_group())
|
||||
throw sg_exception("SGSubsystem::get_manager: subsystem " + name() + " has no group");
|
||||
return get_group()->get_manager();
|
||||
}
|
||||
|
||||
std::string SGSubsystem::nameForState(State s)
|
||||
{
|
||||
switch (s) {
|
||||
case State::INIT: return "init";
|
||||
case State::REINIT: return "reinit";
|
||||
case State::POSTINIT: return "post-init";
|
||||
case State::SHUTDOWN: return "shutdown";
|
||||
case State::BIND: return "bind";
|
||||
case State::UNBIND: return "unbind";
|
||||
case State::ADD: return "add";
|
||||
case State::REMOVE: return "remove";
|
||||
case State::SUSPEND: return "suspend";
|
||||
case State::RESUME: return "resume";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of SGSubsystemGroup.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@ -129,16 +181,16 @@ private:
|
||||
Member (const Member &member);
|
||||
public:
|
||||
Member ();
|
||||
virtual ~Member ();
|
||||
~Member ();
|
||||
|
||||
virtual void update (double delta_time_sec);
|
||||
void update (double delta_time_sec);
|
||||
|
||||
void reportTiming(void) { if (reportTimingCb) reportTimingCb(reportTimingUserData, name, &timeStat); }
|
||||
void updateExecutionTime(double time) { timeStat += time;}
|
||||
|
||||
SampleStatistic timeStat;
|
||||
std::string name;
|
||||
SGSharedPtr<SGSubsystem> subsystem;
|
||||
SGSubsystemRef subsystem;
|
||||
double min_step_sec;
|
||||
double elapsed_sec;
|
||||
bool collectTimeStats;
|
||||
@ -151,79 +203,127 @@ public:
|
||||
SGSubsystemGroup::SGSubsystemGroup () :
|
||||
_fixedUpdateTime(-1.0),
|
||||
_updateTimeRemainder(0.0),
|
||||
_initPosition(0)
|
||||
_initPosition(-1)
|
||||
{
|
||||
}
|
||||
|
||||
SGSubsystemGroup::~SGSubsystemGroup ()
|
||||
{
|
||||
// reverse order to prevent order dependency problems
|
||||
for( size_t i = _members.size(); i > 0; i-- )
|
||||
{
|
||||
delete _members[i-1];
|
||||
}
|
||||
clearSubsystems();
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::init ()
|
||||
{
|
||||
for( size_t i = 0; i < _members.size(); i++ )
|
||||
_members[i]->subsystem->init();
|
||||
forEach([this](SGSubsystem* s){
|
||||
this->notifyWillChange(s, State::INIT);
|
||||
s->init();
|
||||
this->notifyDidChange(s, State::INIT);
|
||||
});
|
||||
}
|
||||
|
||||
SGSubsystem::InitStatus
|
||||
SGSubsystemGroup::incrementalInit()
|
||||
{
|
||||
if (_initPosition >= _members.size())
|
||||
return INIT_DONE;
|
||||
// special case this, simplifies the logic below
|
||||
if (_members.empty()) {
|
||||
return INIT_DONE;
|
||||
}
|
||||
|
||||
SGTimeStamp st;
|
||||
st.stamp();
|
||||
InitStatus memberStatus = _members[_initPosition]->subsystem->incrementalInit();
|
||||
_members[_initPosition]->initTime += st.elapsedMSec();
|
||||
// termination test
|
||||
if (_initPosition >= static_cast<int>(_members.size())) {
|
||||
return INIT_DONE;
|
||||
}
|
||||
|
||||
if (memberStatus == INIT_DONE)
|
||||
++_initPosition;
|
||||
if (_initPosition < 0) {
|
||||
// first call
|
||||
_initPosition = 0;
|
||||
notifyWillChange(_members.front()->subsystem, State::INIT);
|
||||
}
|
||||
|
||||
const auto m = _members[_initPosition];
|
||||
SGTimeStamp st;
|
||||
st.stamp();
|
||||
const InitStatus memberStatus = m->subsystem->incrementalInit();
|
||||
m->initTime += st.elapsedMSec();
|
||||
|
||||
if (memberStatus == INIT_DONE) {
|
||||
// complete init of this one
|
||||
notifyDidChange(m->subsystem, State::INIT);
|
||||
++_initPosition;
|
||||
|
||||
if (_initPosition < _members.size()) {
|
||||
// start init of the next one
|
||||
notifyWillChange( _members[_initPosition]->subsystem, State::INIT);
|
||||
}
|
||||
}
|
||||
|
||||
return INIT_CONTINUE;
|
||||
}
|
||||
|
||||
void SGSubsystemGroup::forEach(std::function<void(SGSubsystem*)> f)
|
||||
{
|
||||
for (auto m : _members) {
|
||||
f(m->subsystem);
|
||||
}
|
||||
}
|
||||
|
||||
void SGSubsystemGroup::reverseForEach(std::function<void(SGSubsystem*)> f)
|
||||
{
|
||||
for (auto it = _members.rbegin(); it != _members.rend(); ++it) {
|
||||
f((*it)->subsystem);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::postinit ()
|
||||
{
|
||||
for( size_t i = 0; i < _members.size(); i++ )
|
||||
_members[i]->subsystem->postinit();
|
||||
forEach([this](SGSubsystem* s){
|
||||
this->notifyWillChange(s, State::POSTINIT);
|
||||
s->postinit();
|
||||
this->notifyDidChange(s, State::POSTINIT);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::reinit ()
|
||||
{
|
||||
for( size_t i = 0; i < _members.size(); i++ )
|
||||
_members[i]->subsystem->reinit();
|
||||
forEach([this](SGSubsystem* s){
|
||||
this->notifyWillChange(s, State::REINIT);
|
||||
s->reinit();
|
||||
this->notifyDidChange(s, State::REINIT);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::shutdown ()
|
||||
{
|
||||
// reverse order to prevent order dependency problems
|
||||
for( size_t i = _members.size(); i > 0; i-- )
|
||||
_members[i-1]->subsystem->shutdown();
|
||||
_initPosition = 0;
|
||||
reverseForEach([this](SGSubsystem* s){
|
||||
this->notifyWillChange(s, State::SHUTDOWN);
|
||||
s->shutdown();
|
||||
this->notifyDidChange(s, State::SHUTDOWN);
|
||||
});
|
||||
_initPosition = -1;
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::bind ()
|
||||
{
|
||||
for( size_t i = 0; i < _members.size(); i++ )
|
||||
_members[i]->subsystem->bind();
|
||||
forEach([this](SGSubsystem* s){
|
||||
this->notifyWillChange(s, State::BIND);
|
||||
s->bind();
|
||||
this->notifyDidChange(s, State::BIND);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::unbind ()
|
||||
{
|
||||
// reverse order to prevent order dependency problems
|
||||
for( size_t i = _members.size(); i > 0; i-- )
|
||||
_members[i-1]->subsystem->unbind();
|
||||
reverseForEach([this](SGSubsystem* s){
|
||||
this->notifyWillChange(s, State::UNBIND);
|
||||
s->unbind();
|
||||
this->notifyDidChange(s, State::UNBIND);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@ -241,20 +341,18 @@ SGSubsystemGroup::update (double delta_time_sec)
|
||||
delta_time_sec = _fixedUpdateTime;
|
||||
}
|
||||
|
||||
bool recordTime = (reportTimingCb != NULL);
|
||||
const bool recordTime = (reportTimingCb != nullptr);
|
||||
SGTimeStamp timeStamp;
|
||||
while (loopCount-- > 0) {
|
||||
for( size_t i = 0; i < _members.size(); i++ )
|
||||
{
|
||||
for (auto member : _members) {
|
||||
if (recordTime)
|
||||
timeStamp = SGTimeStamp::now();
|
||||
|
||||
_members[i]->update(delta_time_sec); // indirect call
|
||||
member->update(delta_time_sec); // indirect call
|
||||
|
||||
if ((recordTime)&&(reportTimingCb))
|
||||
{
|
||||
if (recordTime && reportTimingCb) {
|
||||
timeStamp = SGTimeStamp::now() - timeStamp;
|
||||
_members[i]->updateExecutionTime(timeStamp.toUSecs());
|
||||
member->updateExecutionTime(timeStamp.toUSecs());
|
||||
}
|
||||
}
|
||||
} // of multiple update loop
|
||||
@ -263,32 +361,32 @@ SGSubsystemGroup::update (double delta_time_sec)
|
||||
void
|
||||
SGSubsystemGroup::reportTiming(void)
|
||||
{
|
||||
for( size_t i = _members.size(); i > 0; i-- )
|
||||
{
|
||||
_members[i-1]->reportTiming();
|
||||
for (auto member : _members) {
|
||||
member->reportTiming();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::suspend ()
|
||||
{
|
||||
for( size_t i = 0; i < _members.size(); i++ )
|
||||
_members[i]->subsystem->suspend();
|
||||
SGSubsystem::suspend();
|
||||
forEach([](SGSubsystem* s) { s->suspend(); });
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::resume ()
|
||||
{
|
||||
for( size_t i = 0; i < _members.size(); i++ )
|
||||
_members[i]->subsystem->resume();
|
||||
forEach([](SGSubsystem* s) { s->resume(); });
|
||||
SGSubsystem::resume();
|
||||
}
|
||||
|
||||
string_list
|
||||
SGSubsystemGroup::member_names() const
|
||||
{
|
||||
string_list result;
|
||||
for( size_t i = 0; i < _members.size(); i++ )
|
||||
result.push_back( _members[i]->name );
|
||||
for (auto member : _members) {
|
||||
result.push_back(member->name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -296,6 +394,9 @@ SGSubsystemGroup::member_names() const
|
||||
bool
|
||||
SGSubsystemGroup::is_suspended () const
|
||||
{
|
||||
// important so suspended groups still count dt for their members
|
||||
// but this does mean we need to be careful when notifying suspend
|
||||
// and resume on groups
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -303,45 +404,112 @@ void
|
||||
SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
|
||||
double min_step_sec)
|
||||
{
|
||||
Member * member = get_member(name, true);
|
||||
if (name.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group without a name");
|
||||
// TODO, make this an exception in the future
|
||||
} else if (name != subsystem->name()) {
|
||||
SG_LOG(SG_GENERAL, SG_DEV_WARN, "adding subsystem to group with name '" << name
|
||||
<< "', but name() returns '" << subsystem->name() << "'");
|
||||
}
|
||||
|
||||
notifyWillChange(subsystem, State::ADD);
|
||||
Member* member = get_member(name, true);
|
||||
member->name = name;
|
||||
member->subsystem = subsystem;
|
||||
member->min_step_sec = min_step_sec;
|
||||
subsystem->set_group(this);
|
||||
notifyDidChange(subsystem, State::ADD);
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::set_subsystem (SGSubsystem * subsystem, double min_step_sec)
|
||||
{
|
||||
set_subsystem(subsystem->name(), subsystem, min_step_sec);
|
||||
}
|
||||
|
||||
SGSubsystem *
|
||||
SGSubsystemGroup::get_subsystem (const string &name)
|
||||
{
|
||||
Member * member = get_member(name);
|
||||
if (member != 0)
|
||||
if (has_subsystem(name)) {
|
||||
const Member* member = get_member(name, false);
|
||||
return member->subsystem;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::remove_subsystem (const string &name)
|
||||
{
|
||||
MemberVec::iterator it = _members.begin();
|
||||
for (; it != _members.end(); ++it) {
|
||||
if (name == (*it)->name) {
|
||||
delete *it;
|
||||
_members.erase(it);
|
||||
return;
|
||||
// recursive search
|
||||
for (const auto& m : _members) {
|
||||
if (m->subsystem->is_group()) {
|
||||
auto s = static_cast<SGSubsystemGroup*>(m->subsystem.ptr())->get_subsystem(name);
|
||||
if (s)
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "remove_subsystem: missing:" << name);
|
||||
return {};
|
||||
}
|
||||
|
||||
bool
|
||||
SGSubsystemGroup::remove_subsystem(const string &name)
|
||||
{
|
||||
// direct membership
|
||||
auto it = std::find_if(_members.begin(), _members.end(), [name](const Member* m)
|
||||
{ return m->name == name; });
|
||||
if (it != _members.end()) {
|
||||
// found it, great
|
||||
const auto sub = (*it)->subsystem;
|
||||
notifyWillChange(sub, State::REMOVE);
|
||||
delete *it;
|
||||
_members.erase(it);
|
||||
notifyDidChange(sub, State::REMOVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// recursive removal
|
||||
for (const auto& m : _members) {
|
||||
if (m->subsystem->is_group()) {
|
||||
auto group = static_cast<SGSubsystemGroup*>(m->subsystem.ptr());
|
||||
bool ok = group->remove_subsystem(name);
|
||||
if (ok) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::notifyWillChange(SGSubsystem* sub, SGSubsystemMgr::State s)
|
||||
{
|
||||
auto manager = get_manager();
|
||||
if (manager) {
|
||||
manager->notifyDelegatesWillChange(sub, s);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SGSubsystemGroup::notifyDidChange(SGSubsystem* sub, SGSubsystemMgr::State s)
|
||||
{
|
||||
auto manager = get_manager();
|
||||
if (manager) {
|
||||
manager->notifyDelegatesDidChange(sub, s);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SGSubsystemGroup::clearSubsystems()
|
||||
{
|
||||
for( MemberVec::iterator it = _members.begin();
|
||||
it != _members.end();
|
||||
++it )
|
||||
delete *it;
|
||||
_members.clear();
|
||||
// reverse order to prevent order dependency problems
|
||||
for (auto it = _members.rbegin(); it != _members.rend(); ++it)
|
||||
{
|
||||
// hold a ref here in case (as is very likely) this is the
|
||||
// final ref to the subsystem, so that we can notify-did-change safely
|
||||
SGSubsystemRef sub = (*it)->subsystem;
|
||||
notifyWillChange(sub, State::REMOVE);
|
||||
delete *it;
|
||||
notifyDidChange(sub, State::REMOVE);
|
||||
}
|
||||
|
||||
_members.clear();
|
||||
}
|
||||
|
||||
void
|
||||
@ -353,25 +521,42 @@ SGSubsystemGroup::set_fixed_update_time(double dt)
|
||||
bool
|
||||
SGSubsystemGroup::has_subsystem (const string &name) const
|
||||
{
|
||||
return (((SGSubsystemGroup *)this)->get_member(name) != 0);
|
||||
auto it = std::find_if(_members.begin(), _members.end(), [name](const Member* m)
|
||||
{ return m->name == name; });
|
||||
return it != _members.end();
|
||||
}
|
||||
|
||||
SGSubsystemGroup::Member *
|
||||
SGSubsystemGroup::get_member (const string &name, bool create)
|
||||
auto SGSubsystemGroup::get_member(const string &name, bool create) -> Member*
|
||||
{
|
||||
for( size_t i = 0; i < _members.size(); i++ ) {
|
||||
if (_members[i]->name == name)
|
||||
return _members[i];
|
||||
}
|
||||
if (create) {
|
||||
Member * member = new Member;
|
||||
_members.push_back(member);
|
||||
return member;
|
||||
} else {
|
||||
return 0;
|
||||
auto it = std::find_if(_members.begin(), _members.end(), [name](const Member* m)
|
||||
{ return m->name == name; });
|
||||
if (it == _members.end()) {
|
||||
if (!create)
|
||||
return nullptr;
|
||||
|
||||
Member* m = new Member;
|
||||
m->name = name;
|
||||
_members.push_back(m);
|
||||
return _members.back();
|
||||
}
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
SGSubsystemMgr* SGSubsystemGroup::get_manager() const
|
||||
{
|
||||
auto parentGroup = get_group();
|
||||
if (parentGroup) {
|
||||
return parentGroup->get_manager();
|
||||
}
|
||||
|
||||
return _manager;
|
||||
}
|
||||
|
||||
void SGSubsystemGroup::set_manager(SGSubsystemMgr *manager)
|
||||
{
|
||||
_manager = manager;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of SGSubsystemGroup::Member
|
||||
@ -431,18 +616,18 @@ SGSubsystemGroup::Member::update (double delta_time_sec)
|
||||
|
||||
|
||||
SGSubsystemMgr::SGSubsystemMgr () :
|
||||
_groups(MAX_GROUPS),
|
||||
_initPosition(0)
|
||||
_groups(MAX_GROUPS)
|
||||
{
|
||||
for (int i = 0; i < MAX_GROUPS; i++)
|
||||
_groups[i].reset(new SGSubsystemGroup);
|
||||
for (int i = 0; i < MAX_GROUPS; i++) {
|
||||
auto g = new SGSubsystemGroup;
|
||||
g->set_manager(this);
|
||||
_groups[i].reset(g);
|
||||
}
|
||||
}
|
||||
|
||||
SGSubsystemMgr::~SGSubsystemMgr ()
|
||||
{
|
||||
// ensure get_subsystem returns NULL from now onwards,
|
||||
// before the SGSubsystemGroup destructors are run
|
||||
_subsystem_map.clear();
|
||||
_destructorActive = true;
|
||||
_groups.clear();
|
||||
}
|
||||
|
||||
@ -538,33 +723,31 @@ void
|
||||
SGSubsystemMgr::add (const char * name, SGSubsystem * subsystem,
|
||||
GroupType group, double min_time_sec)
|
||||
{
|
||||
if (get_subsystem(name) != nullptr)
|
||||
throw sg_exception("Duplicate add of subsystem: " + std::string(name));
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Adding subsystem " << name);
|
||||
get_group(group)->set_subsystem(name, subsystem, min_time_sec);
|
||||
|
||||
if (_subsystem_map.find(name) != _subsystem_map.end()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Adding duplicate subsystem " << name);
|
||||
throw sg_exception("duplicate subsystem:" + std::string(name));
|
||||
}
|
||||
_subsystem_map[name] = subsystem;
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
SGSubsystemMgr::remove(const char* name)
|
||||
{
|
||||
SubsystemDict::iterator s =_subsystem_map.find(name);
|
||||
if (s == _subsystem_map.end()) {
|
||||
return;
|
||||
}
|
||||
// drop the cache
|
||||
_subsystemNameCache.clear();
|
||||
|
||||
_subsystem_map.erase(s);
|
||||
// we don't know which group the subsystem belongs too
|
||||
// fortunately this is a very infrequently used code path, so the slow
|
||||
// search is not a problem
|
||||
for (auto group : _groups) {
|
||||
bool didRemove = group->remove_subsystem(name);
|
||||
if (didRemove) {
|
||||
return true;
|
||||
}
|
||||
} // of groups iteration
|
||||
|
||||
// tedious part - we don't know which group the subsystem belongs too
|
||||
for (int i = 0; i < MAX_GROUPS; i++) {
|
||||
if (_groups[i]->get_subsystem(name) != NULL) {
|
||||
_groups[i]->remove_subsystem(name);
|
||||
break;
|
||||
}
|
||||
} // of groups iteration
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "SGSubsystemMgr::remove: not found: " << name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -577,14 +760,34 @@ SGSubsystemMgr::get_group (GroupType group)
|
||||
SGSubsystem *
|
||||
SGSubsystemMgr::get_subsystem (const string &name) const
|
||||
{
|
||||
SubsystemDict::const_iterator s =_subsystem_map.find(name);
|
||||
if (_destructorActive)
|
||||
return nullptr;
|
||||
|
||||
if (s == _subsystem_map.end())
|
||||
return 0;
|
||||
else
|
||||
auto s =_subsystemNameCache.find(name);
|
||||
if (s != _subsystemNameCache.end()) {
|
||||
// in the cache, excellent
|
||||
return s->second;
|
||||
}
|
||||
|
||||
for (auto g : _groups) {
|
||||
auto sub = g->get_subsystem(name);
|
||||
if (sub) {
|
||||
// insert into the cache
|
||||
_subsystemNameCache[name] = sub;
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SGSubsystem*
|
||||
SGSubsystemMgr::get_subsystem(const std::string &name, const std::string& instanceName) const
|
||||
{
|
||||
return get_subsystem(name + "-" + instanceName);
|
||||
}
|
||||
|
||||
|
||||
/** Trigger the timing callback to report data for all subsystems. */
|
||||
void
|
||||
SGSubsystemMgr::reportTiming()
|
||||
@ -594,4 +797,174 @@ SGSubsystemMgr::reportTiming()
|
||||
} // of groups iteration
|
||||
}
|
||||
|
||||
// anonymous namespace to hold registration informatipno
|
||||
|
||||
namespace {
|
||||
struct RegisteredSubsystemData
|
||||
{
|
||||
RegisteredSubsystemData(const std::string& aName, bool aInstanced,
|
||||
SGSubsystemMgr::SubsystemFactoryFunctor aFunctor,
|
||||
SGSubsystemMgr::GroupType aGroup,
|
||||
double aInterval) :
|
||||
name(aName),
|
||||
instanced(aInstanced),
|
||||
functor(aFunctor),
|
||||
defaultGroup(aGroup),
|
||||
defaultUpdateInterval(aInterval)
|
||||
{
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool instanced = false;
|
||||
SGSubsystemMgr::SubsystemFactoryFunctor functor;
|
||||
SGSubsystemMgr::GroupType defaultGroup;
|
||||
double defaultUpdateInterval = 0.0;
|
||||
|
||||
SGSubsystemMgr::DependencyVec depends;
|
||||
};
|
||||
|
||||
using SybsystemRegistrationVec = std::vector<RegisteredSubsystemData>;
|
||||
|
||||
SybsystemRegistrationVec global_registrations;
|
||||
|
||||
SybsystemRegistrationVec::const_iterator findRegistration(const std::string& name)
|
||||
{
|
||||
auto it = std::find_if(global_registrations.begin(),
|
||||
global_registrations.end(),
|
||||
[name](const RegisteredSubsystemData& d)
|
||||
{ return name == d.name; });
|
||||
return it;
|
||||
}
|
||||
} // of anonymous namespace
|
||||
|
||||
void SGSubsystemMgr::registerSubsystem(const std::string& name,
|
||||
SubsystemFactoryFunctor f,
|
||||
GroupType group,
|
||||
bool instanced,
|
||||
double updateInterval,
|
||||
std::initializer_list<Dependency> deps)
|
||||
{
|
||||
if (findRegistration(name) != global_registrations.end()) {
|
||||
throw sg_exception("duplicate subsystem registration for: " + name);
|
||||
}
|
||||
|
||||
global_registrations.push_back({name, instanced, f, group, updateInterval});
|
||||
if (deps.size() > 0) {
|
||||
global_registrations.back().depends = deps;
|
||||
}
|
||||
}
|
||||
|
||||
auto SGSubsystemMgr::defaultGroupFor(const char* name) -> GroupType
|
||||
{
|
||||
auto it = findRegistration(name);
|
||||
if (it == global_registrations.end()) {
|
||||
throw sg_exception("unknown subsystem registration for: " + std::string(name));
|
||||
}
|
||||
|
||||
return it->defaultGroup;
|
||||
}
|
||||
|
||||
double SGSubsystemMgr::defaultUpdateIntervalFor(const char* name)
|
||||
{
|
||||
auto it = findRegistration(name);
|
||||
if (it == global_registrations.end()) {
|
||||
throw sg_exception("unknown subsystem registration for: " + std::string(name));
|
||||
}
|
||||
|
||||
return it->defaultUpdateInterval;
|
||||
}
|
||||
|
||||
const SGSubsystemMgr::DependencyVec&
|
||||
SGSubsystemMgr::dependsFor(const char* name)
|
||||
{
|
||||
auto it = findRegistration(name);
|
||||
if (it == global_registrations.end()) {
|
||||
throw sg_exception("unknown subsystem registration for: " + std::string(name));
|
||||
}
|
||||
|
||||
return it->depends;
|
||||
}
|
||||
|
||||
SGSubsystemRef
|
||||
SGSubsystemMgr::create(const std::string& name)
|
||||
{
|
||||
auto it = findRegistration(name);
|
||||
if (it == global_registrations.end()) {
|
||||
return {}; // or should this throw with a 'not registered'?
|
||||
}
|
||||
|
||||
if (it->instanced) {
|
||||
throw sg_exception("SGSubsystemMgr::create: using non-instanced mode for instanced subsytem");
|
||||
}
|
||||
|
||||
SGSubsystemRef ref = it->functor();
|
||||
if (!ref) {
|
||||
throw sg_exception("SGSubsystemMgr::create: functor failed to create subsystem implementation: " + name);
|
||||
}
|
||||
|
||||
ref->set_name(name);
|
||||
return ref;
|
||||
}
|
||||
|
||||
SGSubsystemRef
|
||||
SGSubsystemMgr::createInstance(const std::string& name, const std::string& instanceName)
|
||||
{
|
||||
auto it = findRegistration(name);
|
||||
if (it == global_registrations.end()) {
|
||||
return {}; // or should this throw with a 'not registered'?
|
||||
}
|
||||
|
||||
if (!it->instanced) {
|
||||
throw sg_exception("SGSubsystemMgr::create: using instanced mode for non-instanced subsytem");
|
||||
}
|
||||
|
||||
SGSubsystemRef ref = it->functor();
|
||||
if (!ref) {
|
||||
throw sg_exception("SGSubsystemMgr::create: functor failed to create an instsance of " + name);
|
||||
}
|
||||
|
||||
const auto combinedName = name + "-" + instanceName;
|
||||
ref->set_name(combinedName);
|
||||
return ref;
|
||||
}
|
||||
|
||||
void SGSubsystemMgr::Delegate::willChange(SGSubsystem*, State)
|
||||
{
|
||||
}
|
||||
|
||||
void SGSubsystemMgr::Delegate::didChange(SGSubsystem*, State)
|
||||
{
|
||||
}
|
||||
|
||||
void SGSubsystemMgr::addDelegate(Delegate * d)
|
||||
{
|
||||
assert(d);
|
||||
_delegates.push_back(d);
|
||||
}
|
||||
|
||||
void SGSubsystemMgr::removeDelegate(Delegate * d)
|
||||
{
|
||||
assert(d);
|
||||
auto it = std::find(_delegates.begin(), _delegates.end(), d);
|
||||
if (it == _delegates.end()) {
|
||||
SG_LOG(SG_GENERAL, SG_DEV_ALERT, "removeDelegate: unknown delegate");
|
||||
return;
|
||||
}
|
||||
|
||||
_delegates.erase(it);
|
||||
}
|
||||
|
||||
void SGSubsystemMgr::notifyDelegatesWillChange(SGSubsystem* sub, State newState)
|
||||
{
|
||||
std::for_each(_delegates.begin(), _delegates.end(), [sub, newState](Delegate* d)
|
||||
{ d->willChange(sub, newState); });
|
||||
}
|
||||
|
||||
void SGSubsystemMgr::notifyDelegatesDidChange(SGSubsystem* sub, State state)
|
||||
{
|
||||
std::for_each(_delegates.begin(), _delegates.end(), [sub, state](Delegate* d)
|
||||
{ d->didChange(sub, state); });
|
||||
}
|
||||
|
||||
|
||||
// end of subsystem_mgr.cxx
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
@ -47,7 +48,10 @@ public:
|
||||
const SGTimeStamp& getTime() const { return time; }
|
||||
};
|
||||
|
||||
// forward decls
|
||||
class SampleStatistic;
|
||||
class SGSubsystemGroup;
|
||||
class SGSubsystemMgr;
|
||||
|
||||
typedef std::vector<TimingInfo> eventTimeVec;
|
||||
typedef std::vector<TimingInfo>::iterator eventTimeVecIterator;
|
||||
@ -125,7 +129,6 @@ typedef void (*SGSubsystemTimingCb)(void* userData, const std::string& name, Sam
|
||||
class SGSubsystem : public SGReferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@ -272,14 +275,52 @@ public:
|
||||
*/
|
||||
void stamp(const std::string& name);
|
||||
|
||||
protected:
|
||||
std::string name() const
|
||||
{ return _name; }
|
||||
|
||||
bool _suspended;
|
||||
virtual bool is_group() const
|
||||
{ return false; }
|
||||
|
||||
virtual SGSubsystemMgr* get_manager() const;
|
||||
|
||||
/// get the parent group of this subsystem
|
||||
SGSubsystemGroup* get_group() const;
|
||||
|
||||
enum class State {
|
||||
ADD,
|
||||
BIND,
|
||||
INIT,
|
||||
POSTINIT,
|
||||
REINIT,
|
||||
SUSPEND,
|
||||
RESUME,
|
||||
UNBIND,
|
||||
SHUTDOWN,
|
||||
REMOVE
|
||||
};
|
||||
|
||||
/**
|
||||
* debug helper, print a state as a string
|
||||
*/
|
||||
static std::string nameForState(State s);
|
||||
protected:
|
||||
friend class SGSubsystemMgr;
|
||||
friend class SGSubsystemGroup;
|
||||
|
||||
void set_name(const std::string& n);
|
||||
|
||||
void set_group(SGSubsystemGroup* group);
|
||||
|
||||
std::string _name;
|
||||
bool _suspended = false;
|
||||
|
||||
eventTimeVec timingInfo;
|
||||
|
||||
static SGSubsystemTimingCb reportTimingCb;
|
||||
static void* reportTimingUserData;
|
||||
|
||||
private:
|
||||
SGSubsystemGroup* _group = nullptr;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<SGSubsystem> SGSubsystemRef;
|
||||
@ -290,27 +331,29 @@ typedef SGSharedPtr<SGSubsystem> SGSubsystemRef;
|
||||
class SGSubsystemGroup : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
|
||||
SGSubsystemGroup ();
|
||||
virtual ~SGSubsystemGroup ();
|
||||
|
||||
virtual void init();
|
||||
virtual InitStatus incrementalInit ();
|
||||
virtual void postinit ();
|
||||
virtual void reinit ();
|
||||
virtual void shutdown ();
|
||||
virtual void bind ();
|
||||
virtual void unbind ();
|
||||
virtual void update (double delta_time_sec);
|
||||
virtual void suspend ();
|
||||
virtual void resume ();
|
||||
virtual bool is_suspended () const;
|
||||
void init() override;
|
||||
InitStatus incrementalInit () override;
|
||||
void postinit () override;
|
||||
void reinit () override;
|
||||
void shutdown () override;
|
||||
void bind () override;
|
||||
void unbind () override;
|
||||
void update (double delta_time_sec) override;
|
||||
void suspend () override;
|
||||
void resume () override;
|
||||
bool is_suspended () const override;
|
||||
|
||||
virtual void set_subsystem (const std::string &name,
|
||||
SGSubsystem * subsystem,
|
||||
double min_step_sec = 0);
|
||||
|
||||
void set_subsystem (SGSubsystem * subsystem, double min_step_sec = 0);
|
||||
|
||||
virtual SGSubsystem * get_subsystem (const std::string &name);
|
||||
virtual void remove_subsystem (const std::string &name);
|
||||
bool remove_subsystem (const std::string &name);
|
||||
virtual bool has_subsystem (const std::string &name) const;
|
||||
|
||||
/**
|
||||
@ -335,19 +378,37 @@ public:
|
||||
{
|
||||
return dynamic_cast<T*>(get_subsystem(T::subsystemName()));
|
||||
}
|
||||
|
||||
bool is_group() const override
|
||||
{ return true; }
|
||||
|
||||
SGSubsystemMgr* get_manager() const override;
|
||||
private:
|
||||
void forEach(std::function<void(SGSubsystem*)> f);
|
||||
void reverseForEach(std::function<void(SGSubsystem*)> f);
|
||||
|
||||
void notifyWillChange(SGSubsystem* sub, SGSubsystem::State s);
|
||||
void notifyDidChange(SGSubsystem* sub, SGSubsystem::State s);
|
||||
|
||||
friend class SGSubsystemMgr;
|
||||
|
||||
void set_manager(SGSubsystemMgr* manager);
|
||||
|
||||
class Member;
|
||||
Member* get_member (const std::string &name, bool create = false);
|
||||
|
||||
typedef std::vector<Member *> MemberVec;
|
||||
using MemberVec = std::vector<Member*>;
|
||||
MemberVec _members;
|
||||
|
||||
double _fixedUpdateTime;
|
||||
double _updateTimeRemainder;
|
||||
|
||||
/// index of the member we are currently init-ing
|
||||
unsigned int _initPosition;
|
||||
int _initPosition;
|
||||
|
||||
/// back-pointer to the manager, for the root groups. (sub-groups
|
||||
/// will have this as null, and chain via their parent)
|
||||
SGSubsystemMgr* _manager = nullptr;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<SGSubsystemGroup> SGSubsystemGroupRef;
|
||||
@ -377,6 +438,7 @@ public:
|
||||
* Types of subsystem groups.
|
||||
*/
|
||||
enum GroupType {
|
||||
INVALID = -1,
|
||||
INIT = 0,
|
||||
GENERAL,
|
||||
FDM, ///< flight model, autopilot, instruments that run coupled
|
||||
@ -389,17 +451,17 @@ public:
|
||||
SGSubsystemMgr ();
|
||||
virtual ~SGSubsystemMgr ();
|
||||
|
||||
virtual void init ();
|
||||
virtual InitStatus incrementalInit ();
|
||||
virtual void postinit ();
|
||||
virtual void reinit ();
|
||||
virtual void shutdown ();
|
||||
virtual void bind ();
|
||||
virtual void unbind ();
|
||||
virtual void update (double delta_time_sec);
|
||||
virtual void suspend ();
|
||||
virtual void resume ();
|
||||
virtual bool is_suspended () const;
|
||||
void init () override;
|
||||
InitStatus incrementalInit () override;
|
||||
void postinit () override;
|
||||
void reinit () override;
|
||||
void shutdown () override;
|
||||
void bind () override;
|
||||
void unbind () override;
|
||||
void update (double delta_time_sec) override;
|
||||
void suspend () override;
|
||||
void resume () override;
|
||||
bool is_suspended () const override;
|
||||
|
||||
virtual void add (const char * name,
|
||||
SGSubsystem * subsystem,
|
||||
@ -407,14 +469,16 @@ public:
|
||||
double min_time_sec = 0);
|
||||
|
||||
/**
|
||||
* remove a subsystem, and return a pointer to it.
|
||||
* returns NULL if the subsystem was not found.
|
||||
* remove a subsystem, and return true on success
|
||||
* returns false if the subsystem was not found
|
||||
*/
|
||||
virtual void remove(const char* name);
|
||||
bool remove(const char* name);
|
||||
|
||||
virtual SGSubsystemGroup * get_group (GroupType group);
|
||||
|
||||
virtual SGSubsystem* get_subsystem(const std::string &name) const;
|
||||
SGSubsystem* get_subsystem(const std::string &name) const;
|
||||
|
||||
SGSubsystem* get_subsystem(const std::string &name, const std::string& instanceName) const;
|
||||
|
||||
void reportTiming();
|
||||
void setReportTimingCb(void* userData,SGSubsystemTimingCb cb) {reportTimingCb = cb;reportTimingUserData = userData;}
|
||||
@ -425,13 +489,156 @@ public:
|
||||
return dynamic_cast<T*>(get_subsystem(T::subsystemName()));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<SGSubsystemGroupRef> _groups;
|
||||
unsigned int _initPosition;
|
||||
// instanced overloads, for both raw char* and std::string
|
||||
// these concatenate the subsystem type name with the instance name
|
||||
template<class T>
|
||||
T* get_subsystem(const char* instanceName) const
|
||||
{
|
||||
return dynamic_cast<T*>(get_subsystem(T::subsystemName(), instanceName));
|
||||
}
|
||||
|
||||
// non-owning reference
|
||||
typedef std::map<std::string, SGSubsystem*> SubsystemDict;
|
||||
SubsystemDict _subsystem_map;
|
||||
template<class T>
|
||||
T* get_subsystem(const std::string& instanceName) const
|
||||
{
|
||||
return dynamic_cast<T*>(get_subsystem(T::subsystemName(), instanceName));
|
||||
}
|
||||
|
||||
struct Dependency {
|
||||
enum Type {
|
||||
HARD,
|
||||
SOFT,
|
||||
PROPERTY
|
||||
};
|
||||
|
||||
std::string name;
|
||||
Type type;
|
||||
};
|
||||
|
||||
using DependencyVec = std::vector<SGSubsystemMgr::Dependency>;
|
||||
using SubsystemFactoryFunctor = std::function<SGSubsystemRef()>;
|
||||
|
||||
/**
|
||||
* @brief register a subsytem with the manager
|
||||
*
|
||||
*/
|
||||
static void registerSubsystem(const std::string& name,
|
||||
SubsystemFactoryFunctor f,
|
||||
GroupType group,
|
||||
bool isInstanced = false,
|
||||
double updateInterval = 0.0,
|
||||
std::initializer_list<Dependency> deps = {});
|
||||
|
||||
template<class T>
|
||||
class Registrant
|
||||
{
|
||||
public:
|
||||
Registrant(GroupType group = GENERAL, double updateInterval = 0.0,
|
||||
std::initializer_list<Dependency> deps = {})
|
||||
{
|
||||
SubsystemFactoryFunctor factory = [](){ return new T; };
|
||||
SGSubsystemMgr::registerSubsystem(T::subsystemName(),
|
||||
factory, group,
|
||||
false, updateInterval,
|
||||
deps);
|
||||
}
|
||||
|
||||
// could implement a dtor to unregister but not needed at the moment
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class InstancedRegistrant
|
||||
{
|
||||
public:
|
||||
InstancedRegistrant(GroupType group = GENERAL,
|
||||
double updateInterval = 0.0,
|
||||
std::initializer_list<Dependency> deps = {})
|
||||
{
|
||||
SubsystemFactoryFunctor factory = [](){ return new T; };
|
||||
SGSubsystemMgr::registerSubsystem(T::subsystemName(),
|
||||
factory, group,
|
||||
true, updateInterval,
|
||||
deps);
|
||||
}
|
||||
|
||||
// could implement a dtor to unregister but not needed at the moment
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief templated add function, subsystem is deduced automatically
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
SGSharedPtr<T> add(GroupType customGroup = INVALID, double customInterval = 0.0)
|
||||
{
|
||||
auto ref = create(T::subsystemName());
|
||||
|
||||
|
||||
const GroupType group = (customGroup == INVALID) ?
|
||||
defaultGroupFor(T::subsystemName()) : customGroup;
|
||||
const double interval = (customInterval == 0.0) ?
|
||||
defaultUpdateIntervalFor(T::subsystemName()) : customInterval;
|
||||
add(ref->name().c_str(), ref.ptr(), group, interval);
|
||||
return dynamic_cast<T*>(ref.ptr());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief templated creation function, only makes the instance but
|
||||
* doesn't add to the manager or group heirarchy
|
||||
*/
|
||||
template <class T>
|
||||
SGSharedPtr<T> create()
|
||||
{
|
||||
auto ref = create(T::subsystemName());
|
||||
return dynamic_cast<T*>(ref.ptr());
|
||||
}
|
||||
|
||||
SGSubsystemRef create(const std::string& name);
|
||||
|
||||
template <class T>
|
||||
SGSharedPtr<T> createInstance(const std::string& instanceName)
|
||||
{
|
||||
auto ref = createInstance(T::subsystemName(), instanceName);
|
||||
return dynamic_cast<T*>(ref.ptr());
|
||||
}
|
||||
|
||||
SGSubsystemRef createInstance(const std::string& name, const std::string& instanceName);
|
||||
|
||||
static GroupType defaultGroupFor(const char* name);
|
||||
static double defaultUpdateIntervalFor(const char* name);
|
||||
static const DependencyVec& dependsFor(const char* name);
|
||||
|
||||
/**
|
||||
* @brief delegate to recieve notifications when the subsystem
|
||||
* configuration changes. For any event/state change, a before (will)
|
||||
* and after (did) change notifications are sent.
|
||||
*/
|
||||
class Delegate
|
||||
{
|
||||
public:
|
||||
virtual void willChange(SGSubsystem* sub, SGSubsystem::State newState);
|
||||
virtual void didChange(SGSubsystem* sub, SGSubsystem::State currentState);
|
||||
};
|
||||
|
||||
void addDelegate(Delegate * d);
|
||||
void removeDelegate(Delegate * d);
|
||||
private:
|
||||
friend class SGSubsystem;
|
||||
friend class SGSubsystemGroup;
|
||||
|
||||
void notifyDelegatesWillChange(SGSubsystem* sub, State newState);
|
||||
void notifyDelegatesDidChange(SGSubsystem* sub, State statee);
|
||||
|
||||
std::vector<SGSubsystemGroupRef> _groups;
|
||||
unsigned int _initPosition = 0;
|
||||
bool _destructorActive = false;
|
||||
|
||||
// non-owning reference, this is to accelerate lookup
|
||||
// by name which otherwise needs a full walk of the entire tree
|
||||
using SubsystemDict = std::map<std::string, SGSubsystem*>;
|
||||
mutable SubsystemDict _subsystemNameCache;
|
||||
|
||||
using DelegateVec = std::vector<Delegate*>;
|
||||
DelegateVec _delegates;
|
||||
};
|
||||
|
||||
#endif // __SUBSYSTEM_MGR_HXX
|
||||
|
408
simgear/structure/subsystem_test.cxx
Normal file
408
simgear/structure/subsystem_test.cxx
Normal file
@ -0,0 +1,408 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// sample subsystems
|
||||
|
||||
class MySub1 : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
static const char* subsystemName() { return "mysub"; }
|
||||
|
||||
void init() override
|
||||
{
|
||||
wasInited = true;
|
||||
}
|
||||
|
||||
void update(double dt) override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool wasInited = false;
|
||||
};
|
||||
|
||||
class AnotherSub : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
static const char* subsystemName() { return "anothersub"; }
|
||||
|
||||
void init() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void update(double dt) override
|
||||
{
|
||||
lastUpdateTime = dt;
|
||||
}
|
||||
|
||||
double lastUpdateTime = 0.0;
|
||||
};
|
||||
|
||||
class FakeRadioSub : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
static const char* subsystemName() { return "fake-radio"; }
|
||||
|
||||
void init() override
|
||||
{
|
||||
wasInited = true;
|
||||
}
|
||||
|
||||
void update(double dt) override
|
||||
{
|
||||
lastUpdateTime = dt;
|
||||
}
|
||||
|
||||
bool wasInited = false;
|
||||
double lastUpdateTime = 0.0;
|
||||
};
|
||||
|
||||
class InstrumentGroup : public SGSubsystemGroup
|
||||
{
|
||||
public:
|
||||
static const char* subsystemName() { return "instruments"; }
|
||||
|
||||
virtual ~InstrumentGroup()
|
||||
{
|
||||
}
|
||||
|
||||
void init() override
|
||||
{
|
||||
wasInited = true;
|
||||
SGSubsystemGroup::init();
|
||||
}
|
||||
|
||||
void update(double dt) override
|
||||
{
|
||||
lastUpdateTime = dt;
|
||||
SGSubsystemGroup::update(dt);
|
||||
}
|
||||
|
||||
bool wasInited = false;
|
||||
double lastUpdateTime = 0.0;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// sample delegate
|
||||
|
||||
class RecorderDelegate : public SGSubsystemMgr::Delegate
|
||||
{
|
||||
public:
|
||||
void willChange(SGSubsystem* sub, SGSubsystem::State newState) override
|
||||
{
|
||||
events.push_back({sub->name(), false, newState});
|
||||
}
|
||||
void didChange(SGSubsystem* sub, SGSubsystem::State newState) override
|
||||
{
|
||||
events.push_back({sub->name(), true, newState});
|
||||
}
|
||||
|
||||
struct Event {
|
||||
string subsystem;
|
||||
bool didChange;
|
||||
SGSubsystem::State event;
|
||||
|
||||
std::string nameForEvent() const
|
||||
{
|
||||
return subsystem + (didChange ? "-did-" : "-will-") + SGSubsystem::nameForState(event);
|
||||
}
|
||||
};
|
||||
|
||||
using EventVec = std::vector<Event>;
|
||||
EventVec events;
|
||||
|
||||
EventVec::const_iterator findEvent(const std::string& name) const
|
||||
{
|
||||
auto it = std::find_if(events.begin(), events.end(), [name](const Event& ev)
|
||||
{ return ev.nameForEvent() == name; });
|
||||
return it;
|
||||
}
|
||||
|
||||
bool hasEvent(const std::string& name) const
|
||||
{
|
||||
return findEvent(name) != events.end();
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SGSubsystemMgr::Registrant<MySub1> registrant(SGSubsystemMgr::GENERAL);
|
||||
SGSubsystemMgr::Registrant<AnotherSub> registrant2(SGSubsystemMgr::FDM);
|
||||
|
||||
SGSubsystemMgr::Registrant<InstrumentGroup> registrant4(SGSubsystemMgr::FDM);
|
||||
|
||||
SGSubsystemMgr::InstancedRegistrant<FakeRadioSub> registrant3(SGSubsystemMgr::POST_FDM);
|
||||
|
||||
void testRegistrationAndCreation()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
|
||||
|
||||
auto anotherSub = manager->create<AnotherSub>();
|
||||
SG_VERIFY(anotherSub);
|
||||
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName());
|
||||
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub"));
|
||||
|
||||
auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
|
||||
auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
|
||||
|
||||
|
||||
}
|
||||
|
||||
void testAddGetRemove()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
auto anotherSub = manager->add<AnotherSub>();
|
||||
SG_VERIFY(anotherSub);
|
||||
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName());
|
||||
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub"));
|
||||
|
||||
SG_VERIFY(d->hasEvent("anothersub-will-add"));
|
||||
SG_VERIFY(d->hasEvent("anothersub-did-add"));
|
||||
|
||||
auto lookup = manager->get_subsystem<AnotherSub>();
|
||||
SG_CHECK_EQUAL(lookup, anotherSub);
|
||||
|
||||
SG_CHECK_EQUAL(manager->get_subsystem("anothersub"), anotherSub);
|
||||
|
||||
// manual create & add
|
||||
auto mySub = manager->create<MySub1>();
|
||||
manager->add(MySub1::subsystemName(), mySub.ptr(), SGSubsystemMgr::DISPLAY, 0.1234);
|
||||
|
||||
SG_VERIFY(d->hasEvent("mysub-will-add"));
|
||||
SG_VERIFY(d->hasEvent("mysub-did-add"));
|
||||
|
||||
SG_CHECK_EQUAL(manager->get_subsystem<MySub1>(), mySub);
|
||||
|
||||
bool ok = manager->remove(AnotherSub::subsystemName());
|
||||
SG_VERIFY(ok);
|
||||
SG_VERIFY(d->hasEvent("anothersub-will-remove"));
|
||||
SG_VERIFY(d->hasEvent("anothersub-did-remove"));
|
||||
|
||||
SG_VERIFY(manager->get_subsystem<AnotherSub>() == nullptr);
|
||||
|
||||
// lookup after remove
|
||||
SG_CHECK_EQUAL(manager->get_subsystem<MySub1>(), mySub);
|
||||
|
||||
// re-add of removed, and let's test overriding
|
||||
auto another2 = manager->add<AnotherSub>(SGSubsystemMgr::SOUND);
|
||||
SG_CHECK_EQUAL(another2->name(), AnotherSub::subsystemName());
|
||||
|
||||
auto soundGroup = manager->get_group(SGSubsystemMgr::SOUND);
|
||||
SG_CHECK_EQUAL(soundGroup->get_subsystem("anothersub"), another2);
|
||||
|
||||
}
|
||||
|
||||
void testSubGrouping()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
auto anotherSub = manager->add<AnotherSub>();
|
||||
auto instruments = manager->add<InstrumentGroup>();
|
||||
|
||||
auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
|
||||
auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
|
||||
|
||||
SG_CHECK_EQUAL(radio1->name(), std::string("fake-radio-nav1"));
|
||||
SG_CHECK_EQUAL(radio2->name(), std::string("fake-radio-nav2"));
|
||||
|
||||
instruments->set_subsystem(radio1);
|
||||
instruments->set_subsystem(radio2);
|
||||
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav1-did-add"));
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav1-will-add"));
|
||||
|
||||
// lookup of the group should also work
|
||||
SG_CHECK_EQUAL(manager->get_subsystem<InstrumentGroup>(), instruments);
|
||||
|
||||
manager->init();
|
||||
|
||||
SG_VERIFY(instruments->wasInited);
|
||||
SG_VERIFY(radio1->wasInited);
|
||||
SG_VERIFY(radio2->wasInited);
|
||||
|
||||
SG_VERIFY(d->hasEvent("instruments-will-init"));
|
||||
SG_VERIFY(d->hasEvent("instruments-did-init"));
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav1-will-init"));
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav2-did-init"));
|
||||
|
||||
manager->update(0.5);
|
||||
SG_CHECK_EQUAL_EP(0.5, instruments->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(0.5, radio1->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(0.5, radio2->lastUpdateTime);
|
||||
|
||||
SG_CHECK_EQUAL(radio1, instruments->get_subsystem("fake-radio-nav1"));
|
||||
SG_CHECK_EQUAL(radio2, instruments->get_subsystem("fake-radio-nav2"));
|
||||
|
||||
// type-safe lookup of instanced
|
||||
SG_CHECK_EQUAL(radio1, manager->get_subsystem<FakeRadioSub>("nav1"));
|
||||
SG_CHECK_EQUAL(radio2, manager->get_subsystem<FakeRadioSub>("nav2"));
|
||||
|
||||
bool ok = manager->remove("fake-radio-nav2");
|
||||
SG_VERIFY(ok);
|
||||
SG_VERIFY(instruments->get_subsystem("fake-radio-nav2") == nullptr);
|
||||
|
||||
manager->update(1.0);
|
||||
SG_CHECK_EQUAL_EP(1.0, instruments->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(1.0, radio1->lastUpdateTime);
|
||||
|
||||
// should not have been updated
|
||||
SG_CHECK_EQUAL_EP(0.5, radio2->lastUpdateTime);
|
||||
|
||||
manager->unbind();
|
||||
SG_VERIFY(d->hasEvent("instruments-will-unbind"));
|
||||
SG_VERIFY(d->hasEvent("instruments-did-unbind"));
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav1-will-unbind"));
|
||||
|
||||
}
|
||||
|
||||
void testIncrementalInit()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
// place everything into the same group, so incremental init has
|
||||
// some work to do
|
||||
auto mySub = manager->add<MySub1>(SGSubsystemMgr::POST_FDM);
|
||||
auto anotherSub = manager->add<AnotherSub>(SGSubsystemMgr::POST_FDM);
|
||||
auto instruments = manager->add<InstrumentGroup>(SGSubsystemMgr::POST_FDM);
|
||||
|
||||
auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
|
||||
auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
|
||||
instruments->set_subsystem(radio1);
|
||||
instruments->set_subsystem(radio2);
|
||||
|
||||
for ( ; ; ) {
|
||||
auto status = manager->incrementalInit();
|
||||
if (status == SGSubsystemMgr::INIT_DONE)
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto ev : d->events) {
|
||||
std::cerr << "ev:" << ev.nameForEvent() << std::endl;
|
||||
}
|
||||
|
||||
SG_VERIFY(mySub->wasInited);
|
||||
|
||||
SG_VERIFY(d->hasEvent("mysub-will-init"));
|
||||
SG_VERIFY(d->hasEvent("mysub-did-init"));
|
||||
|
||||
SG_VERIFY(d->hasEvent("anothersub-will-init"));
|
||||
SG_VERIFY(d->hasEvent("anothersub-did-init"));
|
||||
|
||||
// SG_VERIFY(d->hasEvent("instruments-will-init"));
|
||||
// SG_VERIFY(d->hasEvent("instruments-did-init"));
|
||||
|
||||
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav1-will-init"));
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav1-did-init"));
|
||||
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav2-will-init"));
|
||||
SG_VERIFY(d->hasEvent("fake-radio-nav2-did-init"));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void testSuspendResume()
|
||||
{
|
||||
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
|
||||
auto d = new RecorderDelegate;
|
||||
manager->addDelegate(d);
|
||||
|
||||
auto anotherSub = manager->add<AnotherSub>();
|
||||
auto instruments = manager->add<InstrumentGroup>();
|
||||
|
||||
auto radio1 = manager->createInstance<FakeRadioSub>("nav1");
|
||||
auto radio2 = manager->createInstance<FakeRadioSub>("nav2");
|
||||
|
||||
instruments->set_subsystem(radio1);
|
||||
instruments->set_subsystem(radio2);
|
||||
|
||||
manager->init();
|
||||
manager->update(1.0);
|
||||
|
||||
SG_CHECK_EQUAL_EP(1.0, anotherSub->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(1.0, instruments->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(1.0, radio1->lastUpdateTime);
|
||||
|
||||
anotherSub->suspend();
|
||||
radio1->resume(); // should be a no-op
|
||||
|
||||
SG_VERIFY(d->hasEvent("anothersub-will-suspend"));
|
||||
SG_VERIFY(d->hasEvent("anothersub-did-suspend"));
|
||||
SG_VERIFY(!d->hasEvent("radio1-will-suspend"));
|
||||
|
||||
manager->update(0.5);
|
||||
|
||||
SG_CHECK_EQUAL_EP(1.0, anotherSub->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(0.5, instruments->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(0.5, radio1->lastUpdateTime);
|
||||
|
||||
// suspend the whole group
|
||||
instruments->suspend();
|
||||
anotherSub->resume();
|
||||
SG_VERIFY(d->hasEvent("anothersub-will-resume"));
|
||||
SG_VERIFY(d->hasEvent("anothersub-did-resume"));
|
||||
|
||||
SG_VERIFY(d->hasEvent("instruments-will-suspend"));
|
||||
SG_VERIFY(d->hasEvent("instruments-did-suspend"));
|
||||
|
||||
manager->update(2.0);
|
||||
|
||||
SG_CHECK_EQUAL_EP(2.5, anotherSub->lastUpdateTime);
|
||||
|
||||
// this is significant, since SGSubsystemGroup::update is still
|
||||
// called in this case
|
||||
SG_CHECK_EQUAL_EP(2.0, instruments->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(0.5, radio1->lastUpdateTime);
|
||||
|
||||
// twiddle the state of a radio while its whole group is suspended
|
||||
// this should not notify!
|
||||
d->events.clear();
|
||||
radio2->suspend();
|
||||
SG_VERIFY(d->events.empty());
|
||||
|
||||
instruments->resume();
|
||||
manager->update(3.0);
|
||||
SG_CHECK_EQUAL_EP(3.0, anotherSub->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(3.0, instruments->lastUpdateTime);
|
||||
|
||||
// should see all the passed time now
|
||||
SG_CHECK_EQUAL_EP(5.0, radio1->lastUpdateTime);
|
||||
SG_CHECK_EQUAL_EP(5.0, radio2->lastUpdateTime);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
testRegistrationAndCreation();
|
||||
testAddGetRemove();
|
||||
testSubGrouping();
|
||||
testIncrementalInit();
|
||||
testSuspendResume();
|
||||
|
||||
cout << __FILE__ << ": All tests passed" << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user