swift
simulatorcomponent.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2014 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include "ui_simulatorcomponent.h"
7 
10 #include "core/simulator.h"
11 #include "gui/guiapplication.h"
14 #include "misc/aviation/altitude.h"
16 #include "misc/aviation/heading.h"
17 #include "misc/geo/latitude.h"
18 #include "misc/geo/longitude.h"
19 #include "misc/iconlist.h"
20 #include "misc/pq/angle.h"
21 #include "misc/pq/frequency.h"
22 #include "misc/pq/speed.h"
25 #include "misc/stringutils.h"
26 
27 using namespace swift::misc;
28 using namespace swift::misc::aviation;
29 using namespace swift::misc::physical_quantities;
30 using namespace swift::misc::simulation;
31 using namespace swift::core;
32 using namespace swift::core::context;
33 
34 namespace swift::gui::components
35 {
36  const QStringList &CSimulatorComponent::getLogCategories()
37  {
39  return cats;
40  }
41 
42  CSimulatorComponent::CSimulatorComponent(QWidget *parent) : QTabWidget(parent), ui(new Ui::CSimulatorComponent)
43  {
44  Q_ASSERT_X(sGui, Q_FUNC_INFO, "Need sGui");
45 
46  ui->setupUi(this);
47  this->setCurrentIndex(0);
48  ui->comp_StatusMessages->showFilterDialog();
49 
50  // live data and internals
51  ui->tvp_LiveData->setIconMode(true);
52  ui->tvp_LiveData->setAutoResizeFrequency(10); // only resize every n-th time
53  ui->tvp_Internals->setIconMode(false);
54  this->addOrUpdateLiveDataByName("info", "no data yet", CIcons::StandardIconWarning16);
55 
56  // connects
57  connect(sGui->getIContextSimulator(), &IContextSimulator::simulatorStatusChanged, this,
58  &CSimulatorComponent::onSimulatorStatusChanged, Qt::QueuedConnection);
59  connect(&m_updateTimer, &QTimer::timeout, this, &CSimulatorComponent::update);
60  connect(ui->pb_RefreshInternals, &QPushButton::pressed, this, &CSimulatorComponent::refreshInternals);
62  {
63  connect(sGui->getIContextSimulator(), &IContextSimulator::addingRemoteModelFailed, this,
64  &CSimulatorComponent::onAddingRemoteModelFailed, Qt::QueuedConnection);
65  connect(sGui->getIContextSimulator(), &IContextSimulator::driverMessages, this,
66  &CSimulatorComponent::onSimulatorMessages, Qt::QueuedConnection);
67  }
68 
69  // init status
70  this->onSimulatorStatusChanged(sGui->getIContextSimulator()->getSimulatorStatus());
71  }
72 
74 
75  void CSimulatorComponent::addOrUpdateLiveDataByName(const QString &name, const QString &value, const CIcon &icon)
76  {
77  const bool resize = this->currentWidget() == ui->tb_LiveData; // simulator live data selected?
78  ui->tvp_LiveData->addOrUpdateByName(name, value, icon, resize, false);
79  }
80 
81  void CSimulatorComponent::addOrUpdateLiveDataByName(const QString &name, const QString &value,
82  CIcons::IconIndex iconIndex)
83  {
84  this->addOrUpdateLiveDataByName(name, value, CIcon::iconByIndex(iconIndex));
85  }
86 
87  void CSimulatorComponent::removeLiveDataByName(const QString &name) { ui->tvp_LiveData->removeByName(name); }
88 
89  int CSimulatorComponent::rowCount() const { return ui->tvp_LiveData->rowCount(); }
90 
91  void CSimulatorComponent::clear(bool addInternalsAfterwards)
92  {
93  ui->tvp_LiveData->clear();
94  if (addInternalsAfterwards) { this->refreshInternals(); }
95  }
96 
98  {
99  if (!this->isVisibleWidget()) return; // no updates on invisible widgets
100  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextOwnAircraft()) return;
101 
102  const ISimulator::SimulatorStatus simulatorStatus =
103  static_cast<ISimulator::SimulatorStatus>(sGui->getIContextSimulator()->getSimulatorStatus());
104  if (simulatorStatus == ISimulator::Unspecified || simulatorStatus == ISimulator::Disconnected)
105  {
106  static const QString s("No simulator available");
107  this->addOrUpdateLiveDataByName(QStringLiteral("info"), s, CIcons::StandardIconWarning16);
108  return;
109  }
110 
111  if (!simulatorStatus.testFlag(ISimulator::Simulating))
112  {
113  static const QString s("Simulator (%1) not yet running");
114  this->addOrUpdateLiveDataByName(
115  QStringLiteral("info"), s.arg(sGui->getIContextSimulator()->getSimulatorPluginInfo().getSimulator()),
116  CIcons::StandardIconWarning16);
117  return;
118  }
119 
120  // clear old warnings / information
121  if (this->rowCount() < 5) { this->clear(true); }
122 
124  const CAircraftSituation s = ownAircraft.getSituation();
125  const CComSystem c1 = ownAircraft.getCom1System();
126  const CComSystem c2 = ownAircraft.getCom2System();
127  static const CIcon iconAlt(s.getAltitude().toIcon()); // minor performance improvement
128  static const CIcon iconLatLng(s.latitude().toIcon());
129  static const CIcon iconRadio(CIcon::iconByIndex(CIcons::StandardIconRadio16));
130  static const CIcon iconAttitude(CIcon::iconByIndex(CIcons::AviationAttitudeIndicator));
131  static const CIcon iconPlane(CIcon::iconByIndex(CIcons::StandardIconPaperPlane16));
132 
133  if (m_simulator.isAnySimulator())
134  {
135  this->addOrUpdateLiveDataByName("simulator", m_simulator.toQString(true), m_simulator.toIcon());
136 
137  if (sGui->getISimulator())
138  {
139  const double fps = sGui->getISimulator()->getAverageFPS();
140  this->addOrUpdateLiveDataByName(QStringLiteral("FPS"),
141  fps < 0 ? QStringLiteral("N/A") : QString::number(fps, 'f', 1),
142  CIcon(CIcons::ApplicationSimulator));
143 
144  const double ratio = sGui->getISimulator()->getSimTimeRatio();
145  this->addOrUpdateLiveDataByName(QStringLiteral("Time Ratio"), QString::number(ratio, 'f', 2),
146  CIcon(CIcons::ApplicationSimulator));
147 
148  const double miles = sGui->getISimulator()->getTrackMilesShort();
149  this->addOrUpdateLiveDataByName(QStringLiteral("Miles Short"), QString::number(miles, 'f', 1),
150  CIcon(CIcons::ApplicationSimulator));
151 
152  const double minutes = sGui->getISimulator()->getMinutesLate();
153  this->addOrUpdateLiveDataByName(QStringLiteral("Minutes Late"), QString::number(minutes, 'f', 1),
154  CIcon(CIcons::ApplicationSimulator));
155  }
156  }
157 
158  this->addOrUpdateLiveDataByName(QStringLiteral("latitude"), s.latitude().toQString(), iconLatLng);
159  this->addOrUpdateLiveDataByName(QStringLiteral("longitude"), s.longitude().toQString(), iconLatLng);
160  this->addOrUpdateLiveDataByName(QStringLiteral("altitude, true (ft)"),
161  s.getAltitude().valueRoundedWithUnit(CLengthUnit::ft(), 1), iconAlt);
162  this->addOrUpdateLiveDataByName(QStringLiteral("altitude, true (m)"),
163  s.getAltitude().valueRoundedWithUnit(CLengthUnit::m(), 2), iconAlt);
164  this->addOrUpdateLiveDataByName(QStringLiteral("altitude, pressure (ft)"),
165  s.getPressureAltitude().valueRoundedWithUnit(CLengthUnit::ft(), 1), iconAlt);
166  this->addOrUpdateLiveDataByName(QStringLiteral("altitude, pressure (m)"),
167  s.getPressureAltitude().valueRoundedWithUnit(CLengthUnit::m(), 2), iconAlt);
168 
169  if (s.hasGroundElevation())
170  {
171  this->addOrUpdateLiveDataByName(QStringLiteral("elevation (ft)"),
172  s.getGroundElevation().valueRoundedWithUnit(CLengthUnit::ft(), 1), iconAlt);
173  this->addOrUpdateLiveDataByName(QStringLiteral("elevation (m)"),
174  s.getGroundElevation().valueRoundedWithUnit(CLengthUnit::m(), 2), iconAlt);
175  }
176  else { this->addOrUpdateLiveDataByName(QStringLiteral("elevation"), QStringLiteral("N/A"), iconAlt); }
177 
178  if (ownAircraft.hasCG())
179  {
180  this->addOrUpdateLiveDataByName(QStringLiteral("CG (ft)"),
181  ownAircraft.getCG().valueRoundedWithUnit(CLengthUnit::ft(), 1), iconPlane);
182  this->addOrUpdateLiveDataByName(QStringLiteral("CG (m)"),
183  ownAircraft.getCG().valueRoundedWithUnit(CLengthUnit::m(), 2), iconPlane);
184  }
185  else { this->addOrUpdateLiveDataByName(QStringLiteral("CG"), QStringLiteral("N/A"), iconPlane); }
186 
187  this->addOrUpdateLiveDataByName(QStringLiteral("pitch"), s.getPitch().toQString(), iconAttitude);
188  this->addOrUpdateLiveDataByName(QStringLiteral("bank"), s.getBank().toQString(), iconAttitude);
189 
190  const CHeading heading = s.getHeading().normalizedTo360Degrees();
191  this->addOrUpdateLiveDataByName(QStringLiteral("heading"), heading.valueRoundedWithUnit(CAngleUnit::deg(), 1),
192  s.getHeading().toIcon());
193 
194  const CSpeed gs = s.getGroundSpeed();
195  this->addOrUpdateLiveDataByName(QStringLiteral("ground speed (kts)"),
196  gs.valueRoundedWithUnit(CSpeedUnit::kts(), 1), gs.toIcon());
197  this->addOrUpdateLiveDataByName(QStringLiteral("ground speed (km/h)"),
198  gs.valueRoundedWithUnit(CSpeedUnit::km_h(), 1), gs.toIcon());
199 
200  this->addOrUpdateLiveDataByName(QStringLiteral("COM1 active"), c1.getFrequencyActive().toQString(), iconRadio);
201  this->addOrUpdateLiveDataByName(QStringLiteral("COM2 active"), c2.getFrequencyActive().toQString(), iconRadio);
202  this->addOrUpdateLiveDataByName(QStringLiteral("COM1 standby"), c1.getFrequencyStandby().toQString(),
203  iconRadio);
204  this->addOrUpdateLiveDataByName(QStringLiteral("COM2 standby"), c2.getFrequencyStandby().toQString(),
205  iconRadio);
206  this->addOrUpdateLiveDataByName(QStringLiteral("COM1 volume"), QString::number(c1.getVolumeReceive()),
207  iconRadio);
208  this->addOrUpdateLiveDataByName(QStringLiteral("COM2 volume"), QString::number(c2.getVolumeReceive()),
209  iconRadio);
210  this->addOrUpdateLiveDataByName(QStringLiteral("Transponder"), ownAircraft.getTransponderCodeFormatted(),
211  iconRadio);
212  }
213 
214  void CSimulatorComponent::onSimulatorStatusChanged(int status)
215  {
216  auto simStatus = static_cast<ISimulator::SimulatorStatus>(status);
217  this->clear(); // clean up, will be refreshed
218  if (simStatus.testFlag(ISimulator::Connected))
219  {
220  const int intervalMs = getUpdateIntervalMs();
221  m_updateTimer.start(intervalMs);
222  this->clear(true);
223  }
224  else
225  {
226  m_updateTimer.stop();
227  this->update();
228  }
229  }
230 
231  void CSimulatorComponent::onAddingRemoteModelFailed(const CSimulatedAircraft &aircraft, bool disabled,
232  bool failover, const CStatusMessage &message)
233  {
234  const CStatusMessage msg =
235  CStatusMessage(this).warning(u"Adding model '%1' failed, disabled: %2: failover: %3 details: %4")
236  << aircraft.getModelString() << boolToYesNo(disabled) << boolToYesNo(failover) << aircraft.toQString(true);
237  ui->comp_StatusMessages->appendStatusMessageToList(msg);
238  ui->comp_StatusMessages->appendStatusMessageToList(message);
239  }
240 
241  void CSimulatorComponent::onSimulatorMessages(const CStatusMessageList &messages)
242  {
243  if (messages.isEmpty()) { return; }
244  ui->comp_StatusMessages->appendStatusMessagesToList(messages);
245  }
246 
247  void CSimulatorComponent::refreshInternals()
248  {
249  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
252  m_simulator = simulatorInfo;
253 
254  const QStringList names(internals.getSortedNames());
255  if (names.isEmpty())
256  {
257  ui->tvp_Internals->clear();
258  return;
259  }
260 
261  // Update the INTERNAL view
262  static const CIcon emptyIcon;
263  const bool resize = true;
264  const bool skipEqualValues = true;
265  for (const QString &name : names)
266  {
267  ui->tvp_Internals->addOrUpdateByName(name, internals.getVariantValue(name), emptyIcon, resize,
268  skipEqualValues);
269  }
270  ui->tvp_Internals->fullResizeToContents();
271  }
272 
273  int CSimulatorComponent::getUpdateIntervalMs() const
274  {
275  // much slower updates via DBus
276  if (!sGui || sGui->isShuttingDown()) { return 10000; }
277  return sGui->getIContextSimulator()->isUsingImplementingObject() ? 1000 : 5000;
278  }
279 } // namespace swift::gui::components
const context::IContextOwnAircraft * getIContextOwnAircraft() const
Direct access to contexts if a CCoreFacade has been initialized.
QPointer< ISimulator > getISimulator() const
The simulator plugin, if available.
bool isShuttingDown() const
Is application shutting down?
const context::IContextSimulator * getIContextSimulator() const
Direct access to contexts if a CCoreFacade has been initialized.
bool supportsContexts(bool ignoreShutdownTest=false) const
Supports contexts.
bool isUsingImplementingObject() const
Using local implementing object?
Definition: context.h:45
virtual swift::misc::simulation::CSimulatedAircraft getOwnAircraft() const =0
Get own aircraft.
virtual swift::misc::simulation::CSimulatorPluginInfo getSimulatorPluginInfo() const =0
Simulator info, currently loaded plugin.
virtual swift::misc::simulation::CSimulatorInternals getSimulatorInternals() const =0
Simulator setup.
virtual ISimulator::SimulatorStatus getSimulatorStatus() const =0
Simulator combined status.
Value object for icons. An icon is stored in the global icon repository and identified by its index....
Definition: icon.h:39
IconIndex
Index for each icon, allows to send them via DBus, efficiently store them, etc.
Definition: icons.h:32
static const QString & matching()
Matching.
static const QString & guiComponent()
GUI components.
Definition: logcategories.h:94
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Streamable status message, e.g.
Status messages, e.g. from Core -> GUI.
Value object encapsulating information of an aircraft's situation.
const CAltitude & getGroundElevation() const
Elevation of the ground directly beneath.
bool hasGroundElevation() const
Is ground elevation value available.
const CHeading & getHeading() const
Get heading.
geo::CLongitude longitude() const
Longitude.
geo::CLatitude latitude() const
Latitude.
const physical_quantities::CSpeed & getGroundSpeed() const
Get ground speed.
const CAltitude & getAltitude() const
Get altitude.
const CAltitude & getPressureAltitude() const
Get pressure altitude.
const physical_quantities::CAngle & getBank() const
Get bank (angle)
const physical_quantities::CAngle & getPitch() const
Get pitch.
swift::misc::CIcons::IconIndex toIcon() const
As icon, not implemented by all classes.
Definition: altitude.cpp:397
COM system (aka "radio")
Definition: comsystem.h:37
Heading as used in aviation, can be true or magnetic heading.
Definition: heading.h:41
CHeading normalizedTo360Degrees() const
As [0, 359.99] normalized heading.
Definition: heading.cpp:46
swift::misc::physical_quantities::CFrequency getFrequencyStandby() const
Standby frequency.
Definition: modulator.cpp:36
int getVolumeReceive() const
Output volume 0..100.
Definition: modulator.cpp:54
swift::misc::physical_quantities::CFrequency getFrequencyActive() const
Active frequency.
Definition: modulator.cpp:30
CIcons::IconIndex toIcon() const
As icon, not implemented by all classes.
Definition: earthangle.cpp:156
CIcons::IconIndex toIcon() const
As icon, not implemented by all classes.
Definition: mixinicon.h:39
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
swift::misc::CIcons::IconIndex toIcon() const
As icon, not implemented by all classes.
Definition: angle.cpp:42
QString valueRoundedWithUnit(const MU &unit, int digits=-1, bool withGroupSeparator=false, bool i18n=false) const
Value to QString with the given unit, e.g. "5.00m".
Comprehensive information of an aircraft.
const aviation::CAircraftSituation & getSituation() const
Get situation.
const aviation::CComSystem & getCom2System() const
Get COM2 system.
QString getTransponderCodeFormatted() const
Get transponder code.
const aviation::CComSystem & getCom1System() const
Get COM1 system.
const physical_quantities::CLength & getCG() const
Get CG from model.
const QString & getModelString() const
Get model string.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
CIcons::IconIndex toIcon() const
As icon, not implemented by all classes.
bool isAnySimulator() const
Any simulator?
Simulator internals for flight simulators. Those are obtained from a running simulator and represent ...
QStringList getSortedNames() const
Get sorted names.
CVariant getVariantValue(const QString &name) const
Get value.
const QString & getSimulator() const
Simulator.
const CSimulatorInfo & getSimulatorInfo() const
Simulator info object.
SWIFT_GUI_EXPORT swift::gui::CGuiApplication * sGui
Single instance of GUI application object.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
High level reusable GUI components.
Definition: aboutdialog.cpp:14
Free functions in swift::misc.
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QueuedConnection
void clear()
void setCurrentIndex(int index)
QWidget * currentWidget() const const
void start()
void stop()
void timeout()
void resize(const QSize &)