diff --git a/simgear/canvas/elements/CanvasPath.cxx b/simgear/canvas/elements/CanvasPath.cxx
index c23f1e49..5fd8b775 100644
--- a/simgear/canvas/elements/CanvasPath.cxx
+++ b/simgear/canvas/elements/CanvasPath.cxx
@@ -900,8 +900,10 @@ namespace canvas
else if( name == "coord" )
_attributes_dirty |= COORDS;
else if ( name == "svg")
+ {
_hasSVG = true;
_attributes_dirty |= SVG;
+ }
}
//----------------------------------------------------------------------------
diff --git a/simgear/io/HTTPClient.cxx b/simgear/io/HTTPClient.cxx
index eab0258c..fe6fa737 100644
--- a/simgear/io/HTTPClient.cxx
+++ b/simgear/io/HTTPClient.cxx
@@ -242,8 +242,13 @@ void Client::makeRequest(const Request_ptr& r)
if( r->isComplete() )
return;
+ if (r->url().empty()) {
+ r->setFailure(EINVAL, "no URL specified on request");
+ return;
+ }
+
if( r->url().find("://") == std::string::npos ) {
- r->setFailure(EINVAL, "malformed URL");
+ r->setFailure(EINVAL, "malformed URL: '" + r->url() + "'");
return;
}
diff --git a/simgear/package/CatalogTest.cxx b/simgear/package/CatalogTest.cxx
index 6a4446c0..8e752e82 100644
--- a/simgear/package/CatalogTest.cxx
+++ b/simgear/package/CatalogTest.cxx
@@ -92,6 +92,11 @@ public:
path = ss.str();
}
}
+
+ // return zip data for this computed URL
+ if (path.find("/catalogTest1/movies") == 0) {
+ path = "/catalogTest1/movies-data.zip";
+ }
localPath.append(path);
@@ -152,7 +157,7 @@ int parseTest()
SG_CHECK_EQUAL(cat->description(), "First test catalog");
// check the packages too
- SG_CHECK_EQUAL(cat->packages().size(), 4);
+ SG_CHECK_EQUAL(cat->packages().size(), 5);
pkg::PackageRef p1 = cat->packages().front();
SG_CHECK_EQUAL(p1->catalog(), cat.ptr());
@@ -326,7 +331,7 @@ void testAddCatalog(HTTP::Client* cl)
p.append("org.flightgear.test.catalog1");
p.append("catalog.xml");
SG_VERIFY(p.exists());
- SG_CHECK_EQUAL(root->allPackages().size(), 4);
+ SG_CHECK_EQUAL(root->allPackages().size(), 5);
SG_CHECK_EQUAL(root->catalogs().size(), 1);
pkg::PackageRef p1 = root->getPackageById("alpha");
@@ -559,6 +564,43 @@ void testInstallTarPackage(HTTP::Client* cl)
SG_VERIFY(p.exists());
}
+void testInstallArchiveType(HTTP::Client* cl)
+{
+ global_catalogVersion = 0;
+ SGPath rootPath(simgear::Dir::current().path());
+ rootPath.append("pkg_install_archive_type");
+ simgear::Dir pd(rootPath);
+ pd.removeChildren();
+
+ pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
+ // specify a test dir
+ root->setHTTPClient(cl);
+
+ pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
+ waitForUpdateComplete(cl, root);
+
+ pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.movies");
+ SG_CHECK_EQUAL(p1->id(), "movies");
+ pkg::InstallRef ins = p1->install();
+
+ SG_VERIFY(ins->isQueued());
+
+ waitForUpdateComplete(cl, root);
+ SG_VERIFY(p1->isInstalled());
+ SG_VERIFY(p1->existingInstall() == ins);
+
+ // verify on disk state
+ SGPath p(rootPath);
+ p.append("org.flightgear.test.catalog1");
+ p.append("Aircraft");
+ p.append("movies");
+
+ SG_CHECK_EQUAL(p, ins->path());
+
+ p.append("movie-list.json");
+ SG_VERIFY(p.exists());
+}
+
void testDisableDueToVersion(HTTP::Client* cl)
{
global_catalogVersion = 0;
@@ -915,7 +957,9 @@ int main(int argc, char* argv[])
testRefreshCatalog(&cl);
testInstallTarPackage(&cl);
-
+
+ testInstallArchiveType(&cl);
+
testDisableDueToVersion(&cl);
testOfflineMode(&cl);
diff --git a/simgear/package/Install.cxx b/simgear/package/Install.cxx
index 7e22f0f2..78272005 100644
--- a/simgear/package/Install.cxx
+++ b/simgear/package/Install.cxx
@@ -57,6 +57,10 @@ public:
throw sg_exception("no package download URLs");
}
+ if (m_owner->package()->properties()->hasChild("archive-type")) {
+ setArchiveTypeFromExtension(m_owner->package()->properties()->getStringValue("archive-type"));
+ }
+
// TODO randomise order of m_urls
m_extractPath = aOwner->path().dir();
@@ -165,7 +169,12 @@ protected:
// build a path like /path/to/packages/org.some.catalog/Aircraft/extract_xxxx/MyAircraftDir
SGPath extractedPath = m_extractPath;
- extractedPath.append(m_owner->package()->dirName());
+ if (m_owner->package()->properties()->hasChild("archive-path")) {
+ extractedPath.append(m_owner->package()->properties()->getStringValue("archive-path"));
+ } else {
+ extractedPath.append(m_owner->package()->dirName());
+ }
+
// rename it to path/to/packages/org.some.catalog/Aircraft/MyAircraftDir
bool ok = extractedPath.rename(m_owner->path());
@@ -175,6 +184,8 @@ protected:
}
// extract_xxxx directory is now empty, so remove it
+ // (note it might not be empty if the archive contained some other
+ // files, but we delete those in such a case
if (m_extractPath.exists()) {
simgear::Dir(m_extractPath).remove();
}
@@ -196,7 +207,22 @@ protected:
}
private:
-
+ void setArchiveTypeFromExtension(const std::string& ext)
+ {
+ if (ext.empty())
+ return;
+
+ if (ext == "zip") {
+ m_archiveType = ZIP;
+ return;
+ }
+
+ if ((ext == "tar.gz") || (ext == "tgz")) {
+ m_archiveType = TAR_GZ;
+ return;
+ }
+ }
+
void extractCurrentFile(unzFile zip, char* buffer, size_t bufferSize)
{
unz_file_info fileInfo;
@@ -262,14 +288,22 @@ private:
{
const std::string u(url());
const size_t ul(u.length());
- if (u.rfind(".zip") == (ul - 4)) {
- return extractUnzip();
+
+ if (m_archiveType == AUTO_DETECT) {
+ if (u.rfind(".zip") == (ul - 4)) {
+ m_archiveType = ZIP;
+ } else if (u.rfind(".tar.gz") == (ul - 7)) {
+ m_archiveType = TAR_GZ;
+ }
+ // we will fall through to the error case now
}
-
- if (u.rfind(".tar.gz") == (ul - 7)) {
+
+ if (m_archiveType == ZIP) {
+ return extractUnzip();
+ } else if (m_archiveType == TAR_GZ) {
return extractTar();
}
-
+
SG_LOG(SG_IO, SG_WARN, "unsupported archive format:" << u);
return false;
}
@@ -330,7 +364,14 @@ private:
m_owner->installResult(aReason);
}
+ enum ArchiveType {
+ AUTO_DETECT = 0,
+ ZIP,
+ TAR_GZ
+ };
+
InstallRef m_owner;
+ ArchiveType m_archiveType = AUTO_DETECT;
string_list m_urls;
SG_MD5_CTX m_md5;
std::string m_buffer;
diff --git a/simgear/package/Package.hxx b/simgear/package/Package.hxx
index 5f696dcc..ac38f758 100644
--- a/simgear/package/Package.hxx
+++ b/simgear/package/Package.hxx
@@ -99,10 +99,8 @@ public:
/**
* human-readable name - note this is probably not localised,
* although this is not ruled out for the future.
- *
- * Deprecated - please use nameForVariant
*/
- SG_DEPRECATED(std::string name() const);
+ std::string name() const;
/**
* Human readable name of a variant
@@ -112,12 +110,9 @@ public:
std::string nameForVariant(const unsigned int vIndex) const;
/**
- * syntactic sugar to get the localised description
- *
- * Deprecated - please use getLocalisedProp to get the variant-specific
- * description.
+ * syntactic sugar to get the localised description of the main aircraft
*/
- SG_DEPRECATED(std::string description() const);
+ std::string description() const;
/**
* access the raw property data in the package
diff --git a/simgear/package/Root.cxx b/simgear/package/Root.cxx
index 226e632e..d7e06b4f 100644
--- a/simgear/package/Root.cxx
+++ b/simgear/package/Root.cxx
@@ -778,6 +778,11 @@ bool Root::removeCatalogById(const std::string& aId)
void Root::requestThumbnailData(const std::string& aUrl)
{
+ if (aUrl.empty()) {
+ SG_LOG(SG_GENERAL, SG_DEV_WARN, "requestThumbnailData: empty URL requested");
+ return;
+ }
+
auto it = d->thumbnailCache.find(aUrl);
if (it == d->thumbnailCache.end()) {
bool cachedOnDisk = d->checkPersistentCache(aUrl);
diff --git a/simgear/package/catalogTest1/catalog.xml b/simgear/package/catalogTest1/catalog.xml
index 26fb7025..2a46604f 100644
--- a/simgear/package/catalogTest1/catalog.xml
+++ b/simgear/package/catalogTest1/catalog.xml
@@ -177,4 +177,18 @@
360
acf9eb89cf396eb42f8823d9cdf17584
+
+
+
+ movies
+ movies files for test catalog aircraft
+ 10
+ movies
+
+ http://localhost:2000/catalogTest1/movies?wierd=foo;bar=thing
+ zip
+ movies_6789
+ 232
+ e5f89c3f1ed1bdda16174c868f3c7b30
+
diff --git a/simgear/package/catalogTest1/movies-data.zip b/simgear/package/catalogTest1/movies-data.zip
new file mode 100644
index 00000000..695ec1e7
Binary files /dev/null and b/simgear/package/catalogTest1/movies-data.zip differ
diff --git a/simgear/package/catalogTest1/movies_6789/movie-list.json b/simgear/package/catalogTest1/movies_6789/movie-list.json
new file mode 100644
index 00000000..13be6b27
--- /dev/null
+++ b/simgear/package/catalogTest1/movies_6789/movie-list.json
@@ -0,0 +1,3 @@
+{
+ "id": "awesomething"
+}
\ No newline at end of file
diff --git a/simgear/structure/event_mgr.cxx b/simgear/structure/event_mgr.cxx
index d5ffd01e..12e56d5d 100644
--- a/simgear/structure/event_mgr.cxx
+++ b/simgear/structure/event_mgr.cxx
@@ -69,6 +69,9 @@ void SGEventMgr::init()
return;
}
+ // The event manager dtor and ctor are not called on reset, so reset the flag here.
+ _shutdown = false;
+
_inited = true;
}
diff --git a/simgear/structure/subsystem_mgr.cxx b/simgear/structure/subsystem_mgr.cxx
index ac46eeab..80c7ef0a 100644
--- a/simgear/structure/subsystem_mgr.cxx
+++ b/simgear/structure/subsystem_mgr.cxx
@@ -27,11 +27,14 @@
#include "exception.hxx"
#include "subsystem_mgr.hxx"
+#include "commands.hxx"
+#include
#include
#include "SGSmplstat.hxx"
const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
+const char SUBSYSTEM_NAME_SEPARATOR = '.';
using std::string;
using State = SGSubsystem::State;
@@ -136,6 +139,26 @@ void SGSubsystem::set_name(const std::string &n)
_name = n;
}
+std::string SGSubsystem::typeName() const
+{
+ auto pos = _name.find(SUBSYSTEM_NAME_SEPARATOR);
+ if (pos == std::string::npos) {
+ return _name;
+ }
+
+ return _name.substr(0, pos);
+}
+
+std::string SGSubsystem::instanceName() const
+{
+ auto pos = _name.find(SUBSYSTEM_NAME_SEPARATOR);
+ if (pos == std::string::npos) {
+ return {};
+ }
+
+ return _name.substr(pos+1);
+}
+
void SGSubsystem::set_group(SGSubsystemGroup* group)
{
_group = group;
@@ -156,6 +179,7 @@ SGSubsystemMgr* SGSubsystem::get_manager() const
std::string SGSubsystem::nameForState(State s)
{
switch (s) {
+ case State::INVALID: return "invalid";
case State::INIT: return "init";
case State::REINIT: return "reinit";
case State::POSTINIT: return "post-init";
@@ -215,11 +239,13 @@ SGSubsystemGroup::~SGSubsystemGroup ()
void
SGSubsystemGroup::init ()
{
+ assert(_state == State::BIND);
forEach([this](SGSubsystem* s){
this->notifyWillChange(s, State::INIT);
s->init();
this->notifyDidChange(s, State::INIT);
});
+ _state = State::INIT;
}
SGSubsystem::InitStatus
@@ -232,11 +258,13 @@ SGSubsystemGroup::incrementalInit()
// termination test
if (_initPosition >= static_cast(_members.size())) {
+ _state = State::INIT;
return INIT_DONE;
}
if (_initPosition < 0) {
// first call
+ assert(_state == State::BIND);
_initPosition = 0;
notifyWillChange(_members.front()->subsystem, State::INIT);
}
@@ -278,11 +306,13 @@ void SGSubsystemGroup::reverseForEach(std::function f)
void
SGSubsystemGroup::postinit ()
{
+ assert(_state == State::INIT);
forEach([this](SGSubsystem* s){
this->notifyWillChange(s, State::POSTINIT);
s->postinit();
this->notifyDidChange(s, State::POSTINIT);
});
+ _state = State::POSTINIT;
}
void
@@ -303,6 +333,7 @@ SGSubsystemGroup::shutdown ()
s->shutdown();
this->notifyDidChange(s, State::SHUTDOWN);
});
+ _state = State::SHUTDOWN;
_initPosition = -1;
}
@@ -314,6 +345,7 @@ SGSubsystemGroup::bind ()
s->bind();
this->notifyDidChange(s, State::BIND);
});
+ _state = State::BIND;
}
void
@@ -324,6 +356,7 @@ SGSubsystemGroup::unbind ()
s->unbind();
this->notifyDidChange(s, State::UNBIND);
});
+ _state = State::UNBIND;
}
void
@@ -419,6 +452,16 @@ SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
member->min_step_sec = min_step_sec;
subsystem->set_group(this);
notifyDidChange(subsystem, State::ADD);
+
+ if (_state != State::INVALID) {
+ SG_LOG(SG_GENERAL, SG_DEV_WARN, "TODO: implement SGSubsystemGroup transition when adding after init");
+ // transition to the correct state
+ // bind
+
+ // init
+
+ // post-init
+ }
}
void
@@ -456,6 +499,16 @@ SGSubsystemGroup::remove_subsystem(const string &name)
if (it != _members.end()) {
// found it, great
const auto sub = (*it)->subsystem;
+
+ if (_state != State::INVALID) {
+ // transition out correctly
+ SG_LOG(SG_GENERAL, SG_DEV_WARN, "TODO: implement SGSubsystemGroup transition when removing before shutdown");
+
+ // shutdown
+
+ // unbind
+ }
+
notifyWillChange(sub, State::REMOVE);
delete *it;
_members.erase(it);
@@ -562,7 +615,6 @@ void SGSubsystemGroup::set_manager(SGSubsystemMgr *manager)
// Implementation of SGSubsystemGroup::Member
////////////////////////////////////////////////////////////////////////
-
SGSubsystemGroup::Member::Member ()
: name(""),
subsystem(0),
@@ -614,10 +666,29 @@ SGSubsystemGroup::Member::update (double delta_time_sec)
// Implementation of SGSubsystemMgr.
////////////////////////////////////////////////////////////////////////
+namespace {
+ SGSubsystemMgr* global_defaultSubsystemManager = nullptr;
+
+ void registerSubsystemCommands();
+
+} // end of anonymous namespace
SGSubsystemMgr::SGSubsystemMgr () :
_groups(MAX_GROUPS)
{
+ if (global_defaultSubsystemManager == nullptr) {
+ // register ourselves as the default
+ global_defaultSubsystemManager = this;
+ }
+
+ // disabled until subsystemfactory is removed from FlightGear
+#if 0
+ auto commandManager = SGCommandMgr::instance();
+ if (commandManager && !commandManager->getCommand("add-subsystem")) {
+ registerSubsystemCommands();
+ }
+#endif
+
for (int i = 0; i < MAX_GROUPS; i++) {
auto g = new SGSubsystemGroup;
g->set_manager(this);
@@ -628,7 +699,24 @@ SGSubsystemMgr::SGSubsystemMgr () :
SGSubsystemMgr::~SGSubsystemMgr ()
{
_destructorActive = true;
- _groups.clear();
+ _groups.clear();
+
+ // if we were the global one, null out the pointer so it doesn't dangle.
+ // don't do anything clever to track multiple subsystems for now; if we
+ // a smarter scheme, let's wait until it's clearer what that might be.
+ if (global_defaultSubsystemManager == this) {
+ global_defaultSubsystemManager = nullptr;
+ }
+}
+
+SGSubsystemMgr* SGSubsystemMgr::getManager(const std::string& id)
+{
+ if (id.empty()) {
+ return global_defaultSubsystemManager;
+ }
+
+ // remove me when if/when we supprot multiple subsystem instances
+ throw sg_exception("multiple subsystem instances not supported yet");
}
void
@@ -784,7 +872,7 @@ SGSubsystemMgr::get_subsystem (const string &name) const
SGSubsystem*
SGSubsystemMgr::get_subsystem(const std::string &name, const std::string& instanceName) const
{
- return get_subsystem(name + "-" + instanceName);
+ return get_subsystem(name + SUBSYSTEM_NAME_SEPARATOR + instanceName);
}
@@ -825,10 +913,15 @@ namespace {
using SybsystemRegistrationVec = std::vector;
- SybsystemRegistrationVec global_registrations;
-
+ SybsystemRegistrationVec &getGlobalRegistrations()
+ {
+ static SybsystemRegistrationVec global_registrations;
+ return global_registrations;
+ }
+
SybsystemRegistrationVec::const_iterator findRegistration(const std::string& name)
{
+ auto &global_registrations = getGlobalRegistrations();
auto it = std::find_if(global_registrations.begin(),
global_registrations.end(),
[name](const RegisteredSubsystemData& d)
@@ -844,6 +937,7 @@ void SGSubsystemMgr::registerSubsystem(const std::string& name,
double updateInterval,
std::initializer_list deps)
{
+ auto &global_registrations = getGlobalRegistrations();
if (findRegistration(name) != global_registrations.end()) {
throw sg_exception("duplicate subsystem registration for: " + name);
}
@@ -857,6 +951,7 @@ void SGSubsystemMgr::registerSubsystem(const std::string& name,
auto SGSubsystemMgr::defaultGroupFor(const char* name) -> GroupType
{
auto it = findRegistration(name);
+ auto &global_registrations = getGlobalRegistrations();
if (it == global_registrations.end()) {
throw sg_exception("unknown subsystem registration for: " + std::string(name));
}
@@ -867,6 +962,7 @@ auto SGSubsystemMgr::defaultGroupFor(const char* name) -> GroupType
double SGSubsystemMgr::defaultUpdateIntervalFor(const char* name)
{
auto it = findRegistration(name);
+ auto &global_registrations = getGlobalRegistrations();
if (it == global_registrations.end()) {
throw sg_exception("unknown subsystem registration for: " + std::string(name));
}
@@ -878,6 +974,7 @@ const SGSubsystemMgr::DependencyVec&
SGSubsystemMgr::dependsFor(const char* name)
{
auto it = findRegistration(name);
+ auto &global_registrations = getGlobalRegistrations();
if (it == global_registrations.end()) {
throw sg_exception("unknown subsystem registration for: " + std::string(name));
}
@@ -889,6 +986,7 @@ SGSubsystemRef
SGSubsystemMgr::create(const std::string& name)
{
auto it = findRegistration(name);
+ auto &global_registrations = getGlobalRegistrations();
if (it == global_registrations.end()) {
return {}; // or should this throw with a 'not registered'?
}
@@ -910,6 +1008,7 @@ SGSubsystemRef
SGSubsystemMgr::createInstance(const std::string& name, const std::string& instanceName)
{
auto it = findRegistration(name);
+ auto &global_registrations = getGlobalRegistrations();
if (it == global_registrations.end()) {
return {}; // or should this throw with a 'not registered'?
}
@@ -923,7 +1022,7 @@ SGSubsystemMgr::createInstance(const std::string& name, const std::string& insta
throw sg_exception("SGSubsystemMgr::create: functor failed to create an instsance of " + name);
}
- const auto combinedName = name + "-" + instanceName;
+ const auto combinedName = name + SUBSYSTEM_NAME_SEPARATOR + instanceName;
ref->set_name(combinedName);
return ref;
}
@@ -966,5 +1065,218 @@ void SGSubsystemMgr::notifyDelegatesDidChange(SGSubsystem* sub, State state)
{ d->didChange(sub, state); });
}
+void SGSubsystemMgr::set_root_node(SGPropertyNode_ptr node)
+{
+ _rootNode = node;
+}
+
+SGPropertyNode_ptr
+SGSubsystemMgr::root_node() const
+{
+ return _rootNode;
+}
+
+namespace {
+
+ bool
+ do_check_subsystem_running(const SGPropertyNode * arg, SGPropertyNode * root)
+ {
+ auto manager = SGSubsystemMgr::getManager({});
+ return (manager->get_subsystem(arg->getStringValue("name")) != nullptr);
+ }
+
+
+ SGSubsystemMgr::GroupType mapGroupNameToType(const std::string& s)
+ {
+ if (s == "init") return SGSubsystemMgr::INIT;
+ if (s == "general") return SGSubsystemMgr::GENERAL;
+ if (s == "fdm") return SGSubsystemMgr::FDM;
+ if (s == "post-fdm") return SGSubsystemMgr::POST_FDM;
+ if (s == "display") return SGSubsystemMgr::DISPLAY;
+ if (s == "sound") return SGSubsystemMgr::SOUND;
+
+ SG_LOG(SG_GENERAL, SG_ALERT, "unrecognized subsystem group:" << s);
+ return SGSubsystemMgr::GENERAL;
+ }
+
+ bool
+ do_add_subsystem (const SGPropertyNode * arg, SGPropertyNode * root)
+ {
+ auto manager = SGSubsystemMgr::getManager({});
+ std::string subsystem = arg->getStringValue("subsystem");
+
+ // allow override of the name but defaultt o the subsystem name
+ std::string name = arg->getStringValue("name");
+ std::string instanceName = arg->getStringValue("instance");
+
+ if (name.empty()) {
+ // default name to subsystem name, but before we parse any instance name
+ name = subsystem;
+ }
+
+ auto separatorPos = subsystem.find(SUBSYSTEM_NAME_SEPARATOR);
+ if (separatorPos != std::string::npos) {
+ if (!instanceName.empty()) {
+ SG_LOG(SG_GENERAL, SG_WARN, "Specified a composite subsystem name and an instance name, please do one or the other: "
+ << instanceName << " and " << subsystem);
+ return false;
+ }
+
+ instanceName = subsystem.substr(separatorPos + 1);
+ subsystem = subsystem.substr(0, separatorPos);
+ }
+
+ SGSubsystem* sub = nullptr;
+ if (!instanceName.empty()) {
+ sub = manager->createInstance(subsystem, instanceName);
+ } else {
+ sub = manager->create(subsystem);
+ }
+
+ if (!sub)
+ return false;
+
+ std::string groupArg = arg->getStringValue("group");
+ SGSubsystemMgr::GroupType group = SGSubsystemMgr::GENERAL;
+ if (!groupArg.empty()) {
+ group = mapGroupNameToType(groupArg);
+ }
+
+ double minTime = arg->getDoubleValue("min-time-sec", 0.0);
+
+ const auto combinedName = subsystem + SUBSYSTEM_NAME_SEPARATOR + instanceName;
+ manager->add(combinedName.c_str(),
+ sub,
+ group,
+ minTime);
+
+ if (arg->getBoolValue("do-bind-init", false)) {
+ sub->bind();
+ sub->init();
+ }
+
+ return true;
+ }
+
+ bool do_remove_subsystem(const SGPropertyNode * arg, SGPropertyNode * root)
+ {
+ auto manager = SGSubsystemMgr::getManager({});
+ std::string name = arg->getStringValue("subsystem");
+
+ SGSubsystem* instance = manager->get_subsystem(name);
+ if (!instance) {
+ SG_LOG(SG_GENERAL, SG_ALERT, "do_remove_subsystem: unknown subsytem:" << name);
+ return false;
+ }
+
+ // is it safe to always call these? let's assume so!
+ instance->shutdown();
+ instance->unbind();
+
+ // unplug from the manager (this also deletes the instance!)
+ manager->remove(name.c_str());
+ return true;
+ }
+
+ /**
+ * Built-in command: reinitialize one or more subsystems.
+ *
+ * subsystem[*]: the name(s) of the subsystem(s) to reinitialize; if
+ * none is specified, reinitialize all of them.
+ */
+ bool do_reinit (const SGPropertyNode * arg, SGPropertyNode * root)
+ {
+ bool result = true;
+ auto manager = SGSubsystemMgr::getManager({});
+
+ auto subsystems = arg->getChildren("subsystem");
+ if (subsystems.empty()) {
+ SG_LOG(SG_GENERAL, SG_INFO, "do_reinit: reinit-ing subsystem manager");
+ manager->reinit();
+ } else {
+ for (auto sub : subsystems) {
+ const char * name = sub->getStringValue();
+ SGSubsystem* subsystem = manager->get_subsystem(name);
+ if (subsystem == nullptr) {
+ result = false;
+ SG_LOG( SG_GENERAL, SG_ALERT, "Subsystem " << name << " not found" );
+ } else {
+ subsystem->reinit();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Built-in command: suspend one or more subsystems.
+ *
+ * subsystem[*] - the name(s) of the subsystem(s) to suspend.
+ */
+ bool do_suspend (const SGPropertyNode * arg, SGPropertyNode * root)
+ {
+ bool result = true;
+ auto manager = SGSubsystemMgr::getManager({});
+
+ for (auto subNode : arg->getChildren("subsystem")) {
+ const char * name = subNode->getStringValue();
+ SGSubsystem * subsystem = manager->get_subsystem(name);
+ if (subsystem == nullptr) {
+ result = false;
+ SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << " not found");
+ } else {
+ subsystem->suspend();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Built-in command: suspend one or more subsystems.
+ *
+ * subsystem[*] - the name(s) of the subsystem(s) to suspend.
+ */
+ bool do_resume (const SGPropertyNode * arg, SGPropertyNode * root)
+ {
+ bool result = true;
+ auto manager = SGSubsystemMgr::getManager({});
+
+ for (auto subNode : arg->getChildren("subsystem")) {
+ const char * name = subNode->getStringValue();
+ SGSubsystem * subsystem = manager->get_subsystem(name);
+ if (subsystem == nullptr) {
+ result = false;
+ SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << " not found");
+ } else {
+ subsystem->resume();
+ }
+ }
+ return result;
+ }
+
+ struct CommandDef {
+ const char * name;
+ SGCommandMgr::command_t command;
+ };
+
+ CommandDef built_ins[] = {
+ { "add-subsystem", do_add_subsystem },
+ { "remove-subsystem", do_remove_subsystem },
+ { "subsystem-running", do_check_subsystem_running },
+ { "reinit", do_reinit },
+ { "suspend", do_suspend },
+ { "resume", do_resume },
+ };
+
+ void registerSubsystemCommands()
+ {
+ auto commandManager = SGCommandMgr::instance();
+ for (auto b : built_ins) {
+ commandManager->addCommand(b.name, b.command);
+ }
+ }
+
+} // anonymous namespace implementing subsystem commands
// end of subsystem_mgr.cxx
diff --git a/simgear/structure/subsystem_mgr.hxx b/simgear/structure/subsystem_mgr.hxx
index 26674a02..b3cd71f9 100644
--- a/simgear/structure/subsystem_mgr.hxx
+++ b/simgear/structure/subsystem_mgr.hxx
@@ -33,6 +33,7 @@
#include
#include
#include
+#include
class TimingInfo
{
@@ -275,9 +276,23 @@ public:
*/
void stamp(const std::string& name);
+ /**
+ * composite name for this subsystem (type name & optional instance name)
+ */
std::string name() const
{ return _name; }
+ /**
+ * @brief the type (class)-specific part of the subsystem name.
+ */
+ std::string typeName() const;
+
+ /**
+ * @brief the instance part of the subsystem name. Empty if this
+ * subsystem is not instanced
+ */
+ std::string instanceName() const;
+
virtual bool is_group() const
{ return false; }
@@ -287,7 +302,8 @@ public:
SGSubsystemGroup* get_group() const;
enum class State {
- ADD,
+ INVALID = -1,
+ ADD = 0,
BIND,
INIT,
POSTINIT,
@@ -311,7 +327,11 @@ protected:
void set_group(SGSubsystemGroup* group);
- std::string _name;
+ /// composite name for the subsystem (type name and instance name if this
+ /// is an instanced subsystem. (Since this member was originally defined as
+ /// protected, not private, we can't rename it easily)
+ std::string _name;
+
bool _suspended = false;
eventTimeVec timingInfo;
@@ -400,6 +420,9 @@ private:
using MemberVec = std::vector;
MemberVec _members;
+ // track the state of this group, so we can transition added/removed
+ // members correctly
+ SGSubsystem::State _state = SGSubsystem::State::INVALID;
double _fixedUpdateTime;
double _updateTimeRemainder;
@@ -433,6 +456,8 @@ typedef SGSharedPtr SGSubsystemGroupRef;
class SGSubsystemMgr : public SGSubsystem
{
public:
+ SGSubsystemMgr(const SGSubsystemMgr&) = delete;
+ SGSubsystemMgr& operator=(const SGSubsystemMgr&) = delete;
/**
* Types of subsystem groups.
@@ -483,6 +508,18 @@ public:
void reportTiming();
void setReportTimingCb(void* userData,SGSubsystemTimingCb cb) {reportTimingCb = cb;reportTimingUserData = userData;}
+ /**
+ * @brief set the root property node for this subsystem manager
+ * subsystems can retrieve this value during init/bind (or at any time)
+ * to base their own properties trees off of.
+ */
+ void set_root_node(SGPropertyNode_ptr node);
+
+ /**
+ * @brief retrieve the root property node for this subsystem manager
+ */
+ SGPropertyNode_ptr root_node() const;
+
template
T* get_subsystem() const
{
@@ -621,6 +658,13 @@ public:
void addDelegate(Delegate * d);
void removeDelegate(Delegate * d);
+
+ /**
+ * @brief return a particular subsystem manager by name. Passing an
+ * empty string retrived the default/global subsystem manager, assuming it
+ * has been created.
+ */
+ static SGSubsystemMgr* getManager(const std::string& id);
private:
friend class SGSubsystem;
friend class SGSubsystemGroup;
@@ -631,6 +675,7 @@ private:
std::vector _groups;
unsigned int _initPosition = 0;
bool _destructorActive = false;
+ SGPropertyNode_ptr _rootNode;
// non-owning reference, this is to accelerate lookup
// by name which otherwise needs a full walk of the entire tree
diff --git a/simgear/structure/subsystem_test.cxx b/simgear/structure/subsystem_test.cxx
index cb07e902..dfc816d0 100644
--- a/simgear/structure/subsystem_test.cxx
+++ b/simgear/structure/subsystem_test.cxx
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
using std::string;
using std::cout;
@@ -26,6 +27,13 @@ public:
wasInited = true;
}
+ void bind() override
+ {
+ auto node = get_manager()->root_node();
+ if (node)
+ node->setIntValue("mysub/foo", 42);
+ }
+
void update(double dt) override
{
@@ -44,6 +52,13 @@ public:
}
+ void bind() override
+ {
+ auto node = get_manager()->root_node();
+ if (node)
+ node->setIntValue("anothersub/bar", 172);
+ }
+
void update(double dt) override
{
lastUpdateTime = dt;
@@ -155,7 +170,9 @@ void testRegistrationAndCreation()
SG_VERIFY(anotherSub);
SG_CHECK_EQUAL(anotherSub->name(), AnotherSub::subsystemName());
SG_CHECK_EQUAL(anotherSub->name(), std::string("anothersub"));
-
+ SG_CHECK_EQUAL(anotherSub->typeName(), std::string("anothersub"));
+ SG_CHECK_EQUAL(anotherSub->instanceName(), std::string());
+
auto radio1 = manager->createInstance("nav1");
auto radio2 = manager->createInstance("nav2");
@@ -221,18 +238,21 @@ void testSubGrouping()
auto radio1 = manager->createInstance("nav1");
auto radio2 = manager->createInstance("nav2");
- SG_CHECK_EQUAL(radio1->name(), std::string("fake-radio-nav1"));
- SG_CHECK_EQUAL(radio2->name(), std::string("fake-radio-nav2"));
-
+ SG_CHECK_EQUAL(radio1->name(), std::string("fake-radio.nav1"));
+ SG_CHECK_EQUAL(radio2->name(), std::string("fake-radio.nav2"));
+ SG_CHECK_EQUAL(radio1->typeName(), std::string("fake-radio"));
+ SG_CHECK_EQUAL(radio2->instanceName(), std::string("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"));
+ 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(), instruments);
+ manager->bind();
manager->init();
SG_VERIFY(instruments->wasInited);
@@ -241,24 +261,27 @@ void testSubGrouping()
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"));
+ 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"));
+ SG_CHECK_EQUAL(0, instruments->get_subsystem("fake-radio"));
+
+
+ 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("nav1"));
SG_CHECK_EQUAL(radio2, manager->get_subsystem("nav2"));
- bool ok = manager->remove("fake-radio-nav2");
+ bool ok = manager->remove("fake-radio.nav2");
SG_VERIFY(ok);
- SG_VERIFY(instruments->get_subsystem("fake-radio-nav2") == nullptr);
+ SG_VERIFY(instruments->get_subsystem("fake-radio.nav2") == nullptr);
manager->update(1.0);
SG_CHECK_EQUAL_EP(1.0, instruments->lastUpdateTime);
@@ -270,7 +293,7 @@ void testSubGrouping()
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"));
+ SG_VERIFY(d->hasEvent("fake-radio.nav1-will-unbind"));
}
@@ -291,16 +314,13 @@ void testIncrementalInit()
instruments->set_subsystem(radio1);
instruments->set_subsystem(radio2);
+ manager->bind();
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"));
@@ -313,11 +333,11 @@ void testIncrementalInit()
// 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.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"));
+ SG_VERIFY(d->hasEvent("fake-radio.nav2-will-init"));
+ SG_VERIFY(d->hasEvent("fake-radio.nav2-did-init"));
@@ -338,6 +358,7 @@ void testSuspendResume()
instruments->set_subsystem(radio1);
instruments->set_subsystem(radio2);
+ manager->bind();
manager->init();
manager->update(1.0);
@@ -350,7 +371,7 @@ void testSuspendResume()
SG_VERIFY(d->hasEvent("anothersub-will-suspend"));
SG_VERIFY(d->hasEvent("anothersub-did-suspend"));
- SG_VERIFY(!d->hasEvent("radio1-will-suspend"));
+ SG_VERIFY(!d->hasEvent("fake-radio.nav1-will-suspend"));
manager->update(0.5);
@@ -392,6 +413,32 @@ void testSuspendResume()
SG_CHECK_EQUAL_EP(5.0, radio2->lastUpdateTime);
}
+void testPropertyRoot()
+{
+ SGSharedPtr manager = new SGSubsystemMgr;
+ SGPropertyNode_ptr props(new SGPropertyNode);
+ manager->set_root_node(props);
+
+ auto d = new RecorderDelegate;
+ manager->addDelegate(d);
+
+ manager->add();
+ auto anotherSub = manager->add();
+ auto instruments = manager->add();
+
+ auto radio1 = manager->createInstance("nav1");
+ auto radio2 = manager->createInstance("nav2");
+
+ instruments->set_subsystem(radio1);
+ instruments->set_subsystem(radio2);
+
+ manager->bind();
+ manager->init();
+
+ SG_CHECK_EQUAL(props->getIntValue("mysub/foo"), 42);
+ SG_CHECK_EQUAL(props->getIntValue("anothersub/bar"), 172);
+}
+
///////////////////////////////////////////////////////////////////////////////
@@ -402,6 +449,7 @@ int main(int argc, char* argv[])
testSubGrouping();
testIncrementalInit();
testSuspendResume();
+ testPropertyRoot();
cout << __FILE__ << ": All tests passed" << endl;
return EXIT_SUCCESS;
diff --git a/simgear/timing/sg_time.cxx b/simgear/timing/sg_time.cxx
index 2282db93..83b22aec 100644
--- a/simgear/timing/sg_time.cxx
+++ b/simgear/timing/sg_time.cxx
@@ -72,10 +72,14 @@ void SGTime::init( const SGGeod& location, const SGPath& root, time_t init_time
cur_time = time(NULL);
}
+ char* gmt = asctime(gmtime(&cur_time));
+ char* local = asctime(localtime(&cur_time));
+ gmt[strlen(gmt) - 1] = '\0';
+ local[strlen(local) - 1] = '\0';
SG_LOG( SG_EVENT, SG_DEBUG,
- "Current greenwich mean time = " << asctime(gmtime(&cur_time)));
+ "Current greenwich mean time = " << gmt);
SG_LOG( SG_EVENT, SG_DEBUG,
- "Current local time = " << asctime(localtime(&cur_time)));
+ "Current local time = " << local);
if ( !root.isNull()) {
if (!static_tzContainer.get()) {
diff --git a/version b/version
index 26b1d26a..ddab0701 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-2018.2.0
+2018.3.0