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 
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 
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 
151 
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  if (entities.testFlag(CEntityFlags::LiveryEntity))
171  {
172  CUrl url = this->getLiveryUrl(mode);
173  if (!url.isEmpty())
174  {
175  url.appendQuery(queryLatestTimestamp(newerThan));
176  this->getFromNetworkAndLog(url, { this, &CModelDataReader::parseLiveryData });
177  triggeredRead |= CEntityFlags::LiveryEntity;
178  }
179  else { this->logNoWorkingUrl(CEntityFlags::LiveryEntity); }
180  }
181 
182  if (entities.testFlag(CEntityFlags::DistributorEntity))
183  {
184  CUrl url = this->getDistributorUrl(mode);
185  if (!url.isEmpty())
186  {
187  url.appendQuery(queryLatestTimestamp(newerThan));
188  this->getFromNetworkAndLog(url, { this, &CModelDataReader::parseDistributorData });
189  triggeredRead |= CEntityFlags::DistributorEntity;
190  }
191  else { this->logNoWorkingUrl(CEntityFlags::DistributorEntity); }
192  }
193 
194  if (entities.testFlag(CEntityFlags::ModelEntity))
195  {
196  CUrl url = this->getModelUrl(mode);
197  if (!url.isEmpty())
198  {
199  url.appendQuery(queryLatestTimestamp(newerThan));
200  this->getFromNetworkAndLog(url, { this, &CModelDataReader::parseModelData });
201  triggeredRead |= CEntityFlags::ModelEntity;
202  }
203  else { this->logNoWorkingUrl(CEntityFlags::ModelEntity); }
204  }
205 
206  if (triggeredRead != CEntityFlags::NoEntity)
207  {
208  emit dataRead(triggeredRead, CEntityFlags::ReadStarted, 0, getBaseUrl(mode));
209  }
210  }
211 
212  void CModelDataReader::liveryCacheChanged() { this->cacheHasChanged(CEntityFlags::LiveryEntity); }
213 
214  void CModelDataReader::modelCacheChanged() { this->cacheHasChanged(CEntityFlags::ModelEntity); }
215 
216  void CModelDataReader::distributorCacheChanged() { this->cacheHasChanged(CEntityFlags::DistributorEntity); }
217 
218  void CModelDataReader::baseUrlCacheChanged()
219  {
220  // void
221  }
222 
223  void CModelDataReader::updateReaderUrl(const CUrl &url)
224  {
225  const CUrl current = m_readerUrlCache.get();
226  if (current == url) { return; }
227  const CStatusMessage m = m_readerUrlCache.set(url);
228  if (m.isFailure()) { CLogMessage::preformatted(m); }
229  }
230 
231  CAircraftIcaoCodeList CModelDataReader::getAircraftAircraftIcaos() const
232  {
233  if (!sApp || sApp->isShuttingDown() || !sApp->getWebDataServices()) { return CAircraftIcaoCodeList(); }
235  }
236 
237  CAircraftCategoryList CModelDataReader::getAircraftCategories() const
238  {
239  if (!sApp || sApp->isShuttingDown() || !sApp->getWebDataServices()) { return CAircraftCategoryList(); }
241  }
242 
243  void CModelDataReader::parseLiveryData(QNetworkReply *nwReplyPtr)
244  {
245  // wrap pointer, make sure any exit cleans up reply
246  // required to use delete later as object is created in a different thread
248  if (!this->doWorkCheck()) { return; }
251  if (res.hasErrorMessage())
252  {
254  emit dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0, res.getUrl());
255  return;
256  }
257 
258  // get all or incremental set of distributor
259  emit this->dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadParsing, 0, res.getUrl());
260  CLiveryList liveries;
261  if (res.isRestricted())
262  {
263  // create full list if it was just incremental
264  const CLiveryList incrementalLiveries(CLiveryList::fromDatabaseJson(res));
265  if (incrementalLiveries.isEmpty()) { return; } // currenty ignored
266  liveries = this->getLiveries();
267  liveries.replaceOrAddObjectsByKey(incrementalLiveries);
268  }
269  else
270  {
271  QElapsedTimer time;
272  time.start();
273  liveries = CLiveryList::fromDatabaseJson(res);
274  this->logParseMessage("liveries", liveries.size(), static_cast<int>(time.elapsed()), res);
275  }
276 
277  if (!this->doWorkCheck()) { return; }
278  const int n = liveries.size();
279  qint64 latestTimestamp = liveries.latestTimestampMsecsSinceEpoch();
280  if (n > 0 && latestTimestamp < 0)
281  {
282  CLogMessage(this).error(u"No timestamp in livery list, setting to last modified value");
283  latestTimestamp = lastModifiedMsSinceEpoch(nwReply.data());
284  }
285  const CStatusMessage cacheMsg = m_liveryCache.set(liveries, latestTimestamp);
286  CLogMessage::preformatted(cacheMsg);
287 
288  this->updateReaderUrl(getBaseUrl(CDbFlags::DbReading));
289  this->emitAndLogDataRead(CEntityFlags::LiveryEntity, n, res);
290  }
291 
292  void CModelDataReader::parseDistributorData(QNetworkReply *nwReplyPtr)
293  {
294  // wrap pointer, make sure any exit cleans up reply
295  // required to use delete later as object is created in a different thread
297  if (!this->doWorkCheck()) { return; }
300  if (res.hasErrorMessage())
301  {
303  emit dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0, res.getUrl());
304  return;
305  }
306 
307  // get all or incremental set of distributors
308  emit this->dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadParsing, 0, res.getUrl());
309  CDistributorList distributors;
310  if (res.isRestricted())
311  {
312  // create full list if it was just incremental
313  const CDistributorList incrementalDistributors(CDistributorList::fromDatabaseJson(res));
314  if (incrementalDistributors.isEmpty()) { return; } // currently ignored
315  distributors = this->getDistributors();
316  distributors.replaceOrAddObjectsByKey(incrementalDistributors);
317  }
318  else
319  {
320  QElapsedTimer time;
321  time.start();
322  distributors = CDistributorList::fromDatabaseJson(res);
323  this->logParseMessage("distributors", distributors.size(), static_cast<int>(time.elapsed()), res);
324  }
325 
326  if (!this->doWorkCheck()) { return; }
327  const int n = distributors.size();
328  qint64 latestTimestamp = distributors.latestTimestampMsecsSinceEpoch();
329  if (n > 0 && latestTimestamp < 0)
330  {
331  CLogMessage(this).error(u"No timestamp in distributor list, setting to last modified value");
332  latestTimestamp = lastModifiedMsSinceEpoch(nwReply.data());
333  }
334 
335  const CStatusMessage cacheMsg = m_distributorCache.set(distributors, latestTimestamp);
336  CLogMessage::preformatted(cacheMsg);
337 
338  this->updateReaderUrl(getBaseUrl(CDbFlags::DbReading));
339  this->emitAndLogDataRead(CEntityFlags::DistributorEntity, n, res);
340  }
341 
342  void CModelDataReader::parseModelData(QNetworkReply *nwReplyPtr)
343  {
344  // wrap pointer, make sure any exit cleans up reply
345  // required to use delete later as object is created in a different thread
347  if (!this->doWorkCheck()) { return; }
350  if (res.hasErrorMessage())
351  {
353  emit this->dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0, res.getUrl());
354  return;
355  }
356 
357  // get all or incremental set of models
358  emit this->dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadParsing, 0, res.getUrl());
359 
360  // use prefilled data:
361  // this saves a lot of parsing time as the models do not need to re-parse the sub parts
362  // but can use objects directly
363  const CAircraftCategoryList categories = this->getAircraftCategories();
364  const CLiveryList liveries = this->getLiveries();
365  const CAircraftIcaoCodeList icaos = this->getAircraftAircraftIcaos();
366  const CDistributorList distributors = this->getDistributors();
367 
368  CAircraftModelList models;
369  if (res.isRestricted())
370  {
371  // create full list if it was just incremental
372  const CAircraftModelList incrementalModels(
373  CAircraftModelList::fromDatabaseJsonCaching(res, icaos, categories, liveries, distributors));
374  if (incrementalModels.isEmpty()) { return; } // currently ignored
375  models = this->getModels();
376  models.replaceOrAddObjectsByKey(incrementalModels);
377  }
378  else
379  {
380  QElapsedTimer time;
381  time.start();
382  models = CAircraftModelList::fromDatabaseJsonCaching(res, icaos, categories, liveries, distributors);
383  this->logParseMessage("models", models.size(), static_cast<int>(time.elapsed()), res);
384  }
385 
386  // synchronized update
387  if (!this->doWorkCheck()) { return; }
388  const int n = models.size();
389  qint64 latestTimestamp = models.latestTimestampMsecsSinceEpoch();
390  if (n > 0 && latestTimestamp < 0)
391  {
392  CLogMessage(this).error(u"No timestamp in model list, setting to last modified value");
393  latestTimestamp = lastModifiedMsSinceEpoch(nwReply.data());
394  }
395  const CStatusMessage cacheMsg = m_modelCache.set(models, latestTimestamp);
396  CLogMessage::preformatted(cacheMsg);
397 
398  this->updateReaderUrl(this->getBaseUrl(CDbFlags::DbReading));
399  this->emitAndLogDataRead(CEntityFlags::ModelEntity, n, res);
400  }
401 
402  CStatusMessageList CModelDataReader::readFromJsonFiles(const QString &dir, CEntityFlags::Entity whatToRead,
403  bool overrideNewerOnly)
404  {
405  const QDir directory(dir);
406  if (!directory.exists()) { return CStatusMessage(this).error(u"Missing directory '%1'") << dir; }
407 
408  whatToRead &= CEntityFlags::DistributorLiveryModel; // supported
409  CEntityFlags::Entity reallyRead = CEntityFlags::NoEntity;
410 
411  CStatusMessageList msgs;
412  if (whatToRead.testFlag(CEntityFlags::LiveryEntity))
413  {
414  const QString fileName = CFileUtils::appendFilePaths(directory.absolutePath(), "liveries.json");
415  const QFileInfo fi(fileName);
416  if (!fi.exists()) { msgs.push_back(CStatusMessage(this).warning(u"File '%1' does not exist") << fileName); }
417  else if (!this->overrideCacheFromFile(overrideNewerOnly, fi, CEntityFlags::LiveryEntity, msgs))
418  {
419  // void
420  }
421  else
422  {
423  const QJsonObject liveriesJson(CDatabaseUtils::readQJsonObjectFromDatabaseFile(fileName));
424  const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
425  if (liveriesJson.isEmpty())
426  {
427  msgs.push_back(CStatusMessage(this).error(u"Failed to read from file/empty file '%1'") << fileName);
428  }
429  else
430  {
431  try
432  {
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;
438  }
439  catch (const CJsonException &ex)
440  {
441  emit this->dataRead(CEntityFlags::LiveryEntity, CEntityFlags::ReadFailed, 0, url);
443  ex, this, QStringLiteral("Reading liveries from '%1'").arg(fileName)));
444  }
445  }
446  }
447  }
448 
449  if (whatToRead.testFlag(CEntityFlags::ModelEntity))
450  {
451  const QString fileName = CFileUtils::appendFilePaths(directory.absolutePath(), "models.json");
452  const QFileInfo fi(fileName);
453  if (!fi.exists()) { msgs.push_back(CStatusMessage(this).warning(u"File '%1' does not exist") << fileName); }
454  else if (!this->overrideCacheFromFile(overrideNewerOnly, fi, CEntityFlags::ModelEntity, msgs))
455  {
456  // void
457  }
458  else
459  {
461  const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
462 
463  if (modelsJson.isEmpty())
464  {
465  msgs.push_back(CStatusMessage(this).error(u"Failed to read from file/empty file '%1'") << fileName);
466  }
467  else
468  {
469  try
470  {
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;
476  }
477  catch (const CJsonException &ex)
478  {
479  emit this->dataRead(CEntityFlags::ModelEntity, CEntityFlags::ReadFailed, 0, url);
481  ex, this, QStringLiteral("Reading models from '%1'").arg(fileName)));
482  }
483  }
484  }
485  }
486 
487  if (whatToRead.testFlag(CEntityFlags::DistributorEntity))
488  {
489  const QString fileName = CFileUtils::appendFilePaths(directory.absolutePath(), "distributors.json");
490  const QFileInfo fi(fileName);
491  if (!fi.exists()) { msgs.push_back(CStatusMessage(this).warning(u"File '%1' does not exist") << fileName); }
492  else if (!this->overrideCacheFromFile(overrideNewerOnly, fi, CEntityFlags::DistributorEntity, msgs))
493  {
494  // void
495  }
496  else
497  {
498  const QJsonObject distributorsJson(CDatabaseUtils::readQJsonObjectFromDatabaseFile(fileName));
499  const QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath());
500 
501  if (distributorsJson.isEmpty())
502  {
503  msgs.push_back(CStatusMessage(this).error(u"Failed to read from file/empty file '%1'") << fileName);
504  }
505  else
506  {
507  try
508  {
509  const CDistributorList distributors =
511  const int c = distributors.size();
512  msgs.push_back(
513  m_distributorCache.set(distributors, fi.birthTime().toUTC().toMSecsSinceEpoch()));
514  emit this->dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFinished, c, url);
515  reallyRead |= CEntityFlags::DistributorEntity;
516  }
517  catch (const CJsonException &ex)
518  {
519  emit this->dataRead(CEntityFlags::DistributorEntity, CEntityFlags::ReadFailed, 0, url);
521  ex, this, QStringLiteral("Reading distributors from '%1'").arg(fileName)));
522  }
523  }
524  }
525  }
526 
527  return msgs;
528  }
529 
530  bool CModelDataReader::readFromJsonFilesInBackground(const QString &dir, CEntityFlags::Entity whatToRead,
531  bool overrideNewerOnly)
532  {
533  if (dir.isEmpty() || whatToRead == CEntityFlags::NoEntity) { return false; }
534 
535  QPointer<CModelDataReader> myself(this);
536  QTimer::singleShot(0, this, [=]() {
537  if (!myself) { return; }
538  const CStatusMessageList msgs = this->readFromJsonFiles(dir, whatToRead, overrideNewerOnly);
539  if (msgs.isFailure()) { CLogMessage::preformatted(msgs); }
540  });
541  return true;
542  }
543 
545  {
546  QDir directory(dir);
547  if (!directory.exists()) { return false; }
548  QList<QPair<QString, QString>> fileContents;
549  if (this->getLiveriesCount() > 0)
550  {
551  const QString json(QJsonDocument(this->getLiveries().toJson()).toJson());
552  fileContents.push_back({ "liveries.json", json });
553  }
554 
555  if (this->getModelsCount() > 0)
556  {
557  const QString json(QJsonDocument(this->getModels().toJson()).toJson());
558  fileContents.push_back({ "models.json", json });
559  }
560 
561  if (this->getDistributorsCount() > 0)
562  {
563  const QString json(QJsonDocument(this->getDistributors().toJson()).toJson());
564  fileContents.push_back({ "distributors.json", json });
565  }
566 
567  for (const auto &pair : fileContents)
568  {
569  CWorker::fromTask(this, Q_FUNC_INFO, [pair, directory] {
571  pair.second);
572  });
573  }
574  return true;
575  }
576 
577  CEntityFlags::Entity CModelDataReader::getSupportedEntities() const { return CEntityFlags::DistributorLiveryModel; }
578 
579  void CModelDataReader::synchronizeCaches(CEntityFlags::Entity entities)
580  {
581  if (entities.testFlag(CEntityFlags::LiveryEntity))
582  {
583  if (m_syncedLiveryCache) { return; }
584  m_syncedLiveryCache = true;
585  m_liveryCache.synchronize();
586  }
587  if (entities.testFlag(CEntityFlags::ModelEntity))
588  {
589  if (m_syncedModelCache) { return; }
590  m_syncedModelCache = true;
591  m_modelCache.synchronize();
592  }
593  if (entities.testFlag(CEntityFlags::DistributorEntity))
594  {
595  if (m_syncedDistributorCache) { return; }
596  m_syncedDistributorCache = true;
597  m_distributorCache.synchronize();
598  }
599  }
600 
601  void CModelDataReader::admitCaches(CEntityFlags::Entity entities)
602  {
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(); }
606  }
607 
608  void CModelDataReader::invalidateCaches(CEntityFlags::Entity entities)
609  {
610  if (entities.testFlag(CEntityFlags::LiveryEntity))
611  {
612  CDataCache::instance()->clearAllValues(m_liveryCache.getKey());
613  }
614  if (entities.testFlag(CEntityFlags::ModelEntity))
615  {
616  CDataCache::instance()->clearAllValues(m_modelCache.getKey());
617  }
618  if (entities.testFlag(CEntityFlags::DistributorEntity))
619  {
620  CDataCache::instance()->clearAllValues(m_distributorCache.getKey());
621  }
622  }
623 
624  QDateTime CModelDataReader::getCacheTimestamp(CEntityFlags::Entity entity) const
625  {
626  switch (entity)
627  {
628  case CEntityFlags::LiveryEntity: return m_liveryCache.getAvailableTimestamp();
629  case CEntityFlags::ModelEntity: return m_modelCache.getAvailableTimestamp();
630  case CEntityFlags::DistributorEntity: return m_distributorCache.getAvailableTimestamp();
631  default: return QDateTime();
632  }
633  }
634 
635  int CModelDataReader::getCacheCount(CEntityFlags::Entity entity) const
636  {
637  switch (entity)
638  {
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();
642  default: return 0;
643  }
644  }
645 
646  CEntityFlags::Entity CModelDataReader::getEntitiesWithCacheCount() const
647  {
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;
652  return entities;
653  }
654 
655  CEntityFlags::Entity CModelDataReader::getEntitiesWithCacheTimestampNewerThan(const QDateTime &threshold) const
656  {
657  CEntityFlags::Entity entities = CEntityFlags::NoEntity;
658  if (this->hasCacheTimestampNewerThan(CEntityFlags::LiveryEntity, threshold))
659  entities |= CEntityFlags::LiveryEntity;
660  if (this->hasCacheTimestampNewerThan(CEntityFlags::ModelEntity, threshold))
661  entities |= CEntityFlags::ModelEntity;
662  if (this->hasCacheTimestampNewerThan(CEntityFlags::DistributorEntity, threshold))
663  entities |= CEntityFlags::DistributorEntity;
664  return entities;
665  }
666 
667  bool CModelDataReader::hasChangedUrl(CEntityFlags::Entity entity, CUrl &oldUrlInfo, CUrl &newUrlInfo) const
668  {
669  Q_UNUSED(entity)
670  oldUrlInfo = m_readerUrlCache.get();
671  newUrlInfo = this->getBaseUrl(CDbFlags::DbReading);
672  return CDatabaseReader::isChangedUrl(oldUrlInfo, newUrlInfo);
673  }
674 
676 
677  CUrl CModelDataReader::getLiveryUrl(CDbFlags::DataRetrievalModeFlag mode) const
678  {
679  return getBaseUrl(mode).withAppendedPath(fileNameForMode(CEntityFlags::LiveryEntity, mode));
680  }
681 
682  CUrl CModelDataReader::getDistributorUrl(CDbFlags::DataRetrievalModeFlag mode) const
683  {
684  return getBaseUrl(mode).withAppendedPath(fileNameForMode(CEntityFlags::DistributorEntity, mode));
685  }
686 
687  CUrl CModelDataReader::getModelUrl(CDbFlags::DataRetrievalModeFlag mode) const
688  {
689  return getBaseUrl(mode).withAppendedPath(fileNameForMode(CEntityFlags::ModelEntity, mode));
690  }
691 } // 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 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.
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:225
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.
qint64 toMSecsSinceEpoch() const const
QDateTime toUTC() const const
QString absolutePath() const const
bool exists() const const
qint64 elapsed() const const
QString absoluteFilePath() const const
QDateTime birthTime() const const
bool exists(const QString &path)
bool isEmpty() const const
void push_back(QList< T >::parameter_type value)
QString first(qsizetype n) &&
bool isEmpty() const const
QUrl fromLocalFile(const QString &localFile)
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?