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