diff --git a/resources/shared/bootstrap/bootstrap.json b/resources/shared/bootstrap/bootstrap.json index 7a32758e8..30d6b6674 100644 --- a/resources/shared/bootstrap/bootstrap.json +++ b/resources/shared/bootstrap/bootstrap.json @@ -5,6 +5,17 @@ "url": "https://vatsim-germany.org:50443/mapping/public" }, "development": false, + "fsdTestServers": { + "containerbase": [ + ] + }, + "newsUrls": { + "containerbase": [ + { + "url": "http://swift-project.org/" + } + ] + }, "sharedUrls": { "containerbase": [ { @@ -15,7 +26,7 @@ } ] }, - "timestampMSecsSinceEpoch": 0, + "timestampMSecsSinceEpoch": 1457055734000, "vatsimBookingsUrl": { "url": "http://vatbook.euroutepro.com/xml2.php" }, @@ -26,14 +37,19 @@ } ] }, - "vatsimMetarsUrl": { - "url": "http://metar.vatsim.net/metar.php" - }, - "newsUrls": { + "vatsimMetarsUrls": { "containerbase": [ { - "url": "http://swift-project.org/" + "url": "http://metar.vatsim.net/metar.php" } ] - } -} \ No newline at end of file + }, + "vatsimStatusFileUrls": { + "containerbase": [ + { + "url": "https://status.vatsim.net" + } + ] + }, + "wasLoaded": true +} diff --git a/src/blackcore/application.cpp b/src/blackcore/application.cpp index c504f3e08..58348e7db 100644 --- a/src/blackcore/application.cpp +++ b/src/blackcore/application.cpp @@ -471,7 +471,7 @@ namespace BlackCore this->addParserOption(this->m_cmdDevelopment); this->m_cmdSharedDir = QCommandLineOption({ "shared", "shareddir" }, - QCoreApplication::translate("application", "Local shred directory."), + QCoreApplication::translate("application", "Local shared directory."), "shared"); this->addParserOption(this->m_cmdSharedDir); } @@ -769,4 +769,37 @@ namespace BlackCore if (!supportsContexts()) { return nullptr; } return this->m_coreFacade->getIContextSimulator(); } + + // --------------------------------------------------------------------------------- + // Setup + // --------------------------------------------------------------------------------- + + CUrlList CApplication::getVatsimMetarUrls() const + { + if (this->m_webDataServices) + { + const CUrlList urls(this->m_webDataServices->getVatsimMetarUrls()); + if (!urls.empty()) { return urls; } + } + if (this->m_setupReader) + { + return this->m_setupReader->getSetup().vatsimMetarsUrls(); + } + return CUrlList(); + } + + CUrlList CApplication::getVatsimDataFileUrls() const + { + if (this->m_webDataServices) + { + const CUrlList urls(this->m_webDataServices->getVatsimDataFileUrls()); + if (!urls.empty()) { return urls; } + } + if (this->m_setupReader) + { + return this->m_setupReader->getSetup().vatsimDataFileUrls(); + } + return CUrlList(); + } + } // ns diff --git a/src/blackcore/application.h b/src/blackcore/application.h index e261e2cab..249b9bf8f 100644 --- a/src/blackcore/application.h +++ b/src/blackcore/application.h @@ -49,6 +49,8 @@ namespace BlackCore * - The core facade (aka core runtime) is now part of the application. It can be started via cmd line arguments. * - Settings are loaded * - Setup is loaded (load the so called bootsrap file) to find servers and other resources + * - Update information (new swift versions etc.) are loaded + * - If applicable VATSIM status data (where are the VATSIM files?) are loaded * - An end of lifetime can be specified, aka time bombing * * \sa BlackGui::CGuiApplication for the GUI version of application @@ -213,6 +215,16 @@ namespace BlackCore IContextSimulator *getIContextSimulator(); //! @} + // ----------------------- direct access to some setup data --------------------------------- + + //! Consolidated version of METAR URLs, either from CGlobalSetup or CVatsimSetup + //! \threadsafe + BlackMisc::Network::CUrlList getVatsimMetarUrls() const; + + //! Consolidated version of data file URLs, either from CGlobalSetup or CVatsimSetup + //! \threadsafe + BlackMisc::Network::CUrlList getVatsimDataFileUrls() const; + public slots: //! Graceful shutdown virtual void gracefulShutdown(); diff --git a/src/blackcore/data/globalsetup.cpp b/src/blackcore/data/globalsetup.cpp index 0ecc633f4..b55dd3f4f 100644 --- a/src/blackcore/data/globalsetup.cpp +++ b/src/blackcore/data/globalsetup.cpp @@ -25,8 +25,9 @@ namespace BlackCore ITimestampBased(0), m_dbRootDirectoryUrl("http://ubuntu12/swiftdatastore/public"), m_vatsimBookingsUrl("http://vatbook.euroutepro.com/xml2.php"), - m_vatsimMetarsUrl("http://metar.vatsim.net/metar.php"), - m_vatsimDataFileUrls(QStringList( { "http://info.vroute.net/vatsim-data.txt" })), + m_vatsimMetarsUrls( {"http://metar.vatsim.net/metar.php"}), + m_vatsimStatusFileUrls({ "https://status.vatsim.net" }), + m_vatsimDataFileUrls({ "http://info.vroute.net/vatsim-data.txt" }), m_sharedUrls(CProject::swiftTeamDefaultServers()), m_newsUrls(QStringList({ "http://swift-project.org/" })) { } @@ -72,11 +73,6 @@ namespace BlackCore return this->isDevelopment() == otherSetup.isDevelopment(); } - CUrl CGlobalSetup::vatsimMetarsUrl() const - { - return this->m_vatsimMetarsUrl.withAppendedQuery("id=all"); - } - CUrlList CGlobalSetup::bootstrapFileUrls() const { CUrlList urls(m_sharedUrls); @@ -179,7 +175,7 @@ namespace BlackCore s.append(vatsimBookingsUrl().toQString(i18n)); s.append(separator); s.append("VATSIM METARs: "); - s.append(vatsimMetarsUrl().toQString(i18n)); + s.append(vatsimMetarsUrls().toQString(i18n)); s.append(separator); s.append("VATSIM data file: "); s.append(vatsimDataFileUrls().toQString(i18n)); @@ -206,12 +202,14 @@ namespace BlackCore return CVariant::fromValue(this->m_dbHttpsPort); case IndexDbLoginService: return CVariant::fromValue(this->dbLoginServiceUrl()); + case IndexVatsimStatus: + return CVariant::fromValue(this->m_vatsimStatusFileUrls); case IndexVatsimData: return CVariant::fromValue(this->m_vatsimDataFileUrls); case IndexVatsimBookings: return CVariant::fromValue(this->m_vatsimDataFileUrls); case IndexVatsimMetars: - return CVariant::fromValue(this->m_vatsimMetarsUrl); + return CVariant::fromValue(this->m_vatsimMetarsUrls); case IndexUpdateInfo: return CVariant::fromValue(this->updateInfoFileUrls()); case IndexBootstrap: @@ -257,7 +255,7 @@ namespace BlackCore this->m_vatsimBookingsUrl.setPropertyByIndex(variant, index.copyFrontRemoved()); break; case IndexVatsimMetars: - this->m_vatsimMetarsUrl.setPropertyByIndex(variant, index.copyFrontRemoved()); + this->m_vatsimMetarsUrls = variant.value(); break; case IndexShared: this->m_sharedUrls = variant.value(); @@ -273,7 +271,7 @@ namespace BlackCore const QString &CGlobalSetup::versionString() { - static const QString v("0.6.1"); + static const QString v("0.7.0"); return v; } diff --git a/src/blackcore/data/globalsetup.h b/src/blackcore/data/globalsetup.h index 1123306aa..4ba5baf99 100644 --- a/src/blackcore/data/globalsetup.h +++ b/src/blackcore/data/globalsetup.h @@ -37,6 +37,7 @@ namespace BlackCore IndexDbHttpPort, IndexDbHttpsPort, IndexDbLoginService, + IndexVatsimStatus, IndexVatsimBookings, IndexVatsimMetars, IndexVatsimData, @@ -93,7 +94,10 @@ namespace BlackCore const BlackMisc::Network::CUrl &vatsimBookingsUrl() const { return m_vatsimBookingsUrl; } //! VATSIM METAR URL - BlackMisc::Network::CUrl vatsimMetarsUrl() const; + const BlackMisc::Network::CUrlList &vatsimMetarsUrls() const { return this->m_vatsimMetarsUrls; } + + //! VATSIM status file URLs + const BlackMisc::Network::CUrlList &vatsimStatusFileUrls() const { return m_vatsimStatusFileUrls; } //! VATSIM data file URLs const BlackMisc::Network::CUrlList &vatsimDataFileUrls() const { return m_vatsimDataFileUrls; } @@ -149,7 +153,8 @@ namespace BlackCore bool m_development = false; //!< dev. version? BlackMisc::Network::CUrl m_dbRootDirectoryUrl; //!< Root directory of DB BlackMisc::Network::CUrl m_vatsimBookingsUrl; //!< ATC bookings - BlackMisc::Network::CUrl m_vatsimMetarsUrl; //!< METAR data + BlackMisc::Network::CUrlList m_vatsimMetarsUrls; //!< METAR data + BlackMisc::Network::CUrlList m_vatsimStatusFileUrls; //!< Status file, where to find the VATSIM files (METAR, data, ATIS, other status files) BlackMisc::Network::CUrlList m_vatsimDataFileUrls; //!< Overall VATSIM data file / merely for bootstrapping the first time BlackMisc::Network::CUrlList m_sharedUrls; //!< where we can obtain shared info files such as bootstrap, .. BlackMisc::Network::CUrlList m_newsUrls; //!< where we can obtain latest news @@ -183,9 +188,10 @@ BLACK_DECLARE_TUPLE_CONVERSION(BlackCore::Data::CGlobalSetup, ( attr(o.m_dbRootDirectoryUrl), attr(o.m_dbHttpPort), attr(o.m_dbHttpsPort), - attr(o.m_vatsimBookingsUrl), - attr(o.m_vatsimMetarsUrl), + attr(o.m_vatsimStatusFileUrls), attr(o.m_vatsimDataFileUrls), + attr(o.m_vatsimBookingsUrl), + attr(o.m_vatsimMetarsUrls), attr(o.m_sharedUrls), attr(o.m_newsUrls), attr(o.m_fsdTestServers), diff --git a/src/blackcore/data/vatsimsetup.h b/src/blackcore/data/vatsimsetup.h index ea012ae25..7413f5889 100644 --- a/src/blackcore/data/vatsimsetup.h +++ b/src/blackcore/data/vatsimsetup.h @@ -33,8 +33,12 @@ namespace BlackCore //! Properties by index enum ColumnIndex { - IndexFsdServers = BlackMisc::CPropertyIndex::GlobalIndexCGlobalSetup + 50, - IndexDataFiles + IndexServerFiles = BlackMisc::CPropertyIndex::GlobalIndexCVatsimSetup, + IndexDataFiles, + IndexMetarFiles, + IndexFsdServers, + IndexCVoiceServers, + IndexLastLoginUser }; //! Default constructor @@ -49,6 +53,18 @@ namespace BlackCore //! Set VATSIM data file URLs void setDataFileUrls(const BlackMisc::Network::CUrlList &urls) { m_dataFileUrls = urls; } + //! Server file URLs (like data file, only servers) + const BlackMisc::Network::CUrlList &getServerFileUrls() const { return m_serverFileUrls; } + + //! Set server file URLs (like data file, only servers) + void setServerFileUrls(const BlackMisc::Network::CUrlList &urls) { m_serverFileUrls = urls; } + + //! METAR file URLs + const BlackMisc::Network::CUrlList &getMetarFileUrls() const { return m_metarFileUrls; } + + //! METAR file URLs + void setMetarFileUrls(const BlackMisc::Network::CUrlList &urls) { m_metarFileUrls = urls; } + //! FSD test servers const BlackMisc::Network::CServerList &getFsdServers() const { return m_fsdServers; } @@ -82,7 +98,9 @@ namespace BlackCore private: BLACK_ENABLE_TUPLE_CONVERSION(BlackCore::Data::CVatsimSetup) - BlackMisc::Network::CUrlList m_dataFileUrls; //!< Overall VATSIM data file / merely for bootstrapping the first time + BlackMisc::Network::CUrlList m_serverFileUrls; //!< only the FSD servers + BlackMisc::Network::CUrlList m_dataFileUrls; //!< Full VATSIM files + BlackMisc::Network::CUrlList m_metarFileUrls; //!< METAR files BlackMisc::Network::CServerList m_fsdServers; //!< FSD test servers BlackMisc::Network::CServerList m_voiceServers; //!< voice servers BlackMisc::Network::CUser m_lastLoginUser; //!< last login user @@ -107,9 +125,12 @@ namespace BlackCore Q_DECLARE_METATYPE(BlackCore::Data::CVatsimSetup) BLACK_DECLARE_TUPLE_CONVERSION(BlackCore::Data::CVatsimSetup, ( + attr(o.m_serverFileUrls), attr(o.m_dataFileUrls), + attr(o.m_metarFileUrls), attr(o.m_fsdServers), attr(o.m_voiceServers), + attr(o.m_lastLoginUser), attr(o.m_timestampMSecsSinceEpoch) )) #endif // guard diff --git a/src/blackcore/setupreader.h b/src/blackcore/setupreader.h index c1849304c..ff9694dd7 100644 --- a/src/blackcore/setupreader.h +++ b/src/blackcore/setupreader.h @@ -17,7 +17,6 @@ #include "blackmisc/lockfree.h" #include "blackcore/data/globalsetup.h" #include "blackcore/data/updateinfo.h" - #include #include #include @@ -27,9 +26,13 @@ namespace BlackCore { //! Read the central URLs / locations of our data / setup. - //! This should be only used in BlackCore::CApplication + //! + //! \details This class should be only used in BlackCore::CApplication. It will also trigger reading + //! update information. + //! //! \note This class is no(!) BlackCore::CThreadedReader as it will be loaded once during startup - //! and is usually fast. + //! and reading setup data is fast. + //! //! \sa BlackCore::Data::GlobalSetup //! \sa BlackCore::Data::UpdateInfo class BLACKCORE_EXPORT CSetupReader : public QObject diff --git a/src/blackcore/vatsimdatafilereader.cpp b/src/blackcore/vatsimdatafilereader.cpp index 6accec274..7c4297861 100644 --- a/src/blackcore/vatsimdatafilereader.cpp +++ b/src/blackcore/vatsimdatafilereader.cpp @@ -158,7 +158,8 @@ namespace BlackCore // remark: Don't use QThread to run network operations in the background // see http://qt-project.org/doc/qt-4.7/qnetworkaccessmanager.html Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing application"); - const QUrl url(sApp->getGlobalSetup().vatsimDataFileUrls().getRandomUrl()); + CFailoverUrlList urls(sApp->getVatsimDataFileUrls()); + const QUrl url(urls.obtainNextWorkingUrl(true)); if (url.isEmpty()) { return; } sApp->getFromNetwork(url, { this, &CVatsimDataFileReader::ps_parseVatsimFile}); @@ -239,7 +240,7 @@ namespace BlackCore if (callsign.isEmpty()) { break; } const BlackMisc::Network::CUser user(clientPartsMap["cid"], clientPartsMap["realname"], callsign); const QString clientType = clientPartsMap["clienttype"].toLower(); - if (clientType.isEmpty()) break; // sometimes type is empty + if (clientType.isEmpty()) { break; } // sometimes type is empty const double lat = clientPartsMap["latitude"].toDouble(); const double lng = clientPartsMap["longitude"].toDouble(); const double alt = clientPartsMap["altitude"].toDouble(); @@ -384,8 +385,8 @@ namespace BlackCore QMap parts; for (int i = 0; i < clientSectionAttributes.size(); i++) { - Q_ASSERT(i < clientSectionAttributes.size()); - Q_ASSERT(i < clientParts.size()); + BLACK_VERIFY_X(i < clientSectionAttributes.size(), Q_FUNC_INFO, "Wrong section attribute size"); + BLACK_VERIFY_X(i < clientParts.size(), Q_FUNC_INFO, "Wrong parts size"); parts.insert(clientSectionAttributes.at(i).toLower(), clientParts.at(i)); } return parts; diff --git a/src/blackcore/vatsimdatafilereader.h b/src/blackcore/vatsimdatafilereader.h index 4f9a3c60a..9443cdb02 100644 --- a/src/blackcore/vatsimdatafilereader.h +++ b/src/blackcore/vatsimdatafilereader.h @@ -28,7 +28,8 @@ namespace BlackCore { - //! Read bookings from VATSIM + //! Read vatsim data file + //! \sa http://info.vroute.net/vatsim-data.txt class BLACKCORE_EXPORT CVatsimDataFileReader : public BlackMisc::CThreadedReader { Q_OBJECT @@ -57,7 +58,7 @@ namespace BlackCore //! \threadsafe BlackMisc::Network::CServerList getVoiceServers() const; - //! Get all FSD servers + //! Get all VATSIM FSD servers //! \threadsafe BlackMisc::Network::CServerList getFsdServers() const; diff --git a/src/blackcore/vatsimmetarreader.cpp b/src/blackcore/vatsimmetarreader.cpp index 487bcac0c..fbdb72a2c 100644 --- a/src/blackcore/vatsimmetarreader.cpp +++ b/src/blackcore/vatsimmetarreader.cpp @@ -63,10 +63,11 @@ namespace BlackCore void CVatsimMetarReader::ps_readMetars() { this->threadAssertCheck(); - const CUrl url(sApp->getGlobalSetup().vatsimMetarsUrl()); + CFailoverUrlList urls(sApp->getVatsimMetarUrls()); + const CUrl url(urls.obtainNextWorkingUrl(true)); if (url.isEmpty()) { return; } Q_ASSERT_X(sApp, Q_FUNC_INFO, "No Application"); - sApp->getFromNetwork(url, { this, &CVatsimMetarReader::ps_decodeMetars}); + sApp->getFromNetwork(url.withAppendedQuery("id=all"), { this, &CVatsimMetarReader::ps_decodeMetars}); } void CVatsimMetarReader::ps_decodeMetars(QNetworkReply *nwReplyPtr) diff --git a/src/blackcore/vatsimstatusfilereader.cpp b/src/blackcore/vatsimstatusfilereader.cpp new file mode 100644 index 000000000..ca4897de7 --- /dev/null +++ b/src/blackcore/vatsimstatusfilereader.cpp @@ -0,0 +1,162 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackcore/vatsimstatusfilereader.h" +#include "blackcore/application.h" +#include "blackmisc/network/urllist.h" +#include "blackmisc/logmessage.h" + +using namespace BlackMisc; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Network; +using namespace BlackMisc::Geo; +using namespace BlackMisc::Simulation; +using namespace BlackMisc::PhysicalQuantities; +using namespace BlackCore::Data; + +namespace BlackCore +{ + CVatsimStatusFileReader::CVatsimStatusFileReader(QObject *owner) : + CThreadedReader(owner, "CVatsimStatusFileReader") + { + this->connect(this->m_updateTimer, &QTimer::timeout, this, &CVatsimStatusFileReader::ps_read); + } + + void CVatsimStatusFileReader::readInBackgroundThread() + { + bool s = QMetaObject::invokeMethod(this, "ps_read"); + Q_ASSERT_X(s, Q_FUNC_INFO, "Invoke failed"); + Q_UNUSED(s); + } + + CUrlList CVatsimStatusFileReader::getMetarFileUrls() const + { + return this->m_lastGoodSetup.getCopy().getMetarFileUrls(); + } + + CUrlList CVatsimStatusFileReader::getDataFileUrls() const + { + return this->m_lastGoodSetup.getCopy().getDataFileUrls(); + } + + void CVatsimStatusFileReader::cleanup() + { + // void + } + + void CVatsimStatusFileReader::ps_read() + { + this->threadAssertCheck(); + + Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing application"); + CFailoverUrlList urls(sApp->getGlobalSetup().vatsimStatusFileUrls()); + const CUrl url(urls.obtainNextWorkingUrl(true)); // random working URL + if (url.isEmpty()) { return; } + sApp->getFromNetwork(url, { this, &CVatsimStatusFileReader::ps_parseVatsimFile}); + } + + void CVatsimStatusFileReader::ps_parseVatsimFile(QNetworkReply *nwReplyPtr) + { + // wrap pointer, make sure any exit cleans up reply + // required to use delete later as object is created in a different thread + QScopedPointer nwReply(nwReplyPtr); + + this->threadAssertCheck(); + + // Worker thread, make sure to write only synced here! + if (this->isAbandoned()) + { + CLogMessage(this).debug() << Q_FUNC_INFO; + CLogMessage(this).info("Terminated VATSIM status file parsing process"); // for users + return; // stop, terminate straight away, ending thread + } + + QStringList illegalIcaoCodes; + if (nwReply->error() == QNetworkReply::NoError) + { + const QString dataFileData = nwReply->readAll(); + nwReply->close(); // close asap + + if (dataFileData.isEmpty()) return; + const QStringList lines = dataFileData.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + if (lines.isEmpty()) { return; } + + CUrlList dataFiles; + CUrlList serverFiles; + CUrlList metarFiles; + + for (const QString &cl : lines) + { + if (this->isAbandoned()) + { + CLogMessage(this).debug() << Q_FUNC_INFO; + CLogMessage(this).info("Terminated status parsing process"); // for users + return; // stop, terminate straight away, ending thread + } + + // parse lines + const QString currentLine(cl.trimmed()); + if (currentLine.isEmpty()) { continue; } + if (currentLine.startsWith(";")) { continue; } + if (!currentLine.contains("=")) { continue; } + + const QStringList parts(currentLine.split('=')); + if (parts.length() != 2) { continue; } + const QString key(parts[0].trimmed().toLower()); + const QString value(parts[1].trimmed()); + const CUrl url(value); + if (key.startsWith("url0")) + { + dataFiles.push_back(url); + } + else if (key.startsWith("url1")) + { + serverFiles.push_back(url); + } + else if (key.startsWith("metar")) + { + metarFiles.push_back(url); + } + else if (key.startsWith("atis")) + { + // not yet used + } + } // for each line + + // this part needs to be synchronized + { + // cache itself is thread safe + CVatsimSetup vs(this->m_lastGoodSetup.getCopy()); + vs.setDataFileUrls(dataFiles); + vs.setMetarFileUrls(metarFiles); + vs.setServerFileUrls(serverFiles); + vs.setUtcTimestamp(QDateTime::currentDateTime()); + this->m_lastGoodSetup.set(vs); + } + + // warnings, if required + if (!illegalIcaoCodes.isEmpty()) + { + CLogMessage(this).info("Illegal / ignored ICAO code(s) in VATSIM data file: %1") << illegalIcaoCodes.join(", "); + } + + // data read finished + emit this->dataFileRead(lines.count()); + emit this->dataRead(CEntityFlags::VatsimStatusFile, CEntityFlags::ReadFinished, lines.count()); + } + else + { + // network error + CLogMessage(this).warning("Reading VATSIM status file failed %1 %2") << nwReply->errorString() << nwReply->url().toString(); + nwReply->abort(); + emit this->dataRead(CEntityFlags::VatsimStatusFile, CEntityFlags::ReadFailed, 0); + } + } + +} // namespace diff --git a/src/blackcore/vatsimstatusfilereader.h b/src/blackcore/vatsimstatusfilereader.h new file mode 100644 index 000000000..a23ed0937 --- /dev/null +++ b/src/blackcore/vatsimstatusfilereader.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_VATSIMSTATUSFILEREADER_H +#define BLACKCORE_VATSIMSTATUSFILEREADER_H + +#include "blackcoreexport.h" +#include "blackcore/data/vatsimsetup.h" +#include "blackmisc/threadedreader.h" +#include "blackmisc/network/urllist.h" + +#include +#include +#include + +namespace BlackCore +{ + //! Sole purpose is to read the URLs where VATSIM data can be downloaded + //! \sa https://status.vatsim.net/ + class BLACKCORE_EXPORT CVatsimStatusFileReader : public BlackMisc::CThreadedReader + { + Q_OBJECT + + public: + //! Constructor + explicit CVatsimStatusFileReader(QObject *owner); + + //! METAR URLs + //! \threadsafe + BlackMisc::Network::CUrlList getMetarFileUrls() const; + + //! Data file URLs + //! \threadsafe + BlackMisc::Network::CUrlList getDataFileUrls() const; + + public slots: + //! Start reading in own thread + void readInBackgroundThread(); + + signals: + //! Data have been read + void dataFileRead(int lines); + + //! Data have been read + void dataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number); + + protected: + //! \copydoc BlackMisc::CThreadedReader::cleanup + virtual void cleanup() override; + + private slots: + //! Data have been read, parse VATSIM file + void ps_parseVatsimFile(QNetworkReply *nwReply); + + //! Read / re-read data file + void ps_read(); + + private: + BlackMisc::CData m_lastGoodSetup { this }; + }; +} // ns + +#endif // guard diff --git a/src/blackcore/webdataservices.cpp b/src/blackcore/webdataservices.cpp index 96eb422a1..3506deabe 100644 --- a/src/blackcore/webdataservices.cpp +++ b/src/blackcore/webdataservices.cpp @@ -13,6 +13,7 @@ #include "blackcore/modeldatareader.h" #include "blackcore/icaodatareader.h" #include "blackcore/databasewriter.h" +#include "blackcore/vatsimstatusfilereader.h" #include "blackcore/vatsimbookingreader.h" #include "blackcore/vatsimdatafilereader.h" #include "blackcore/vatsimmetarreader.h" @@ -99,6 +100,18 @@ namespace BlackCore return CServerList(); } + CUrlList CWebDataServices::getVatsimMetarUrls() const + { + if (m_vatsimStatusReader) { return m_vatsimStatusReader->getMetarFileUrls(); } + return CUrlList(); + } + + CUrlList CWebDataServices::getVatsimDataFileUrls() const + { + if (m_vatsimStatusReader) { return m_vatsimStatusReader->getDataFileUrls(); } + return CUrlList(); + } + CUserList CWebDataServices::getUsersForCallsign(const CCallsign &callsign) const { if (m_vatsimDataFileReader) { return m_vatsimDataFileReader->getUsersForCallsign(callsign); } @@ -392,6 +405,7 @@ namespace BlackCore void CWebDataServices::gracefulShutdown() { this->disconnect(); // all signals + if (this->m_vatsimStatusReader) { this->m_vatsimStatusReader->gracefulShutdown(); } if (this->m_vatsimBookingReader) { this->m_vatsimBookingReader->gracefulShutdown(); } if (this->m_vatsimDataFileReader) { this->m_vatsimDataFileReader->gracefulShutdown(); } if (this->m_vatsimMetarReader) { this->m_vatsimMetarReader->gracefulShutdown(); } @@ -408,7 +422,16 @@ namespace BlackCore void CWebDataServices::initReaders(CWebReaderFlags::WebReader flags) { - // 1. VATSIM bookings + // 1. Status file, updating the cache + if (flags.testFlag(CWebReaderFlags::WebReaderFlag::VatsimDataReader) || flags.testFlag(CWebReaderFlags::WebReaderFlag::VatsimMetarReader)) + { + this->m_vatsimStatusReader = new CVatsimStatusFileReader(this); + this->m_vatsimStatusReader->start(QThread::LowPriority); + this->m_vatsimStatusReader->setInterval(60 * 60 * 1000); // very slow updates required only + QTimer::singleShot(100, this->m_vatsimStatusReader, &CVatsimStatusFileReader::readInBackgroundThread); + } + + // 2. VATSIM bookings if (flags.testFlag(CWebReaderFlags::WebReaderFlag::VatsimBookingReader)) { this->m_vatsimBookingReader = new CVatsimBookingReader(this); @@ -419,7 +442,7 @@ namespace BlackCore this->m_vatsimBookingReader->setInterval(3 * 60 * 1000); } - // 2. VATSIM data file + // 3. VATSIM data file if (flags.testFlag(CWebReaderFlags::WebReaderFlag::VatsimDataReader)) { this->m_vatsimDataFileReader = new CVatsimDataFileReader(this); @@ -430,7 +453,7 @@ namespace BlackCore this->m_vatsimDataFileReader->setInterval(90 * 1000); } - // 3. VATSIM metar data + // 4. VATSIM metar data if (flags.testFlag(CWebReaderFlags::WebReaderFlag::VatsimMetarReader)) { this->m_vatsimMetarReader = new CVatsimMetarReader(this); @@ -441,7 +464,7 @@ namespace BlackCore this->m_vatsimMetarReader->setInterval(90 * 1000); } - // 4. ICAO data reader + // 5. ICAO data reader if (flags.testFlag(CWebReaderFlags::WebReaderFlag::IcaoDataReader)) { bool c; @@ -452,7 +475,7 @@ namespace BlackCore this->m_icaoDataReader->start(QThread::LowPriority); } - // 5. Model reader + // 6. Model reader if (flags.testFlag(CWebReaderFlags::WebReaderFlag::ModelReader)) { this->m_modelDataReader = new CModelDataReader(this); diff --git a/src/blackcore/webdataservices.h b/src/blackcore/webdataservices.h index 0d7012f9b..0e8e0f362 100644 --- a/src/blackcore/webdataservices.h +++ b/src/blackcore/webdataservices.h @@ -20,6 +20,7 @@ #include "blackmisc/aviation/airlineicaocodelist.h" #include "blackmisc/aviation/aircrafticaocodelist.h" #include "blackmisc/network/serverlist.h" +#include "blackmisc/network/urllist.h" #include "blackmisc/network/voicecapabilities.h" #include "blackmisc/network/entityflags.h" #include "blackmisc/simulation/distributorlist.h" @@ -31,6 +32,7 @@ namespace BlackCore { + class CVatsimStatusFileReader; class CVatsimBookingReader; class CVatsimDataFileReader; class CVatsimMetarReader; @@ -84,6 +86,14 @@ namespace BlackCore //! \threadsafe BlackMisc::Network::CServerList getVatsimVoiceServers() const; + //! METAR URLs (from status file) + //! \threadsafe + BlackMisc::Network::CUrlList getVatsimMetarUrls() const; + + //! Data file locations (from status file) + //! \threadsafe + BlackMisc::Network::CUrlList getVatsimDataFileUrls() const; + //! Users by callsign //! \threadsafe BlackMisc::Network::CUserList getUsersForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const; @@ -283,14 +293,15 @@ namespace BlackCore bool m_initialRead = false; //!< Initial read conducted // for reading XML and VATSIM data files - CVatsimBookingReader *m_vatsimBookingReader = nullptr; - CVatsimDataFileReader *m_vatsimDataFileReader = nullptr; - CVatsimMetarReader *m_vatsimMetarReader = nullptr; - CIcaoDataReader *m_icaoDataReader = nullptr; - CModelDataReader *m_modelDataReader = nullptr; + CVatsimStatusFileReader *m_vatsimStatusReader = nullptr; + CVatsimBookingReader *m_vatsimBookingReader = nullptr; + CVatsimDataFileReader *m_vatsimDataFileReader = nullptr; + CVatsimMetarReader *m_vatsimMetarReader = nullptr; + CIcaoDataReader *m_icaoDataReader = nullptr; + CModelDataReader *m_modelDataReader = nullptr; // writing objects directly into DB - CDatabaseWriter *m_databaseWriter = nullptr; + CDatabaseWriter *m_databaseWriter = nullptr; }; } // namespace diff --git a/src/blackmisc/network/entityflags.h b/src/blackmisc/network/entityflags.h index 84b0cb7e5..ccec3e07d 100644 --- a/src/blackmisc/network/entityflags.h +++ b/src/blackmisc/network/entityflags.h @@ -29,31 +29,32 @@ namespace BlackMisc //! Which data to read, requires corresponding readers enum EntityFlag { - NoEntity = 0, ///< no data at all - VatsimDataFile = 1 << 0, ///< the VATSIM data file (multiple data entities) - BookingEntity = 1 << 1, ///< bookings - MetarEntity = 1 << 2, - AircraftIcaoEntity = 1 << 3, ///< ICAO codes for aircraft - AirlineIcaoEntity = 1 << 4, ///< ICAO codes for airlines - CountryEntity = 1 << 5, ///< country codes - DistributorEntity = 1 << 6, ///< distributors - LiveryEntity = 1 << 7, ///< liveries - ModelEntity = 1 << 8, ///< models - AllIcaoEntities = AircraftIcaoEntity | AirlineIcaoEntity, ///< all ICAO codes - AllIcaoAndCountries = AircraftIcaoEntity | AirlineIcaoEntity | CountryEntity, ///< all ICAO codes and countries - DistributorLiveryModel = DistributorEntity | LiveryEntity | ModelEntity, ///< Combinded - AllDbEntities = AllIcaoEntities | DistributorLiveryModel, ///< All DB stuff - AllEntities = 0xFFFF ///< everything + NoEntity = 0, //!< no data at all + BookingEntity = 1 << 0, //!< bookings + MetarEntity = 1 << 1, //!< METAR + AircraftIcaoEntity = 1 << 2, //!< ICAO codes for aircraft + AirlineIcaoEntity = 1 << 3, //!< ICAO codes for airlines + CountryEntity = 1 << 4, //!< country codes + DistributorEntity = 1 << 5, //!< distributors + LiveryEntity = 1 << 6, //!< liveries + ModelEntity = 1 << 7, //!< models + VatsimDataFile = 1 << 8, //!< the VATSIM data file (multiple data entities) + VatsimStatusFile = 1 << 9, //!< the VATSIM status file (URLs for data files etc.) + AllEntities = ((1<<10)-1), //!< everything + AllIcaoEntities = AircraftIcaoEntity | AirlineIcaoEntity, //!< all ICAO codes + AllIcaoAndCountries = AircraftIcaoEntity | AirlineIcaoEntity | CountryEntity, //!< all ICAO codes and countries + DistributorLiveryModel = DistributorEntity | LiveryEntity | ModelEntity, //!< Combinded + AllDbEntities = AllIcaoEntities | DistributorLiveryModel, //!< All DB stuff }; Q_DECLARE_FLAGS(Entity, EntityFlag) //! State of operation enum ReadState { - StartRead, ///< reading has been started - ReadFinished, ///< reading done - ReadFinishedRestricted, ///< finished a timestamp restricted read - ReadFailed ///< reading failed + StartRead, //!< reading has been started + ReadFinished, //!< reading done + ReadFinishedRestricted, //!< finished a timestamp restricted read + ReadFailed //!< reading failed }; //! Convert to string diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index 3e27f34c6..6bb241a81 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -94,6 +94,7 @@ namespace BlackMisc GlobalIndexIDatastoreString = 11100, GlobalIndexCGlobalSetup = 12000, GlobalIndexCUpdateInfo = 12100, + GlobalIndexCVatsimSetup = 12200, GlobalIndexAbuseMode = 20000 // property index abused as map key or otherwise, to be removed if no longer needed };