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 
72  QString IAircraftModelLoader::enumToString(LoadMode mode)
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,
93  Qt::QueuedConnection);
94 
96  &CCentralMultiSimulatorModelCachesProvider::modelCachesInstance();
97  connect(centralCaches, &CCentralMultiSimulatorModelCachesProvider::cacheChanged, this,
98  &IAircraftModelLoader::onCacheChanged, Qt::QueuedConnection);
99  this->setObjectInfo(simulator);
100  }
101 
103 
105  const IAircraftModelLoader::ModelConsolidationCallback &modelConsolidation,
106  const QStringList &modelDirectories)
107  {
108  if (m_loadingInProgress) { return; }
109  if (mode == NotSet) { return; }
110  m_loadingInProgress = true;
112 
113  const CSimulatorInfo simulator = this->getSimulator();
114  const bool needsCacheSynced = IAircraftModelLoader::needsCacheSynchronized(mode);
115  if (needsCacheSynced) { this->synchronizeCache(simulator); }
116 
117  const bool useCachedData = !mode.testFlag(CacheSkipped) && this->hasCachedData();
118  if (useCachedData && (mode.testFlag(CacheFirst) || mode.testFlag(CacheOnly)))
119  {
120  // we just just cache data
121  static const CStatusMessage status(this, CStatusMessage::SeverityInfo, u"Using cached data");
122  emit this->loadingFinished(status, simulator, CacheLoaded);
123  return;
124  }
125  if (mode.testFlag(CacheOnly))
126  {
127  // only cache, but we did not find any data yet (still in progress?)
128  // here we rely on the cache load slot, no need to emit here, will
129  // be done later in ps_cacheChanged. An alternative was to sync cache here
130  m_loadingInProgress = false;
131  return;
132  }
133 
134  // really load from disk?
135  const QStringList modelDirs = this->getInitializedModelDirectories(modelDirectories, simulator);
136  if (m_skipLoadingEmptyModelDir && modelDirs.isEmpty())
137  {
139  u"Empty or not existing '%1' directory '%2', skipping read")
140  << simulator.toQString() << modelDirectories.join(", ");
143  emit this->loadingFinished(m_loadingMessages, simulator, LoadingSkipped);
144  return;
145  }
146 
147  this->setObjectInfo(simulator);
148  this->startLoadingFromDisk(mode, modelConsolidation, modelDirs);
149  }
150 
152  {
154  return md;
155  }
156 
158  {
159  this->setModelsForSimulator(models, m_simulator);
160  }
161 
163  {
164  return this->updateModelsForSimulator(models, m_simulator);
165  }
166 
167  QStringList IAircraftModelLoader::getInitializedModelDirectories(const QStringList &modelDirectories,
168  const CSimulatorInfo &simulator) const
169  {
170  QStringList modelDirs =
171  modelDirectories.isEmpty() ? m_settings.getModelDirectoriesOrDefault(simulator) : modelDirectories;
172  modelDirs = CFileUtils::fixWindowsUncPaths(modelDirs);
174  }
175 
177 
178  void IAircraftModelLoader::setObjectInfo(const CSimulatorInfo &simulatorInfo)
179  {
180  this->setObjectName("Model loader for: '" + simulatorInfo.toQString(true) + "'");
181  }
182 
183  void IAircraftModelLoader::onLoadingFinished(const CStatusMessageList &statusMsgs, const CSimulatorInfo &simulator,
185  {
186  if (!this->supportsSimulator(simulator)) { return; } // none of my business
187  this->setObjectInfo(simulator);
188 
189  // remark: in the past status used to be bool, now it is CStatusMessage
190  // so there is some redundancy here between status and m_loadingMessages
191  m_loadingInProgress = false;
192 
193  const QMap<int, int> counts = statusMsgs.countSeverities();
194  const int errors = counts.value(SeverityError);
195  const int warnings = counts.value(SeverityWarning);
196 
197  if (statusMsgs.hasWarningOrErrorMessages())
198  {
200  u"Message loading produced %1 error and %2 warning messages")
201  << errors << warnings;
202  }
203  else
204  {
205  CLogMessage(this).info(u"Loading '%1' finished, success for '%2'")
206  << IAircraftModelLoader::enumToString(info) << simulator.toQString();
207  }
208  }
209 
210  void IAircraftModelLoader::onCacheChanged(const CSimulatorInfo &simulator)
211  {
213  {
214  return;
215  } // this change signal is redundant as it will be handled by onLoadingFinished
216  if (!this->supportsSimulator(simulator)) { return; } // none of my business
217  emit this->cacheChanged(simulator);
218  }
219 
220  CDummyModelLoader::CDummyModelLoader(const CSimulatorInfo &simulator, QObject *parent)
221  : IAircraftModelLoader(simulator, parent)
222  {
223  // void
224  }
225 
227  {
228  // fake loading
229  const qint64 now = QDateTime::currentMSecsSinceEpoch();
230  return m_loadingStartedTs > 0 && now > (m_loadingStartedTs + 5000);
231  }
232 
233  void
235  const IAircraftModelLoader::ModelConsolidationCallback &modelConsolidation,
236  const QStringList &modelDirectories)
237  {
238  Q_UNUSED(mode);
239  Q_UNUSED(modelConsolidation);
240  Q_UNUSED(modelDirectories);
241  m_loadingStartedTs = QDateTime::currentMSecsSinceEpoch();
242  }
243 
244 } // 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:453
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:76
Value object encapsulating a list of aircraft models.
CDummyModelLoader(const CSimulatorInfo &simulator, QObject *parent)
Dummy loader.
virtual bool isLoadingFinished() const
IAircraftModelLoader::isLoadingFinished.
virtual 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.
virtual 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.
virtual 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.