swift
backgroundvalidation.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QDateTime>
7 
8 #include "misc/eventloop.h"
9 #include "misc/logmessage.h"
12 #include "misc/threadutils.h"
13 
14 using namespace swift::misc::network;
15 using namespace swift::misc::simulation::data;
16 
17 namespace swift::misc::simulation
18 {
19  const QStringList &CBackgroundValidation::getLogCategories()
20  {
21  static const QStringList cats({ CLogCategories::worker(), CLogCategories::modelSetCache() });
22  return cats;
23  }
24 
25  CBackgroundValidation::CBackgroundValidation(QObject *owner) : CContinuousWorker(owner, "Background validation")
26  {
27  connect(&m_updateTimer, &QTimer::timeout, this, &CBackgroundValidation::doWork);
28  m_updateTimer.setInterval(60 * 1000);
29  }
30 
31  void CBackgroundValidation::setCurrentSimulator(const CSimulatorInfo &simulator, const QString &simDirectory,
32  const QStringList &modelDirList)
33  {
34  QWriteLocker l(&m_lock);
35  m_simulator = simulator;
36  m_simDirectory = simDirectory;
37  m_modelDirList = modelDirList;
38  }
39 
41  {
42  QReadLocker l(&m_lock);
43  return m_checkedSimulatorMsgs.contains(simulator);
44  }
45 
47  {
48  QWriteLocker l(&m_lock);
49  m_checkedSimulatorMsgs.remove(simulator);
50  }
51 
53  {
54  QReadLocker l(&m_lock);
55  return m_simulator;
56  }
57 
58  bool CBackgroundValidation::triggerValidation(const CSimulatorInfo &simulator, const QString &simDirectory)
59  {
60  const QPointer<CBackgroundValidation> myself(this);
61  if (simulator.isNoSimulator()) { return this->requestLastValidationResults(); }
62 
63  {
64  QWriteLocker l(&m_lock);
65  if (m_inWork) { return false; }
66  m_simulator = simulator;
67  m_simDirectory = simDirectory;
68  m_checkedSimulatorMsgs.remove(simulator);
69  }
70  QTimer::singleShot(5, this, [=] {
71  if (!myself) { return; }
72  myself->doWork();
73  });
74  return true;
75  }
76 
78  {
79  CAircraftModelList valid;
80  CAircraftModelList invalid;
81  CAircraftModelList models;
82  CStatusMessageList msgs;
83  CSimulatorInfo simulator;
84  bool wasStopped = false;
85  {
86  QReadLocker l(&m_lock);
87  simulator = m_lastResultSimulator;
88  valid = m_lastResultValid;
89  invalid = m_lastResultInvalid;
90  msgs = m_lastResultMsgs;
91  wasStopped = m_lastResultWasStopped;
92  }
93  if (m_lastResultSimulator.isUnspecified()) { return false; }
94  emit this->validated(simulator, valid, invalid, wasStopped, msgs);
95  return true;
96  }
97 
99  {
100  m_wasStopped = true; // stop in utility functions
101  this->stopUpdateTimer();
102  }
103 
104  void CBackgroundValidation::doWork()
105  {
106  if (m_inWork) { return; }
107  m_inWork = true;
108  emit this->validating(true);
109 
110  const bool isTimerBased = (QObject::sender() == &m_updateTimer);
111  CAircraftModelList valid;
112  CAircraftModelList invalid;
113  CStatusMessageList msgs;
114  bool validated = false;
115  bool onlyErrorsAndWarnings = false;
116  const CSimulatorInfo simulator = this->getCurrentSimulator();
117  const qint64 started = QDateTime::currentMSecsSinceEpoch();
118  m_wasStopped = false;
119 
120  do {
121  if (!simulator.isSingleSimulator()) { break; }
122  if (this->wasAlreadyChecked(simulator)) { break; }
123 
124  const CAircraftMatcherSetup setup = m_matchingSettings.get();
125  if (!setup.doVerificationAtStartup()) { break; }
126 
127  onlyErrorsAndWarnings = setup.onlyShowVerificationWarningsAndErrors();
128  const CAircraftModelList models = m_modelSets.getCachedModels(simulator);
129  const qint64 now = QDateTime::currentMSecsSinceEpoch();
130  validated = true;
131 
132  if (models.isEmpty())
133  {
134  msgs.push_back(
135  CStatusMessage(this, CStatusMessage::SeverityWarning,
136  QStringLiteral("No models in set for '%1'").arg(simulator.toQString(true))));
137  }
138  else
139  {
140  msgs = CAircraftModelUtilities::validateModelFiles(simulator, models, valid, invalid, false, 25,
141  m_wasStopped, m_simDirectory);
142  }
143 
144  const qint64 deltaTimeMs = now - started;
145  msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityInfo,
146  QStringLiteral("Validated in %1ms").arg(deltaTimeMs)));
148  msgs.freezeOrder();
149 
150  QWriteLocker l(&m_lock);
151  m_lastResultValid = valid;
152  m_lastResultInvalid = invalid;
153  m_lastResultWasStopped = m_wasStopped;
154  m_lastResultSimulator = simulator;
155  m_lastResultMsgs = msgs;
156  m_checkedSimulatorMsgs.insert(simulator, msgs);
157  }
158  while (false);
159 
160  m_inWork = false;
161  if (isTimerBased)
162  {
163  m_timerBasedRuns++;
164 
165  // stop timer after some runs
166  if (m_timerBasedRuns > 3) { m_updateTimer.stop(); }
167  }
168 
169  emit this->validating(false);
170  if (validated)
171  {
172  const bool e = !onlyErrorsAndWarnings || (!invalid.isEmpty() || msgs.hasWarningOrErrorMessages());
173  if (e || !isTimerBased) { emit this->validated(simulator, valid, invalid, m_wasStopped, msgs); }
174  }
175  }
176 } // namespace swift::misc::simulation
Base class for a long-lived worker object which lives in its own thread.
Definition: worker.h:275
QTimer m_updateTimer
timer which can be used by implementing classes
Definition: worker.h:333
void stopUpdateTimer()
Safely stop update time.
Definition: worker.cpp:262
static const QString & worker()
Background task.
static const QString & modelSetCache()
Model set cache.
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
constexpr static auto SeverityInfo
Status severities.
constexpr static auto SeverityWarning
Status severities.
Status messages, e.g. from Core -> GUI.
bool hasWarningOrErrorMessages() const
Warning or error messages.
void sortBySeverityHighestFirst()
Sort by severity, highest first.
void freezeOrder()
Current order of list will be new order values.
Value object encapsulating a list of aircraft models.
static CStatusMessageList validateModelFiles(const CSimulatorInfo &simulator, const CAircraftModelList &models, CAircraftModelList &validModels, CAircraftModelList &invalidModels, bool ignoreEmpty, int stopAtFailedFiles, std::atomic_bool &wasStopped, const QString &simulatorDir)
Validate aircraft.cfg entries.
bool wasAlreadyChecked(const CSimulatorInfo &simulator) const
Was already checked for simulator?
swift::misc::simulation::CSimulatorInfo getCurrentSimulator() const
Corresponding simulator.
void validating(bool running)
Validating.
bool requestLastValidationResults()
Request last results (again), if there are any.
void setCurrentSimulator(const CSimulatorInfo &simulator, const QString &simDirectory, const QStringList &modelDirList)
Corresponding simulator.
virtual void beforeQuit() noexcept
Called before quit is called.
void resetAlreadyChecked(const CSimulatorInfo &simulator)
Reset checked for simulator.
bool triggerValidation(const CSimulatorInfo &simulator, const QString &simDirectory)
Trigger a validation, returns false if "work in progress".
void validated(const CSimulatorInfo &simulator, const CAircraftModelList &validModels, const CAircraftModelList &invalidModels, bool stopped, const CStatusMessageList &msgs)
Validated for simulator.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
bool isNoSimulator() const
No simulator?
bool isUnspecified() const
Unspecified simulator.
CAircraftModelList getCachedModels(const CSimulatorInfo &simulator) const
Models for simulator.
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