7 #include <QElapsedTimer>
10 #include <QJsonDocument>
11 #include <QNetworkReply>
13 #include <QReadLocker>
14 #include <QScopedPointer>
15 #include <QScopedPointerDeleteLater>
18 #include <QWriteLocker>
32 using namespace swift::misc::db;
33 using namespace swift::misc::aviation;
34 using namespace swift::misc::simulation;
35 using namespace swift::misc::network;
52 if (!CLivery::isValidCombinedCode(combinedCode)) {
return CLivery(); }
66 if (
id < 0) {
return CLivery(); }
97 if (modelString.isEmpty()) {
return false; }
124 const QString &combinedCode)
163 const QDateTime &newerThan)
167 entities &= CEntityFlags::DistributorLiveryModel;
169 CEntityFlags::Entity triggeredRead = CEntityFlags::NoEntity;
172 if (entities.testFlag(CEntityFlags::LiveryEntity))
174 url = this->getLiveryUrl(mode);
179 triggeredRead |= CEntityFlags::LiveryEntity;
184 if (entities.testFlag(CEntityFlags::DistributorEntity))
186 url = this->getDistributorUrl(mode);
191 triggeredRead |= CEntityFlags::DistributorEntity;
196 if (entities.testFlag(CEntityFlags::ModelEntity))
198 url = this->getModelUrl(mode);
203 triggeredRead |= CEntityFlags::ModelEntity;
208 if (triggeredRead != CEntityFlags::NoEntity)
210 emit
dataRead(triggeredRead, CEntityFlags::ReadStarted, 0, url);
214 void CModelDataReader::liveryCacheChanged() { this->
cacheHasChanged(CEntityFlags::LiveryEntity); }
216 void CModelDataReader::modelCacheChanged() { this->
cacheHasChanged(CEntityFlags::ModelEntity); }
218 void CModelDataReader::distributorCacheChanged() { this->
cacheHasChanged(CEntityFlags::DistributorEntity); }
220 void CModelDataReader::baseUrlCacheChanged()
225 void CModelDataReader::updateReaderUrl(
const CUrl &url)
227 const CUrl current = m_readerUrlCache.
get();
228 if (current == url) {
return; }
245 void CModelDataReader::parseLiveryData(QNetworkReply *nwReplyPtr)
249 QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
256 emit
dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0, res.
getUrl());
261 emit this->
dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadParsing, 0, res.
getUrl());
266 const CLiveryList incrementalLiveries(CLiveryList::fromDatabaseJson(res));
267 if (incrementalLiveries.isEmpty()) {
return; }
275 liveries = CLiveryList::fromDatabaseJson(res);
280 const int n = liveries.
size();
282 if (n > 0 && latestTimestamp < 0)
284 CLogMessage(
this).
error(u
"No timestamp in livery list, setting to last modified value");
290 this->updateReaderUrl(
getBaseUrl(CDbFlags::DbReading));
294 void CModelDataReader::parseDistributorData(QNetworkReply *nwReplyPtr)
298 QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
305 emit
dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0, res.
getUrl());
310 emit this->
dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadParsing, 0, res.
getUrl());
316 if (incrementalDistributors.isEmpty()) {
return; }
325 this->
logParseMessage(
"distributors", distributors.
size(),
static_cast<int>(time.elapsed()), res);
329 const int n = distributors.
size();
331 if (n > 0 && latestTimestamp < 0)
333 CLogMessage(
this).
error(u
"No timestamp in distributor list, setting to last modified value");
337 const CStatusMessage cacheMsg = m_distributorCache.
set(distributors, latestTimestamp);
340 this->updateReaderUrl(
getBaseUrl(CDbFlags::DbReading));
344 void CModelDataReader::parseModelData(QNetworkReply *nwReplyPtr)
348 QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
355 emit this->
dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0, res.
getUrl());
360 emit this->
dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadParsing, 0, res.
getUrl());
376 if (incrementalModels.isEmpty()) {
return; }
390 const int n = models.
size();
392 if (n > 0 && latestTimestamp < 0)
394 CLogMessage(
this).
error(u
"No timestamp in model list, setting to last modified value");
400 this->updateReaderUrl(this->
getBaseUrl(CDbFlags::DbReading));
405 bool overrideNewerOnly)
407 const QDir directory(dir);
408 if (!directory.exists()) {
return CStatusMessage(
this).
error(u
"Missing directory '%1'") << dir; }
410 whatToRead &= CEntityFlags::DistributorLiveryModel;
411 CEntityFlags::Entity reallyRead = CEntityFlags::NoEntity;
414 if (whatToRead.testFlag(CEntityFlags::LiveryEntity))
417 const QFileInfo fi(fileName);
426 const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
427 if (liveriesJson.isEmpty())
435 const CLiveryList liveries = CLiveryList::fromMultipleJsonFormats(liveriesJson);
436 const int c = liveries.
size();
437 msgs.
push_back(m_liveryCache.
set(liveries, fi.birthTime().toUTC().toMSecsSinceEpoch()));
438 emit this->
dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFinished, c, url);
439 reallyRead |= CEntityFlags::LiveryEntity;
443 emit this->
dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0, url);
445 ex,
this, QStringLiteral(
"Reading liveries from '%1'").arg(fileName)));
451 if (whatToRead.testFlag(CEntityFlags::ModelEntity))
454 const QFileInfo fi(fileName);
463 const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
465 if (modelsJson.isEmpty())
474 const int c = models.
size();
475 msgs.
push_back(m_modelCache.
set(models, fi.birthTime().toUTC().toMSecsSinceEpoch()));
476 emit this->
dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFinished, c, url);
477 reallyRead |= CEntityFlags::ModelEntity;
481 emit this->
dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0, url);
483 ex,
this, QStringLiteral(
"Reading models from '%1'").arg(fileName)));
489 if (whatToRead.testFlag(CEntityFlags::DistributorEntity))
492 const QFileInfo fi(fileName);
501 const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
503 if (distributorsJson.isEmpty())
513 const int c = distributors.
size();
515 m_distributorCache.
set(distributors, fi.birthTime().toUTC().toMSecsSinceEpoch()));
516 emit this->
dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFinished, c, url);
517 reallyRead |= CEntityFlags::DistributorEntity;
521 emit this->
dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0, url);
523 ex,
this, QStringLiteral(
"Reading distributors from '%1'").arg(fileName)));
533 bool overrideNewerOnly)
535 if (dir.isEmpty() || whatToRead == CEntityFlags::NoEntity) {
return false; }
537 QPointer<CModelDataReader> myself(
this);
539 if (!myself) {
return; }
549 if (!directory.exists()) {
return false; }
550 QList<QPair<QString, QString>> fileContents;
553 const QString json(QJsonDocument(this->
getLiveries().toJson()).toJson());
554 fileContents.push_back({
"liveries.json", json });
559 const QString json(QJsonDocument(this->
getModels().toJson()).toJson());
560 fileContents.push_back({
"models.json", json });
565 const QString json(QJsonDocument(this->
getDistributors().toJson()).toJson());
566 fileContents.push_back({
"distributors.json", json });
569 for (
const auto &pair : fileContents)
583 if (entities.testFlag(CEntityFlags::LiveryEntity))
585 if (m_syncedLiveryCache) {
return; }
586 m_syncedLiveryCache =
true;
589 if (entities.testFlag(CEntityFlags::ModelEntity))
591 if (m_syncedModelCache) {
return; }
592 m_syncedModelCache =
true;
595 if (entities.testFlag(CEntityFlags::DistributorEntity))
597 if (m_syncedDistributorCache) {
return; }
598 m_syncedDistributorCache =
true;
605 if (entities.testFlag(CEntityFlags::LiveryEntity)) { m_liveryCache.
admit(); }
606 if (entities.testFlag(CEntityFlags::ModelEntity)) { m_modelCache.
admit(); }
607 if (entities.testFlag(CEntityFlags::DistributorEntity)) { m_distributorCache.
admit(); }
612 if (entities.testFlag(CEntityFlags::LiveryEntity))
616 if (entities.testFlag(CEntityFlags::ModelEntity))
620 if (entities.testFlag(CEntityFlags::DistributorEntity))
633 default:
return QDateTime();
641 case CEntityFlags::LiveryEntity:
return m_liveryCache.
get().size();
642 case CEntityFlags::ModelEntity:
return m_modelCache.
get().size();
643 case CEntityFlags::DistributorEntity:
return m_distributorCache.
get().size();
650 CEntityFlags::Entity entities = CEntityFlags::NoEntity;
651 if (this->
getCacheCount(CEntityFlags::LiveryEntity) > 0) entities |= CEntityFlags::LiveryEntity;
652 if (this->
getCacheCount(CEntityFlags::ModelEntity) > 0) entities |= CEntityFlags::ModelEntity;
653 if (this->
getCacheCount(CEntityFlags::DistributorEntity) > 0) entities |= CEntityFlags::DistributorEntity;
659 CEntityFlags::Entity entities = CEntityFlags::NoEntity;
661 entities |= CEntityFlags::LiveryEntity;
663 entities |= CEntityFlags::ModelEntity;
665 entities |= CEntityFlags::DistributorEntity;
672 oldUrlInfo = m_readerUrlCache.
get();
673 newUrlInfo = this->
getBaseUrl(CDbFlags::DbReading);
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
data::CGlobalSetup getGlobalSetup() const
Global setup.
bool isShuttingDown() const
Is application shutting down?
CWebDataServices * getWebDataServices() const
Get the web data services.
void threadAssertCheck() const
Make sure everthing runs correctly in own thread.
QNetworkReply * getFromNetworkAndLog(const swift::misc::network::CUrl &url, const swift::misc::CSlot< void(QNetworkReply *)> &callback)
Get request from network, and log with m_urlReadLog.
bool doWorkCheck() const
Still enabled etc.?
qint64 lastModifiedMsSinceEpoch(QNetworkReply *nwReply) const
When was reply last modified, -1 if N/A.
swift::misc::aviation::CAircraftCategoryList getAircraftCategories() const
Aircraft categories.
swift::misc::aviation::CAircraftIcaoCodeList getAircraftIcaoCodes() const
Aircraft ICAO codes.
swift::misc::network::CUrl getDbModelReaderUrl() const
Model reader URL.
Value object encapsulating a list of reader configs.
Specialized version of threaded reader for DB data.
void emitAndLogDataRead(swift::misc::network::CEntityFlags::Entity entity, int number, const JsonDatastoreResponse &res)
Emit signal and log when data have been read.
static QString queryLatestTimestamp(const QDateTime &ts)
Latest timestamp query for DB.
virtual void cacheHasChanged(swift::misc::network::CEntityFlags::Entity entities)
Cache for given entity has changed.
void logNoWorkingUrl(swift::misc::network::CEntityFlags::Entity entity)
Log if no working URL exists, using m_noWorkingUrlSeverity.
bool overrideCacheFromFile(bool overrideNewerOnly, const QFileInfo &fileInfo, swift::misc::network::CEntityFlags::Entity entity, swift::misc::CStatusMessageList &msgs) const
Override cache from file.
swift::misc::network::CUrl getBaseUrl(swift::misc::db::CDbFlags::DataRetrievalModeFlag mode) const
Base URL for mode (either a shared or DB URL)
void dataRead(swift::misc::network::CEntityFlags::Entity entities, swift::misc::network::CEntityFlags::ReadState state, int number, const QUrl &url)
Combined read signal.
static bool isChangedUrl(const swift::misc::network::CUrl &oldUrl, const swift::misc::network::CUrl ¤tUrl)
Has URL been changed? Means we load from a different server.
static QString fileNameForMode(swift::misc::network::CEntityFlags::Entity entity, swift::misc::db::CDbFlags::DataRetrievalModeFlag mode)
File name for given mode, either php service or shared file name.
CDatabaseReader::JsonDatastoreResponse setStatusAndTransformReplyIntoDatastoreResponse(QNetworkReply *nwReply)
Check if terminated or error, otherwise split into array of objects.
void logParseMessage(const QString &entity, int size, int msElapsed, const JsonDatastoreResponse &response) const
Parsing info message.
bool hasCacheTimestampNewerThan(swift::misc::network::CEntityFlags::Entity entity, const QDateTime &threshold) const
Has entity a valid and newer timestamp.
static QJsonObject readQJsonObjectFromDatabaseFile(const QString &filename)
QJsonObject from database JSON file (normally shared file)
bool containsModelString(const QString &modelString) const
Contains modelstring?
swift::misc::simulation::CAircraftModel getModelForModelString(const QString &modelString) const
Get model for string.
swift::misc::simulation::CAircraftModel getModelForDbKey(int dbKey) const
Get model for DB key.
bool writeToJsonFiles(const QString &dir)
Write to JSON file.
swift::misc::aviation::CLivery getLiveryForCombinedCode(const QString &combinedCode) const
Get aircraft livery for code.
int getLiveriesCount() const
Get aircraft liveries count.
swift::misc::simulation::CDistributor getDistributorForDbKey(const QString &dbKey) const
Get distributor for id.
QSet< int > getModelDbKeys() const
Get model keys.
bool areAllDataRead() const
All data read?
QStringList getModelStringList(bool sort=false) const
Get model keys.
swift::misc::aviation::CLivery smartLiverySelector(const swift::misc::aviation::CLivery &livery) const
Best match specified by livery.
int getDistributorsCount() const
Get model distributors count.
virtual swift::misc::network::CEntityFlags::Entity getEntitiesWithCacheCount() const
Entities already having data in cache.
virtual void admitCaches(swift::misc::network::CEntityFlags::Entity entities)
Admit caches for given entities.
swift::misc::simulation::CDistributorList getDistributors() const
Get distributors (of models)
virtual int getCacheCount(swift::misc::network::CEntityFlags::Entity entity) const
Cache`s number of entities.
swift::misc::simulation::CAircraftModelList getModelsForAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode)
Get model for designator/combined code.
virtual swift::misc::network::CUrl getDbServiceBaseUrl() const
Get the service URL, individual for each reader.
virtual bool hasChangedUrl(swift::misc::network::CEntityFlags::Entity entity, swift::misc::network::CUrl &oldUrlInfo, swift::misc::network::CUrl &newUrlInfo) const
Changed URL, means the cache values have been read from elsewhere.
swift::misc::aviation::CAircraftIcaoCodeList getAicraftIcaoCodesForAirline(const swift::misc::aviation::CAirlineIcaoCode &code) const
Get aircraft ICAO designators (e.g. B737, ..) for given airline.
swift::misc::simulation::CDistributor smartDistributorSelector(const swift::misc::simulation::CDistributor &distributorPattern) const
Best match specified by distributor.
virtual QDateTime getCacheTimestamp(swift::misc::network::CEntityFlags::Entity entity) const
Get cache timestamp.
virtual swift::misc::network::CEntityFlags::Entity getSupportedEntities() const
Supported entities by this reader.
int getModelsCount() const
Get models count.
swift::misc::aviation::CLivery getStdLiveryForAirlineVDesignator(const swift::misc::aviation::CAirlineIcaoCode &icao) const
Get aircraft livery for ICAO code.
QSet< QString > getAircraftDesignatorsForAirline(const swift::misc::aviation::CAirlineIcaoCode &code) const
Get aircraft ICAO designators (e.g. B737, ..) for given airline.
virtual void synchronizeCaches(swift::misc::network::CEntityFlags::Entity entities)
Admit caches for given entities.
swift::misc::aviation::CLivery getLiveryForDbKey(int id) const
Get aircraft livery for id.
swift::misc::simulation::CAircraftModelList getModels() const
Get models.
virtual bool readFromJsonFilesInBackground(const QString &dir, swift::misc::network::CEntityFlags::Entity whatToRead, bool overrideNewerOnly)
Data read from local data.
swift::misc::aviation::CLiveryList getLiveries() const
Get aircraft liveries.
virtual swift::misc::network::CEntityFlags::Entity getEntitiesWithCacheTimestampNewerThan(const QDateTime &threshold) const
Entities already having data in cache (based on timestamp assumption)
virtual void read(swift::misc::network::CEntityFlags::Entity entities=swift::misc::network::CEntityFlags::DistributorLiveryModel, swift::misc::db::CDbFlags::DataRetrievalModeFlag mode=swift::misc::db::CDbFlags::DbReading, const QDateTime &newerThan=QDateTime())
Read / re-read data file.
virtual void invalidateCaches(swift::misc::network::CEntityFlags::Entity entities)
Invalidate the caches for given entities.
virtual swift::misc::CStatusMessageList readFromJsonFiles(const QString &dir, swift::misc::network::CEntityFlags::Entity whatToRead, bool overrideNewerOnly)
Data read from local data.
const QString & getKey() const
Get the key string of this value.
T get() const
Get a copy of the current value.
static CDataCache * instance()
Return the singleton instance.
CStatusMessage set(const typename Trait::type &value, qint64 timestamp=0)
Write a new value. Must be called from the thread in which the owner lives.
void admit()
If the value is load-deferred, trigger the deferred load (async).
QDateTime getAvailableTimestamp() const
Get the timestamp of the value, or of the deferred value that is available to be loaded.
void synchronize()
If the value is currently being loaded, wait for it to finish loading, and call the notification slot...
static bool writeStringToFile(const QString &content, const QString &fileNameAndPath)
Write string to text file.
static QString appendFilePaths(const QString &path1, const QString &path2)
Append file paths.
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Class for emitting a log message.
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
size_type size() const
Returns number of elements in the sequence.
void push_back(const T &value)
Appends an element at the end of the sequence.
Streamable status message, e.g.
static CStatusMessage fromJsonException(const CJsonException &ex, const CLogCategoryList &categories, const QString &prefix)
Object from JSON exception message.
bool isFailure() const
Operation considered unsuccessful.
Status messages, e.g. from Core -> GUI.
bool isFailure() const
Any message is marked as failure.
void clearAllValues(const QString &keyPrefix={})
Clear all values from the cache.
static CWorker * fromTask(QObject *owner, const QString &name, F &&task)
Returns a new worker object which lives in a new thread.
qint64 latestTimestampMsecsSinceEpoch() const
Latest timestamp.
Value object encapsulating a list of ICAO codes.
Value object encapsulating a list of ICAO codes.
Value object for ICAO classification.
bool hasValidDesignator() const
Airline designator available?
Value object encapsulating information about an airpot.
Value object for a list of airports.
CLivery smartLiverySelector(const CLivery &liveryPattern) const
Find by multiple criteria.
CLivery findStdLiveryByAirlineIcaoVDesignator(const QString &icao) const
Find livery by airline.
CLivery findByCombinedCode(const QString &combinedCode) const
Find livery by combined code.
DataRetrievalModeFlag
Which data to read, requires corresponding readers.
int replaceOrAddObjectsByKey(const CONTAINER &container)
Update or insert data (based on DB key)
OBJ findByKey(KEYTYPE key, const OBJ ¬Found=OBJ()) const
Object with key, notFound otherwise.
static CDistributorList fromDatabaseJson(const QJsonArray &array)
From DB JSON with default prefixes.
QSet< KEYTYPE > toDbKeySet() const
All keys as set.
static CAircraftModelList fromMultipleJsonFormats(const QJsonObject &jsonObject)
From multiple JSON formats.
Value object encapsulating information of a location, kind of simplified CValueObject compliant versi...
bool isEmpty() const
Empty.
CUrl withAppendedPath(const QString &path) const
Append path.
void appendQuery(const QString &queryToAppend)
Append query.
Aircraft model (used by another pilot, my models on disk)
Value object encapsulating a list of aircraft models.
static CAircraftModelList fromDatabaseJsonCaching(const QJsonArray &array, const aviation::CAircraftIcaoCodeList &aircraftIcaos={}, const aviation::CAircraftCategoryList &aircraftCategories={}, const aviation::CLiveryList &liveries={}, const CDistributorList &distributors={})
Newer version.
QStringList getModelStringList(bool sort=true) const
Model strings.
aviation::CAircraftIcaoCodeList getAicraftIcaoCodesForAirline(const aviation::CAirlineIcaoCode &airlineCode) const
Aircraft ICAO codes for airline.
CAircraftModel findFirstByModelStringOrDefault(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Find first by model string.
CAircraftModelList findByAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode) const
Find by designator and livery code.
QSet< QString > getAircraftDesignatorsForAirline(const aviation::CAirlineIcaoCode &airlineCode) const
Aircraft designators for airline.
bool containsModelString(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Contains model string?
Value object encapsulating information of software distributor.
Value object encapsulating a list of distributors.
CDistributor findByKeyOrAlias(const QString &keyOrAlias) const
Find by id or alias.
CDistributor smartDistributorSelector(const CDistributor &distributorPattern) const
Best match by given pattern.
Core data traits (aka cached values) and classes.
Classes interacting with the swift database (aka "datastore").
Free functions in swift::misc.
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...
Response from our database (depending on JSON DB backend generates)
bool isRestricted() const
Incremental data, restricted by query?