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  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
247  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
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
296  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
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
346  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
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  {
460  const QJsonObject modelsJson(CDatabaseUtils::readQJsonObjectFromDatabaseFile(fileName));
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 
544  bool CModelDataReader::writeToJsonFiles(const QString &dir)
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] {
570  CFileUtils::writeStringToFile(CFileUtils::appendFilePaths(directory.absolutePath(), pair.first),
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.
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?