swift
modeldatareader.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QDir>
7 #include <QElapsedTimer>
8 #include <QFileInfo>
9 #include <QFlags>
10 #include <QJsonDocument>
11 #include <QNetworkReply>
12 #include <QPointer>
13 #include <QReadLocker>
14 #include <QScopedPointer>
15 #include <QScopedPointerDeleteLater>
16 #include <QTimer>
17 #include <QUrl>
18 #include <QWriteLocker>
19 #include <Qt>
20 #include <QtGlobal>
21 
22 #include "core/application.h"
23 #include "core/data/globalsetup.h"
24 #include "core/db/databaseutils.h"
25 #include "core/webdataservices.h"
26 #include "misc/fileutils.h"
27 #include "misc/json.h"
28 #include "misc/logmessage.h"
29 #include "misc/statusmessage.h"
30 
31 using namespace swift::misc;
32 using namespace swift::misc::db;
33 using namespace swift::misc::aviation;
34 using namespace swift::misc::simulation;
35 using namespace swift::misc::network;
36 using namespace swift::core::data;
37 using namespace swift::core::db;
38 
39 namespace swift::core::db
40 {
41  CModelDataReader::CModelDataReader(QObject *owner, const CDatabaseReaderConfigList &config)
42  : CDatabaseReader(owner, config, "CModelDataReader")
43  {
44  // init to avoid threading issues
45  getBaseUrl(CDbFlags::DbReading);
46  }
47 
48  CLiveryList CModelDataReader::getLiveries() const { return m_liveryCache.get(); }
49 
50  CLivery CModelDataReader::getLiveryForCombinedCode(const QString &combinedCode) const
51  {
52  if (!CLivery::isValidCombinedCode(combinedCode)) { return CLivery(); }
53  const CLiveryList liveries(getLiveries());
54  return liveries.findByCombinedCode(combinedCode);
55  }
56 
58  {
59  if (!icao.hasValidDesignator()) { return CLivery(); }
60  const CLiveryList liveries(getLiveries());
61  return liveries.findStdLiveryByAirlineIcaoVDesignator(icao);
62  }
63 
65  {
66  if (id < 0) { return CLivery(); }
67  const CLiveryList liveries(getLiveries());
68  return liveries.findByKey(id);
69  }
70 
72  {
73  const CLiveryList liveries(this->getLiveries()); // thread safe copy
74  return liveries.smartLiverySelector(liveryPattern);
75  }
76 
77  CDistributorList CModelDataReader::getDistributors() const { return m_distributorCache.get(); }
78 
80  {
81  if (dbKey.isEmpty()) { return CDistributor(); }
82  const CDistributorList distributors(getDistributors());
83  return distributors.findByKeyOrAlias(dbKey);
84  }
85 
86  CAircraftModelList CModelDataReader::getModels() const { return m_modelCache.get(); }
87 
88  CAircraftModel CModelDataReader::getModelForModelString(const QString &modelString) const
89  {
90  if (modelString.isEmpty()) { return CAircraftModel(); }
91  const CAircraftModelList models(this->getModels());
92  return models.findFirstByModelStringOrDefault(modelString);
93  }
94 
95  bool CModelDataReader::containsModelString(const QString &modelString) const
96  {
97  if (modelString.isEmpty()) { return false; }
98  return this->getModels().containsModelString(modelString);
99  }
100 
102  {
103  if (dbKey < 0) { return CAircraftModel(); }
104  const CAircraftModelList models(this->getModels());
105  return models.findByKey(dbKey);
106  }
107 
109  {
110  if (!code.hasValidDesignator()) { return QSet<QString>(); }
111  const CAircraftModelList models(this->getModels());
112  return models.getAircraftDesignatorsForAirline(code);
113  }
114 
116  {
117  if (!code.hasValidDesignator()) { return CAircraftIcaoCodeList(); }
118  const CAircraftModelList models(this->getModels());
119  return models.getAicraftIcaoCodesForAirline(code);
120  }
121 
124  const QString &combinedCode)
125  {
126  if (aircraftDesignator.isEmpty()) { return CAircraftModelList(); }
127  const CAircraftModelList models(this->getModels());
128  return models.findByAircraftDesignatorAndLiveryCombinedCode(aircraftDesignator, combinedCode);
129  }
130 
131  int CModelDataReader::getLiveriesCount() const { return this->getLiveries().size(); }
132 
134 
136  {
137  const CDistributorList distributors(getDistributors()); // thread safe copy
138  return distributors.smartDistributorSelector(distributorPattern);
139  }
140 
142  const CAircraftModel &model) const
143  {
144  const CDistributorList distributors(getDistributors()); // thread safe copy
145  return distributors.smartDistributorSelector(distributorPattern, model);
146  }
147 
148  int CModelDataReader::getModelsCount() const { return this->getModels().size(); }
149 
150  QSet<int> CModelDataReader::getModelDbKeys() const { return this->getModels().toDbKeySet(); }
151 
152  QStringList CModelDataReader::getModelStringList(bool sort) const
153  {
154  return this->getModels().getModelStringList(sort);
155  }
156 
158  {
159  return this->getLiveriesCount() > 0 && this->getModelsCount() > 0 && this->getDistributorsCount() > 0;
160  }
161 
162  void CModelDataReader::read(CEntityFlags::Entity entities, CDbFlags::DataRetrievalModeFlag mode,
163  const QDateTime &newerThan)
164  {
165  this->threadAssertCheck();
166  if (!this->doWorkCheck()) { return; }
167  entities &= CEntityFlags::DistributorLiveryModel;
168 
169  CEntityFlags::Entity triggeredRead = CEntityFlags::NoEntity;
170  CUrl url;
171 
172  if (entities.testFlag(CEntityFlags::LiveryEntity))
173  {
174  url = this->getLiveryUrl(mode);
175  if (!url.isEmpty())
176  {
177  url.appendQuery(queryLatestTimestamp(newerThan));
178  this->getFromNetworkAndLog(url, { this, &CModelDataReader::parseLiveryData });
179  triggeredRead |= CEntityFlags::LiveryEntity;
180  }
181  else { this->logNoWorkingUrl(CEntityFlags::LiveryEntity); }
182  }
183 
184  if (entities.testFlag(CEntityFlags::DistributorEntity))
185  {
186  url = this->getDistributorUrl(mode);
187  if (!url.isEmpty())
188  {
189  url.appendQuery(queryLatestTimestamp(newerThan));
190  this->getFromNetworkAndLog(url, { this, &CModelDataReader::parseDistributorData });
191  triggeredRead |= CEntityFlags::DistributorEntity;
192  }
193  else { this->logNoWorkingUrl(CEntityFlags::DistributorEntity); }
194  }
195 
196  if (entities.testFlag(CEntityFlags::ModelEntity))
197  {
198  url = this->getModelUrl(mode);
199  if (!url.isEmpty())
200  {
201  url.appendQuery(queryLatestTimestamp(newerThan));
202  this->getFromNetworkAndLog(url, { this, &CModelDataReader::parseModelData });
203  triggeredRead |= CEntityFlags::ModelEntity;
204  }
205  else { this->logNoWorkingUrl(CEntityFlags::ModelEntity); }
206  }
207 
208  if (triggeredRead != CEntityFlags::NoEntity)
209  {
210  emit dataRead(triggeredRead, CEntityFlags::ReadStarted, 0, url);
211  }
212  }
213 
214  void CModelDataReader::liveryCacheChanged() { this->cacheHasChanged(CEntityFlags::LiveryEntity); }
215 
216  void CModelDataReader::modelCacheChanged() { this->cacheHasChanged(CEntityFlags::ModelEntity); }
217 
218  void CModelDataReader::distributorCacheChanged() { this->cacheHasChanged(CEntityFlags::DistributorEntity); }
219 
220  void CModelDataReader::baseUrlCacheChanged()
221  {
222  // void
223  }
224 
225  void CModelDataReader::updateReaderUrl(const CUrl &url)
226  {
227  const CUrl current = m_readerUrlCache.get();
228  if (current == url) { return; }
229  const CStatusMessage m = m_readerUrlCache.set(url);
230  if (m.isFailure()) { CLogMessage::preformatted(m); }
231  }
232 
233  CAircraftIcaoCodeList CModelDataReader::getAircraftAircraftIcaos() const
234  {
235  if (!sApp || sApp->isShuttingDown() || !sApp->getWebDataServices()) { return CAircraftIcaoCodeList(); }
237  }
238 
239  CAircraftCategoryList CModelDataReader::getAircraftCategories() const
240  {
241  if (!sApp || sApp->isShuttingDown() || !sApp->getWebDataServices()) { return CAircraftCategoryList(); }
243  }
244 
245  void CModelDataReader::parseLiveryData(QNetworkReply *nwReplyPtr)
246  {
247  // wrap pointer, make sure any exit cleans up reply
248  // required to use delete later as object is created in a different thread
249  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
250  if (!this->doWorkCheck()) { return; }
253  if (res.hasErrorMessage())
254  {
256  emit dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0, res.getUrl());
257  return;
258  }
259 
260  // get all or incremental set of distributor
261  emit this->dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadParsing, 0, res.getUrl());
262  CLiveryList liveries;
263  if (res.isRestricted())
264  {
265  // create full list if it was just incremental
266  const CLiveryList incrementalLiveries(CLiveryList::fromDatabaseJson(res));
267  if (incrementalLiveries.isEmpty()) { return; } // currenty ignored
268  liveries = this->getLiveries();
269  liveries.replaceOrAddObjectsByKey(incrementalLiveries);
270  }
271  else
272  {
273  QElapsedTimer time;
274  time.start();
275  liveries = CLiveryList::fromDatabaseJson(res);
276  this->logParseMessage("liveries", liveries.size(), static_cast<int>(time.elapsed()), res);
277  }
278 
279  if (!this->doWorkCheck()) { return; }
280  const int n = liveries.size();
281  qint64 latestTimestamp = liveries.latestTimestampMsecsSinceEpoch();
282  if (n > 0 && latestTimestamp < 0)
283  {
284  CLogMessage(this).error(u"No timestamp in livery list, setting to last modified value");
285  latestTimestamp = lastModifiedMsSinceEpoch(nwReply.data());
286  }
287  const CStatusMessage cacheMsg = m_liveryCache.set(liveries, latestTimestamp);
288  CLogMessage::preformatted(cacheMsg);
289 
290  this->updateReaderUrl(getBaseUrl(CDbFlags::DbReading));
291  this->emitAndLogDataRead(CEntityFlags::LiveryEntity, n, res);
292  }
293 
294  void CModelDataReader::parseDistributorData(QNetworkReply *nwReplyPtr)
295  {
296  // wrap pointer, make sure any exit cleans up reply
297  // required to use delete later as object is created in a different thread
298  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
299  if (!this->doWorkCheck()) { return; }
302  if (res.hasErrorMessage())
303  {
305  emit dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0, res.getUrl());
306  return;
307  }
308 
309  // get all or incremental set of distributors
310  emit this->dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadParsing, 0, res.getUrl());
311  CDistributorList distributors;
312  if (res.isRestricted())
313  {
314  // create full list if it was just incremental
315  const CDistributorList incrementalDistributors(CDistributorList::fromDatabaseJson(res));
316  if (incrementalDistributors.isEmpty()) { return; } // currently ignored
317  distributors = this->getDistributors();
318  distributors.replaceOrAddObjectsByKey(incrementalDistributors);
319  }
320  else
321  {
322  QElapsedTimer time;
323  time.start();
324  distributors = CDistributorList::fromDatabaseJson(res);
325  this->logParseMessage("distributors", distributors.size(), static_cast<int>(time.elapsed()), res);
326  }
327 
328  if (!this->doWorkCheck()) { return; }
329  const int n = distributors.size();
330  qint64 latestTimestamp = distributors.latestTimestampMsecsSinceEpoch();
331  if (n > 0 && latestTimestamp < 0)
332  {
333  CLogMessage(this).error(u"No timestamp in distributor list, setting to last modified value");
334  latestTimestamp = lastModifiedMsSinceEpoch(nwReply.data());
335  }
336 
337  const CStatusMessage cacheMsg = m_distributorCache.set(distributors, latestTimestamp);
338  CLogMessage::preformatted(cacheMsg);
339 
340  this->updateReaderUrl(getBaseUrl(CDbFlags::DbReading));
341  this->emitAndLogDataRead(CEntityFlags::DistributorEntity, n, res);
342  }
343 
344  void CModelDataReader::parseModelData(QNetworkReply *nwReplyPtr)
345  {
346  // wrap pointer, make sure any exit cleans up reply
347  // required to use delete later as object is created in a different thread
348  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
349  if (!this->doWorkCheck()) { return; }
352  if (res.hasErrorMessage())
353  {
355  emit this->dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0, res.getUrl());
356  return;
357  }
358 
359  // get all or incremental set of models
360  emit this->dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadParsing, 0, res.getUrl());
361 
362  // use prefilled data:
363  // this saves a lot of parsing time as the models do not need to re-parse the sub parts
364  // but can use objects directly
365  const CAircraftCategoryList categories = this->getAircraftCategories();
366  const CLiveryList liveries = this->getLiveries();
367  const CAircraftIcaoCodeList icaos = this->getAircraftAircraftIcaos();
368  const CDistributorList distributors = this->getDistributors();
369 
370  CAircraftModelList models;
371  if (res.isRestricted())
372  {
373  // create full list if it was just incremental
374  const CAircraftModelList incrementalModels(
375  CAircraftModelList::fromDatabaseJsonCaching(res, icaos, categories, liveries, distributors));
376  if (incrementalModels.isEmpty()) { return; } // currently ignored
377  models = this->getModels();
378  models.replaceOrAddObjectsByKey(incrementalModels);
379  }
380  else
381  {
382  QElapsedTimer time;
383  time.start();
384  models = CAircraftModelList::fromDatabaseJsonCaching(res, icaos, categories, liveries, distributors);
385  this->logParseMessage("models", models.size(), static_cast<int>(time.elapsed()), res);
386  }
387 
388  // synchronized update
389  if (!this->doWorkCheck()) { return; }
390  const int n = models.size();
391  qint64 latestTimestamp = models.latestTimestampMsecsSinceEpoch();
392  if (n > 0 && latestTimestamp < 0)
393  {
394  CLogMessage(this).error(u"No timestamp in model list, setting to last modified value");
395  latestTimestamp = lastModifiedMsSinceEpoch(nwReply.data());
396  }
397  const CStatusMessage cacheMsg = m_modelCache.set(models, latestTimestamp);
398  CLogMessage::preformatted(cacheMsg);
399 
400  this->updateReaderUrl(this->getBaseUrl(CDbFlags::DbReading));
401  this->emitAndLogDataRead(CEntityFlags::ModelEntity, n, res);
402  }
403 
404  CStatusMessageList CModelDataReader::readFromJsonFiles(const QString &dir, CEntityFlags::Entity whatToRead,
405  bool overrideNewerOnly)
406  {
407  const QDir directory(dir);
408  if (!directory.exists()) { return CStatusMessage(this).error(u"Missing directory '%1'") << dir; }
409 
410  whatToRead &= CEntityFlags::DistributorLiveryModel; // supported
411  CEntityFlags::Entity reallyRead = CEntityFlags::NoEntity;
412 
413  CStatusMessageList msgs;
414  if (whatToRead.testFlag(CEntityFlags::LiveryEntity))
415  {
416  const QString fileName = CFileUtils::appendFilePaths(directory.absolutePath(), "liveries.json");
417  const QFileInfo fi(fileName);
418  if (!fi.exists()) { msgs.push_back(CStatusMessage(this).warning(u"File '%1' does not exist") << fileName); }
419  else if (!this->overrideCacheFromFile(overrideNewerOnly, fi, CEntityFlags::LiveryEntity, msgs))
420  {
421  // void
422  }
423  else
424  {
425  const QJsonObject liveriesJson(CDatabaseUtils::readQJsonObjectFromDatabaseFile(fileName));
426  const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
427  if (liveriesJson.isEmpty())
428  {
429  msgs.push_back(CStatusMessage(this).error(u"Failed to read from file/empty file '%1'") << fileName);
430  }
431  else
432  {
433  try
434  {
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;
440  }
441  catch (const CJsonException &ex)
442  {
443  emit this->dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0, url);
445  ex, this, QStringLiteral("Reading liveries from '%1'").arg(fileName)));
446  }
447  }
448  }
449  }
450 
451  if (whatToRead.testFlag(CEntityFlags::ModelEntity))
452  {
453  const QString fileName = CFileUtils::appendFilePaths(directory.absolutePath(), "models.json");
454  const QFileInfo fi(fileName);
455  if (!fi.exists()) { msgs.push_back(CStatusMessage(this).warning(u"File '%1' does not exist") << fileName); }
456  else if (!this->overrideCacheFromFile(overrideNewerOnly, fi, CEntityFlags::ModelEntity, msgs))
457  {
458  // void
459  }
460  else
461  {
462  const QJsonObject modelsJson(CDatabaseUtils::readQJsonObjectFromDatabaseFile(fileName));
463  const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
464 
465  if (modelsJson.isEmpty())
466  {
467  msgs.push_back(CStatusMessage(this).error(u"Failed to read from file/empty file '%1'") << fileName);
468  }
469  else
470  {
471  try
472  {
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;
478  }
479  catch (const CJsonException &ex)
480  {
481  emit this->dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0, url);
483  ex, this, QStringLiteral("Reading models from '%1'").arg(fileName)));
484  }
485  }
486  }
487  }
488 
489  if (whatToRead.testFlag(CEntityFlags::DistributorEntity))
490  {
491  const QString fileName = CFileUtils::appendFilePaths(directory.absolutePath(), "distributors.json");
492  const QFileInfo fi(fileName);
493  if (!fi.exists()) { msgs.push_back(CStatusMessage(this).warning(u"File '%1' does not exist") << fileName); }
494  else if (!this->overrideCacheFromFile(overrideNewerOnly, fi, CEntityFlags::DistributorEntity, msgs))
495  {
496  // void
497  }
498  else
499  {
500  const QJsonObject distributorsJson(CDatabaseUtils::readQJsonObjectFromDatabaseFile(fileName));
501  const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
502 
503  if (distributorsJson.isEmpty())
504  {
505  msgs.push_back(CStatusMessage(this).error(u"Failed to read from file/empty file '%1'") << fileName);
506  }
507  else
508  {
509  try
510  {
511  const CDistributorList distributors =
513  const int c = distributors.size();
514  msgs.push_back(
515  m_distributorCache.set(distributors, fi.birthTime().toUTC().toMSecsSinceEpoch()));
516  emit this->dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFinished, c, url);
517  reallyRead |= CEntityFlags::DistributorEntity;
518  }
519  catch (const CJsonException &ex)
520  {
521  emit this->dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0, url);
523  ex, this, QStringLiteral("Reading distributors from '%1'").arg(fileName)));
524  }
525  }
526  }
527  }
528 
529  return msgs;
530  }
531 
532  bool CModelDataReader::readFromJsonFilesInBackground(const QString &dir, CEntityFlags::Entity whatToRead,
533  bool overrideNewerOnly)
534  {
535  if (dir.isEmpty() || whatToRead == CEntityFlags::NoEntity) { return false; }
536 
537  QPointer<CModelDataReader> myself(this);
538  QTimer::singleShot(0, this, [=]() {
539  if (!myself) { return; }
540  const CStatusMessageList msgs = this->readFromJsonFiles(dir, whatToRead, overrideNewerOnly);
541  if (msgs.isFailure()) { CLogMessage::preformatted(msgs); }
542  });
543  return true;
544  }
545 
546  bool CModelDataReader::writeToJsonFiles(const QString &dir)
547  {
548  QDir directory(dir);
549  if (!directory.exists()) { return false; }
550  QList<QPair<QString, QString>> fileContents;
551  if (this->getLiveriesCount() > 0)
552  {
553  const QString json(QJsonDocument(this->getLiveries().toJson()).toJson());
554  fileContents.push_back({ "liveries.json", json });
555  }
556 
557  if (this->getModelsCount() > 0)
558  {
559  const QString json(QJsonDocument(this->getModels().toJson()).toJson());
560  fileContents.push_back({ "models.json", json });
561  }
562 
563  if (this->getDistributorsCount() > 0)
564  {
565  const QString json(QJsonDocument(this->getDistributors().toJson()).toJson());
566  fileContents.push_back({ "distributors.json", json });
567  }
568 
569  for (const auto &pair : fileContents)
570  {
571  CWorker::fromTask(this, Q_FUNC_INFO, [pair, directory] {
572  CFileUtils::writeStringToFile(CFileUtils::appendFilePaths(directory.absolutePath(), pair.first),
573  pair.second);
574  });
575  }
576  return true;
577  }
578 
579  CEntityFlags::Entity CModelDataReader::getSupportedEntities() const { return CEntityFlags::DistributorLiveryModel; }
580 
581  void CModelDataReader::synchronizeCaches(CEntityFlags::Entity entities)
582  {
583  if (entities.testFlag(CEntityFlags::LiveryEntity))
584  {
585  if (m_syncedLiveryCache) { return; }
586  m_syncedLiveryCache = true;
587  m_liveryCache.synchronize();
588  }
589  if (entities.testFlag(CEntityFlags::ModelEntity))
590  {
591  if (m_syncedModelCache) { return; }
592  m_syncedModelCache = true;
593  m_modelCache.synchronize();
594  }
595  if (entities.testFlag(CEntityFlags::DistributorEntity))
596  {
597  if (m_syncedDistributorCache) { return; }
598  m_syncedDistributorCache = true;
599  m_distributorCache.synchronize();
600  }
601  }
602 
603  void CModelDataReader::admitCaches(CEntityFlags::Entity entities)
604  {
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(); }
608  }
609 
610  void CModelDataReader::invalidateCaches(CEntityFlags::Entity entities)
611  {
612  if (entities.testFlag(CEntityFlags::LiveryEntity))
613  {
614  CDataCache::instance()->clearAllValues(m_liveryCache.getKey());
615  }
616  if (entities.testFlag(CEntityFlags::ModelEntity))
617  {
618  CDataCache::instance()->clearAllValues(m_modelCache.getKey());
619  }
620  if (entities.testFlag(CEntityFlags::DistributorEntity))
621  {
622  CDataCache::instance()->clearAllValues(m_distributorCache.getKey());
623  }
624  }
625 
626  QDateTime CModelDataReader::getCacheTimestamp(CEntityFlags::Entity entity) const
627  {
628  switch (entity)
629  {
630  case CEntityFlags::LiveryEntity: return m_liveryCache.getAvailableTimestamp();
631  case CEntityFlags::ModelEntity: return m_modelCache.getAvailableTimestamp();
632  case CEntityFlags::DistributorEntity: return m_distributorCache.getAvailableTimestamp();
633  default: return QDateTime();
634  }
635  }
636 
637  int CModelDataReader::getCacheCount(CEntityFlags::Entity entity) const
638  {
639  switch (entity)
640  {
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();
644  default: return 0;
645  }
646  }
647 
648  CEntityFlags::Entity CModelDataReader::getEntitiesWithCacheCount() const
649  {
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;
654  return entities;
655  }
656 
657  CEntityFlags::Entity CModelDataReader::getEntitiesWithCacheTimestampNewerThan(const QDateTime &threshold) const
658  {
659  CEntityFlags::Entity entities = CEntityFlags::NoEntity;
660  if (this->hasCacheTimestampNewerThan(CEntityFlags::LiveryEntity, threshold))
661  entities |= CEntityFlags::LiveryEntity;
662  if (this->hasCacheTimestampNewerThan(CEntityFlags::ModelEntity, threshold))
663  entities |= CEntityFlags::ModelEntity;
664  if (this->hasCacheTimestampNewerThan(CEntityFlags::DistributorEntity, threshold))
665  entities |= CEntityFlags::DistributorEntity;
666  return entities;
667  }
668 
669  bool CModelDataReader::hasChangedUrl(CEntityFlags::Entity entity, CUrl &oldUrlInfo, CUrl &newUrlInfo) const
670  {
671  Q_UNUSED(entity)
672  oldUrlInfo = m_readerUrlCache.get();
673  newUrlInfo = this->getBaseUrl(CDbFlags::DbReading);
674  return CDatabaseReader::isChangedUrl(oldUrlInfo, newUrlInfo);
675  }
676 
678 
679  CUrl CModelDataReader::getLiveryUrl(CDbFlags::DataRetrievalModeFlag mode) const
680  {
681  return getBaseUrl(mode).withAppendedPath(fileNameForMode(CEntityFlags::LiveryEntity, mode));
682  }
683 
684  CUrl CModelDataReader::getDistributorUrl(CDbFlags::DataRetrievalModeFlag mode) const
685  {
686  return getBaseUrl(mode).withAppendedPath(fileNameForMode(CEntityFlags::DistributorEntity, mode));
687  }
688 
689  CUrl CModelDataReader::getModelUrl(CDbFlags::DataRetrievalModeFlag mode) const
690  {
691  return getBaseUrl(mode).withAppendedPath(fileNameForMode(CEntityFlags::ModelEntity, mode));
692  }
693 } // namespace swift::core::db
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
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.
Definition: globalsetup.cpp:35
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 &currentUrl)
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.
Definition: valuecache.h:445
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
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.
Definition: datacache.h:350
void admit()
If the value is load-deferred, trigger the deferred load (async).
Definition: datacache.h:393
QDateTime getAvailableTimestamp() const
Get the timestamp of the value, or of the deferred value that is available to be loaded.
Definition: datacache.h:383
void synchronize()
If the value is currently being loaded, wait for it to finish loading, and call the notification slot...
Definition: datacache.h:400
static bool writeStringToFile(const QString &content, const QString &fileNameAndPath)
Write string to text file.
Definition: fileutils.cpp:40
static QString appendFilePaths(const QString &path1, const QString &path2)
Append file paths.
Definition: fileutils.cpp:95
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Definition: jsonexception.h:24
Class for emitting a log message.
Definition: logmessage.h:27
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.
Definition: sequence.h:273
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
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.
Definition: worker.h:201
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.
Definition: livery.h:29
Value object for a list of airports.
Definition: liverylist.h:29
CLivery smartLiverySelector(const CLivery &liveryPattern) const
Find by multiple criteria.
Definition: liverylist.cpp:144
CLivery findStdLiveryByAirlineIcaoVDesignator(const QString &icao) const
Find livery by airline.
Definition: liverylist.cpp:49
CLivery findByCombinedCode(const QString &combinedCode) const
Find livery by combined code.
Definition: liverylist.cpp:106
DataRetrievalModeFlag
Which data to read, requires corresponding readers.
Definition: dbflags.h:25
int replaceOrAddObjectsByKey(const CONTAINER &container)
Update or insert data (based on DB key)
OBJ findByKey(KEYTYPE key, const OBJ &notFound=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...
Definition: url.h:27
bool isEmpty() const
Empty.
Definition: url.cpp:54
CUrl withAppendedPath(const QString &path) const
Append path.
Definition: url.cpp:115
void appendQuery(const QString &queryToAppend)
Append query.
Definition: url.cpp:66
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
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.
Definition: distributor.h:33
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...
Definition: threadutils.h:30
const swift::misc::network::CUrl & getUrl() const
URL loaded.
const swift::misc::CStatusMessage & lastWarningOrAbove() const
Last error or warning.
Response from our database (depending on JSON DB backend generates)
bool isRestricted() const
Incremental data, restricted by query?