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;
170 if (entities.testFlag(CEntityFlags::LiveryEntity))
172 CUrl url = this->getLiveryUrl(mode);
177 triggeredRead |= CEntityFlags::LiveryEntity;
182 if (entities.testFlag(CEntityFlags::DistributorEntity))
184 CUrl url = this->getDistributorUrl(mode);
189 triggeredRead |= CEntityFlags::DistributorEntity;
194 if (entities.testFlag(CEntityFlags::ModelEntity))
196 CUrl url = this->getModelUrl(mode);
201 triggeredRead |= CEntityFlags::ModelEntity;
206 if (triggeredRead != CEntityFlags::NoEntity)
212 void CModelDataReader::liveryCacheChanged() { this->
cacheHasChanged(CEntityFlags::LiveryEntity); }
214 void CModelDataReader::modelCacheChanged() { this->
cacheHasChanged(CEntityFlags::ModelEntity); }
216 void CModelDataReader::distributorCacheChanged() { this->
cacheHasChanged(CEntityFlags::DistributorEntity); }
218 void CModelDataReader::baseUrlCacheChanged()
223 void CModelDataReader::updateReaderUrl(
const CUrl &url)
225 const CUrl current = m_readerUrlCache.
get();
226 if (current == url) {
return; }
243 void CModelDataReader::parseLiveryData(QNetworkReply *nwReplyPtr)
247 QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
254 emit
dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0, res.
getUrl());
259 emit this->
dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadParsing, 0, res.
getUrl());
264 const CLiveryList incrementalLiveries(CLiveryList::fromDatabaseJson(res));
265 if (incrementalLiveries.isEmpty()) {
return; }
273 liveries = CLiveryList::fromDatabaseJson(res);
278 const int n = liveries.
size();
280 if (n > 0 && latestTimestamp < 0)
282 CLogMessage(
this).
error(u
"No timestamp in livery list, setting to last modified value");
288 this->updateReaderUrl(
getBaseUrl(CDbFlags::DbReading));
292 void CModelDataReader::parseDistributorData(QNetworkReply *nwReplyPtr)
296 QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
303 emit
dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0, res.
getUrl());
308 emit this->
dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadParsing, 0, res.
getUrl());
314 if (incrementalDistributors.isEmpty()) {
return; }
323 this->
logParseMessage(
"distributors", distributors.
size(),
static_cast<int>(time.elapsed()), res);
327 const int n = distributors.
size();
329 if (n > 0 && latestTimestamp < 0)
331 CLogMessage(
this).
error(u
"No timestamp in distributor list, setting to last modified value");
335 const CStatusMessage cacheMsg = m_distributorCache.
set(distributors, latestTimestamp);
338 this->updateReaderUrl(
getBaseUrl(CDbFlags::DbReading));
342 void CModelDataReader::parseModelData(QNetworkReply *nwReplyPtr)
346 QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
353 emit this->
dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0, res.
getUrl());
358 emit this->
dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadParsing, 0, res.
getUrl());
374 if (incrementalModels.isEmpty()) {
return; }
388 const int n = models.
size();
390 if (n > 0 && latestTimestamp < 0)
392 CLogMessage(
this).
error(u
"No timestamp in model list, setting to last modified value");
398 this->updateReaderUrl(this->
getBaseUrl(CDbFlags::DbReading));
403 bool overrideNewerOnly)
405 const QDir directory(dir);
406 if (!directory.exists()) {
return CStatusMessage(
this).
error(u
"Missing directory '%1'") << dir; }
408 whatToRead &= CEntityFlags::DistributorLiveryModel;
409 CEntityFlags::Entity reallyRead = CEntityFlags::NoEntity;
412 if (whatToRead.testFlag(CEntityFlags::LiveryEntity))
415 const QFileInfo fi(fileName);
424 const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
425 if (liveriesJson.isEmpty())
433 const CLiveryList liveries = CLiveryList::fromMultipleJsonFormats(liveriesJson);
434 const int c = liveries.
size();
435 msgs.
push_back(m_liveryCache.
set(liveries, fi.birthTime().toUTC().toMSecsSinceEpoch()));
436 emit this->
dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFinished, c, url);
437 reallyRead |= CEntityFlags::LiveryEntity;
441 emit this->
dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0, url);
443 ex,
this, QStringLiteral(
"Reading liveries from '%1'").arg(fileName)));
449 if (whatToRead.testFlag(CEntityFlags::ModelEntity))
452 const QFileInfo fi(fileName);
461 const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
463 if (modelsJson.isEmpty())
472 const int c = models.
size();
473 msgs.
push_back(m_modelCache.
set(models, fi.birthTime().toUTC().toMSecsSinceEpoch()));
474 emit this->
dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFinished, c, url);
475 reallyRead |= CEntityFlags::ModelEntity;
479 emit this->
dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0, url);
481 ex,
this, QStringLiteral(
"Reading models from '%1'").arg(fileName)));
487 if (whatToRead.testFlag(CEntityFlags::DistributorEntity))
490 const QFileInfo fi(fileName);
499 const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
501 if (distributorsJson.isEmpty())
511 const int c = distributors.
size();
513 m_distributorCache.
set(distributors, fi.birthTime().toUTC().toMSecsSinceEpoch()));
514 emit this->
dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFinished, c, url);
515 reallyRead |= CEntityFlags::DistributorEntity;
519 emit this->
dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0, url);
521 ex,
this, QStringLiteral(
"Reading distributors from '%1'").arg(fileName)));
531 bool overrideNewerOnly)
533 if (dir.isEmpty() || whatToRead == CEntityFlags::NoEntity) {
return false; }
535 QPointer<CModelDataReader> myself(
this);
537 if (!myself) {
return; }
547 if (!directory.exists()) {
return false; }
548 QList<QPair<QString, QString>> fileContents;
551 const QString json(QJsonDocument(this->
getLiveries().toJson()).toJson());
552 fileContents.push_back({
"liveries.json", json });
557 const QString json(QJsonDocument(this->
getModels().toJson()).toJson());
558 fileContents.push_back({
"models.json", json });
563 const QString json(QJsonDocument(this->
getDistributors().toJson()).toJson());
564 fileContents.push_back({
"distributors.json", json });
567 for (
const auto &pair : fileContents)
581 if (entities.testFlag(CEntityFlags::LiveryEntity))
583 if (m_syncedLiveryCache) {
return; }
584 m_syncedLiveryCache =
true;
587 if (entities.testFlag(CEntityFlags::ModelEntity))
589 if (m_syncedModelCache) {
return; }
590 m_syncedModelCache =
true;
593 if (entities.testFlag(CEntityFlags::DistributorEntity))
595 if (m_syncedDistributorCache) {
return; }
596 m_syncedDistributorCache =
true;
603 if (entities.testFlag(CEntityFlags::LiveryEntity)) { m_liveryCache.
admit(); }
604 if (entities.testFlag(CEntityFlags::ModelEntity)) { m_modelCache.
admit(); }
605 if (entities.testFlag(CEntityFlags::DistributorEntity)) { m_distributorCache.
admit(); }
610 if (entities.testFlag(CEntityFlags::LiveryEntity))
614 if (entities.testFlag(CEntityFlags::ModelEntity))
618 if (entities.testFlag(CEntityFlags::DistributorEntity))
631 default:
return QDateTime();
639 case CEntityFlags::LiveryEntity:
return m_liveryCache.
get().size();
640 case CEntityFlags::ModelEntity:
return m_modelCache.
get().size();
641 case CEntityFlags::DistributorEntity:
return m_distributorCache.
get().size();
648 CEntityFlags::Entity entities = CEntityFlags::NoEntity;
649 if (this->
getCacheCount(CEntityFlags::LiveryEntity) > 0) entities |= CEntityFlags::LiveryEntity;
650 if (this->
getCacheCount(CEntityFlags::ModelEntity) > 0) entities |= CEntityFlags::ModelEntity;
651 if (this->
getCacheCount(CEntityFlags::DistributorEntity) > 0) entities |= CEntityFlags::DistributorEntity;
657 CEntityFlags::Entity entities = CEntityFlags::NoEntity;
659 entities |= CEntityFlags::LiveryEntity;
661 entities |= CEntityFlags::ModelEntity;
663 entities |= CEntityFlags::DistributorEntity;
670 oldUrlInfo = m_readerUrlCache.
get();
671 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 everything 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?