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)
44  {
45  Q_ASSERT_X(sGui, Q_FUNC_INFO, "Need sGui");
46 
47  ui->setupUi(this);
48  this->setCurrentIndex(0);
49  ui->comp_StatusMessages->showFilterDialog();
50 
51  // live data and internals
52  ui->tvp_LiveData->setIconMode(true);
53  ui->tvp_LiveData->setAutoResizeFrequency(10); // only resize every n-th time
54  ui->tvp_Internals->setIconMode(false);
55  this->addOrUpdateLiveDataByName("info", "no data yet", CIcons::StandardIconWarning16);
56 
57  // connects
58  connect(sGui->getIContextSimulator(), &IContextSimulator::simulatorStatusChanged, this,
59  &CSimulatorComponent::onSimulatorStatusChanged, Qt::QueuedConnection);
60  connect(&m_updateTimer, &QTimer::timeout, this, &CSimulatorComponent::update);
61  connect(ui->pb_RefreshInternals, &QPushButton::pressed, this, &CSimulatorComponent::refreshInternals);
63  {
64  connect(sGui->getIContextSimulator(), &IContextSimulator::addingRemoteModelFailed, this,
65  &CSimulatorComponent::onAddingRemoteModelFailed, Qt::QueuedConnection);
66  connect(sGui->getIContextSimulator(), &IContextSimulator::driverMessages, this,
67  &CSimulatorComponent::onSimulatorMessages, Qt::QueuedConnection);
68  }
69 
70  // init status
71  this->onSimulatorStatusChanged(sGui->getIContextSimulator()->getSimulatorStatus());
72  }
73 
75 
76  void CSimulatorComponent::addOrUpdateLiveDataByName(const QString &name, const QString &value, const CIcon &icon)
77  {
78  const bool resize = this->currentWidget() == ui->tb_LiveData; // simulator live data selected?
79  ui->tvp_LiveData->addOrUpdateByName(name, value, icon, resize, false);
80  }
81 
82  void CSimulatorComponent::addOrUpdateLiveDataByName(const QString &name, const QString &value,
83  CIcons::IconIndex iconIndex)
84  {
85  this->addOrUpdateLiveDataByName(name, value, CIcon::iconByIndex(iconIndex));
86  }
87 
88  void CSimulatorComponent::removeLiveDataByName(const QString &name) { ui->tvp_LiveData->removeByName(name); }
89 
90  int CSimulatorComponent::rowCount() const { return ui->tvp_LiveData->rowCount(); }
91 
92  void CSimulatorComponent::clear(bool addInternalsAfterwards)
93  {
94  ui->tvp_LiveData->clear();
95  if (addInternalsAfterwards) { this->refreshInternals(); }
96  }
97 
99  {
100  if (!this->isVisibleWidget()) return; // no updates on invisible widgets
101  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextOwnAircraft()) return;
102 
103  const ISimulator::SimulatorStatus simulatorStatus =
104  static_cast<ISimulator::SimulatorStatus>(sGui->getIContextSimulator()->getSimulatorStatus());
105  if (simulatorStatus == ISimulator::Unspecified || simulatorStatus == ISimulator::Disconnected)
106  {
107  static const QString s("No simulator available");
108  this->addOrUpdateLiveDataByName(QStringLiteral("info"), s, CIcons::StandardIconWarning16);
109  return;
110  }
111 
112  if (!simulatorStatus.testFlag(ISimulator::Simulating))
113  {
114  static const QString s("Simulator (%1) not yet running");
115  this->addOrUpdateLiveDataByName(
116  QStringLiteral("info"), s.arg(sGui->getIContextSimulator()->getSimulatorPluginInfo().getSimulator()),
117  CIcons::StandardIconWarning16);
118  return;
119  }
120 
121  // clear old warnings / information
122  if (this->rowCount() < 5) { this->clear(true); }
123 
125  const CAircraftSituation s = ownAircraft.getSituation();
126  const CComSystem c1 = ownAircraft.getCom1System();
127  const CComSystem c2 = ownAircraft.getCom2System();
128  static const CIcon iconAlt(s.getAltitude().toIcon()); // minor performance improvement
129  static const CIcon iconLatLng(s.latitude().toIcon());
130  static const CIcon iconRadio(CIcon::iconByIndex(CIcons::StandardIconRadio16));
131  static const CIcon iconAttitude(CIcon::iconByIndex(CIcons::AviationAttitudeIndicator));
132  static const CIcon iconPlane(CIcon::iconByIndex(CIcons::StandardIconPaperPlane16));
133 
134  if (m_simulator.isAnySimulator())
135  {
136  this->addOrUpdateLiveDataByName("simulator", m_simulator.toQString(true), m_simulator.toIcon());
137 
138  if (sGui->getISimulator())
139  {
140  const double fps = sGui->getISimulator()->getAverageFPS();
141  this->addOrUpdateLiveDataByName(QStringLiteral("FPS"),
142  fps < 0 ? QStringLiteral("N/A") : QString::number(fps, 'f', 1),
143  CIcon(CIcons::ApplicationSimulator));
144 
145  const double ratio = sGui->getISimulator()->getSimTimeRatio();
146  this->addOrUpdateLiveDataByName(QStringLiteral("Time Ratio"), QString::number(ratio, 'f', 2),
147  CIcon(CIcons::ApplicationSimulator));
148 
149  const double miles = sGui->getISimulator()->getTrackMilesShort();
150  this->addOrUpdateLiveDataByName(QStringLiteral("Miles Short"), QString::number(miles, 'f', 1),
151  CIcon(CIcons::ApplicationSimulator));
152 
153  const double minutes = sGui->getISimulator()->getMinutesLate();
154  this->addOrUpdateLiveDataByName(QStringLiteral("Minutes Late"), QString::number(minutes, 'f', 1),
155  CIcon(CIcons::ApplicationSimulator));
156  }
157  }
158 
159  this->addOrUpdateLiveDataByName(QStringLiteral("latitude"), s.latitude().toQString(), iconLatLng);
160  this->addOrUpdateLiveDataByName(QStringLiteral("longitude"), s.longitude().toQString(), iconLatLng);
161  this->addOrUpdateLiveDataByName(QStringLiteral("altitude, true (ft)"),
162  s.getAltitude().valueRoundedWithUnit(CLengthUnit::ft(), 1), iconAlt);
163  this->addOrUpdateLiveDataByName(QStringLiteral("altitude, true (m)"),
164  s.getAltitude().valueRoundedWithUnit(CLengthUnit::m(), 2), iconAlt);
165  this->addOrUpdateLiveDataByName(QStringLiteral("altitude, pressure (ft)"),
166  s.getPressureAltitude().valueRoundedWithUnit(CLengthUnit::ft(), 1), iconAlt);
167  this->addOrUpdateLiveDataByName(QStringLiteral("altitude, pressure (m)"),
168  s.getPressureAltitude().valueRoundedWithUnit(CLengthUnit::m(), 2), iconAlt);
169 
170  if (s.hasGroundElevation())
171  {
172  this->addOrUpdateLiveDataByName(QStringLiteral("elevation (ft)"),
173  s.getGroundElevation().valueRoundedWithUnit(CLengthUnit::ft(), 1), iconAlt);
174  this->addOrUpdateLiveDataByName(QStringLiteral("elevation (m)"),
175  s.getGroundElevation().valueRoundedWithUnit(CLengthUnit::m(), 2), iconAlt);
176  }
177  else { this->addOrUpdateLiveDataByName(QStringLiteral("elevation"), QStringLiteral("N/A"), iconAlt); }
178 
179  if (ownAircraft.hasCG())
180  {
181  this->addOrUpdateLiveDataByName(QStringLiteral("CG (ft)"),
182  ownAircraft.getCG().valueRoundedWithUnit(CLengthUnit::ft(), 1), iconPlane);
183  this->addOrUpdateLiveDataByName(QStringLiteral("CG (m)"),
184  ownAircraft.getCG().valueRoundedWithUnit(CLengthUnit::m(), 2), iconPlane);
185  }
186  else { this->addOrUpdateLiveDataByName(QStringLiteral("CG"), QStringLiteral("N/A"), iconPlane); }
187 
188  this->addOrUpdateLiveDataByName(QStringLiteral("pitch"), s.getPitch().toQString(), iconAttitude);
189  this->addOrUpdateLiveDataByName(QStringLiteral("bank"), s.getBank().toQString(), iconAttitude);
190 
191  const CHeading heading = s.getHeading().normalizedTo360Degrees();
192  this->addOrUpdateLiveDataByName(QStringLiteral("heading"), heading.valueRoundedWithUnit(CAngleUnit::deg(), 1),
193  s.getHeading().toIcon());
194 
195  const CSpeed gs = s.getGroundSpeed();
196  this->addOrUpdateLiveDataByName(QStringLiteral("ground speed (kts)"),
197  gs.valueRoundedWithUnit(CSpeedUnit::kts(), 1), gs.toIcon());
198  this->addOrUpdateLiveDataByName(QStringLiteral("ground speed (km/h)"),
199  gs.valueRoundedWithUnit(CSpeedUnit::km_h(), 1), gs.toIcon());
200 
201  this->addOrUpdateLiveDataByName(QStringLiteral("COM1 active"), c1.getFrequencyActive().toQString(), iconRadio);
202  this->addOrUpdateLiveDataByName(QStringLiteral("COM2 active"), c2.getFrequencyActive().toQString(), iconRadio);
203  this->addOrUpdateLiveDataByName(QStringLiteral("COM1 standby"), c1.getFrequencyStandby().toQString(),
204  iconRadio);
205  this->addOrUpdateLiveDataByName(QStringLiteral("COM2 standby"), c2.getFrequencyStandby().toQString(),
206  iconRadio);
207  this->addOrUpdateLiveDataByName(QStringLiteral("COM1 volume"), QString::number(c1.getVolumeReceive()),
208  iconRadio);
209  this->addOrUpdateLiveDataByName(QStringLiteral("COM2 volume"), QString::number(c2.getVolumeReceive()),
210  iconRadio);
211  this->addOrUpdateLiveDataByName(QStringLiteral("Transponder"), ownAircraft.getTransponderCodeFormatted(),
212  iconRadio);
213  }
214 
215  void CSimulatorComponent::onSimulatorStatusChanged(int status)
216  {
217  ISimulator::SimulatorStatus simStatus = static_cast<ISimulator::SimulatorStatus>(status);
218  this->clear(); // clean up, will be refreshed
219  if (simStatus.testFlag(ISimulator::Connected))
220  {
221  const int intervalMs = getUpdateIntervalMs();
222  m_updateTimer.start(intervalMs);
223  this->clear(true);
224  }
225  else
226  {
227  m_updateTimer.stop();
228  this->update();
229  }
230  }
231 
232  void CSimulatorComponent::onAddingRemoteModelFailed(const CSimulatedAircraft &aircraft, bool disabled,
233  bool failover, const CStatusMessage &message)
234  {
235  const CStatusMessage msg =
236  CStatusMessage(this).warning(u"Adding model '%1' failed, disabled: %2: failover: %3 details: %4")
237  << aircraft.getModelString() << boolToYesNo(disabled) << boolToYesNo(failover) << aircraft.toQString(true);
238  ui->comp_StatusMessages->appendStatusMessageToList(msg);
239  ui->comp_StatusMessages->appendStatusMessageToList(message);
240  }
241 
242  void CSimulatorComponent::onSimulatorMessages(const CStatusMessageList &messages)
243  {
244  if (messages.isEmpty()) { return; }
245  ui->comp_StatusMessages->appendStatusMessagesToList(messages);
246  }
247 
248  void CSimulatorComponent::refreshInternals()
249  {
250  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return; }
253  m_simulator = simulatorInfo;
254 
255  const QStringList names(internals.getSortedNames());
256  if (names.isEmpty())
257  {
258  ui->tvp_Internals->clear();
259  return;
260  }
261 
262  // Update the INTERNAL view
263  static const CIcon emptyIcon;
264  const bool resize = true;
265  const bool skipEqualValues = true;
266  for (const QString &name : names)
267  {
268  ui->tvp_Internals->addOrUpdateByName(name, internals.getVariantValue(name), emptyIcon, resize,
269  skipEqualValues);
270  }
271  ui->tvp_Internals->fullResizeToContents();
272  }
273 
274  int CSimulatorComponent::getUpdateIntervalMs() const
275  {
276  // much slower updates via DBus
277  if (!sGui || sGui->isShuttingDown()) { return 10000; }
278  return sGui->getIContextSimulator()->isUsingImplementingObject() ? 1000 : 5000;
279  }
280 } // 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.
Helper class: If a component is residing in an dockable widget. This class provides access to its inf...
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.
virtual 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.
virtual geo::CLongitude longitude() const
Longitude.
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:13
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 &)