Files
pilotclient/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp
Klaus Basan c56775e815 Ref T637, allow 3 letter ICAOs in FP
* strict or lenient airport check
* allow to enter 3 letter ICAO codes in FP
2019-05-13 11:20:48 +02:00

416 lines
25 KiB
C++

/* Copyright (C) 2013
* swift Project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
* or distributed except according to the terms contained in the LICENSE file.
*/
#include "simulatorfsxcommon.h"
#include "simconnectdatadefinition.h"
#include "blackcore/application.h"
#include "blackmisc/simulation/fscommon/bcdconversions.h"
#include "blackmisc/simulation/fsx/simconnectutilities.h"
#include "blackmisc/simulation/simulatorplugininfo.h"
#include "blackmisc/aviation/airportlist.h"
#include "blackmisc/logmessage.h"
#include "blackconfig/buildconfig.h"
#include <cstring>
using namespace BlackCore;
using namespace BlackConfig;
using namespace BlackMisc;
using namespace BlackMisc::Simulation;
using namespace BlackMisc::Aviation;
using namespace BlackMisc::PhysicalQuantities;
using namespace BlackMisc::Geo;
using namespace BlackMisc::Network;
using namespace BlackMisc::Simulation;
using namespace BlackMisc::Simulation::FsCommon;
using namespace BlackMisc::Simulation::Fsx;
namespace BlackSimPlugin
{
namespace FsxCommon
{
void CALLBACK CSimulatorFsxCommon::SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext)
{
// IMPORTANT:
// all tasks called in this function (ie all called functions) must perform fast or shall be called asynchronously
const qint64 procTimeStart = QDateTime::currentMSecsSinceEpoch();
CSimulatorFsxCommon *simulatorFsxP3D = static_cast<CSimulatorFsxCommon *>(pContext);
const SIMCONNECT_RECV_ID recvId = static_cast<SIMCONNECT_RECV_ID>(pData->dwID);
simulatorFsxP3D->m_dispatchReceiveIdLast = recvId;
simulatorFsxP3D->m_dispatchProcCount++;
switch (recvId)
{
case SIMCONNECT_RECV_ID_OPEN:
{
SIMCONNECT_RECV_OPEN *event = static_cast<SIMCONNECT_RECV_OPEN *>(pData);
const QString simConnectVersion = QStringLiteral("%1.%2.%3.%4").arg(event->dwSimConnectVersionMajor).arg(event->dwSimConnectVersionMinor).arg(event->dwSimConnectBuildMajor).arg(event->dwSimConnectBuildMinor);
const QString version = QStringLiteral("%1.%2.%3.%4").arg(event->dwApplicationVersionMajor).arg(event->dwApplicationVersionMinor).arg(event->dwApplicationBuildMajor).arg(event->dwApplicationBuildMinor);
const QString name = CSimulatorFsxCommon::fsxCharToQString(event->szApplicationName);
const QString details = QStringLiteral("Name: '%1' Version: %2 SimConnect: %3").arg(name, version, simConnectVersion);
simulatorFsxP3D->setSimulatorDetails(name, details, version);
simulatorFsxP3D->m_simConnectVersion = simConnectVersion;
CLogMessage(simulatorFsxP3D).info(u"Connected to %1: '%2'") << simulatorFsxP3D->getSimulatorPluginInfo().getIdentifier() << details;
simulatorFsxP3D->setSimConnected();
break; // SIMCONNECT_RECV_ID_OPEN
}
case SIMCONNECT_RECV_ID_EXCEPTION:
{
if (!simulatorFsxP3D->stillDisplayReceiveExceptions()) { break; }
simulatorFsxP3D->triggerAutoTraceSendId();
SIMCONNECT_RECV_EXCEPTION *exception = static_cast<SIMCONNECT_RECV_EXCEPTION *>(pData);
const DWORD exceptionId = exception->dwException;
const DWORD sendId = exception->dwSendID;
const DWORD index = exception->dwIndex; // index of parameter that was source of error, 4294967295/0xFFFFFFFF means unknown, 0 means also UNKNOWN INDEX
const DWORD data = cbData;
const TraceFsxSendId trace = simulatorFsxP3D->getSendIdTrace(sendId);
bool logGenericExceptionInfo = true;
switch (exceptionId)
{
case SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE: break;
case SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID: break; // Specifies that the client event, request ID, data definition ID, or object ID was not recognized
case SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED:
{
if (trace.isValid())
{
// it can happen the object is not yet
CSimConnectObject simObject = simulatorFsxP3D->getSimObjectForTrace(trace);
if (simObject.isInvalid()) { simObject = trace.simObject; } // take the one in the trace
if (simObject.isValid())
{
if (simObject.isAircraft())
{
simulatorFsxP3D->addingAircraftFailed(simObject);
logGenericExceptionInfo = false;
}
else
{
const bool removed = simulatorFsxP3D->m_simConnectObjects.remove(simObject.getCallsign());
Q_UNUSED(removed);
CLogMessage(simulatorFsxP3D).warning(u"Adding probe failed: %1 %2") << simObject.getCallsign().asString() << simObject.getAircraftModelString();
if (simulatorFsxP3D->isUsingFsxTerrainProbe())
{
CLogMessage(simulatorFsxP3D).warning(u"Disabling terrain probe");
simulatorFsxP3D->setUsingFsxTerrainProbe(false);
}
logGenericExceptionInfo = false;
} // aircraft
} // valid
} // trace
} // SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED:
break;
default:
break;
} // switch exception id
// generic exception warning
if (logGenericExceptionInfo)
{
QString ex = QString::asprintf("Exception=%lu | SendID=%lu | Index=%lu | cbData=%lu", exceptionId, sendId, index, data);
const QString exceptionString(CSimConnectUtilities::simConnectExceptionToString(static_cast<DWORD>(exception->dwException)));
const QString sendIdDetails = simulatorFsxP3D->getSendIdTraceDetails(sendId);
CLogMessage(simulatorFsxP3D).warning(u"Caught simConnect exception: '%1' '%2' | send details: '%3'")
<< exceptionString << ex
<< (sendIdDetails.isEmpty() ? "N/A" : sendIdDetails);
}
break; // SIMCONNECT_RECV_ID_EXCEPTION
}
case SIMCONNECT_RECV_ID_QUIT:
{
simulatorFsxP3D->onSimExit();
break;
}
case SIMCONNECT_RECV_ID_EVENT:
{
const SIMCONNECT_RECV_EVENT *event = static_cast<SIMCONNECT_RECV_EVENT *>(pData);
switch (event->uEventID)
{
case SystemEventSimStatus:
{
const bool running = event->dwData ? true : false;
if (running)
{
simulatorFsxP3D->onSimRunning();
}
else
{
simulatorFsxP3D->onSimStopped();
}
break;
}
case SystemEventPause:
{
const bool paused = event->dwData ? true : false;
if (simulatorFsxP3D->m_simPaused != paused)
{
simulatorFsxP3D->m_simPaused = paused;
simulatorFsxP3D->emitSimulatorCombinedStatus();
}
break;
}
default:
break;
}
break; // SIMCONNECT_RECV_ID_EVENT
}
case SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE:
{
const SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE *event = static_cast<SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE *>(pData);
const DWORD objectId = event->dwData;
const SIMCONNECT_SIMOBJECT_TYPE objectType = event->eObjType;
if (objectType != SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT && objectType != SIMCONNECT_SIMOBJECT_TYPE_HELICOPTER)
{
break; // SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE
}
// such an object is not necessarily one of ours
// for instance, I always see object "5" when I start the simulator
if (simulatorFsxP3D->getSimConnectObjects().isKnownSimObjectId(objectId))
{
switch (event->uEventID)
{
case SystemEventObjectRemoved:
simulatorFsxP3D->simulatorReportedObjectRemoved(objectId);
break;
case SystemEventObjectAdded:
// added in SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
// this event here is normally received before SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
break;
default:
break;
}
}
break; // SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE
}
case SIMCONNECT_RECV_ID_EVENT_FRAME:
{
const SIMCONNECT_RECV_EVENT_FRAME *event = static_cast<SIMCONNECT_RECV_EVENT_FRAME *>(pData);
switch (event->uEventID)
{
case SystemEventFrame:
// doing interpolation
simulatorFsxP3D->onSimFrame();
break;
default:
break;
}
break; // SIMCONNECT_RECV_ID_EVENT_FRAME
}
case SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID:
{
const SIMCONNECT_RECV_ASSIGNED_OBJECT_ID *event = static_cast<SIMCONNECT_RECV_ASSIGNED_OBJECT_ID *>(pData);
const DWORD requestId = event->dwRequestID;
const DWORD objectId = event->dwObjectID;
simulatorFsxP3D->m_dispatchRequestIdLast = requestId;
if (CSimulatorFsxCommon::isRequestForSimConnectObject(requestId))
{
bool success = simulatorFsxP3D->setSimConnectObjectId(requestId, objectId);
if (!success) { break; } // not an request ID of ours
success = simulatorFsxP3D->simulatorReportedObjectAdded(objectId); // adding failed (no IDs), trigger follow up actions
if (!success)
{
// getting here would mean object was removed in the meantime
// otherwise we will detect it in verification
const CSimConnectObject simObject = simulatorFsxP3D->getSimObjectForObjectId(objectId);
const CSimulatedAircraft remoteAircraft(simObject.getAircraft());
const CStatusMessage msg = CStatusMessage(simulatorFsxP3D).error(u"Cannot add object %1, cs: '%2' model: '%3'") << objectId << remoteAircraft.getCallsignAsString() << remoteAircraft.getModelString();
CLogMessage::preformatted(msg);
emit simulatorFsxP3D->physicallyAddingRemoteModelFailed(remoteAircraft, false, false, msg);
}
}
break; // SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID
}
case SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE:
{
// SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE *)pData;
break;
}
case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
{
const SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = static_cast<SIMCONNECT_RECV_SIMOBJECT_DATA *>(pData);
const DWORD requestId = pObjData->dwRequestID;
simulatorFsxP3D->m_dispatchRequestIdLast = requestId;
switch (requestId)
{
case CSimConnectDefinitions::RequestOwnAircraft:
{
static_assert(sizeof(DataDefinitionOwnAircraft) == 38 * sizeof(double), "DataDefinitionOwnAircraft has an incorrect size.");
const DataDefinitionOwnAircraft *ownAircaft = reinterpret_cast<const DataDefinitionOwnAircraft *>(&pObjData->dwData);
simulatorFsxP3D->updateOwnAircraftFromSimulator(*ownAircaft);
break;
}
case CSimConnectDefinitions::RequestOwnAircraftTitle:
{
const DataDefinitionOwnAircraftModel *dataDefinitionModel = reinterpret_cast<const DataDefinitionOwnAircraftModel *>(&pObjData->dwData);
const CAircraftModel model(dataDefinitionModel->title, CAircraftModel::TypeOwnSimulatorModel);
simulatorFsxP3D->reverseLookupAndUpdateOwnAircraftModel(model);
break;
}
case CSimConnectDefinitions::RequestSimEnvironment:
{
const DataDefinitionSimEnvironment *simEnv = reinterpret_cast<const DataDefinitionSimEnvironment *>(&pObjData->dwData);
simulatorFsxP3D->synchronizeTime(simEnv);
break;
}
default:
{
const DWORD objectId = pObjData->dwObjectID;
if (CSimulatorFsxCommon::isRequestForSimObjAircraft(requestId))
{
const CSimConnectObject simObject = simulatorFsxP3D->getSimObjectForObjectId(objectId);
if (!simObject.hasValidRequestAndObjectId()) { break; }
const CSimConnectDefinitions::SimObjectRequest subRequest = CSimulatorFsxCommon::requestToSimObjectRequest(requestId);
if (subRequest == CSimConnectDefinitions::SimObjectPositionData)
{
static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double), "DataDefinitionPosData has an incorrect size.");
const DataDefinitionPosData *remoteAircraftSimData = reinterpret_cast<const DataDefinitionPosData *>(&pObjData->dwData);
// extra check, but ids should be the same
if (objectId == simObject.getObjectId())
{
simulatorFsxP3D->triggerUpdateRemoteAircraftFromSimulator(simObject, *remoteAircraftSimData);
}
} // position
else if (subRequest == CSimConnectDefinitions::SimObjectModel)
{
static_assert(sizeof(DataDefinitionRemoteAircraftModel) == sizeof(double) + 168 + 256, "DataDefinitionRemoteAircraftModel has an incorrect size.");
const DataDefinitionRemoteAircraftModel *remoteAircraftModel = reinterpret_cast<const DataDefinitionRemoteAircraftModel *>(&pObjData->dwData);
// extra check, but ids should be the same
if (objectId == simObject.getObjectId())
{
simulatorFsxP3D->triggerUpdateRemoteAircraftFromSimulator(simObject, *remoteAircraftModel);
}
} // model
else if (subRequest == CSimConnectDefinitions::SimObjectLights)
{
static_assert(sizeof(DataDefinitionRemoteAircraftLights) == 8 * sizeof(double), "DataDefinitionRemoteAircraftLights has an incorrect size.");
const DataDefinitionRemoteAircraftLights *remoteAircraftLights = reinterpret_cast<const DataDefinitionRemoteAircraftLights *>(&pObjData->dwData);
// extra check, but ids should be the same
if (objectId == simObject.getObjectId())
{
const CCallsign callsign(simObject.getCallsign());
const CAircraftLights lights(remoteAircraftLights->toLights()); // as in simulator
simulatorFsxP3D->setCurrentLights(callsign, lights);
if (simObject.getLightsAsSent().isNull())
{
// allows to compare for toggle
simulatorFsxP3D->setLightsAsSent(callsign, lights);
}
}
break;
} // lights
else
{
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
CLogMessage(simulatorFsxP3D).error(u"Unknown subrequest (aircraft): '%1' %2")
<< CSimConnectDefinitions::simObjectRequestToString(subRequest)
<< simObject.toQString();
}
}
}
else if (CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId))
{
const CSimConnectObject probeObj = simulatorFsxP3D->getSimObjectForObjectId(objectId);
if (!probeObj.hasValidRequestAndObjectId()) { break; }
Q_ASSERT_X(probeObj.isTerrainProbe(), Q_FUNC_INFO, "No probe");
const CSimConnectDefinitions::SimObjectRequest subRequest = CSimulatorFsxCommon::requestToSimObjectRequest(requestId);
if (subRequest == CSimConnectDefinitions::SimObjectPositionData)
{
static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double), "DataDefinitionRemoteAircraftSimData has an incorrect size.");
const DataDefinitionPosData *probeSimData = reinterpret_cast<const DataDefinitionPosData *>(&pObjData->dwData);
// extra check, but ids should be the same
if (objectId == probeObj.getObjectId())
{
const CCallsign cs = simulatorFsxP3D->m_pendingProbeRequests.value(requestId);
if (cs.isEmpty()) { break; }
simulatorFsxP3D->updateProbeFromSimulator(cs, *probeSimData);
}
}
else
{
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
CLogMessage(simulatorFsxP3D).error(u"Unknown subrequest (probe): '%1' %2")
<< CSimConnectDefinitions::simObjectRequestToString(subRequest)
<< probeObj.toQString();
}
}
} // probe
}
break; // default (SIMCONNECT_RECV_ID_SIMOBJECT_DATA)
}
break; // SIMCONNECT_RECV_ID_SIMOBJECT_DATA
}
case SIMCONNECT_RECV_ID_AIRPORT_LIST:
{
const SIMCONNECT_RECV_AIRPORT_LIST *pAirportList = static_cast<SIMCONNECT_RECV_AIRPORT_LIST *>(pData);
CAirportList simAirports;
for (unsigned i = 0; i < pAirportList->dwArraySize; ++i)
{
const SIMCONNECT_DATA_FACILITY_AIRPORT *pFacilityAirport = pAirportList->rgData + i;
if (!pFacilityAirport) { break; }
const QString icao(pFacilityAirport->Icao);
if (icao.isEmpty()) { continue; } // airfield without ICAO code
if (!CAirportIcaoCode::isValidIcaoDesignator(icao, true)) { continue; } // tiny airfields/strips in simulator
if (CAirportIcaoCode::containsNumbers(icao)) { continue; } // tiny airfields/strips in simulator
const CCoordinateGeodetic pos(pFacilityAirport->Latitude, pFacilityAirport->Longitude, pFacilityAirport->Altitude);
const CAirport airport(CAirportIcaoCode(icao), pos);
simAirports.push_back(airport);
}
if (!simAirports.isEmpty())
{
simulatorFsxP3D->triggerUpdateAirports(simAirports); // real "work" outside SimConnectProc
}
break; // SIMCONNECT_RECV_ID_AIRPORT_LIST
}
case SIMCONNECT_RECV_ID_CLIENT_DATA:
{
if (!simulatorFsxP3D->m_useSbOffsets) { break; }
simulatorFsxP3D->m_sbDataReceived++;
const SIMCONNECT_RECV_CLIENT_DATA *clientData = static_cast<SIMCONNECT_RECV_CLIENT_DATA *>(pData);
if (clientData->dwRequestID == CSimConnectDefinitions::RequestSbData)
{
//! \fixme FSUIPC vs SimConnect why is offset 19 ident 2/0? In FSUIPC it is 0/1, according to documentation it is 0/1 but I receive 2/0 here. Whoever knows, add comment or fix if wrong
DataDefinitionClientAreaSb sbData;
std::memcpy(&sbData.data, &clientData->dwData, 128);
simulatorFsxP3D->updateOwnAircraftFromSimulator(sbData);
}
break; // SIMCONNECT_RECV_ID_CLIENT_DATA
}
case SIMCONNECT_RECV_ID_EVENT_FILENAME:
{
const SIMCONNECT_RECV_EVENT_FILENAME *event = static_cast<SIMCONNECT_RECV_EVENT_FILENAME *>(pData);
switch (event->uEventID)
{
case SystemEventFlightLoaded: break;
default: break;
}
break; // SIMCONNECT_RECV_ID_EVENT_FILENAME
}
default:
simulatorFsxP3D->m_dispatchProcEmptyCount++;
break;
} // main switch
// performance stats
const qint64 procTimeEnd = QDateTime::currentMSecsSinceEpoch();
simulatorFsxP3D->m_dispatchProcTimeMs = procTimeEnd - procTimeStart;
if (simulatorFsxP3D->m_dispatchProcTimeMs > simulatorFsxP3D->m_dispatchProcMaxTimeMs)
{
simulatorFsxP3D->m_dispatchProcMaxTimeMs = simulatorFsxP3D->m_dispatchProcTimeMs;
}
} // method
} // namespace
} // namespace