swift
simulatedaircraftview.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QPointer>
7 #include <QStringBuilder>
8 #include <QtGlobal>
9 
10 #include "config/buildconfig.h"
13 #include "gui/guiapplication.h"
14 #include "gui/menus/menuaction.h"
17 #include "misc/aviation/callsign.h"
18 #include "misc/icons.h"
20 
21 using namespace swift::config;
22 using namespace swift::misc;
23 using namespace swift::misc::physical_quantities;
24 using namespace swift::misc::aviation;
25 using namespace swift::misc::simulation;
26 using namespace swift::core::context;
27 using namespace swift::gui::models;
28 using namespace swift::gui::menus;
29 
30 namespace swift::gui::views
31 {
32  CSimulatedAircraftView::CSimulatedAircraftView(QWidget *parent) : CViewWithCallsignObjects(parent)
33  {
36  }
37 
39  {
40  Q_ASSERT(m_model);
41  m_model->setAircraftMode(mode);
42  this->setSortIndicator();
43  }
44 
45  void CSimulatedAircraftView::configureMenu(bool menuRecalculate, bool menuHighlightAndFollow,
46  bool menuEnableAircraft, bool menuFastPositionUpdates, bool menuGndFlag,
47  bool menuFlightPlan)
48  {
49  m_withRecalculate = menuRecalculate;
50  m_withMenuEnableAircraft = menuEnableAircraft;
51  m_withMenuFastPosition = menuFastPositionUpdates;
52  m_withMenuHighlightAndFollow = menuHighlightAndFollow;
53  m_withMenuEnableGndFlag = menuGndFlag;
54  m_withMenuFlightPlan = menuFlightPlan;
55  }
56 
58  {
59  m_withMenuFastPosition = menuFastPositionUpdates;
60  }
61 
63  {
64  // sub menus, only adding the path, the content will be added below
65  // => configureMenu
66  menuActions.addMenuDisplayModels();
67  menuActions.addMenuRenderModels();
68  menuActions.addMenuDataTransfer();
69 
70  if (m_menus.testFlag(MenuDisableModelsTemp) && this->hasSelection())
71  {
72  menuActions.addAction(CIcons::delete16(), "Temp.disable model from set",
73  CMenuAction::pathClientSimulationRender(),
74  { this, &CSimulatedAircraftView::requestTempDisable });
75  }
76 
77  if (m_withRecalculate)
78  {
79  menuActions.addAction(CIcons::appInterpolation16(), "Re-calculate all aircraft",
80  CMenuAction::pathClientSimulationRender(),
81  { this, &CSimulatedAircraftView::recalculateAllAircraft });
82  menuActions.addAction(CIcons::appInterpolation16(), "Re-matching all aircraft",
83  CMenuAction::pathClientSimulationRender(),
84  { this, &CSimulatedAircraftView::doMatchingsAgain });
85  if (this->hasSelection())
86  {
87  menuActions.addAction(CIcons::appInterpolation16(), "Re-matching selected",
88  CMenuAction::pathClientSimulationRender(),
89  { this, &CSimulatedAircraftView::doMatchingsAgainForSelected });
90  }
91  }
92 
93  if (m_withMenuEnableAircraft && !this->isEmpty())
94  {
95  menuActions.addAction(CIcons::appAircraft16(), "Enable all aircraft",
96  CMenuAction::pathClientSimulationRender(),
97  { this, &CSimulatedAircraftView::enableAllDisabledAircraft });
98  menuActions.addAction(CIcons::appAircraft16(), "Re-enable unrendered aircraft",
99  CMenuAction::pathClientSimulationRender(),
100  { this, &CSimulatedAircraftView::reEnableAllUnrenderedAircraft });
101  menuActions.addAction(CIcons::appAircraft16(), "Disable all aircraft",
102  CMenuAction::pathClientSimulationRender(),
103  { this, &CSimulatedAircraftView::disableAllEnabledAircraft });
104  }
105 
106  if (this->hasSelection())
107  {
108  const bool hasContexts = sGui && sGui->supportsContexts();
109  if (hasContexts)
110  {
111  const CSimulatedAircraft aircraft(this->selectedObject());
112  Q_ASSERT(!aircraft.getCallsign().isEmpty());
113  menuActions.addMenuCom();
114  menuActions.addAction(CIcons::appTextMessages16(), "Show text messages", CMenuAction::pathClientCom(),
115  { this, &CSimulatedAircraftView::requestTextMessage });
116 
117  if (m_withMenuFlightPlan && networkContext() && networkContext()->isConnected())
118  {
119  menuActions.addAction(CIcons::appFlightPlan16(), "Flight plan", CMenuAction::pathClientFlightPlan(),
120  { this, &CSimulatedAircraftView::showFlightPlanDialog });
121  }
122  if (m_withMenuEnableAircraft)
123  {
124  menuActions.addAction(CIcons::appAircraft16(),
125  aircraft.isEnabled() ? "Disable aircraft" : "Enabled aircraft",
126  CMenuAction::pathClientSimulationRender(),
127  { this, &CSimulatedAircraftView::toggleEnabledAircraft });
128  }
129  if (m_withMenuHighlightAndFollow)
130  {
131  menuActions.addAction(CIcons::appAircraft16(), "Follow in simulator",
132  CMenuAction::pathClientFollowInSim(),
133  { this, &CSimulatedAircraftView::requestFollowInSimulator });
134  if (!menuActions.isEmpty())
135  {
136  menuActions.addSeparator(CMenuAction::pathClientSimulationDisplay());
137  }
138  if (aircraft.isPartsSynchronized())
139  {
140  menuActions.addAction(CIcons::appAircraft16(), "Temp.disable parts",
141  CMenuAction::pathClientSimulationDisplay(),
142  { this, &CSimulatedAircraftView::requestDisableParts });
143  menuActions.addAction(CIcons::appAircraft16(), "Re/enabled parts",
144  CMenuAction::pathClientSimulationDisplay(),
145  { this, &CSimulatedAircraftView::requestEnableParts });
146  }
147  menuActions.addAction(CIcons::appAircraft16(), "Zero 0 pitch on ground",
148  CMenuAction::pathClientSimulationDisplay(),
149  { this, &CSimulatedAircraftView::request0PitchOnGround });
150  menuActions.addAction(CIcons::appAircraft16(), "Remove pitch manipulation",
151  CMenuAction::pathClientSimulationDisplay(),
152  { this, &CSimulatedAircraftView::requestNullPitchOnGround });
153  }
154  if (m_withMenuEnableGndFlag)
155  {
156  menuActions.addAction(CIcons::geoPosition16(),
157  aircraft.isSupportingGndFlag() ? "Disable gnd.flag" : "Enabled gnd.flag",
158  CMenuAction::pathClientSimulationTransfer(),
159  { this, &CSimulatedAircraftView::toggleSupportingGndFlag });
160  }
161  if (m_withMenuFastPosition)
162  {
163  menuActions.addAction(CIcons::globe16(),
164  aircraft.fastPositionUpdates() ? "Normal updates" :
165  "Fast position updates (send)",
166  CMenuAction::pathClientSimulationTransfer(),
167  { this, &CSimulatedAircraftView::toggleFastPositionUpdates });
168  }
169 
170  const bool any = m_withMenuEnableAircraft || m_withMenuFastPosition || m_withMenuHighlightAndFollow ||
171  m_withMenuEnableGndFlag || m_withMenuFlightPlan;
172  if (any && (sApp && sApp->isDeveloperFlagSet()))
173  {
174  menuActions.addAction(CIcons::appSimulator16(), "Show position log.",
175  CMenuAction::pathClientSimulationDisplay(),
176  { this, &CSimulatedAircraftView::showPositionLogInSimulator });
177  }
178  } // contexts
179  }
180  CViewBase::customMenu(menuActions);
181  }
182 
183  void CSimulatedAircraftView::requestTextMessage()
184  {
185  const CSimulatedAircraft aircraft(this->selectedObject());
186  if (aircraft.getCallsign().isEmpty()) { return; }
187  emit this->requestTextMessageWidget(aircraft.getCallsign());
188  }
189 
190  void CSimulatedAircraftView::toggleEnabledAircraft()
191  {
192  CSimulatedAircraft aircraft(this->selectedObject());
193  if (aircraft.getCallsign().isEmpty()) { return; }
194  aircraft.setEnabled(!aircraft.isEnabled());
195  this->updateAircraftEnabled(aircraft);
196  }
197 
198  void CSimulatedAircraftView::toggleFastPositionUpdates()
199  {
200  CSimulatedAircraft aircraft(this->selectedObject());
201  if (aircraft.getCallsign().isEmpty()) { return; }
202  aircraft.toggleFastPositionUpdates();
203  this->enableFastPositionUpdates(aircraft);
204  }
205 
206  void CSimulatedAircraftView::toggleSupportingGndFlag()
207  {
208  CSimulatedAircraft aircraft(this->selectedObject());
209  if (aircraft.getCallsign().isEmpty()) { return; }
210  aircraft.setSupportingGndFlag(!aircraft.isSupportingGndFlag());
211  this->updateAircraftSupportingGndFLag(aircraft);
212  }
213 
214  void CSimulatedAircraftView::requestFollowInSimulator()
215  {
216  const CSimulatedAircraft aircraft(this->selectedObject());
217  if (aircraft.getCallsign().isEmpty()) { return; }
218  this->followAircraftInSimulator(aircraft);
219  }
220 
221  void CSimulatedAircraftView::requestEnableParts()
222  {
223  const CSimulatedAircraft aircraft(this->selectedObject());
224  if (aircraft.getCallsign().isEmpty()) { return; }
225  this->enableParts(aircraft, true);
226  }
227 
228  void CSimulatedAircraftView::requestDisableParts()
229  {
230  const CSimulatedAircraft aircraft(this->selectedObject());
231  if (aircraft.getCallsign().isEmpty()) { return; }
232  this->enableParts(aircraft, false);
233  }
234 
235  void CSimulatedAircraftView::request0PitchOnGround()
236  {
237  const CSimulatedAircraft aircraft(this->selectedObject());
238  if (aircraft.getCallsign().isEmpty()) { return; }
239  this->setPitchOnGround(aircraft, CAngle(0, CAngleUnit::deg()));
240  }
241 
242  void CSimulatedAircraftView::requestNullPitchOnGround()
243  {
244  const CSimulatedAircraft aircraft(this->selectedObject());
245  if (aircraft.getCallsign().isEmpty()) { return; }
246  this->setPitchOnGround(aircraft, CAngle::null());
247  }
248 
249  void CSimulatedAircraftView::requestTempDisable()
250  {
251  if (!m_menus.testFlag(MenuDisableModelsTemp)) { return; }
252  if (!this->hasSelection()) { return; }
253  const CAircraftModelList models(this->selectedObjects().getModels());
254  emit this->requestTempDisableModelsForMatching(models);
255  sGui->displayInStatusBar(CStatusMessage(CStatusMessage::SeverityInfo,
256  u"Temp.disabled " % models.getModelStringList(true).join(" ")));
257  }
258 
259  void CSimulatedAircraftView::showPositionLogInSimulator()
260  {
261  IContextSimulator *simContext = simulatorContext();
262  if (!simContext) { return; }
263  const CSimulatedAircraft aircraft(this->selectedObject());
264  if (aircraft.getCallsign().isEmpty()) { return; }
265 
266  const CIdentifier i(this->objectName());
267  const QString dotCmd(".drv pos " + aircraft.getCallsignAsString());
268  simContext->parseCommandLine(dotCmd, i);
269  }
270 
271  void CSimulatedAircraftView::enableAllDisabledAircraft()
272  {
273  if (!sGui || sGui->isShuttingDown()) { return; }
274  const CSimulatedAircraftList aircraft = this->container().findByEnabled(false);
275  this->enableOrDisableAircraft(aircraft, true);
276  }
277 
278  void CSimulatedAircraftView::disableAllEnabledAircraft()
279  {
280  if (!sGui || sGui->isShuttingDown()) { return; }
281  const CSimulatedAircraftList aircraft = this->container().findByEnabled(true);
282  this->enableOrDisableAircraft(aircraft, false);
283  }
284 
285  void CSimulatedAircraftView::reEnableAllUnrenderedAircraft()
286  {
287  if (!sGui || sGui->isShuttingDown()) { return; }
288  const CSimulatedAircraftList aircraft = this->container().findByRendered(false);
289  this->enableOrDisableAircraft(aircraft, true);
290  }
291 
292  void CSimulatedAircraftView::enableOrDisableAircraft(const CSimulatedAircraftList &aircraft, bool newEnabled)
293  {
294  if (aircraft.isEmpty()) { return; }
295 
296  const QPointer<CSimulatedAircraftView> myself(this);
297  for (const CSimulatedAircraft &sa : aircraft)
298  {
299  QTimer::singleShot(10, this, [=] {
300  if (!myself) { return; }
301  if (!sGui || sGui->isShuttingDown()) { return; }
302  CSimulatedAircraft enabledAircraft(sa);
303  enabledAircraft.setEnabled(newEnabled);
304  this->updateAircraftEnabled(enabledAircraft);
305  });
306  }
307  }
308 
309  void CSimulatedAircraftView::followAircraftInSimulator(const CSimulatedAircraft &aircraft)
310  {
311  IContextSimulator *simContext = simulatorContext();
312  if (!simContext) { return; }
313  simContext->followAircraft(aircraft.getCallsign());
314  }
315 
316  void CSimulatedAircraftView::enableParts(const CSimulatedAircraft &aircraft, bool enabled)
317  {
318  IContextSimulator *simContext = simulatorContext();
319  if (!simContext || !aircraft.hasCallsign()) { return; }
322  if (setup.isAircraftPartsEnabled() == enabled) { return; }
323  setup.setEnabledAircraftParts(enabled);
324  setup.setCallsign(aircraft.getCallsign());
325  simContext->setInterpolationAndRenderingSetupsPerCallsign(setup, true);
326  }
327 
328  void CSimulatedAircraftView::setPitchOnGround(const CSimulatedAircraft &aircraft,
329  const physical_quantities::CAngle &pitch)
330  {
331  IContextSimulator *simContext = simulatorContext();
332  if (!simContext || !aircraft.hasCallsign()) { return; }
335  if (setup.getPitchOnGround() == pitch) { return; }
336  setup.setPitchOnGround(pitch);
337  setup.setCallsign(aircraft.getCallsign());
338  simContext->setInterpolationAndRenderingSetupsPerCallsign(setup, true);
339  }
340 
341  bool CSimulatedAircraftView::isSupportingAircraftParts(const CCallsign &cs) const
342  {
343  const IContextNetwork *netContext = networkContext();
344  if (!netContext) { return false; }
345  return netContext->isRemoteAircraftSupportingParts(cs);
346  }
347 
348  void CSimulatedAircraftView::recalculateAllAircraft()
349  {
350  IContextSimulator *simContext = simulatorContext();
351  if (!simContext) { return; }
352  simContext->recalculateAllAircraft();
353  }
354 
355  void CSimulatedAircraftView::doMatchingsAgain()
356  {
357  IContextSimulator *simContext = simulatorContext();
358  if (!simContext) { return; }
359  const int rematchedNumber = simContext->doMatchingsAgain();
360  CLogMessage(this).info(u"Triggered re-matching of %1 aircraft") << rematchedNumber;
361  }
362 
363  void CSimulatedAircraftView::doMatchingsAgainForSelected()
364  {
365  IContextSimulator *simContext = simulatorContext();
366  if (!simContext) { return; }
367  if (!this->hasSelection()) { return; }
368  const CCallsign cs = this->selectedObject().getCallsign();
369  if (simContext->doMatchingAgain(cs))
370  {
371  CLogMessage(this).info(u"Triggered re-matching of '%1'") << cs.asString();
372  }
373  }
374 
375  void CSimulatedAircraftView::enableFastPositionUpdates(const CSimulatedAircraft &aircraft)
376  {
377  IContextNetwork *nwContext = networkContext();
378  if (!nwContext) { return; }
379  nwContext->updateFastPositionEnabled(aircraft.getCallsign(), aircraft.fastPositionUpdates());
380  }
381 
382  void CSimulatedAircraftView::updateAircraftEnabled(const CSimulatedAircraft &aircraft)
383  {
384  IContextNetwork *nwContext = networkContext();
385  if (!nwContext) { return; }
386  nwContext->updateAircraftEnabled(aircraft.getCallsign(), aircraft.isEnabled());
387  }
388 
389  void CSimulatedAircraftView::updateAircraftSupportingGndFLag(const CSimulatedAircraft &aircraft)
390  {
391  IContextNetwork *nwContext = networkContext();
392  if (!nwContext) { return; }
393  nwContext->updateAircraftSupportingGndFLag(aircraft.getCallsign(), aircraft.isSupportingGndFlag());
394  }
395 
396  void CSimulatedAircraftView::showFlightPlanDialog()
397  {
398  if (!m_withMenuFlightPlan) { return; }
399  if (!networkContext() || !networkContext()->isConnected()) { return; }
400 
401  const CSimulatedAircraft aircraft = this->selectedObject();
402  if (!aircraft.hasCallsign()) { return; }
403 
404  const CCallsign cs = aircraft.getCallsign();
405  if (!m_fpDialog) { m_fpDialog = new CFlightPlanDialog(this); }
406  m_fpDialog->showFlightPlan(cs);
407  }
408 
409  IContextSimulator *CSimulatedAircraftView::simulatorContext()
410  {
411  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextSimulator()) { return nullptr; }
412  return sGui->getIContextSimulator();
413  }
414 
415  IContextNetwork *CSimulatedAircraftView::networkContext()
416  {
417  if (!sGui || sGui->isShuttingDown() || !sGui->getIContextNetwork()) { return nullptr; }
418  return sGui->getIContextNetwork();
419  }
420 } // namespace swift::gui::views
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
bool isDeveloperFlagSet() const
Running with dev.flag?
Definition: application.h:173
const context::IContextNetwork * getIContextNetwork() const
Direct access to contexts if a CCoreFacade has been initialized.
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.
virtual bool parseCommandLine(const QString &commandLine, const swift::misc::CIdentifier &originator)=0
Parse a given command line.
virtual bool updateAircraftEnabled(const swift::misc::aviation::CCallsign &callsign, bool enabledForRendering)=0
Enable/disable rendering.
virtual bool updateFastPositionEnabled(const swift::misc::aviation::CCallsign &callsign, bool enableFastPositionSending)=0
Change fast position updates.
virtual bool updateAircraftSupportingGndFLag(const swift::misc::aviation::CCallsign &callsign, bool supportGndFlag)=0
Enable/disable support of gnd. flag.
virtual bool isRemoteAircraftSupportingParts(const swift::misc::aviation::CCallsign &callsign) const =0
Parts supported?
virtual bool doMatchingAgain(const swift::misc::aviation::CCallsign &callsign)=0
Repeat the matching callsign.
virtual void recalculateAllAircraft()=0
Recalculate all aircraft.
virtual bool setInterpolationAndRenderingSetupsPerCallsign(const swift::misc::simulation::CInterpolationSetupList &setups, bool ignoreSameAsGlobal)=0
Set all setups per callsign.
virtual int doMatchingsAgain()=0
Repeat all matchings.
virtual swift::misc::simulation::CInterpolationAndRenderingSetupPerCallsign getInterpolationAndRenderingSetupPerCallsignOrDefault(const swift::misc::aviation::CCallsign &callsign) const =0
Get the setup for callsign, if not existing the global setup.
virtual bool followAircraft(const swift::misc::aviation::CCallsign &callsign)=0
Follow aircraft im simulator view.
bool displayInStatusBar(const swift::misc::CStatusMessage &message)
direct access to main application window
Bunch of CMenuAction objects.
Definition: menuaction.h:384
void addSeparator(const QString &path)
Add a separator.
Definition: menuaction.cpp:187
CMenuAction addMenuDisplayModels()
Add display model menu.
Definition: menuaction.cpp:473
CMenuAction addMenuCom()
Add COM model menu.
Definition: menuaction.cpp:467
CMenuAction addAction(const CMenuAction &menuAction)
Add menu action.
Definition: menuaction.cpp:210
bool isEmpty() const
Empty?
Definition: menuaction.h:405
CMenuAction addMenuRenderModels()
Add render model menu.
Definition: menuaction.cpp:479
CMenuAction addMenuDataTransfer()
Add data transfer menu.
Definition: menuaction.cpp:485
void showFlightPlan(const swift::misc::aviation::CCallsign &callsign)
Show a particular callsign flight plan.
virtual void customMenu(menus::CMenuActions &menuActions)
Method creating the menu.
void setAircraftMode(models::CSimulatedAircraftListModel::AircraftMode mode)
Mode.
void requestTextMessageWidget(const swift::misc::aviation::CCallsign &callsign)
Request a text message.
void requestTempDisableModelsForMatching(const swift::misc::simulation::CAircraftModelList &models)
Disable for matching.
void configureMenuFastPositionUpdates(bool menuFastPositionUpdates)
Configure fast position updates menu.
void configureMenu(bool menuRecalculate, bool menuHighlightAndFollow, bool menuEnableAircraft, bool menuFastPositionUpdates, bool menuGndFlag, bool menuFlightPlan)
Configure the menu.
void standardInit(ModelClass *model=nullptr)
Standard initialization.
Definition: viewbase.cpp:572
virtual bool isEmpty() const
Empty?
Definition: viewbase.cpp:400
virtual ContainerType selectedObjects() const
Selected objects.
Definition: viewbase.cpp:240
ModelClass * m_model
corresponding model
Definition: viewbase.h:877
void setSortIndicator()
Set the search indicator based on model.
Definition: viewbase.cpp:562
ObjectType selectedObject() const
Selected object (or default)
Definition: viewbase.cpp:307
const ContainerType & container() const
Access to container.
Definition: viewbase.cpp:200
virtual void customMenu(menus::CMenuActions &menuActions)
Method creating the menu.
Definition: viewbase.cpp:680
Menu m_menus
Default menu settings.
Definition: viewbase.h:603
@ MenuRefresh
allow refreshing the view via menu
Definition: viewbase.h:122
bool hasSelection() const
Selection (selected rows)
Base class for views with DB objects.
Value object encapsulating information identifying a component of a modular distributed swift process...
Definition: identifier.h:29
Class for emitting a log message.
Definition: logmessage.h:27
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Streamable status message, e.g.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
const QString & asString() const
Get callsign (normalized)
Definition: callsign.h:96
bool isEmpty() const
Is empty?
Definition: callsign.h:63
Physical unit angle (radians, degrees)
Definition: angle.h:23
Value object encapsulating a list of aircraft models.
bool setPitchOnGround(const physical_quantities::CAngle &pitchOnGround)
Force a given pitch on ground.
bool setEnabledAircraftParts(bool enabled)
Set enabled aircraft parts.
bool isAircraftPartsEnabled() const
Aircraft parts enabled (still requires the other aircraft to send parts)
const physical_quantities::CAngle & getPitchOnGround() const
Force a given pitch on ground.
Value object for interpolator and rendering per callsign.
void setCallsign(const aviation::CCallsign &callsign)
Set callsign.
Comprehensive information of an aircraft.
bool isPartsSynchronized() const
Have parts been synchronized with a remote client?
bool isSupportingGndFlag() const
Is supporting gnd.flag?
bool fastPositionUpdates() const
Support fast position updates.
bool hasCallsign() const
Callsign not empty, no further checks.
const aviation::CCallsign & getCallsign() const
Get callsign.
bool setEnabled(bool enabled)
Enabled / disabled.
bool isEnabled() const
Enabled? Enable means it shall be displayed in the simulator.
Value object encapsulating a list of aircraft.
SWIFT_GUI_EXPORT swift::gui::CGuiApplication * sGui
Single instance of GUI application object.
Models to be used with views, mainly QTableView.
Views, mainly QTableView.
Free functions in swift::misc.
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