swift
aircraftmodelloader.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 <QMap>
8 #include <Qt>
9 #include <QtGlobal>
10 
11 #include "misc/directoryutils.h"
12 #include "misc/logmessage.h"
15 
16 using namespace swift::misc;
17 using namespace swift::misc::simulation::data;
18 using namespace swift::misc::simulation::settings;
19 
20 namespace swift::misc::simulation
21 {
23  {
24  static const QStringList cats({ CLogCategories::modelLoader() });
25  return cats;
26  }
27 
29  {
30  static const QString loaded("cache loaded");
31  static const QString skipped("loading skipped");
32  static const QString parsed("parsed data");
33  static const QString failed("failed");
34 
35  switch (info)
36  {
37  case CacheLoaded: return loaded;
38  case ParsedData: return parsed;
39  case LoadingSkipped: return skipped;
40  case LoadingFailed: return failed;
41  default: break;
42  }
43 
44  static const QString unknown("??");
45  return unknown;
46  }
47 
49  {
50  static const QString notSet("not set");
51  static const QString directly("load directly");
52  static const QString background("load in background");
53  static const QString cacheFirst("cache first");
54  static const QString cacheSkipped("cache skipped");
55  static const QString cacheOnly("cacheOnly");
56 
57  switch (modeFlag)
58  {
59  case NotSet: return notSet;
60  case LoadDirectly: return directly;
61  case LoadInBackground: return background;
62  case CacheFirst: return cacheFirst;
63  case CacheSkipped: return cacheSkipped;
64  case CacheOnly: return cacheOnly;
65  default: break;
66  }
67 
68  static const QString unknown("??");
69  return unknown;
70  }
71 
73  {
74  QStringList modes;
75  if (mode.testFlag(NotSet)) { modes << enumToString(NotSet); }
76  if (mode.testFlag(LoadDirectly)) { modes << enumToString(LoadDirectly); }
77  if (mode.testFlag(LoadInBackground)) { modes << enumToString(LoadInBackground); }
78  if (mode.testFlag(CacheFirst)) { modes << enumToString(CacheFirst); }
79  if (mode.testFlag(CacheSkipped)) { modes << enumToString(CacheSkipped); }
80  return modes.join(", ");
81  }
82 
84  {
85  return mode.testFlag(CacheFirst) || mode.testFlag(CacheOnly);
86  }
87 
89  : QObject(parent), m_simulator(simulator)
90  {
91  Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Only one simulator per loader");
92  connect(this, &IAircraftModelLoader::loadingFinished, this, &IAircraftModelLoader::onLoadingFinished,
94 
96  &CCentralMultiSimulatorModelCachesProvider::modelCachesInstance();
97  connect(centralCaches, &CCentralMultiSimulatorModelCachesProvider::cacheChanged, this,
98  &IAircraftModelLoader::onCacheChanged, Qt::QueuedConnection);
99  this->setObjectInfo(simulator);
100  }
101 
103  const IAircraftModelLoader::ModelConsolidationCallback &modelConsolidation,
104  const QStringList &modelDirectories)
105  {
106  if (m_loadingInProgress) { return; }
107  if (mode == NotSet) { return; }
108  m_loadingInProgress = true;
110 
111  const CSimulatorInfo simulator = this->getSimulator();
112  const bool needsCacheSynced = IAircraftModelLoader::needsCacheSynchronized(mode);
113  if (needsCacheSynced) { this->synchronizeCache(simulator); }
114 
115  const bool useCachedData = !mode.testFlag(CacheSkipped) && this->hasCachedData();
116  if (useCachedData && (mode.testFlag(CacheFirst) || mode.testFlag(CacheOnly)))
117  {
118  // we just just cache data
119  static const CStatusMessage status(this, CStatusMessage::SeverityInfo, u"Using cached data");
120  emit this->loadingFinished(status, simulator, CacheLoaded);
121  return;
122  }
123  if (mode.testFlag(CacheOnly))
124  {
125  // only cache, but we did not find any data yet (still in progress?)
126  // here we rely on the cache load slot, no need to emit here, will
127  // be done later in ps_cacheChanged. An alternative was to sync cache here
128  m_loadingInProgress = false;
129  return;
130  }
131 
132  // really load from disk?
133  const QStringList modelDirs = this->getInitializedModelDirectories(modelDirectories, simulator);
134  if (m_skipLoadingEmptyModelDir && modelDirs.isEmpty())
135  {
137  u"Empty or not existing '%1' directory '%2', skipping read")
138  << simulator.toQString() << modelDirectories.join(", ");
141  emit this->loadingFinished(m_loadingMessages, simulator, LoadingSkipped);
142  return;
143  }
144 
145  this->setObjectInfo(simulator);
146  this->startLoadingFromDisk(mode, modelConsolidation, modelDirs);
147  }
148 
150  {
152  return md;
153  }
154 
156  {
157  this->setModelsForSimulator(models, m_simulator);
158  }
159 
161  {
162  return this->updateModelsForSimulator(models, m_simulator);
163  }
164 
166  const CSimulatorInfo &simulator) const
167  {
168  QStringList modelDirs =
169  modelDirectories.isEmpty() ? m_settings.getModelDirectoriesOrDefault(simulator) : modelDirectories;
170  modelDirs = CFileUtils::fixWindowsUncPaths(modelDirs);
172  }
173 
175 
176  void IAircraftModelLoader::setObjectInfo(const CSimulatorInfo &simulatorInfo)
177  {
178  this->setObjectName("Model loader for: '" + simulatorInfo.toQString(true) + "'");
179  }
180 
181  void IAircraftModelLoader::onLoadingFinished(const CStatusMessageList &statusMsgs, const CSimulatorInfo &simulator,
183  {
184  if (!this->supportsSimulator(simulator)) { return; } // none of my business
185  this->setObjectInfo(simulator);
186 
187  // remark: in the past status used to be bool, now it is CStatusMessage
188  // so there is some redundancy here between status and m_loadingMessages
189  m_loadingInProgress = false;
190 
191  const QMap<int, int> counts = statusMsgs.countSeverities();
192  const int errors = counts.value(SeverityError);
193  const int warnings = counts.value(SeverityWarning);
194 
195  if (statusMsgs.hasWarningOrErrorMessages())
196  {
198  u"Message loading produced %1 error and %2 warning messages")
199  << errors << warnings;
200  }
201  else
202  {
203  CLogMessage(this).info(u"Loading '%1' finished, success for '%2'")
204  << IAircraftModelLoader::enumToString(info) << simulator.toQString();
205  }
206  }
207 
208  void IAircraftModelLoader::onCacheChanged(const CSimulatorInfo &simulator)
209  {
211  {
212  return;
213  } // this change signal is redundant as it will be handled by onLoadingFinished
214  if (!this->supportsSimulator(simulator)) { return; } // none of my business
215  emit this->cacheChanged(simulator);
216  }
217 
219  : IAircraftModelLoader(simulator, parent)
220  {
221  // void
222  }
223 
225  {
226  // fake loading
227  const qint64 now = QDateTime::currentMSecsSinceEpoch();
228  return m_loadingStartedTs > 0 && now > (m_loadingStartedTs + 5000);
229  }
230 
231  void
233  const IAircraftModelLoader::ModelConsolidationCallback &modelConsolidation,
234  const QStringList &modelDirectories)
235  {
236  Q_UNUSED(mode);
237  Q_UNUSED(modelConsolidation);
238  Q_UNUSED(modelDirectories);
239  m_loadingStartedTs = QDateTime::currentMSecsSinceEpoch();
240  }
241 
242 } // namespace swift::misc::simulation
static QStringList getExistingUnemptyDirectories(const QStringList &directories)
Get the existing directories.
static QStringList fixWindowsUncPaths(const QStringList &filePaths)
Fix UNC file paths.
Definition: fileutils.cpp:447
static const QString & modelLoader()
Model loader.
Class for emitting a log message.
Definition: logmessage.h:27
Derived & log(StatusSeverity s, const char16_t(&m)[N])
Set the severity and format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
void clear()
Removes all elements in the sequence.
Definition: sequence.h:288
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Streamable status message, e.g.
constexpr static auto SeverityInfo
Status severities.
constexpr static auto SeverityWarning
Status severities.
Status messages, e.g. from Core -> GUI.
CStatusMessage::StatusSeverity worstSeverity() const
Find worst severity.
bool hasWarningOrErrorMessages() const
Warning or error messages.
QMap< int, int > countSeverities() const
Count number of messages per severity.
void freezeOrder()
Current order of list will be new order values.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
Value object encapsulating a list of aircraft models.
CDummyModelLoader(const CSimulatorInfo &simulator, QObject *parent)
Dummy loader.
bool isLoadingFinished() const
IAircraftModelLoader::isLoadingFinished.
void startLoadingFromDisk(LoadMode mode, const ModelConsolidationCallback &modelConsolidation, const QStringList &modelDirectories)
IAircraftModelLoader::startLoadingFromDisk.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
bool isSingleSimulator() const
Single simulator selected.
Load the aircraft for a simulator.
std::function< int(swift::misc::simulation::CAircraftModelList &, bool)> ModelConsolidationCallback
Callback to consolidate data, normally with DB data.
void setModels(const CAircraftModelList &models)
Set models.
const CSimulatorInfo & getSimulator() const
Simulator.
void cacheChanged(const CSimulatorInfo &simulator)
Relayed from centralized caches.
static const QString & enumToString(LoadFinishedInfo info)
Enum as string.
QStringList getInitializedModelDirectories(const QStringList &modelDirectories, const CSimulatorInfo &simulator) const
Get model directories from settings if empty, otherwise checked and UNC path fixed.
int updateModels(const CAircraftModelList &models)
Update models.
void loadingFinished(const CStatusMessageList &status, const CSimulatorInfo &simulator, IAircraftModelLoader::LoadFinishedInfo info)
Parsing is finished or cache has been loaded.
const CSimulatorInfo m_simulator
related simulator
void startLoading(LoadMode mode=InBackgroundWithCache, const ModelConsolidationCallback &modelConsolidation={}, const QStringList &modelDirectories={})
Start the loading process from disk. Optional DB models can be passed and used for data consolidation...
QString getFirstModelDirectoryOrDefault() const
First directory, can be used when only 1 directory is expected.
static const QStringList & getLogCategories()
Log categories.
bool supportsSimulator(const CSimulatorInfo &simulator) const
Supported simulator.
settings::CMultiSimulatorSettings m_settings
settings
IAircraftModelLoader(const CSimulatorInfo &simulator, QObject *parent=nullptr)
Constructor.
std::atomic< bool > m_loadingInProgress
loading in progress
virtual void startLoadingFromDisk(LoadMode mode, const ModelConsolidationCallback &modelConsolidation, const QStringList &modelDirectories)=0
Start the loading process from disk.
@ LoadingSkipped
loading skipped (empty directory)
CStatusMessageList m_loadingMessages
loading messages
@ CacheOnly
only read cache, never load from disk
@ CacheFirst
always use cache (if it has data)
std::atomic< bool > m_skipLoadingEmptyModelDir
loading empty model dirs might erase the cache, so normally we skip it
static bool needsCacheSynchronized(LoadMode mode)
Is that mode needing caches synchronized?
void synchronizeCache(const CSimulatorInfo &simulator)
Look like IMultiSimulatorModelCaches interface.
Definition: modelcaches.h:566
void setModelsForSimulator(const CAircraftModelList &models, const CSimulatorInfo &simulator)
Set models.
Definition: modelcaches.h:597
int updateModelsForSimulator(const CAircraftModelList &models, const CSimulatorInfo &simulator)
Set models.
Definition: modelcaches.h:603
CAircraftModelList getCachedModels(const CSimulatorInfo &simulator) const
Look like IMultiSimulatorModelCaches interface.
Definition: modelcaches.h:537
QString getFirstModelDirectoryOrDefault(const CSimulatorInfo &simulator) const
First model directoy.
QStringList getModelDirectoriesOrDefault(const CSimulatorInfo &simulator) const
Model directory or default model path per simulator.
Free functions in swift::misc.
qint64 currentMSecsSinceEpoch()
bool isEmpty() const const
T value(const Key &key, const T &defaultValue) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setObjectName(QAnyStringView name)
QString join(QChar separator) const const
QueuedConnection