swift
simulatorfsxsimconnectproc.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 <cstring>
5 
7 #include "simulatorfsxcommon.h"
8 
9 #include "config/buildconfig.h"
10 #include "core/application.h"
12 #include "misc/logmessage.h"
16 
17 using namespace swift::core;
18 using namespace swift::config;
19 using namespace swift::misc;
20 using namespace swift::misc::simulation;
21 using namespace swift::misc::aviation;
22 using namespace swift::misc::physical_quantities;
23 using namespace swift::misc::geo;
24 using namespace swift::misc::network;
25 using namespace swift::misc::simulation;
26 using namespace swift::misc::simulation::fscommon;
27 using namespace swift::misc::simulation::fsx;
28 
29 namespace swift::simplugin::fsxcommon
30 {
31  void CALLBACK CSimulatorFsxCommon::SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext)
32  {
33  // IMPORTANT:
34  // all tasks called in this function (ie all called functions) must perform fast or shall be called
35  // asynchronously
36 
37  const qint64 procTimeStart = QDateTime::currentMSecsSinceEpoch();
38  CSimulatorFsxCommon *simulatorFsxP3D = static_cast<CSimulatorFsxCommon *>(pContext);
39  const SIMCONNECT_RECV_ID recvId = static_cast<SIMCONNECT_RECV_ID>(pData->dwID);
40  simulatorFsxP3D->m_dispatchReceiveIdLast = recvId;
41  simulatorFsxP3D->m_dispatchProcCount++;
42  switch (recvId)
43  {
44  case SIMCONNECT_RECV_ID_OPEN:
45  {
46  SIMCONNECT_RECV_OPEN *event = static_cast<SIMCONNECT_RECV_OPEN *>(pData);
47  const QString simConnectVersion = QStringLiteral("%1.%2.%3.%4")
48  .arg(event->dwSimConnectVersionMajor)
49  .arg(event->dwSimConnectVersionMinor)
50  .arg(event->dwSimConnectBuildMajor)
51  .arg(event->dwSimConnectBuildMinor);
52  const QString version = QStringLiteral("%1.%2.%3.%4")
53  .arg(event->dwApplicationVersionMajor)
54  .arg(event->dwApplicationVersionMinor)
55  .arg(event->dwApplicationBuildMajor)
56  .arg(event->dwApplicationBuildMinor);
57  const QString name = CSimulatorFsxCommon::fsxCharToQString(event->szApplicationName);
58  const QString details =
59  QStringLiteral("Name: '%1' Version: %2 SimConnect: %3").arg(name, version, simConnectVersion);
60  simulatorFsxP3D->setSimulatorDetails(name, details, version);
61  simulatorFsxP3D->m_simConnectVersion = simConnectVersion;
62  CLogMessage(simulatorFsxP3D).info(u"Connected to %1: '%2'")
63  << simulatorFsxP3D->getSimulatorPluginInfo().getIdentifier() << details;
64  simulatorFsxP3D->setSimConnected();
65  break; // SIMCONNECT_RECV_ID_OPEN
66  }
67  case SIMCONNECT_RECV_ID_EXCEPTION:
68  {
69  if (!simulatorFsxP3D->stillDisplayReceiveExceptions()) { break; }
70  simulatorFsxP3D->triggerAutoTraceSendId();
71 
72  SIMCONNECT_RECV_EXCEPTION *exception = static_cast<SIMCONNECT_RECV_EXCEPTION *>(pData);
73  const DWORD exceptionId = exception->dwException;
74  const DWORD sendId = exception->dwSendID;
75  const DWORD index = exception->dwIndex; // index of parameter that was source of error,
76  // 4294967295/0xFFFFFFFF means unknown, 0 means also UNKNOWN INDEX
77  const DWORD data = cbData;
78  const TraceFsxSendId trace = simulatorFsxP3D->getSendIdTrace(sendId);
79  bool logGenericExceptionInfo = true;
80 
81  switch (exceptionId)
82  {
83  case SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE: break;
84  case SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID:
85  break; // Specifies that the client event, request ID, data definition ID, or object ID was not
86  // recognized
87  case SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED:
88  {
89  if (trace.isValid())
90  {
91  // it can happen the object is not yet existing
92  CSimConnectObject simObject = simulatorFsxP3D->getSimObjectForTrace(trace);
93  if (simObject.isInvalid()) { simObject = trace.simObject; } // take the one in the trace
94  if (simObject.isValid())
95  {
96  if (simObject.isAircraft())
97  {
98  simulatorFsxP3D->addingAircraftFailed(simObject);
99  logGenericExceptionInfo = false;
100  }
101  else
102  {
103  const bool removed = simulatorFsxP3D->m_simConnectObjects.remove(simObject.getCallsign());
104  Q_UNUSED(removed);
105  CLogMessage(simulatorFsxP3D).warning(u"Adding probe failed: %1 %2")
106  << simObject.getCallsign().asString() << simObject.getAircraftModelString();
107  if (simulatorFsxP3D->isUsingFsxTerrainProbe())
108  {
109  CLogMessage(simulatorFsxP3D).warning(u"Disabling terrain probe");
110  simulatorFsxP3D->setUsingFsxTerrainProbe(false);
111  }
112  logGenericExceptionInfo = false;
113  } // aircraft
114  } // valid
115  } // trace
116  } // SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED:
117  break;
118  default: break;
119  } // switch exception id
120 
121  // generic exception warning
122  if (logGenericExceptionInfo)
123  {
124  QString ex = QString::asprintf("Exception=%lu | SendID=%lu | Index=%lu | cbData=%lu", exceptionId,
125  sendId, index, data);
126  const QString exceptionString(
127  CSimConnectUtilities::simConnectExceptionToString(static_cast<DWORD>(exception->dwException)));
128  const QString sendIdDetails = simulatorFsxP3D->getSendIdTraceDetails(sendId);
129  CLogMessage(simulatorFsxP3D).warning(u"Caught simConnect exception: '%1' '%2' | send details: '%3'")
130  << exceptionString << ex << (sendIdDetails.isEmpty() ? "N/A" : sendIdDetails);
131  }
132  break; // SIMCONNECT_RECV_ID_EXCEPTION
133  }
134  case SIMCONNECT_RECV_ID_QUIT:
135  {
136  simulatorFsxP3D->onSimExit();
137  break;
138  }
139  case SIMCONNECT_RECV_ID_EVENT:
140  {
141  const SIMCONNECT_RECV_EVENT *event = static_cast<SIMCONNECT_RECV_EVENT *>(pData);
142  switch (event->uEventID)
143  {
144  case SystemEventSimStatus:
145  {
146  const bool running = event->dwData ? true : false;
147  if (running) { simulatorFsxP3D->onSimRunning(); }
148  else { simulatorFsxP3D->onSimStopped(); }
149  break;
150  }
151  case SystemEventPause:
152  {
153  const bool paused = event->dwData ? true : false;
154  if (simulatorFsxP3D->m_simPaused != paused)
155  {
156  simulatorFsxP3D->m_simPaused = paused;
157  simulatorFsxP3D->emitSimulatorCombinedStatus();
158  }
159  break;
160  }
161  default: break;
162  }
163  break; // SIMCONNECT_RECV_ID_EVENT
164  }
165  case SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE:
166  {
167  const SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE *event =
168  static_cast<SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE *>(pData);
169  const DWORD objectId = event->dwData;
170  const SIMCONNECT_SIMOBJECT_TYPE objectType = event->eObjType;
171  if (objectType != SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT && objectType != SIMCONNECT_SIMOBJECT_TYPE_HELICOPTER)
172  {
173  break; // SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE
174  }
175 
176  // such an object is not necessarily one of ours
177  // for instance, I always see object "5" when I start the simulator
178  if (simulatorFsxP3D->getSimConnectObjects().isKnownSimObjectId(objectId))
179  {
180  switch (event->uEventID)
181  {
182  case SystemEventObjectRemoved: simulatorFsxP3D->simulatorReportedObjectRemoved(objectId); break;
183  case SystemEventObjectAdded:
184  // added in SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
185  // this event here is normally received before SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
186  break;
187  default: break;
188  }
189  }
190  break; // SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE
191  }
192  case SIMCONNECT_RECV_ID_EVENT_FRAME:
193  {
194  const SIMCONNECT_RECV_EVENT_FRAME *event = static_cast<SIMCONNECT_RECV_EVENT_FRAME *>(pData);
195  switch (event->uEventID)
196  {
197  case SystemEventFrame:
198  // doing interpolation
199  simulatorFsxP3D->onSimFrame();
200  break;
201  default: break;
202  }
203  break; // SIMCONNECT_RECV_ID_EVENT_FRAME
204  }
205  case SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID:
206  {
207  const SIMCONNECT_RECV_ASSIGNED_OBJECT_ID *event = static_cast<SIMCONNECT_RECV_ASSIGNED_OBJECT_ID *>(pData);
208  const DWORD requestId = event->dwRequestID;
209  const DWORD objectId = event->dwObjectID;
210  simulatorFsxP3D->m_dispatchRequestIdLast = requestId;
211 
212  if (CSimulatorFsxCommon::isRequestForSimConnectObject(requestId))
213  {
214  bool success = simulatorFsxP3D->setSimConnectObjectId(requestId, objectId);
215  if (!success) { break; } // not an request ID of ours
216  success = simulatorFsxP3D->simulatorReportedObjectAdded(
217  objectId); // adding failed (no IDs), trigger follow up actions
218  if (!success)
219  {
220  // getting here would mean object was removed in the meantime
221  // otherwise we will detect it in verification
222  const CSimConnectObject simObject = simulatorFsxP3D->getSimObjectForObjectId(objectId);
223  const CSimulatedAircraft remoteAircraft(simObject.getAircraft());
224  const CStatusMessage msg =
225  CStatusMessage(simulatorFsxP3D).error(u"Cannot add object %1, cs: '%2' model: '%3'")
226  << objectId << remoteAircraft.getCallsignAsString() << remoteAircraft.getModelString();
228  emit simulatorFsxP3D->physicallyAddingRemoteModelFailed(remoteAircraft, false, false, msg);
229  }
230  }
231  break; // SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
232  }
233  case SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE:
234  {
235  // SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE *)pData;
236  break;
237  }
238  case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
239  {
240  const SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = static_cast<SIMCONNECT_RECV_SIMOBJECT_DATA *>(pData);
241  const DWORD requestId = pObjData->dwRequestID;
242  simulatorFsxP3D->m_dispatchRequestIdLast = requestId;
243 
244  switch (requestId)
245  {
246  case CSimConnectDefinitions::RequestOwnAircraft:
247  {
248  static_assert(sizeof(DataDefinitionOwnAircraft) == 45 * sizeof(double),
249  "DataDefinitionOwnAircraft has an incorrect size.");
250  const DataDefinitionOwnAircraft *ownAircaft =
251  reinterpret_cast<const DataDefinitionOwnAircraft *>(&pObjData->dwData);
252  simulatorFsxP3D->updateOwnAircraftFromSimulator(*ownAircaft);
253  break;
254  }
255  case CSimConnectDefinitions::RequestOwnAircraftTitle:
256  {
257  const DataDefinitionOwnAircraftModel *dataDefinitionModel =
258  reinterpret_cast<const DataDefinitionOwnAircraftModel *>(&pObjData->dwData);
259  const CAircraftModel model(dataDefinitionModel->title, CAircraftModel::TypeOwnSimulatorModel);
260  simulatorFsxP3D->reverseLookupAndUpdateOwnAircraftModel(model);
261  break;
262  }
263  case CSimConnectDefinitions::RequestMSFSTransponder:
264  {
265  const DataDefinitionMSFSTransponderMode *transponderMode =
266  reinterpret_cast<const DataDefinitionMSFSTransponderMode *>(&pObjData->dwData);
267  simulatorFsxP3D->updateMSFSTransponderMode(*transponderMode);
268  break;
269  }
270  default:
271  {
272  const DWORD objectId = pObjData->dwObjectID;
273  if (CSimulatorFsxCommon::isRequestForSimObjAircraft(requestId))
274  {
275  const CSimConnectObject simObject = simulatorFsxP3D->getSimObjectForObjectId(objectId);
276  if (!simObject.hasValidRequestAndObjectId()) { break; }
278  CSimulatorFsxCommon::requestToSimObjectRequest(requestId);
279 
280  if (subRequest == CSimConnectDefinitions::SimObjectPositionData)
281  {
282  static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double),
283  "DataDefinitionPosData has an incorrect size.");
284  const DataDefinitionPosData *remoteAircraftSimData =
285  reinterpret_cast<const DataDefinitionPosData *>(&pObjData->dwData);
286  // extra check, but ids should be the same
287  if (objectId == simObject.getObjectId())
288  {
289  simulatorFsxP3D->triggerUpdateRemoteAircraftFromSimulator(simObject,
290  *remoteAircraftSimData);
291  }
292  } // position
293  else if (subRequest == CSimConnectDefinitions::SimObjectModel)
294  {
295  static_assert(sizeof(DataDefinitionRemoteAircraftModel) == sizeof(double) + 168 + 256,
296  "DataDefinitionRemoteAircraftModel has an incorrect size.");
297  const DataDefinitionRemoteAircraftModel *remoteAircraftModel =
298  reinterpret_cast<const DataDefinitionRemoteAircraftModel *>(&pObjData->dwData);
299  // extra check, but ids should be the same
300  if (objectId == simObject.getObjectId())
301  {
302  simulatorFsxP3D->triggerUpdateRemoteAircraftFromSimulator(simObject, *remoteAircraftModel);
303  }
304  } // model
305  else if (subRequest == CSimConnectDefinitions::SimObjectLights)
306  {
307  static_assert(sizeof(DataDefinitionRemoteAircraftLights) == 8 * sizeof(double),
308  "DataDefinitionRemoteAircraftLights has an incorrect size.");
309  const DataDefinitionRemoteAircraftLights *remoteAircraftLights =
310  reinterpret_cast<const DataDefinitionRemoteAircraftLights *>(&pObjData->dwData);
311  // extra check, but ids should be the same
312  if (objectId == simObject.getObjectId())
313  {
314  const CCallsign callsign(simObject.getCallsign());
315  const CAircraftLights lights(remoteAircraftLights->toLights()); // as in simulator
316  simulatorFsxP3D->setCurrentLights(callsign, lights);
317  if (simObject.getLightsAsSent().isNull())
318  {
319  // allows to compare for toggle
320  simulatorFsxP3D->setLightsAsSent(callsign, lights);
321  }
322  }
323  break;
324  } // lights
325  else
326  {
327  if (CBuildConfig::isLocalDeveloperDebugBuild())
328  {
329  CLogMessage(simulatorFsxP3D).error(u"Unknown subrequest (aircraft): '%1' %2")
330  << CSimConnectDefinitions::simObjectRequestToString(subRequest)
331  << simObject.toQString();
332  }
333  }
334  }
335  else if (CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId))
336  {
337  const CSimConnectObject probeObj = simulatorFsxP3D->getSimObjectForObjectId(objectId);
338  if (!probeObj.hasValidRequestAndObjectId()) { break; }
339  Q_ASSERT_X(probeObj.isTerrainProbe(), Q_FUNC_INFO, "No probe");
341  CSimulatorFsxCommon::requestToSimObjectRequest(requestId);
342 
343  if (subRequest == CSimConnectDefinitions::SimObjectPositionData)
344  {
345  static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double),
346  "DataDefinitionRemoteAircraftSimData has an incorrect size.");
347  const DataDefinitionPosData *probeSimData =
348  reinterpret_cast<const DataDefinitionPosData *>(&pObjData->dwData);
349  // extra check, but ids should be the same
350  if (objectId == probeObj.getObjectId())
351  {
352  const CCallsign cs = simulatorFsxP3D->m_pendingProbeRequests.value(requestId);
353  if (cs.isEmpty()) { break; }
354  simulatorFsxP3D->updateProbeFromSimulator(cs, *probeSimData);
355  }
356  }
357  else
358  {
359  if (CBuildConfig::isLocalDeveloperDebugBuild())
360  {
361  CLogMessage(simulatorFsxP3D).error(u"Unknown subrequest (probe): '%1' %2")
362  << CSimConnectDefinitions::simObjectRequestToString(subRequest) << probeObj.toQString();
363  }
364  }
365  } // probe
366  }
367  break; // default (SIMCONNECT_RECV_ID_SIMOBJECT_DATA)
368  }
369  break; // SIMCONNECT_RECV_ID_SIMOBJECT_DATA
370  }
371  case SIMCONNECT_RECV_ID_CLIENT_DATA:
372  {
373  if (!simulatorFsxP3D->m_useSbOffsets) { break; }
374  simulatorFsxP3D->m_sbDataReceived++;
375  const SIMCONNECT_RECV_CLIENT_DATA *clientData = static_cast<SIMCONNECT_RECV_CLIENT_DATA *>(pData);
376  if (clientData->dwRequestID == CSimConnectDefinitions::RequestSbData)
377  {
381  std::memcpy(&sbData.data, &clientData->dwData, 128);
382  simulatorFsxP3D->updateOwnAircraftFromSimulator(sbData);
383  }
384  break; // SIMCONNECT_RECV_ID_CLIENT_DATA
385  }
386  case SIMCONNECT_RECV_ID_EVENT_FILENAME:
387  {
388  const SIMCONNECT_RECV_EVENT_FILENAME *event = static_cast<SIMCONNECT_RECV_EVENT_FILENAME *>(pData);
389  switch (event->uEventID)
390  {
391  case SystemEventFlightLoaded: break;
392  default: break;
393  }
394  break; // SIMCONNECT_RECV_ID_EVENT_FILENAME
395  }
396  default: simulatorFsxP3D->m_dispatchProcEmptyCount++; break;
397  } // main switch
398 
399  // performance stats
400  const qint64 procTimeEnd = QDateTime::currentMSecsSinceEpoch();
401  simulatorFsxP3D->m_dispatchProcTimeMs = procTimeEnd - procTimeStart;
402  if (simulatorFsxP3D->m_dispatchProcTimeMs > simulatorFsxP3D->m_dispatchProcMaxTimeMs)
403  {
404  simulatorFsxP3D->m_dispatchProcMaxTimeMs = simulatorFsxP3D->m_dispatchProcTimeMs;
405  }
406  } // method
407 } // namespace swift::simplugin::fsxcommon
void physicallyAddingRemoteModelFailed(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft, bool disabled, bool requestFailover, const swift::misc::CStatusMessage &message)
Adding the remote model failed.
void reverseLookupAndUpdateOwnAircraftModel(const swift::misc::simulation::CAircraftModel &model)
Set own model.
Definition: simulator.cpp:1212
void emitSimulatorCombinedStatus(SimulatorStatus oldStatus=Unspecified)
Emit the combined status.
Definition: simulator.cpp:812
Class for emitting a log message.
Definition: logmessage.h:27
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Streamable status message, e.g.
Value object encapsulating information about aircraft's lights.
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
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
@ TypeOwnSimulatorModel
represents own simulator model (AI model, model on disk)
Definition: aircraftmodel.h:84
Comprehensive information of an aircraft.
QString getCallsignAsString() const
Get callsign.
const QString & getModelString() const
Get model string.
const QString & getIdentifier() const
Identifier.
void setSimulatorDetails(const QString &name, const QString &details, const QString &version)
Set version and simulator details from running simulator.
CSimulatorPluginInfo getSimulatorPluginInfo() const
Get the represented plugin.
SimObjectRequest
SimObject requests used for AI aircraft and probes.
Class representing a SimConnect object.
DWORD getObjectId() const
Get SimConnect object id.
QString toQString() const
SimObject as string.
bool hasValidRequestAndObjectId() const
Was the object really added to simulator.
const swift::misc::aviation::CAircraftLights & getLightsAsSent() const
Lights as sent to simulator.
const swift::misc::simulation::CSimulatedAircraft & getAircraft() const
Simulated aircraft (as added)
const swift::misc::aviation::CCallsign & getCallsign() const
Get callsign.
const QString & getAircraftModelString() const
Simulated aircraft model string.
bool isKnownSimObjectId(DWORD objectId) const
Is the object id one of our AI objects?
bool isUsingFsxTerrainProbe() const
FSX Terrain probe.
CSimConnectObjects m_simConnectObjects
AI objects and their object and request ids.
QMap< DWORD, swift::misc::aviation::CCallsign > m_pendingProbeRequests
pending elevation requests: requestId/aircraft callsign
void setUsingFsxTerrainProbe(bool use)
FSX terrain probe.
bool triggerAutoTraceSendId(qint64 traceTimeMs=AutoTraceOffsetMs)
Trigger tracing ids for some while.
CSimConnectObject getSimObjectForTrace(const TraceFsxSendId &trace) const
CSimConnectObject for trace.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
Free functions in swift::misc.
unsigned long DWORD
Fake Windows DWORD.
Data structure for MSFS transponder mode information.
Data for AI object and probe sent back from simulator.
swift::misc::aviation::CAircraftLights toLights() const
Convert to lights.
CSimConnectObject simObject
CSimConnectObject at the time of the trace.