swift
simconnectobject.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 
4 #include "simconnectobject.h"
5 
6 #include "simulatorfsxcommon.h"
7 
8 #include "config/buildconfig.h"
9 #include "core/simulator.h"
11 #include "misc/stringutils.h"
12 
13 using namespace swift::config;
14 using namespace swift::misc;
15 using namespace swift::misc::aviation;
16 using namespace swift::misc::simulation;
17 using namespace swift::core;
18 
19 namespace swift::simplugin::fsxcommon
20 {
21  CSimConnectObject::CSimConnectObject() { this->resetCameraPositions(); }
22 
23  CSimConnectObject::CSimConnectObject(CSimConnectObject::SimObjectType type) : m_type(type)
24  {
25  this->resetCameraPositions();
26  }
27 
29  ISimulationEnvironmentProvider *simEnvProvider,
30  IInterpolationSetupProvider *setupProvider,
31  IRemoteAircraftProvider *remoteAircraftProvider, CInterpolationLogger *logger)
32  : m_aircraft(aircraft), m_requestId(requestId), m_validRequestId(true),
33  m_interpolator(QSharedPointer<CInterpolatorMulti>::create(aircraft.getCallsign(), simEnvProvider,
34  setupProvider, remoteAircraftProvider, logger))
35  {
36  this->resetCameraPositions();
37  m_type = aircraft.isTerrainProbe() ? TerrainProbe : AircraftNonAtc;
38  m_interpolator->initCorrespondingModel(aircraft.getModel());
39  m_callsignByteArray = aircraft.getCallsignAsString().toLatin1();
40  }
41 
43  {
44  m_aircraft = aircraft;
45  m_callsignByteArray = aircraft.getCallsignAsString().toLatin1();
46  m_type = aircraft.isTerrainProbe() ? TerrainProbe : AircraftNonAtc;
47  }
48 
49  void CSimConnectObject::setAircraftModelString(const QString &modelString)
50  {
51  if (modelString.isEmpty()) { return; }
52  m_aircraft.setModelString(modelString);
53  }
54 
56  {
57  if (cg.isNull()) { return; }
58  m_aircraft.setCG(cg);
59  }
60 
62  {
63  m_requestId = id;
64  m_validRequestId = true;
65  const SimObjectType type = requestIdToType(id);
66  this->setType(type);
67  }
68 
70  {
71  if (CBuildConfig::isLocalDeveloperDebugBuild())
72  {
73  const SimObjectType type = requestIdToType(m_requestId);
74  const bool same = CSimConnectObject::isSameTypeGroup(type, this->getType());
75  Q_ASSERT_X(same, Q_FUNC_INFO, "Type mismatch");
76  }
77 
78  DWORD os = 0;
79  switch (this->getType())
80  {
81  case TerrainProbe: os = static_cast<DWORD>(CSimulatorFsxCommon::offsetSimObjTerrainProbe(offset)); break;
82  case AircraftNonAtc:
83  case AircraftSimulatedObject:
84  default: os = static_cast<DWORD>(CSimulatorFsxCommon::offsetSimObjAircraft(offset)); break;
85  }
86  return os + m_requestId;
87  }
88 
90  {
91  m_objectId = id;
92  m_validObjectId = true;
93  }
94 
95  bool CSimConnectObject::isPendingAdded() const { return !this->hasValidRequestAndObjectId() || !m_confirmedAdded; }
96 
97  bool CSimConnectObject::isOutdatedPendingAdded(qint64 thresholdMs, qint64 currentMsSinceEpoch) const
98  {
99  if (!this->isPendingAdded()) { return false; }
100  if (currentMsSinceEpoch < 0) { currentMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); }
101  if (m_tsCreated < 0) { return true; } // no valid timestamp
102  const qint64 delta = currentMsSinceEpoch - m_tsCreated;
103  return delta > thresholdMs;
104  }
105 
107  {
108  Q_ASSERT_X(!m_confirmedAdded || this->hasValidRequestAndObjectId(), Q_FUNC_INFO, "confirmed but invalid ids");
109  return m_confirmedAdded;
110  }
111 
113  {
114  m_confirmedAdded = confirm;
115  m_removedWhileAdding = false;
116  m_addedWhileRemoving = false;
117  m_aircraft.setRendered(true);
118  }
119 
120  void CSimConnectObject::setAddedWhileRemoving(bool addedWileRemoved) { m_addedWhileRemoving = addedWileRemoved; }
121 
122  void CSimConnectObject::setRemovedWhileAdding(bool removedWhileAdding)
123  {
124  m_removedWhileAdding = removedWhileAdding;
125  }
126 
128  {
129  return !this->isPending() && !m_addedWhileRemoving && !m_removedWhileAdding;
130  }
131 
133  {
134  m_pendingRemoved = pending;
135  m_removedWhileAdding = false;
136  m_addedWhileRemoving = false;
137  m_aircraft.setRendered(false);
138  }
139 
141  {
142  m_cameraPosition.x = 0;
143  m_cameraPosition.y = 0;
144  m_cameraPosition.z = 0;
145  m_cameraRotation.Pitch = 0;
146  m_cameraRotation.Bank = 0;
147  m_cameraRotation.Heading = 0;
148  }
149 
151  {
152  m_pendingRemoved = false;
153  m_confirmedAdded = false;
154  m_removedWhileAdding = false;
155  m_addedWhileRemoving = false;
156  m_camera = false;
157  m_currentLightsInSim = CAircraftLights();
158  m_lightsAsSent = CAircraftLights();
159  m_requestId = 0;
160  m_objectId = 0;
161  m_addingExceptions = 0;
162  m_validRequestId = false;
163  m_validObjectId = false;
164  m_tsCreated = -1;
165  this->resetCameraPositions();
166  }
167 
169  {
170  const CSimConnectObject old(*this);
171  this->resetState();
172  this->copyAddingFailureCounters(old);
173  }
174 
176  {
177  return this->hasValidRequestId() && this->hasValidObjectId();
178  }
179 
181  {
182  m_addingExceptions = otherObject.m_addingExceptions;
183  m_addingDirectlyRemoved = otherObject.m_addingDirectlyRemoved;
184  }
185 
187  {
188  Q_ASSERT(m_interpolator);
189  return m_interpolator->getInterpolatorInfo(mode);
190  }
191 
193  {
194  Q_ASSERT(m_interpolator);
195  m_interpolator->attachLogger(logger);
196  }
197 
200  uint32_t aircraftNumber) const
201  {
202  if (!m_interpolator) { return {}; }
203  return m_interpolator->getInterpolation(currentTimeSinceEpoch, setup, aircraftNumber);
204  }
205 
206  const CAircraftSituation &
208  {
209  if (!m_interpolator) { return CAircraftSituation::null(); }
210  return m_interpolator->getLastInterpolatedSituation(mode);
211  }
212 
213  const CStatusMessageList &
215  {
216  static const CStatusMessageList empty;
217  if (!m_interpolator) { return empty; }
218  return m_interpolator->getInterpolationMessages(mode);
219  }
220 
222  {
223  static const QString s(
224  "CS: '%1' obj: %2 req: %3 conf.added: %4 pend.rem.: %5 rwa: %6 awr: %7 aEx: %8 aRem: %9");
225  return s.arg(this->getCallsign().asString())
226  .arg(m_objectId)
227  .arg(m_requestId)
228  .arg(boolToYesNo(m_confirmedAdded), boolToYesNo(m_pendingRemoved), boolToYesNo(m_removedWhileAdding),
229  boolToYesNo(m_addedWhileRemoving))
230  .arg(m_addingExceptions)
231  .arg(m_addingDirectlyRemoved);
232  }
233 
235  {
236  if (CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId)) { return TerrainProbe; }
237  if (CSimulatorFsxCommon::isRequestForSimObjAircraft(requestId)) { return AircraftNonAtc; }
238  Q_ASSERT_X(false, Q_FUNC_INFO, "Wrong range");
239  return AircraftNonAtc;
240  }
241 
243  {
244  static const QString a1("aircraft (non ATC)");
245  static const QString a2("aircraft (sim.object)");
246  static const QString p("probe");
247  static const QString u("unknown");
248  switch (type)
249  {
250  case AircraftNonAtc: return a1;
251  case AircraftSimulatedObject: return a2;
252  case TerrainProbe: return p;
253  default: break;
254  }
255  return u;
256  }
257 
259  {
260  if (t1 == t2) { return true; }
261  return isAircraft(t1) && isAircraft(t2);
262  }
263 
265  {
266  return CSimConnectObject::AircraftNonAtc == type || CSimConnectObject::AircraftSimulatedObject;
267  }
268 
269  bool CSimConnectObjects::insert(const CSimConnectObject &simObject, bool updateTimestamp)
270  {
271  if (!simObject.hasCallsign()) { return false; }
272  if (updateTimestamp)
273  {
274  CSimConnectObject simObj(simObject);
275  simObj.resetTimestampToNow();
276  (*this)[simObj.getCallsign()] = simObj;
277  }
278  else { (*this)[simObject.getCallsign()] = simObject; }
279  return true;
280  }
281 
283  {
284  // First check, if this request id belongs to us
285  auto it = std::find_if(this->begin(), this->end(),
286  [requestId](const CSimConnectObject &obj) { return obj.getRequestId() == requestId; });
287  if (it == this->end()) { return false; }
288 
289  // belongs to us
290  it->setObjectId(objectId);
291  return true;
292  }
293 
295  {
296  return this->getSimObjectForObjectId(objectId).getCallsign();
297  }
298 
300  {
301  if (this->isEmpty()) { return CCallsignSet(); }
302  if (!withoutProbes) { return CCallsignSet(this->keys()); }
303  CCallsignSet callsigns;
304  for (const CSimConnectObject &simObject : *this)
305  {
306  if (simObject.isAircraft()) { callsigns.insert(simObject.getCallsign().asString()); }
307  }
308  return callsigns;
309  }
310 
311  QStringList CSimConnectObjects::getAllCallsignStrings(bool sorted, bool withoutProbes) const
312  {
313  return this->getAllCallsigns(withoutProbes).getCallsignStrings(sorted);
314  }
315 
316  QString CSimConnectObjects::getAllCallsignStringsAsString(bool sorted, const QString &separator) const
317  {
318  return this->getAllCallsignStrings(sorted).join(separator);
319  }
320 
322  {
323  for (const CSimConnectObject &simObject : *this)
324  {
325  if (simObject.getObjectId() == objectId) { return simObject; }
326  }
327  return CSimConnectObject();
328  }
329 
331  {
332  if (this->isEmpty()) { return CSimConnectObject(); }
333  CSimConnectObject oldestSimObj = *this->begin();
334  for (const CSimConnectObject &simObj : *this)
335  {
336  if (!simObj.hasCreatedTimestamp()) { continue; }
337  if (!oldestSimObj.hasCreatedTimestamp() ||
338  oldestSimObj.getCreatedTimestamp() > simObj.getCreatedTimestamp())
339  {
340  oldestSimObj = simObj;
341  }
342  }
343  return oldestSimObj;
344  }
345 
347  {
348  for (const CSimConnectObject &simObject : *this)
349  {
350  if (simObject.getRequestId() == requestId) { return simObject; }
351  }
352  return CSimConnectObject();
353  }
354 
356  {
357  if (otherSimObj.hasValidObjectId())
358  {
359  CSimConnectObject obj = this->getSimObjectForObjectId(otherSimObj.getObjectId());
360  if (!obj.isInvalid()) { return obj; }
361  }
362  if (!otherSimObj.hasValidRequestId()) { return CSimConnectObject(); }
363  return this->getSimObjectForRequestId(otherSimObj.getRequestId());
364  }
365 
367  {
368  const CSimConnectObject simObject(this->getSimObjectForObjectId(objectId));
369  return simObject.hasValidRequestAndObjectId() && objectId == simObject.getObjectId();
370  }
371 
373  {
374  const CSimConnectObject simObject(this->getSimObjectForObjectId(objectId));
375  const int c = this->remove(simObject.getCallsign());
376  return c > 0;
377  }
378 
380  {
381  const int c = this->remove(otherSimObj.getCallsign());
382  return c > 0;
383  }
384 
386  {
387  const QList<CSimConnectObject> probes = this->getProbes();
388  int c = 0;
389  for (const CSimConnectObject &probe : probes)
390  {
391  this->remove(probe.getCallsign());
392  c++;
393  }
394  return c;
395  }
396 
398  {
399  for (const CSimConnectObject &simObject : *this)
400  {
401  if (simObject.isPendingAdded()) { return true; }
402  }
403  return false;
404  }
405 
407  {
408  for (const CSimConnectObject &simObject : *this)
409  {
410  if (simObject.isPendingRemoved()) { return true; }
411  }
412  return false;
413  }
414 
416  {
417  int c = 0;
418  for (const CSimConnectObject &simObject : *this)
419  {
420  if (simObject.isPendingAdded()) { c++; }
421  }
422  return c;
423  }
424 
426  {
427  int c = 0;
428  for (const CSimConnectObject &simObject : *this)
429  {
430  if (simObject.isPendingRemoved()) { c++; }
431  }
432  return c;
433  }
434 
436  {
437  int c = 0;
438  for (const CSimConnectObject &simObject : std::as_const(*this))
439  {
440  if (simObject.isConfirmedAdded()) { c++; }
441  }
442  return c;
443  }
444 
446  {
447  CCallsignSet callsigns;
448  for (const CSimConnectObject &simObject : *this)
449  {
450  if (simObject.isPendingAdded()) { callsigns.push_back(simObject.getCallsign()); }
451  }
452  return callsigns;
453  }
454 
456  {
457  CCallsignSet callsigns;
458  for (const CSimConnectObject &simObject : *this)
459  {
460  if (simObject.isPendingRemoved()) { callsigns.push_back(simObject.getCallsign()); }
461  }
462  return callsigns;
463  }
464 
466  {
467  QList<CSimConnectObject> objs;
468  for (const CSimConnectObject &simObject : *this)
469  {
470  if (simObject.getType() == type) { objs.push_back(simObject); }
471  }
472  return objs;
473  }
474 
475  QList<CSimConnectObject> CSimConnectObjects::getAircraft() const
476  {
477  QList<CSimConnectObject> l = this->getByType(CSimConnectObject::AircraftNonAtc);
478  l.append(this->getByType(CSimConnectObject::AircraftSimulatedObject));
479  return l;
480  }
481 
483  {
484  for (const CSimConnectObject &simObject : *this)
485  {
486  if (simObject.getType() == CSimConnectObject::TerrainProbe && !simObject.isPending()) { return simObject; }
487  }
488  return CSimConnectObject();
489  }
490 
492  {
493  CSimConnectObject oldestProbe;
494  for (const CSimConnectObject &simObject : *this)
495  {
496  if (simObject.getType() == CSimConnectObject::TerrainProbe && !simObject.isPending())
497  {
498  if (!oldestProbe.hasCreatedTimestamp() ||
499  oldestProbe.getCreatedTimestamp() > simObject.getCreatedTimestamp())
500  {
501  oldestProbe = simObject;
502  }
503  }
504  }
505  return oldestProbe;
506  }
507 
509  {
510  for (const CSimConnectObject &simObject : *this)
511  {
512  if (simObject.getType() == type) { return true; }
513  }
514  return false;
515  }
516 
518  {
519  return this->containsType(CSimConnectObject::AircraftNonAtc) ||
520  this->containsType(CSimConnectObject::AircraftSimulatedObject);
521  }
522 
524  {
525  int c = 0;
526  for (const CCallsign &cs : callsigns) { c += this->remove(cs); }
527  return c;
528  }
529 
531  {
533  CSimConnectObjects removedObjects;
534 
535  const qint64 ts = QDateTime::currentMSecsSinceEpoch();
536  for (const CSimConnectObject &simObject : std::as_const(*this))
537  {
538  // verification takes at least a second, so we need some time before outdating
539  if (type != CSimConnectObject::AllTypes && simObject.getType() != type) { continue; }
540  if (!simObject.isOutdatedPendingAdded(5000, ts)) { continue; }
541  removedObjects.insert(simObject);
542  removeCallsigns.insert(simObject.getCallsign());
543  }
544  if (!removeCallsigns.isEmpty()) { this->removeCallsigns(removeCallsigns); }
545  return removedObjects;
546  }
547 } // namespace swift::simplugin::fsxcommon
iterator insert(const_iterator hint, const T &value)
For compatibility with std::inserter.
Definition: collection.h:199
iterator push_back(const T &value)
Synonym for insert.
Definition: collection.h:238
Status messages, e.g. from Core -> GUI.
Value object encapsulating information about aircraft's lights.
Value object encapsulating information of an aircraft's situation.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
const QString & asString() const
Get callsign (normalized)
Definition: callsign.h:96
Value object for a set of callsigns.
Definition: callsignset.h:26
QStringList getCallsignStrings(bool sorted=false) const
The callsign strings.
Definition: callsignset.cpp:37
Physical unit length (length)
Definition: length.h:18
Value object for interpolator and rendering per callsign.
Record internal state of interpolator for debugging.
Multiplexed interpolator which allows switching between modes at runtime.
Comprehensive information of an aircraft.
bool setCG(const physical_quantities::CLength &cg)
Set the center of gravity.
void setModelString(const QString &modelString)
Set model string.
const simulation::CAircraftModel & getModel() const
Get model (model used for mapping)
QString getCallsignAsString() const
Get callsign.
Direct in memory access to interpolation setup, normally implemented by simulator.
Direct thread safe in memory access to remote aircraft.
SimObjectRequest
SimObject requests used for AI aircraft and probes.
Class representing a SimConnect object.
bool isPendingAdded() const
Object is requested in simulator, not yet confirmed added.
bool isConfirmedAdded() const
Adding is confirmed.
bool hasValidRequestId() const
Valid request id?
void setConfirmedAdded(bool confirm)
Marked as confirmed, means the simulator has "confirmed" the objectId as added and not instantly remo...
void resetCameraPositions()
Reset camera positions.
void setAddedWhileRemoving(bool addedWileRemoved)
Special states.
const swift::misc::aviation::CAircraftSituation & getLastInterpolatedSituation(swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
Latest interpolation result.
DWORD getObjectId() const
Get SimConnect object id.
static bool isSameTypeGroup(SimObjectType t1, SimObjectType t2)
Same type.
QString toQString() const
SimObject as string.
bool hasValidRequestAndObjectId() const
Was the object really added to simulator.
swift::misc::simulation::CInterpolationResult getInterpolation(qint64 currentTimeSinceEpoch, const swift::misc::simulation::CInterpolationAndRenderingSetupPerCallsign &setup, uint32_t aircraftNumber) const
Get interpolated situation.
bool isPending() const
Pending added or removed?
void setRequestId(DWORD id)
Set Simconnect request id.
static SimObjectType requestIdToType(DWORD requestId)
Type of id.
static const QString & typeToString(SimObjectType type)
Type to string.
void resetToAddAgain()
Reset so it can be added again.
DWORD getRequestId() const
Get SimConnect request id.
void setAircraftCG(const swift::misc::physical_quantities::CLength &cg)
Set CG.
const swift::misc::aviation::CCallsign & getCallsign() const
Get callsign.
void attachInterpolatorLogger(swift::misc::simulation::CInterpolationLogger *logger) const
Attach an observer to read the interpolator's state for debugging.
void resetState()
Reset the state (like it was a new onject) without affecting interpolator and aircraft.
bool isOutdatedPendingAdded(qint64 thresholdMs=5000, qint64 currentMsSinceEpoch=-1) const
Still pending.
qint64 getCreatedTimestamp() const
Created timestamp.
void setRemovedWhileAdding(bool removedWhileAdding)
Special states.
void setAircraftModelString(const QString &modelString)
Set model string.
void setType(SimObjectType type)
Set the type.
void setAircraft(const swift::misc::simulation::CSimulatedAircraft &aircraft)
Set the aircraft.
bool isPendingRemoved() const
Removing is pending.
void copyAddingFailureCounters(const CSimConnectObject &otherObject)
Copy the counters from another object.
bool hasCreatedTimestamp() const
Created timestamp?
QString getInterpolatorInfo(swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
Get an interpolator info string (for debug info)
void setPendingRemoved(bool pending)
Marked as pending for removal.
SimObjectType getType() const
Object type.
void setObjectId(DWORD id)
Set Simconnect object id.
const swift::misc::CStatusMessageList & getInterpolationMessages(swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
Interpolation messages.
bool isReadyToSend() const
Object which can be used for sending, not pending and valid ids.
Simulator objects (aka AI aircraft)
bool containsPendingRemoved() const
Pending removed condition.
CSimConnectObject getSimObjectForRequestId(DWORD requestId) const
Get object per request id.
bool removeByOtherSimObject(const CSimConnectObject &otherSimObj)
Remove by object id or request id.
int countPendingAdded() const
Number of pending added.
int countPendingRemoved() const
Number of pending removed.
swift::misc::aviation::CCallsignSet getAllCallsigns(bool withoutProbes=true) const
Get all callsigns.
CSimConnectObject getSimObjectForObjectId(DWORD objectId) const
Get object per object id.
bool removeByObjectId(DWORD objectId)
Remove by id.
bool insert(const CSimConnectObject &simObject, bool updateTimestamp=false)
Insert.
swift::misc::aviation::CCallsignSet getPendingRemovedCallsigns() const
Callsigns of pending removed callsigns.
CSimConnectObject getSimObjectForOtherSimObject(const CSimConnectObject &otherSimObj) const
Get by request or object id, just as possible.
bool setSimConnectObjectIdForRequestId(DWORD requestId, DWORD objectId)
Set ID of a SimConnect object, so far we only have an request id in the object.
CSimConnectObject getOldestNotPendingProbe() const
Get a non pending probe.
CSimConnectObject getNotPendingProbe() const
Get a non pending probe.
swift::misc::aviation::CCallsignSet getPendingAddedCallsigns() const
Callsigns of pending added callsigns.
QList< CSimConnectObject > getByType(CSimConnectObject::SimObjectType type) const
Get by type.
CSimConnectObject getOldestObject() const
Get the oldest object.
bool containsType(CSimConnectObject::SimObjectType type) const
Contains object of type.
bool isKnownSimObjectId(DWORD objectId) const
Is the object id one of our AI objects?
QString getAllCallsignStringsAsString(bool sorted=false, const QString &separator=", ") const
Get all callsign strings as string.
bool containsPendingAdded() const
Pending add condition.
int removeCallsigns(const swift::misc::aviation::CCallsignSet &callsigns)
Remove callsigns.
QList< CSimConnectObject > getProbes() const
All probes.
CSimConnectObjects removeOutdatedPendingAdded(CSimConnectObject::SimObjectType type)
Remove all pending added objects.
QList< CSimConnectObject > getAircraft() const
All aircraft.
swift::misc::aviation::CCallsign getCallsignForObjectId(DWORD objectId) const
Find which callsign belongs to the object id.
QStringList getAllCallsignStrings(bool sorted=false, bool withoutProbes=true) const
Get all callsign strings.
static DWORD offsetSimObjAircraft(CSimConnectDefinitions::SimObjectRequest req)
Offsets.
static bool isRequestForSimObjTerrainProbe(DWORD requestId)
Request for probe (elevation)?
static bool isRequestForSimObjAircraft(DWORD requestId)
Request for sim data (request in range of sim data)?
static DWORD offsetSimObjTerrainProbe(CSimConnectDefinitions::SimObjectRequest req)
Offsets.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
Free functions in swift::misc.
T::const_iterator begin(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:332
T::const_iterator end(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:338
unsigned long DWORD
Fake Windows DWORD.