diff --git a/.gitignore b/.gitignore index 759db19d5..7a94f3b6d 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,4 @@ cmake-build-*/ /out/ /dist/ CMakeUserPresets.json -/third_party/externals +/third_party/externals \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 48fba4b95..af8de4e27 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -170,8 +170,6 @@ add_library(core SHARED fsd/pong.h fsd/rehost.cpp fsd/rehost.h - fsd/revbclientparts.cpp - fsd/revbclientparts.h fsd/serializer.cpp fsd/serializer.h fsd/servererror.cpp diff --git a/src/core/aircraftmatcher.cpp b/src/core/aircraftmatcher.cpp index 8d1163a91..fc79df142 100644 --- a/src/core/aircraftmatcher.cpp +++ b/src/core/aircraftmatcher.cpp @@ -1710,6 +1710,14 @@ namespace swift::core const QString &livery) { Q_UNUSED(livery) + // TODO TZ remove after testing + CLogMessage(this).info(u"CAircraftMatcher::onIcaoCodesReceived CHECK:" + u"callsign %1 " + u"aircraftIcao %2 " + u"airlineIcao %3 " + u"livery %4 ") + << callsign << aircraftIcao << airlineIcao << livery; + Q_ASSERT_X(sApp && sApp->hasWebDataServices(), Q_FUNC_INFO, "Missing web data services"); if (m_modelSet.isEmpty()) { return; } // ignore empty sets to not create silly stats if (sessionId.isEmpty()) { return; } diff --git a/src/core/airspacemonitor.cpp b/src/core/airspacemonitor.cpp index b4bdc515d..26d1137df 100644 --- a/src/core/airspacemonitor.cpp +++ b/src/core/airspacemonitor.cpp @@ -90,8 +90,6 @@ namespace swift::core connect(m_fsdClient, &CFSDClient::serverResponseReceived, this, &CAirspaceMonitor::onServerReplyReceived); connect(m_fsdClient, &CFSDClient::aircraftConfigReceived, this, &CAirspaceMonitor::onAircraftConfigReceived); connect(m_fsdClient, &CFSDClient::connectionStatusChanged, this, &CAirspaceMonitor::onConnectionStatusChanged); - connect(m_fsdClient, &CFSDClient::revbAircraftConfigReceived, this, - &CAirspaceMonitor::onRevBAircraftConfigReceived); Q_ASSERT_X(sApp && sApp->hasWebDataServices(), Q_FUNC_INFO, "Missing data reader"); @@ -546,9 +544,21 @@ namespace swift::core void CAirspaceMonitor::sendReadyForModelMatching(const CCallsign &callsign, MatchingReadinessFlag rf) { + if (!this->isConnectedAndNotShuttingDown()) { return; } Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "missing callsign"); + // TODO TZ remove when testing is done + // CLogMessage(this).info(u"CAirspaceMonitor::sendReadyForModelMatching " + // u"callsign %1 Flag %2 ") + // << callsign << rf; + + CStatusMessageList reverseLookupMessages; + CCallsign::addLogDetailsToList(&reverseLookupMessages, callsign, + QStringLiteral("CAirspaceMonitor::sendReadyForModelMatching Flag: %1").arg(rf), + CAirspaceMonitor::getLogCategories()); + // TODO remove + // set flag and init ts Readiness &readiness = this->addMatchingReadinessFlag(callsign, rf); @@ -668,6 +678,14 @@ namespace swift::core // normally we should never get here CLogMessage(this).info(u"Verified '%1' again, has ICAO codes, ready for matching!") << callsign; + + // TODO TZ remove when testing is done + CStatusMessageList reverseLookupMessages; + CCallsign::addLogDetailsToList(&reverseLookupMessages, callsign, + QStringLiteral("CAirspaceMonitor::verifyReceivedIcaoData"), + CAirspaceMonitor::getLogCategories()); + // end TZ remove + this->sendReadyForModelMatching(callsign, Verified); } @@ -773,6 +791,15 @@ namespace swift::core const QString &aircraftIcaoDesignator, const QString &combinedAircraftType, const QString &modelString) { + // TODO TZ remove when testing is done + CLogMessage(this).info(u"CAirspaceMonitor::onCustomFSInnPacketReceived CHECK:" + u"callsign %1 " + u"airlineIcaoDesignator %2 " + u"aircraftIcaoDesignator %3 " + u"combinedAircraftType %4 " + u"modelString %5 ") + << callsign << airlineIcaoDesignator << aircraftIcaoDesignator << combinedAircraftType << modelString; + // it can happen this is called before any queries // ES sends FsInn packets for callsigns such as ACCGER1, which are hard to distinguish // 1) checking if they are already in the list checks again ATC position which is safe @@ -818,7 +845,7 @@ namespace swift::core usedModelString.clear(); CCallsign::addLogDetailsToList( pReverseLookupMessages, callsign, - QStringLiteral("FsInn modelstring '%1' ignored because of setuo").arg(modelString)); + QStringLiteral("FsInn modelstring '%1' ignored because of setup").arg(modelString)); } else if (!CAircraftMatcher::isKnownModelString(modelString, callsign, pReverseLookupMessages)) { @@ -846,6 +873,15 @@ namespace swift::core void CAirspaceMonitor::onIcaoCodesReceived(const CCallsign &callsign, const QString &aircraftIcaoDesignator, const QString &airlineIcaoDesignator, const QString &livery) { + // TODO TZ remove logmessage when testing is done + CLogMessage(this).info(u"CAirspaceMonitor::onIcaoCodesReceived CHECK:" + u"callsign %1 " + u"aircraftIcaoDesignator %2 " + u"airlineIcaoDesignator %3 " + u"livery %4 ") + << callsign << aircraftIcaoDesignator << airlineIcaoDesignator << livery; + // End TODO TZ + Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "not in main thread"); if (!this->isConnectedAndNotShuttingDown()) { return; } if (CBuildConfig::isLocalDeveloperDebugBuild()) @@ -865,13 +901,18 @@ namespace swift::core CAirspaceMonitor::getLogCategories()); const CClient client = this->getClientOrDefaultForCallsign(callsign); - this->addOrUpdateAircraftInRange(callsign, aircraftIcaoDesignator, airlineIcaoDesignator, livery, - client.getQueriedModelString(), CAircraftModel::TypeQueriedFromNetwork, - pReverseLookupMessages); - this->addReverseLookupMessages(callsign, reverseLookupMessages); - this->sendReadyForModelMatching(callsign, ReceivedIcaoCodes); // ICAO codes received + const CSimulatedAircraft aircraft = this->addOrUpdateAircraftInRange( + callsign, aircraftIcaoDesignator, airlineIcaoDesignator, livery, client.getQueriedModelString(), + CAircraftModel::TypeQueriedFromNetwork, pReverseLookupMessages); + + // we do not change manually assigned models + if (aircraft.getModel().getModelType() != CAircraftModel::TypeManuallySet) + { + this->addReverseLookupMessages(callsign, reverseLookupMessages); + this->sendReadyForModelMatching(callsign, ReceivedIcaoCodes); // ICAO codes received - emit this->requestedNewAircraft(callsign, aircraftIcaoDesignator, airlineIcaoDesignator, livery); + emit this->requestedNewAircraft(callsign, aircraftIcaoDesignator, airlineIcaoDesignator, livery); + } } CAircraftModel CAirspaceMonitor::reverseLookupModelWithFlightplanData( @@ -1178,8 +1219,8 @@ namespace swift::core const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign); if (aircraft.hasValidCallsign()) { - // only if we do not have a DB model yet - if (!aircraft.getModel().hasValidDbKey()) + // we do not change manually assigned models (msfs2024) + if (!aircraft.getModel().hasValidDbKey() && aircraft.getModelType() != CAircraftModel::TypeManuallySet) { CAircraftModel model = this->reverseLookupModelWithFlightplanData(callsign, aircraftIcao, airlineIcao, livery, modelString, modelType, log); @@ -1439,69 +1480,6 @@ namespace swift::core this->updateAircraftInRange(callsign, vm); } - void CAirspaceMonitor::onRevBAircraftConfigReceived(const CCallsign &callsign, const QString &config, - qint64 currentOffsetMs) - { - - Q_ASSERT(CThreadUtils::isInThisThread(this)); - SWIFT_AUDIT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Need callsign"); - if (callsign.isEmpty()) { return; } - - unsigned long pp = 0; - bool ok {}; - pp = config.toULong(&ok, 10); - - bool gear = (pp & 1U); - bool landLight = (pp & 2U); - bool navLight = (pp & 4U); - bool strobeLight = (pp & 8U); - bool beaconLight = (pp & 16U); - bool taxiLight = (pp & 32U); - bool engine1Running = (pp & 64U); - bool engine2Running = (pp & 128U); - bool engine3Running = (pp & 256U); - bool engine4Running = (pp & 512U); - - // CLogMessage(this).info(u"taxiLight %1 landLight %2 beaconLight %3 strobeLight %4 gear %5") << taxiLight << - // landLight << beaconLight << strobeLight << gear; CLogMessage(this).info(u"engine1Running %1 engine2Running %2 - // engine3Running %3 engine4Running %4") << engine1Running << engine2Running << engine3Running << - // engine4Running; - - CAircraftParts aircraftparts; - aircraftparts.setGearDown(gear); - - CAircraftLights lights; - lights.setStrobeOn(strobeLight); - lights.setLandingOn(landLight); - lights.setTaxiOn(taxiLight); - lights.setBeaconOn(beaconLight); - lights.setNavOn(navLight); - aircraftparts.setLights(lights); - - CAircraftEngineList engines; - engines.initEngines(4, false); - engines.setEngineOn(1, engine1Running); - engines.setEngineOn(2, engine2Running); - engines.setEngineOn(3, engine3Running); - engines.setEngineOn(4, engine4Running); - aircraftparts.setEngines(engines); - - // make sure in any case right time and correct details - aircraftparts.setCurrentUtcTime(); - aircraftparts.setTimeOffsetMs(currentOffsetMs); - aircraftparts.setPartsDetails(CAircraftParts::FSDAircraftParts); - - // store parts - this->storeAircraftParts(callsign, aircraftparts, true); - - // update client capability - CClient client = this->getClientOrDefaultForCallsign(callsign); - client.setUserCallsign(callsign); // make valid by setting a callsign - if (client.hasCapability(CClient::FsdWithAircraftConfig)) { return; } - client.addCapability(CClient::FsdWithAircraftConfig); - this->setOtherClient(client); - } - void CAirspaceMonitor::onAircraftConfigReceived(const CCallsign &callsign, const QJsonObject &jsonObject, qint64 currentOffsetMs) { diff --git a/src/core/airspacemonitor.h b/src/core/airspacemonitor.h index 7e5db2a36..ce9ae8326 100644 --- a/src/core/airspacemonitor.h +++ b/src/core/airspacemonitor.h @@ -473,8 +473,6 @@ namespace swift::core const QString &aircraftIcao, const QString &airlineIcao); void onConnectionStatusChanged(swift::misc::network::CConnectionStatus oldStatus, swift::misc::network::CConnectionStatus newStatus); - void onRevBAircraftConfigReceived(const swift::misc::aviation::CCallsign &callsign, const QString &config, - qint64 currentOffsetMs); }; } // namespace swift::core diff --git a/src/core/context/contextnetworkimpl.cpp b/src/core/context/contextnetworkimpl.cpp index 12b0b337a..1fff22575 100644 --- a/src/core/context/contextnetworkimpl.cpp +++ b/src/core/context/contextnetworkimpl.cpp @@ -1137,6 +1137,7 @@ namespace swift::core::context if (c) { const CSimulatedAircraft aircraft(this->getAircraftInRangeForCallsign(callsign)); + Q_ASSERT_X(!aircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "missing callsign"); emit this->changedRemoteAircraftModel(aircraft, originator); // update aircraft model } diff --git a/src/core/db/databaseutils.cpp b/src/core/db/databaseutils.cpp index cf53239cd..8395658f8 100644 --- a/src/core/db/databaseutils.cpp +++ b/src/core/db/databaseutils.cpp @@ -75,6 +75,7 @@ namespace swift::core::db dbModelModified.updateMissingParts(model); dbModelModified.setDistributorOrder(distributorOrder); dbModelModified.setSimulator(dbModel.getSimulator()); // DB simulator settings have priority + dbModelModified.setModelLivery(model.getModelLivery()); // keep local livery settings msfs2024 return dbModelModified; } @@ -88,6 +89,7 @@ namespace swift::core::db { if (modified) { *modified = true; } consolidatedModel.setLivery(dbLivery); + consolidatedModel.setModelLivery(model.getModelLivery()); // keep local livery settings msfs2024 } } if (!consolidatedModel.getAircraftIcaoCode().hasValidDbKey() && consolidatedModel.hasAircraftDesignator()) @@ -99,6 +101,7 @@ namespace swift::core::db { if (modified) { *modified = true; } consolidatedModel.setAircraftIcaoCode(dbIcao); + consolidatedModel.setModelLivery(model.getModelLivery()); // keep local livery settings msfs2024 } } @@ -108,6 +111,7 @@ namespace swift::core::db { if (modified) { *modified = true; } consolidatedModel.setDistributor(dbDistributor); + consolidatedModel.setModelLivery(model.getModelLivery()); // keep local livery settings msfs2024 } consolidatedModel.updateLocalFileNames(model); consolidatedModel.setDistributorOrder(distributorOrder); diff --git a/src/core/fsd/fsdclient.cpp b/src/core/fsd/fsdclient.cpp index bbe6bc906..bc4bb8c6b 100644 --- a/src/core/fsd/fsdclient.cpp +++ b/src/core/fsd/fsdclient.cpp @@ -34,7 +34,6 @@ #include "core/fsd/planeinformationfsinn.h" #include "core/fsd/pong.h" #include "core/fsd/rehost.h" -#include "core/fsd/revbclientparts.h" #include "core/fsd/serializer.h" #include "core/fsd/servererror.h" #include "core/fsd/textmessage.h" @@ -1078,16 +1077,6 @@ namespace swift::core::fsd // Euroscope m_messageTypeMapping["SIMDATA"] = MessageType::EuroscopeSimData; - - // IVAO only - // Ref: https://github.com/DemonRem/X-IvAP/blob/1b0a14880532a0f5c8fe84be44e462c6892a5596/src/XIvAp/FSDprotocol.h - m_messageTypeMapping["!R"] = MessageType::RegistrationInfo; - m_messageTypeMapping["-MD"] = MessageType::RevBClientParts; - m_messageTypeMapping["-PD"] = MessageType::RevBPilotDescription; // not handled, to avoid error messages - - // IVAO parts - // https://discordapp.com/channels/539048679160676382/695961646992195644/707915838845485187 - // https://github.com/swift-project/pilotclient/wiki/Knowledgebase-Simulation:-IVAO-parts } void CFSDClient::handleAtcDataUpdate(const QStringList &tokens) @@ -1620,23 +1609,6 @@ namespace swift::core::fsd if (serverError.isFatalError()) { disconnectFromServer(); } } - void CFSDClient::handleRevBClientPartsPacket(const QStringList &tokens) - { - CLogMessage(this).debug(u"handleRevBClientPartsPacket"); - - const RevBClientParts RevBClientParts = RevBClientParts::fromTokens(tokens); - const CCallsign callsign(RevBClientParts.sender(), CCallsign::Aircraft); - - const bool inRange = isAircraftInRange(callsign); - - if (!inRange) { return; } // sort out all broadcasts we DO NOT NEED - if (!getSetupForServer().receiveAircraftParts()) { return; } - - const qint64 offsetTimeMs = currentOffsetTime(callsign); - emit revbAircraftConfigReceived(RevBClientParts.sender(), RevBClientParts.m_partsval1, offsetTimeMs); - CLogMessage(this).debug(u"Set Config at %1 ") << offsetTimeMs; - } - void CFSDClient::handleRehost(const QStringList &tokens) { const Rehost rehost = Rehost::fromTokens(tokens); @@ -2283,9 +2255,7 @@ namespace swift::core::fsd case MessageType::AddPilot: case MessageType::ServerHeartbeat: case MessageType::ProController: - case MessageType::ClientIdentification: - case MessageType::RegistrationInfo: - case MessageType::RevBPilotDescription: break; + case MessageType::ClientIdentification: break; // handled ones case MessageType::AtcDataUpdate: handleAtcDataUpdate(tokens); break; @@ -2308,7 +2278,6 @@ namespace swift::core::fsd case MessageType::ServerError: handleServerError(tokens); break; case MessageType::TextMessage: handleTextMessage(tokens); break; case MessageType::PilotClientCom: handleCustomPilotPacket(tokens); break; - case MessageType::RevBClientParts: handleRevBClientPartsPacket(tokens); break; case MessageType::VisualPilotDataUpdate: case MessageType::VisualPilotDataPeriodic: case MessageType::VisualPilotDataStopped: handleVisualPilotDataUpdate(tokens, messageType); break; diff --git a/src/core/fsd/fsdclient.h b/src/core/fsd/fsdclient.h index 498364bce..31183116d 100644 --- a/src/core/fsd/fsdclient.h +++ b/src/core/fsd/fsdclient.h @@ -313,7 +313,7 @@ namespace swift::core::fsd void planeInformationFsinnReceived(const swift::misc::aviation::CCallsign &callsign, const QString &airlineIcaoDesignator, const QString &aircraftDesignator, const QString &combinedAircraftType, const QString &modelString); - void revbAircraftConfigReceived(const QString &sender, const QString &config, qint64 currentOffsetTimeMs); + void muteRequestReceived(bool mute); //! @} @@ -474,7 +474,6 @@ namespace swift::core::fsd #ifdef SWIFT_VATSIM_SUPPORT void handleFsdIdentification(const QStringList &tokens); #endif - void handleRevBClientPartsPacket(const QStringList &tokens); void handleRehost(const QStringList &tokens); void handleMute(const QStringList &tokens); diff --git a/src/core/fsd/messagebase.h b/src/core/fsd/messagebase.h index f9f317043..1454f9195 100644 --- a/src/core/fsd/messagebase.h +++ b/src/core/fsd/messagebase.h @@ -42,11 +42,8 @@ enum class MessageType Pong, ServerError, ServerHeartbeat, - RegistrationInfo, // IVAO only TextMessage, PilotClientCom, - RevBClientParts, // IVAO only - RevBPilotDescription, // -PD IVAO only not handled in swift Rehost, Mute, }; diff --git a/src/core/fsd/revbclientparts.cpp b/src/core/fsd/revbclientparts.cpp deleted file mode 100644 index 36bd8b3bf..000000000 --- a/src/core/fsd/revbclientparts.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (C) 2020 swift Project Community / Contributors -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 - -#include "core/fsd/revbclientparts.h" - -#include "core/fsd/serializer.h" -#include "misc/logmessage.h" - -namespace swift::core::fsd -{ - RevBClientParts::RevBClientParts(const QString &sender, const QString &partsval1, const QString &partsval2, - const QString &partsval3) - : MessageBase(sender), m_partsval1(partsval1), m_partsval2(partsval2), m_partsval3(partsval3) - - {} - - QStringList RevBClientParts::toTokens() const - { - QStringList tokens; - tokens.push_back(m_sender); - tokens.push_back(m_partsval1); - tokens.push_back(m_partsval2); - tokens.push_back(m_partsval3); - return tokens; - } - - RevBClientParts RevBClientParts::fromTokens(const QStringList &tokens) - { - if (tokens.size() < 4) - { - swift::misc::CLogMessage(static_cast(nullptr)).debug(u"Wrong number of arguments."); - return {}; - } - return { tokens[0], tokens[1], tokens[2], tokens[3] }; - } - -} // namespace swift::core::fsd diff --git a/src/core/fsd/revbclientparts.h b/src/core/fsd/revbclientparts.h deleted file mode 100644 index e1d77dbab..000000000 --- a/src/core/fsd/revbclientparts.h +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (C) 2020 swift Project Community / Contributors -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 - -//! \file - -#ifndef SWIFT_CORE_FSD_REVBCLIENTPARTS_H -#define SWIFT_CORE_FSD_REVBCLIENTPARTS_H - -#include "core/fsd/enums.h" -#include "core/fsd/messagebase.h" - -namespace swift::core::fsd -{ - //! This packet is used to translate client’s parts from RevB IVAO -MD. - //! -MDMDN0104:262396:262396:262396 - //! - class SWIFT_CORE_EXPORT RevBClientParts : public MessageBase - { - public: - //! Constructor - RevBClientParts(const QString &sender, const QString &partsval1, const QString &partsval2, - const QString &partsval3); - - //! Message converted to tokens - QStringList toTokens() const; - - //! Construct from tokens - static RevBClientParts fromTokens(const QStringList &tokens); - - //! PDU identifier - static QString pdu() { return "-MD"; } - - //! @{ - //! Properties - ClientQueryType m_queryType = ClientQueryType::Unknown; - QStringList m_queryData; - QString m_partsval1; - QString m_partsval2; - QString m_partsval3; - //! @} - - private: - RevBClientParts() = default; - }; - - //! Equal to operator - inline bool operator==(const RevBClientParts &lhs, const RevBClientParts &rhs) - { - return lhs.sender() == rhs.sender() && lhs.m_partsval1 == rhs.m_partsval1 && - lhs.m_partsval2 == rhs.m_partsval2 && lhs.m_partsval3 == rhs.m_partsval3; - } - - //! Not equal to operator - inline bool operator!=(const RevBClientParts &lhs, const RevBClientParts &rhs) { return !(lhs == rhs); } - -} // namespace swift::core::fsd - -#endif // SWIFT_CORE_FSD_REVBCLIENTPARTS_H diff --git a/src/core/modelsetbuilder.cpp b/src/core/modelsetbuilder.cpp index 2cbe84325..40f8739b8 100644 --- a/src/core/modelsetbuilder.cpp +++ b/src/core/modelsetbuilder.cpp @@ -49,8 +49,9 @@ namespace swift::core } else { - // without any information we can not use them - modelSet = modelSet.findWithKnownAircraftDesignator(); + if (!options.testFlag(ShowAllInstalledModels)) + // without any information we can not use them + modelSet = modelSet.findWithKnownAircraftDesignator(); } // Include only diff --git a/src/core/modelsetbuilder.h b/src/core/modelsetbuilder.h index c85137707..530047f1f 100644 --- a/src/core/modelsetbuilder.h +++ b/src/core/modelsetbuilder.h @@ -34,7 +34,8 @@ namespace swift::core OnlyDbIcaoCodes = 1 << 2, Incremental = 1 << 3, SortByDistributors = 1 << 4, - ConsolidateWithDb = 1 << 5 + ConsolidateWithDb = 1 << 5, + ShowAllInstalledModels = 1 << 6, }; Q_DECLARE_FLAGS(Builder, BuilderFlag) diff --git a/src/gui/components/dbownmodelscomponent.cpp b/src/gui/components/dbownmodelscomponent.cpp index ba55a51d0..25b5940de 100644 --- a/src/gui/components/dbownmodelscomponent.cpp +++ b/src/gui/components/dbownmodelscomponent.cpp @@ -277,10 +277,10 @@ namespace swift::gui::components { QMessageBox msgBox(QMessageBox::Question, "Reload models from disk", QStringLiteral("Completely reload '%1' models from disk?").arg(simulator.toQString(true)), - QMessageBox::Ok | QMessageBox::Cancel, this); + QMessageBox::Yes | QMessageBox::No, this); msgBox.setDefaultButton(QMessageBox::Cancel); const auto reply = static_cast(msgBox.exec()); - if (reply != QMessageBox::Ok) { return; } + if (reply != QMessageBox::Yes) { return; } this->requestSimulatorModels(simulator, IAircraftModelLoader::InBackgroundNoCache); } @@ -332,6 +332,22 @@ namespace swift::gui::components ui->tvp_OwnAircraftModels->updateContainerMaybeAsync(this->getOwnModels()); } + // TODO TZ this is a stub for SimConnect loading + void CDbOwnModelsComponent::loadInstalledModelsSimConnect(const CSimulatorInfo &simulator, + IAircraftModelLoader::LoadMode mode, + const QStringList &modelDirectories) + { + Q_UNUSED(mode); + Q_UNUSED(modelDirectories); + + using namespace std::chrono_literals; + const CStatusMessage msg = CLogMessage(this).info(u"Triiger loading models for %1 from SimConnect") + << simulator.toQString(); + this->showOverlayHTMLMessage(msg, 2s); + + return; + } + void CDbOwnModelsComponent::loadInstalledModels(const CSimulatorInfo &simulator, IAircraftModelLoader::LoadMode mode, const QStringList &modelDirectories) @@ -512,6 +528,9 @@ namespace swift::gui::components IAircraftModelLoader::LoadMode mode, const QStringList &modelDirectories) { + // TODO TZ add SimConnect loading + if (simulator.isMSFS2024()) this->loadInstalledModelsSimConnect(simulator, mode, modelDirectories); + this->loadInstalledModels(simulator, mode, modelDirectories); } diff --git a/src/gui/components/dbownmodelscomponent.h b/src/gui/components/dbownmodelscomponent.h index 22f099d5b..3b7334246 100644 --- a/src/gui/components/dbownmodelscomponent.h +++ b/src/gui/components/dbownmodelscomponent.h @@ -153,6 +153,8 @@ namespace swift::gui void ownModelsSimulatorChanged(const swift::misc::simulation::CSimulatorInfo &simulator); private: + static constexpr std::chrono::milliseconds OverlayMsgTimeout { 5000 }; //!< how long overlay is displayed + QScopedPointer ui; swift::misc::simulation::IAircraftModelLoader *m_modelLoader = nullptr; //!< read own aircraft models, aka models on disk @@ -167,7 +169,12 @@ namespace swift::gui //! Request own models void requestOwnModelsUpdate(); - //! Load the models + //! Load the models via SimConnect + void loadInstalledModelsSimConnect(const swift::misc::simulation::CSimulatorInfo &simulator, + swift::misc::simulation::IAircraftModelLoader::LoadMode mode, + const QStringList &modelDirectories = {}); + + //! Load the models via disk void loadInstalledModels(const swift::misc::simulation::CSimulatorInfo &simulator, swift::misc::simulation::IAircraftModelLoader::LoadMode mode, const QStringList &modelDirectories = {}); diff --git a/src/gui/components/logincomponent.cpp b/src/gui/components/logincomponent.cpp index e9eb99d7f..e5a8e6f52 100644 --- a/src/gui/components/logincomponent.cpp +++ b/src/gui/components/logincomponent.cpp @@ -41,7 +41,9 @@ #include "misc/logmessage.h" #include "misc/network/connectionstatus.h" #include "misc/network/entityflags.h" +// TODO TZ remove after testing. it is in another branch already #include "misc/network/server.h" +// TODO TZ end remove afte testing #include "misc/network/serverlist.h" #include "misc/simulation/aircraftmodel.h" #include "misc/simulation/simulatedaircraft.h" @@ -299,11 +301,13 @@ namespace swift::gui::components { if (!m_updatePilotOnServerChanges) { return; } const bool vatsim = this->isVatsimNetworkTabSelected(); + // TODO TZ remove after testing. it is on another branch already // const CUser user = vatsim ? this->getCurrentVatsimServer().getUser() : server.getUser(); const CUser user = server.getServerType() != CServer::FSDServer ? this->getCurrentVatsimServer().getUser() : server.getUser(); if ((vatsim && server.getServerType() != CServer::FSDServer) || (!vatsim && server.getServerType() == CServer::FSDServer)) + // End remove after testing ui->form_Pilot->setUser(user); } @@ -403,7 +407,7 @@ namespace swift::gui::components { int v = ui->pb_LogoffTimeout->value(); v -= 1; - v = std::max(v, 0); + if (v < 0) { v = 0; } ui->pb_LogoffTimeout->setValue(v); if (v <= 0) { diff --git a/src/gui/components/mappingcomponent.cpp b/src/gui/components/mappingcomponent.cpp index 246085534..2c6cdd8f0 100644 --- a/src/gui/components/mappingcomponent.cpp +++ b/src/gui/components/mappingcomponent.cpp @@ -333,7 +333,10 @@ namespace swift::gui::components } const CCallsign callsign(this->validateRenderedCallsign()); if (callsign.isEmpty()) { return; } + + // Because of msfs2024, the model string contains the combination of title and livery. const QString modelString = ui->completer_ModelStrings->getModelString(); + if (modelString.isEmpty()) { this->showOverlayHTMLMessage(CStatusMessage(this).validationError(u"Missing model for mapping"), @@ -366,6 +369,7 @@ namespace swift::gui::components if (aircraftFromBackend.getModelString() != modelString) { const CAircraftModelList models = sGui->getIContextSimulator()->getModelSetModelsStartingWith(modelString); + if (models.isEmpty()) { const CStatusMessage msg = CStatusMessage(this).validationError(u"No model for title: '%1'") @@ -375,6 +379,7 @@ namespace swift::gui::components } CAircraftModel model(models.front()); + // found more than one model? if (models.size() > 1) { if (models.containsModelString(modelString)) @@ -391,8 +396,10 @@ namespace swift::gui::components model.setModelType(CAircraftModel::TypeManuallySet); CLogMessage(this).info(u"Requesting changes for '%1'") << callsign.asString(); - // enable in any case + // rendering-flag enable in any case sGui->getIContextNetwork()->updateAircraftEnabled(aircraftFromBackend.getCallsign(), true); + + // trigger model change changed = sGui->getIContextNetwork()->updateAircraftModel(aircraftFromBackend.getCallsign(), model, identifier()); } diff --git a/src/gui/components/mappingcomponent.ui b/src/gui/components/mappingcomponent.ui index f06bb5f7d..da52efbf9 100644 --- a/src/gui/components/mappingcomponent.ui +++ b/src/gui/components/mappingcomponent.ui @@ -1,358 +1,359 @@ - CMappingComponent - - - - 0 - 0 - 376 - 293 - - - - Mapping component - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 0 + CMappingComponent + + + + 0 + 0 + 376 + 293 + + + + Mapping component + + + + 0 - - - Rendered aircraft - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - - - - - - Aircraft models - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - - - - - - Statistics - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - Matching log - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - 16777215 - 80 - + + 0 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - validate - - - - - - - Icon - - - - - - - do all matchings again - - - re-match - - - - - - - save selected model - - - save - - - - - - - reset model by callsign - - - reset - - - - :/pastel/icons/pastel/16/arrow-refresh.png:/pastel/icons/pastel/16/arrow-refresh.png - - - - - - - callsign of aircraft - - - 15 - - - true - - - callsign - - - - - - - load model set - - - load model set - - - - - - - aircraft enabled - - - Enbl. - - - - - - - - 50 - 0 - - - - - - - - - 0 - 20 - - - - - - - - show / hide icon of aircraft - - - - - - false - - - - - - - aircraft enabled/disable - - - - + + 0 + + + 0 + + + + + Qt::Vertical + + + + 0 + + + + Rendered aircraft + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + + + + + + Aircraft models + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + + + + + + Statistics + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + Matching log + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + 16777215 + 80 + + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + validate + + + + + + + Icon + + + + + + + do all matchings again + + + re-match + + + + + + + save selected model + + + save + + + + + + + reset model by callsign + + + reset + + + + :/pastel/icons/pastel/16/arrow-refresh.png:/pastel/icons/pastel/16/arrow-refresh.png + + + + + + + + callsign of aircraft + + + 15 + + + true + + + callsign + + + + + + + load model set + + + load model set + + + + + + + aircraft enabled + + + Enbl. + + + + + + + + 50 + 0 + + + + + + + + + 0 + 20 + + + + + + + + show / hide icon of aircraft + + + + + + false + + + + + + + aircraft enabled/disable + + + + + + + + - - - - - - - - - - swift::gui::views::CAircraftModelView - QTableView -
gui/views/aircraftmodelview.h
-
- - swift::gui::components::CSimulatorSelector - QFrame -
gui/components/simulatorselector.h
- 1 -
- - swift::gui::views::CSimulatedAircraftView - QTableView -
gui/views/simulatedaircraftview.h
-
- - swift::gui::components::CModelMatcherLogComponent - QFrame -
gui/components/modelmatcherlogcomponent.h
- 1 -
- - swift::gui::components::CAircraftModelStringCompleter - QFrame -
gui/components/aircraftmodelstringcompleter.h
- 1 -
- - swift::gui::components::CMatchingStatisticsComponent - QFrame -
gui/components/matchingstatisticscomponent.h
- 1 -
-
- - tw_SpecializedViews - tvp_RenderedAircraft - cb_AircraftIconDisplayed - pb_ValidateModelSet - cb_AircraftEnabled - le_Callsign - pb_ResetAircraft - pb_DoMatchingAgain - pb_SaveAircraft - tvp_AircraftModels - - - - - + + + + + + swift::gui::views::CAircraftModelView + QTableView +
gui/views/aircraftmodelview.h
+
+ + swift::gui::components::CSimulatorSelector + QFrame +
gui/components/simulatorselector.h
+ 1 +
+ + swift::gui::views::CSimulatedAircraftView + QTableView +
gui/views/simulatedaircraftview.h
+
+ + swift::gui::components::CModelMatcherLogComponent + QFrame +
gui/components/modelmatcherlogcomponent.h
+ 1 +
+ + swift::gui::components::CAircraftModelStringCompleter + QFrame +
gui/components/aircraftmodelstringcompleter.h
+ 1 +
+ + swift::gui::components::CMatchingStatisticsComponent + QFrame +
gui/components/matchingstatisticscomponent.h
+ 1 +
+
+ + tw_SpecializedViews + tvp_RenderedAircraft + cb_AircraftIconDisplayed + pb_ValidateModelSet + cb_AircraftEnabled + le_Callsign + pb_ResetAircraft + pb_DoMatchingAgain + pb_SaveAircraft + tvp_AircraftModels + + + + +
diff --git a/src/gui/components/settingssimulatorbasicscomponent.cpp b/src/gui/components/settingssimulatorbasicscomponent.cpp index 50b0ac80a..97fb0ad05 100644 --- a/src/gui/components/settingssimulatorbasicscomponent.cpp +++ b/src/gui/components/settingssimulatorbasicscomponent.cpp @@ -138,12 +138,16 @@ namespace swift::gui::components s.setSimulatorDirectory(simulatorDir); s.setModelDirectories(modelDirs); s.setModelExcludeDirectories(relativeDirs); + s.setPropertyModelSet(ui->cb_LoadNewModelset->isChecked()); + s.setPropertyWithDbEntry(ui->cb_WithDbEntry->isChecked()); + s.setPropertyDistributorFiltered(ui->cb_DistributorFiltered->isChecked()); + const CStatusMessageList msgs = m_settings.setAndValidateSettings(s, simulator); if (msgs.isSuccess()) { const CStatusMessage m = m_settings.setAndSaveSettings(s, simulator); if (!m.isEmpty()) { CLogMessage::preformatted(m); } - if (m.isSuccess()) { this->showOverlayHTMLMessage("Saved settings", 5s); } + if (m.isSuccess()) { this->showOverlayHTMLMessage("Saved settings", 2s); } else { this->showOverlayMessage(m); } m_unsavedChanges = m_unsavedChanges && !m.isSuccess(); // reset if success, but only if there were changes @@ -227,6 +231,40 @@ namespace swift::gui::components { const CSimulatorInfo simulator(ui->comp_SimulatorSelector->getValue()); this->displaySettings(simulator); + + // special handling for MSFS 2024 only visual changes + if (simulator == CSimulatorInfo::msfs2024()) + { + ui->gb_ModelSet->setVisible(true); + ui->lbl_ExcludeDirectories->setText(QStringLiteral("modelstring exclude patterns:")); + ui->lbl_ModelDirectory->setText(QStringLiteral("modelstring filter patterns:")); + ui->lbl_ModelDirectory->setToolTip( + QStringLiteral("If the field is not empty, these patterns are used as a filter for the model string.")); + ui->pb_AdjustModelDirectory->setVisible(false); + ui->pb_ExcludeFileDialog->setVisible(false); + ui->pb_ModelFileDialog->setVisible(false); + const QString html = + "

"These " + "filter settings are applied to the model string passed from the flight simulator to swift. If the " + "effect is unclear, start with the default settings!".

"; + ui->lbl_ModelDirsInfo->setText(html); + } + else + { + ui->gb_ModelSet->setVisible(false); + ui->lbl_ExcludeDirectories->setText(QStringLiteral("Exclude directory patterns:")); + ui->lbl_ModelDirectory->setText(QStringLiteral("Model directories:")); + ui->lbl_ModelDirectory->setToolTip(QStringLiteral("remove redundant directories, fix file paths ..")); + ui->pb_AdjustModelDirectory->setVisible(true); + ui->pb_ExcludeFileDialog->setVisible(true); + ui->pb_ModelFileDialog->setVisible(true); + const QString html = + "

"If you change the model " + "directories, you must update your model set. See documentation. ".

"; + ui->lbl_ModelDirsInfo->setText(html); + } + this->displayDefaultValuesAsPlaceholder(simulator); } @@ -326,8 +364,27 @@ namespace swift::gui::components void CSettingsSimulatorBasicsComponent::displaySettings(const CSimulatorInfo &simulator) { - this->displayExcludeDirectoryPatterns(m_settings.getModelExcludeDirectoryPatternsIfNotDefault(simulator)); - this->displayModelDirectories(m_settings.getModelDirectoriesIfNotDefault(simulator)); + // if (simulator.isMSFS2024()) + //{ + + // //CSpecializedSimulatorSettings settings = this->getSimulatorSettings(simulator.isMSFS2024()); + // //CSimulatorSettings m_generic = settings.getGenericSettings(); + // //QStringList excludePatterns = m_generic.getModelExcludeDirectoryPatterns(); + // //QStringList filterPatterns = m_generic.getModelDirectories(); + + // //const CSimulatorSettings s = m_settings.getSettings(simulator); + // //const QString es = s.getSimulatorDirectory(); + // ui->pte_ModelDirectories->clear(); + // ui->pte_ExcludeDirectories->clear(); + + // //this->displayModelDirectories(excludePatterns); + // //this->displayExcludeDirectoryPatterns(filterPatterns); + //} + // else + //{ + // this->displayModelDirectories(m_settings.getModelDirectoriesIfNotDefault(simulator)); + // this->displayExcludeDirectoryPatterns(m_settings.getModelExcludeDirectoryPatternsIfNotDefault(simulator)); + //} // ui->le_SimulatorDirectory->setText(m_settings.getSimulatorDirectoryIfNotDefault(simulator)); // based on discussion here, always display: @@ -339,26 +396,59 @@ namespace swift::gui::components void CSettingsSimulatorBasicsComponent::displayDefaultValuesAsPlaceholder(const CSimulatorInfo &simulator) { const QString simDir = this->getFileBrowserSimulatorDirectory(); - ui->le_SimulatorDirectory->setPlaceholderText(simDir.isEmpty() ? "Simulator directory" : simDir); + + // only real placeholder shut set as placeholder + simDir.isEmpty() ? (ui->le_SimulatorDirectory->setPlaceholderText("Simulator directory")) : + (ui->le_SimulatorDirectory->setText(simDir)); // we take the settings and update to latest sim.directory CSpecializedSimulatorSettings settings = m_settings.getSpecializedSettings(simulator); settings.setSimulatorDirectory(simDir); - const QStringList m = settings.getModelDirectoriesFromSimulatorDirectoryOrDefault(); - if (m.isEmpty()) { ui->pte_ModelDirectories->setPlaceholderText("Model directories"); } - else + CSimulatorSettings m_generic = settings.getGenericSettings(); + + // this setting is only visable for msfs2024 but we set it always to keep code simple. the checkbox is hidden in + // other simulators this settings are used to load/not load the modelset when starting swiftgui + ui->cb_WithDbEntry->setChecked(m_generic.getPropertyWithDbEntry()); + ui->cb_DistributorFiltered->setChecked(m_generic.getPropertyDistributorFiltered()); + ui->cb_LoadNewModelset->setChecked(m_generic.getPropertyModelSet()); + + // Storeable content should be displayed as planetext so that it is actually saved. + if (simulator.isMSFS2024()) { - const QString ms = m.join("\n"); - ui->pte_ModelDirectories->setPlaceholderText(ms); + QStringList m = m_generic.getModelDirectories(); + if (m.isEmpty()) { ui->pte_ModelDirectories->setPlainText("*"); } + else + { + const QString ms = m.join("\n"); + ui->pte_ModelDirectories->setPlainText(ms); + } + + QStringList e = m_generic.getModelExcludeDirectoryPatterns(); + if (e.isEmpty()) { ui->pte_ExcludeDirectories->setPlainText("PassiveAircraft\nSTUB\nZZZZ"); } + else + { + const QString es = e.join("\n"); + ui->pte_ExcludeDirectories->setPlainText(es); + } } - - const QStringList e = settings.getDefaultModelExcludeDirectoryPatterns(); - if (e.isEmpty()) { ui->pte_ExcludeDirectories->setPlaceholderText("Exclude directories"); } else { - const QString es = e.join("\n"); - ui->pte_ExcludeDirectories->setPlaceholderText(es); + const QStringList m = settings.getModelDirectoriesFromSimulatorDirectoryOrDefault(); + if (m.isEmpty()) { ui->pte_ModelDirectories->setPlaceholderText("Model directories"); } + else + { + const QString ms = m.join("\n"); + ui->pte_ModelDirectories->setPlainText(ms); + } + + const QStringList e = settings.getDefaultModelExcludePatterns(); + if (e.isEmpty()) { ui->pte_ExcludeDirectories->setPlaceholderText("Exclude directories"); } + else + { + const QString es = e.join("\n"); + ui->pte_ExcludeDirectories->setPlainText(es); + } } } diff --git a/src/gui/components/settingssimulatorbasicscomponent.ui b/src/gui/components/settingssimulatorbasicscomponent.ui index a34d86d8b..08f847500 100644 --- a/src/gui/components/settingssimulatorbasicscomponent.ui +++ b/src/gui/components/settingssimulatorbasicscomponent.ui @@ -1,195 +1,268 @@ - CSettingsSimulatorBasicsComponent - - - - 0 - 0 - 459 - 347 - - - - Simulator basic settings - - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Model directories - - - - - - - Simulator: - - - - - - - remove redundant directories, fix file paths ... - - - adjust - - - - - - - Excluded from model loading - - - Exclude directory patterns: - - - - - - - - 150 - 25 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 50 - 20 - - - - - - - - save - - - - - - - reset - - - - - - - Simulator directory: - - - - - - - Model directories: - - - - - - - ... - - - - - - - ... - - - - - - - Excluded directory patterns - - - QPlainTextEdit::NoWrap - - - Excluded directory patterns - - - - - - - Simulator directory path - - - - - - - copy (materialize) defaults - - - copy def. - - - - - - - ... - - - - - - - <html><head/><body><p><img src=":/diagona/icons/diagona/icons/exclamation--frame.png"/> Changing model directories means you have to update your model set! Check documentation on &quot;creating a model set&quot;.</p></body></html> - - - true - - - - - - - - swift::gui::components::CSimulatorSelector - QFrame -
gui/components/simulatorselector.h
- 1 -
-
- - le_SimulatorDirectory - pte_ModelDirectories - pte_ExcludeDirectories - - - + CSettingsSimulatorBasicsComponent + + + + 0 + 0 + 459 + 347 + + + + Simulator basic settings + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + Model directories + + + + + + + Simulator: + + + + + + + + remove redundant directories, fix file paths ... + + + adjust + + + + + + + Excluded from model loading + + + Exclude directory patterns: + + + + + + + + 0 + 100 + + + + Model set handling + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + true + + + Create new modelset when swiftgui starts? + + + Every time swiftgui is restarted, the models currently in the simulator are read in and a new model set is created + + + + + + + Load models only with database entries + + + Only models with swift database entry are used + + + + + + + Distributors filtered? + + + Only the distributors from the configuration file are used + + + + + + + + + + + 150 + 25 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 50 + 20 + + + + + + + + save + + + + + + + reset + + + Sets the input mask to the default values ​​and deletes the saved values. + + + + + + + Simulator directory: + + + + + + + Model directories: + + + remove redundant directories, fix file paths ... + + + + + + + ... + + + + + + + ... + + + + + + + Excluded directory patterns + + + QPlainTextEdit::NoWrap + + + Excluded directory patterns + + + + + + + Simulator directory path + + + + + + + copy (materialize) defaults + + + copy def. + + + + + + + ... + + + + + + + <html><head/><body><p><img src=":/diagona/icons/diagona/icons/exclamation--frame.png"/> Changing model directories means you have to update your model set! Check documentation on &quot;creating a model set&quot;.</p></body></html> + + + true + + + + 150 + 100 + + + + + + + + + swift::gui::components::CSimulatorSelector + QFrame +
gui/components/simulatorselector.h
+ 1 +
+
+ + le_SimulatorDirectory + pte_ModelDirectories + pte_ExcludeDirectories + + +
diff --git a/src/gui/editors/aircraftpartsform.cpp b/src/gui/editors/aircraftpartsform.cpp index f2d98a231..75e8a2372 100644 --- a/src/gui/editors/aircraftpartsform.cpp +++ b/src/gui/editors/aircraftpartsform.cpp @@ -100,6 +100,9 @@ namespace swift::gui::editors ui->cb_AircraftPartsLightsBeacon->setChecked(on); ui->cb_AircraftPartsLightsNav->setChecked(on); ui->cb_AircraftPartsLightsLogo->setChecked(on); + ui->cb_AircraftPartsLightsWing->setChecked(on); + ui->cb_AircraftPartsLightsRecognition->setChecked(on); + ui->cb_AircraftPartsLightsCabin->setChecked(on); } void CAircraftPartsForm::setAllEngines() @@ -115,10 +118,13 @@ namespace swift::gui::editors aviation::CAircraftParts CAircraftPartsForm::guiToAircraftParts() const { + // added new values for lights and engines here const CAircraftLights lights( ui->cb_AircraftPartsLightsStrobe->isChecked(), ui->cb_AircraftPartsLightsLanding->isChecked(), ui->cb_AircraftPartsLightsTaxi->isChecked(), ui->cb_AircraftPartsLightsBeacon->isChecked(), - ui->cb_AircraftPartsLightsNav->isChecked(), ui->cb_AircraftPartsLightsLogo->isChecked()); + ui->cb_AircraftPartsLightsNav->isChecked(), ui->cb_AircraftPartsLightsLogo->isChecked(), + ui->cb_AircraftPartsLightsRecognition->isChecked(), ui->cb_AircraftPartsLightsCabin->isChecked(), + ui->cb_AircraftPartsLightsWing->isChecked()); const CAircraftEngineList engines( { ui->cb_AircraftPartsEngine1->isChecked(), ui->cb_AircraftPartsEngine2->isChecked(), ui->cb_AircraftPartsEngine3->isChecked(), ui->cb_AircraftPartsEngine4->isChecked(), @@ -143,6 +149,7 @@ namespace swift::gui::editors ui->cb_AircraftPartsLightsNav->setChecked(lights.isNavOn()); ui->cb_AircraftPartsLightsStrobe->setChecked(lights.isStrobeOn()); ui->cb_AircraftPartsLightsTaxi->setChecked(lights.isTaxiOn()); + ui->cb_AircraftPartsLightsWing->setChecked(lights.isWingOn()); const CAircraftEngineList engines = parts.getEngines(); ui->cb_AircraftPartsEngine1->setChecked(engines.isEngineOn(1)); diff --git a/src/gui/editors/aircraftpartsform.ui b/src/gui/editors/aircraftpartsform.ui index ee86bbb11..9d28ebb74 100644 --- a/src/gui/editors/aircraftpartsform.ui +++ b/src/gui/editors/aircraftpartsform.ui @@ -32,7 +32,7 @@ 3 - + Strobe @@ -70,7 +70,31 @@ - + + + + + Wing + + + + + + + Recognition + + + + + + + Cabin + + + + + + Nav @@ -91,7 +115,7 @@ - + Beacon @@ -140,7 +164,7 @@ - + Logo diff --git a/src/gui/editors/ownmodelsetform.ui b/src/gui/editors/ownmodelsetform.ui index c075c493d..99ecf31ca 100644 --- a/src/gui/editors/ownmodelsetform.ui +++ b/src/gui/editors/ownmodelsetform.ui @@ -164,6 +164,9 @@ all shown below + + true + bg_Distributors @@ -194,9 +197,6 @@ all distributors - - true - bg_Distributors diff --git a/src/gui/models/aircraftmodellistmodel.cpp b/src/gui/models/aircraftmodellistmodel.cpp index 1031b9ff5..c312407ac 100644 --- a/src/gui/models/aircraftmodellistmodel.cpp +++ b/src/gui/models/aircraftmodellistmodel.cpp @@ -81,7 +81,7 @@ namespace swift::gui::models case OwnAircraftModelMappingTool: case StashModel: - m_columns.addColumn(CColumn::standardString("model", CAircraftModel::IndexModelString)); + m_columns.addColumn(CColumn::standardString("model", { CAircraftModel::IndexModelString })); m_columns.addColumn( CColumn("DB", "DB metadata", CAircraftModel::IndexDatabaseIcon, new CPixmapFormatter())); if (mode == StashModel) diff --git a/src/misc/CMakeLists.txt b/src/misc/CMakeLists.txt index 6d0e075a9..07858420c 100644 --- a/src/misc/CMakeLists.txt +++ b/src/misc/CMakeLists.txt @@ -532,6 +532,10 @@ add_library(misc SHARED simulation/flightgear/aircraftmodelloaderflightgear.h simulation/flightgear/flightgearutil.cpp simulation/flightgear/flightgearutil.h + simulation/msfs2024/aircraftmodelloadermsfs2024.cpp + simulation/msfs2024/aircraftmodelloadermsfs2024.h + simulation/msfs2024/simconnectutilities.cpp + simulation/msfs2024/simconnectutilities.h simulation/fscommon/aircraftcfgentries.cpp simulation/fscommon/aircraftcfgentries.h simulation/fscommon/aircraftcfgentrieslist.cpp diff --git a/src/misc/aviation/aircraftlights.cpp b/src/misc/aviation/aircraftlights.cpp index 0c822e3fc..1d034e393 100644 --- a/src/misc/aviation/aircraftlights.cpp +++ b/src/misc/aviation/aircraftlights.cpp @@ -20,17 +20,23 @@ namespace swift::misc::aviation CAircraftLights::CAircraftLights(bool strobeOn, bool landingOn, bool taxiOn, bool beaconOn, bool navOn, bool logoOn, bool recognition, bool cabin) : m_strobeOn(strobeOn), m_landingOn(landingOn), m_taxiOn(taxiOn), m_beaconOn(beaconOn), m_navOn(navOn), - m_logoOn(logoOn), m_recognition(recognition), m_cabin(cabin) + m_logoOn(logoOn), m_recognitionOn(recognition), m_cabinOn(cabin) + {} + + CAircraftLights::CAircraftLights(bool strobeOn, bool landingOn, bool taxiOn, bool beaconOn, bool navOn, bool logoOn, + bool recognition, bool cabin, bool wing) + : m_strobeOn(strobeOn), m_landingOn(landingOn), m_taxiOn(taxiOn), m_beaconOn(beaconOn), m_navOn(navOn), + m_logoOn(logoOn), m_recognitionOn(recognition), m_cabinOn(cabin), m_wingOn(wing) {} CAircraftLights CAircraftLights::allLightsOn() { - return CAircraftLights { true, true, true, true, true, true, true, true }; + return CAircraftLights { true, true, true, true, true, true, true, true, true }; } CAircraftLights CAircraftLights::allLightsOff() { - return CAircraftLights { false, false, false, false, false, false, false, false }; + return CAircraftLights { false, false, false, false, false, false, false, false, false }; } QString CAircraftLights::convertToQString(bool i18n) const @@ -39,7 +45,8 @@ namespace swift::misc::aviation const QString s = u"strobe: " % boolToYesNo(m_strobeOn) % u" landing: " % boolToYesNo(m_landingOn) % u" taxi: " % boolToYesNo(m_taxiOn) % u" beacon: " % boolToYesNo(m_beaconOn) % u" nav: " % boolToYesNo(m_navOn) % u" logo: " % boolToYesNo(m_logoOn) % u" recognition: " % - boolToYesNo(m_recognition) % u" cabin: " % boolToYesNo(m_cabin); + boolToYesNo(m_recognitionOn) % u" cabin: " % boolToYesNo(m_cabinOn) % u" cabin: " % + boolToYesNo(m_wingOn); return s; } @@ -57,8 +64,9 @@ namespace swift::misc::aviation case IndexNav: return QVariant::fromValue(m_navOn); case IndexStrobe: return QVariant::fromValue(m_strobeOn); case IndexTaxi: return QVariant::fromValue(m_taxiOn); - case IndexRecognition: return QVariant::fromValue(m_recognition); - case IndexCabin: return QVariant::fromValue(m_cabin); + case IndexRecognition: return QVariant::fromValue(m_recognitionOn); + case IndexCabin: return QVariant::fromValue(m_cabinOn); + case IndexWing: return QVariant::fromValue(m_wingOn); default: return CValueObject::propertyByIndex(index); } } @@ -81,8 +89,9 @@ namespace swift::misc::aviation case IndexNav: m_navOn = variant.toBool(); break; case IndexStrobe: m_strobeOn = variant.toBool(); break; case IndexTaxi: m_taxiOn = variant.toBool(); break; - case IndexCabin: m_cabin = variant.toBool(); break; - case IndexRecognition: m_recognition = variant.toBool(); break; + case IndexCabin: m_cabinOn = variant.toBool(); break; + case IndexRecognition: m_recognitionOn = variant.toBool(); break; + case IndexWing: m_wingOn = variant.toBool(); break; default: CValueObject::setPropertyByIndex(index, variant); break; } } @@ -99,8 +108,9 @@ namespace swift::misc::aviation case IndexNav: return Compare::compare(m_navOn, compareValue.isNavOn()); case IndexStrobe: return Compare::compare(m_strobeOn, compareValue.isStrobeOn()); case IndexTaxi: return Compare::compare(m_taxiOn, compareValue.isTaxiOn()); - case IndexCabin: return Compare::compare(m_cabin, compareValue.isCabinOn()); - case IndexRecognition: return Compare::compare(m_recognition, compareValue.isRecognitionOn()); + case IndexCabin: return Compare::compare(m_cabinOn, compareValue.isCabinOn()); + case IndexRecognition: return Compare::compare(m_recognitionOn, compareValue.isRecognitionOn()); + case IndexWing: return Compare::compare(m_wingOn, compareValue.isWingOn()); default: break; } return 0; @@ -114,8 +124,9 @@ namespace swift::misc::aviation m_navOn = true; m_strobeOn = true; m_taxiOn = true; - m_cabin = true; - m_recognition = true; + m_cabinOn = true; + m_recognitionOn = true; + m_wingOn = true; } void CAircraftLights::setAllOff() @@ -126,7 +137,8 @@ namespace swift::misc::aviation m_navOn = false; m_strobeOn = false; m_taxiOn = false; - m_recognition = false; - m_cabin = false; + m_recognitionOn = false; + m_cabinOn = false; + m_wingOn = false; } } // namespace swift::misc::aviation diff --git a/src/misc/aviation/aircraftlights.h b/src/misc/aviation/aircraftlights.h index 846ce3df0..e1740fef5 100644 --- a/src/misc/aviation/aircraftlights.h +++ b/src/misc/aviation/aircraftlights.h @@ -33,7 +33,8 @@ namespace swift::misc::aviation IndexNav, IndexLogo, IndexRecognition, - IndexCabin + IndexCabin, + IndexWing, }; //! Default constructor @@ -49,6 +50,10 @@ namespace swift::misc::aviation CAircraftLights(bool strobeOn, bool landingOn, bool taxiOn, bool beaconOn, bool navOn, bool logoOn, bool recognition, bool cabin); + //! Constructor + CAircraftLights(bool strobeOn, bool landingOn, bool taxiOn, bool beaconOn, bool navOn, bool logoOn, + bool recognitionOn, bool cabin, bool wingOn); + //! Strobes lights on? bool isStrobeOn() const { return m_strobeOn; } @@ -86,16 +91,22 @@ namespace swift::misc::aviation void setLogoOn(bool on) { m_logoOn = on; } //! Recognition lights on? - bool isRecognitionOn() const { return m_recognition; } + bool isRecognitionOn() const { return m_recognitionOn; } //! Set recognition lights - void setRecognitionOn(bool on) { m_recognition = on; } + void setRecognitionOn(bool on) { m_recognitionOn = on; } //! Cabin lights on? - bool isCabinOn() const { return m_cabin; } + bool isCabinOn() const { return m_cabinOn; } //! Set cabin lights - void setCabinOn(bool on) { m_cabin = on; } + void setCabinOn(bool on) { m_cabinOn = on; } + + //! Wing light on? + bool isWingOn() const { return m_wingOn; } + + //! Set wing lights + void setWingOn(bool on) { m_wingOn = on; } //! All on void setAllOn(); @@ -135,9 +146,11 @@ namespace swift::misc::aviation bool m_beaconOn = false; bool m_navOn = false; bool m_logoOn = false; - bool m_recognition = false; //!< not supported by aircraft config (VATSIM) - bool m_cabin = false; //!< not supported by aircraft config (VATSIM) + bool m_recognitionOn = false; //!< not supported by aircraft config (VATSIM) + bool m_cabinOn = false; //!< not supported by aircraft config (VATSIM) + bool m_wingOn = false; //!< not supported by aircraft config (VATSIM) + // TODO TZ check if disabled lights can be activated. for testing we keep them enabled in JSON SWIFT_METACLASS( CAircraftLights, SWIFT_METAMEMBER(isNull, 0, DisabledForJson), // disable since JSON is used for network @@ -147,8 +160,12 @@ namespace swift::misc::aviation SWIFT_METAMEMBER_NAMED(beaconOn, "beacon_on"), SWIFT_METAMEMBER_NAMED(navOn, "nav_on"), SWIFT_METAMEMBER_NAMED(logoOn, "logo_on"), - SWIFT_METAMEMBER(recognition, 0, DisabledForJson), // disable since JSON is used for network - SWIFT_METAMEMBER(cabin, 0, DisabledForJson) // disable since JSON is used for network + SWIFT_METAMEMBER_NAMED(recognitionOn,"recognition_on"), + SWIFT_METAMEMBER_NAMED(cabinOn, "cabin_on"), + SWIFT_METAMEMBER_NAMED(wingOn, "wing_on") + //SWIFT_METAMEMBER(recognitionOn, 0, DisabledForJson), // disable since JSON is used for network + //SWIFT_METAMEMBER(cabinOn, 0, DisabledForJson), // disable since JSON is used for network + //SWIFT_METAMEMBER(wingOn, 0, DisabledForJson) // disable since JSON is used for network ); }; } // namespace swift::misc::aviation diff --git a/src/misc/simulation/aircraftmodel.cpp b/src/misc/simulation/aircraftmodel.cpp index 78685515e..9485c67ae 100644 --- a/src/misc/simulation/aircraftmodel.cpp +++ b/src/misc/simulation/aircraftmodel.cpp @@ -42,6 +42,10 @@ namespace swift::misc::simulation : m_modelString(model.trimmed().toUpper()), m_modelType(type) {} + CAircraftModel::CAircraftModel(const QString &model, const QString &livery, CAircraftModel::ModelType type) + : m_modelString(model.trimmed().toUpper()), m_modelLivery(livery.trimmed().toUpper()), m_modelType(type) + {} + CAircraftModel::CAircraftModel(const QString &model, CAircraftModel::ModelType type, const CAircraftIcaoCode &icao, const CLivery &livery) : m_aircraftIcao(icao), m_livery(livery), m_modelString(model.trimmed().toUpper()), m_modelType(type) @@ -241,6 +245,12 @@ namespace swift::misc::simulation return this->getAllModelStringsAndAliases() % " " % this->getDbKeyAsStringInParentheses(); } + QString CAircraftModel::getMsfs2024ModelString() + { + m_modelString = m_modelString.trimmed().toUpper() % u" " % m_modelLivery.trimmed().toUpper(); + return m_modelString; + } + bool CAircraftModel::isVtol() const { return this->getAircraftIcaoCode().isVtol(); } QVariant CAircraftModel::propertyByIndex(swift::misc::CPropertyIndexRef index) const @@ -588,6 +598,23 @@ namespace swift::misc::simulation return (sim.isFG()) ? this->getSwiftLiveryString(true, false, false) : this->getSwiftLiveryString(); } + QString CAircraftModel::getShortModelString() const + { + + QString shortModelString = m_modelString; + if (m_modelString.contains(m_modelLivery)) + { + int lastIndex = m_modelString.lastIndexOf(m_modelLivery); + + if (lastIndex != -1) + { + const QString newModelString = m_modelString.left(lastIndex); + shortModelString = newModelString; + } + } + return shortModelString; + } + DBTripleIds CAircraftModel::parseNetworkLiveryString(const QString &liveryString) { // "swift_m22l33a11" @@ -770,6 +797,13 @@ namespace swift::misc::simulation return (p.contains(path, cs)); } + bool CAircraftModel::matchesModelStringAndLivery(const QString &modelString, const QString &modelLivery, + Qt::CaseSensitivity sensitivity) const + { + return (stringCompare(modelString, m_modelString, sensitivity) && + stringCompare(modelLivery, m_modelLivery, sensitivity)); + } + bool CAircraftModel::matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const { return stringCompare(modelString, m_modelString, sensitivity); @@ -1003,6 +1037,7 @@ namespace swift::misc::simulation const QString modelName(json.value(prefix % u"name").toString()); const QString modelMode(json.value(prefix % u"mode").toString()); const QString parts(json.value(prefix % u"parts").toString()); + const QString modelLivery(json.value(prefix % u"modellivery").toString()); // check for undefined to rule out 0ft values const QJsonValue cgjv = json.value(prefix % u"cgft"); @@ -1012,6 +1047,7 @@ namespace swift::misc::simulation const CSimulatorInfo simInfo = CSimulatorInfo::fromDatabaseJson(json, prefix); CAircraftModel model(modelString, CAircraftModel::TypeDatabaseEntry, simInfo, modelName, modelDescription); model.setModelStringAlias(modelStringAlias); + model.setModelLivery(modelLivery); // msfs2024 model.setModelModeAsString(modelMode); model.setSupportedParts(parts); model.setCG(cg); diff --git a/src/misc/simulation/aircraftmodel.h b/src/misc/simulation/aircraftmodel.h index 575751e07..938403f11 100644 --- a/src/misc/simulation/aircraftmodel.h +++ b/src/misc/simulation/aircraftmodel.h @@ -83,7 +83,9 @@ namespace swift::misc TypeManuallySet, //!< manually set, e.g. from GUI TypeOwnSimulatorModel, //!< represents own simulator model (AI model, model on disk) TypeVPilotRuleBased, //!< based on a vPilot rule - TypeTerrainProbe //!< peudo aircraft used for terrain probing (FSX) + TypeTerrainProbe, //!< peudo aircraft used for terrain probing (FSX) + TypeOwnSimulatorLivery //!< represents own simulator model livery (msfs2024) + }; //! Mode, decides if a model is supposed to be used in the model set for model matching @@ -124,7 +126,7 @@ namespace swift::misc IndexSupportedParts, IndexModelModeAsIcon, IndexHasQueriedModelString, - IndexMembersDbStatus + IndexMembersDbStatus, }; //! \copydoc swift::misc::CValueObject::registerMetadata @@ -136,6 +138,9 @@ namespace swift::misc //! Constructor. CAircraftModel(const QString &model, ModelType type); + //! Constructor. + CAircraftModel(const QString &model, const QString &livery, ModelType type); + //! Constructor. CAircraftModel(const QString &model, ModelType type, const aviation::CAircraftIcaoCode &icao, const aviation::CLivery &livery); @@ -181,12 +186,21 @@ namespace swift::misc //! Model key, either queried or loaded from simulator model const QString &getModelString() const { return m_modelString; } + //! Model Livery, part of model string in MSFS 2024 + const QString &getModelLivery() const { return m_modelLivery; } + //! Model string and DB key (if available) QString getModelStringAndDbKey() const; //! Model string void setModelString(const QString &modelString) { m_modelString = modelString.trimmed().toUpper(); } + //! Model livery msfs2024 + void setModelLivery(const QString &modelLivery) { m_modelLivery = modelLivery.trimmed().toUpper(); } + + //! Model livery whitout part for lifery msfs2024 + QString getShortModelString() const; + //! Model key, either queried or loaded from simulator model const QString &getModelStringAlias() const { return m_modelStringAlias; } @@ -196,6 +210,9 @@ namespace swift::misc //! Get model string and aliases QString getAllModelStringsAliasesAndDbKey() const; + //! Get model string and Livery + QString getMsfs2024ModelString(); + //! Model string alias void setModelStringAlias(const QString &alias) { m_modelStringAlias = alias.trimmed().toUpper(); } @@ -419,6 +436,10 @@ namespace swift::misc //! Matches model string? bool matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const; + //! Matches model string and livery? + bool matchesModelStringAndLivery(const QString &modelString, const QString &modelLivery, + Qt::CaseSensitivity sensitivity) const; + //! Matches model string or alias? bool matchesModelStringOrAlias(const QString &modelString, Qt::CaseSensitivity sensitivity) const; @@ -567,11 +588,13 @@ namespace swift::misc CSimulatorInfo m_simulator; //!< model for given simulator CDistributor m_distributor; //!< who designed or distributed the model QString m_modelString; //!< Simulator model key, unique + QString m_modelLivery; //!< Simulator livery (msfs2024) QString m_modelStringAlias; //!< Simulator model key alias, unique QString m_name; //!< Model name QString m_description; //!< descriptive text QString m_fileName; //!< file name QString m_supportedParts; //!< supported parts + QString m_shortModelString; //!< cached short model string qint64 m_fileTimestamp = -1; //!< file timestamp of originating file (if applicable) ModelType m_modelType = TypeUnknown; //!< model string is coming representing ...? ModelMode m_modelMode = Include; //!< model mode (include / exclude) @@ -591,6 +614,7 @@ namespace swift::misc SWIFT_METAMEMBER(supportedParts), SWIFT_METAMEMBER(modelString, 0, CaseInsensitiveComparison), SWIFT_METAMEMBER(modelStringAlias, 0, CaseInsensitiveComparison), + SWIFT_METAMEMBER(modelLivery, 0, CaseInsensitiveComparison), SWIFT_METAMEMBER(name), SWIFT_METAMEMBER(description, 0, DisabledForComparison), SWIFT_METAMEMBER(fileName, 0, DisabledForComparison), diff --git a/src/misc/simulation/aircraftmodellist.cpp b/src/misc/simulation/aircraftmodellist.cpp index 3acdad57e..8dd3a58ba 100644 --- a/src/misc/simulation/aircraftmodellist.cpp +++ b/src/misc/simulation/aircraftmodellist.cpp @@ -725,12 +725,14 @@ namespace swift::misc::simulation return d; } - bool CAircraftModelList::removeModelWithString(const QString &modelString, Qt::CaseSensitivity sensitivity) + bool CAircraftModelList::removeModelWithString(const QString &modelString, const QString &modelLivery, + Qt::CaseSensitivity sensitivity) { if (modelString.isEmpty()) { return false; } if (this->isEmpty()) { return false; } - const int r = this->removeIf( - [&](const CAircraftModel &model) { return model.matchesModelString(modelString, sensitivity); }); + const int r = this->removeIf([&](const CAircraftModel &model) { + return model.matchesModelStringAndLivery(modelString, modelLivery, sensitivity); + }); return r > 0; } @@ -843,7 +845,11 @@ namespace swift::misc::simulation Qt::CaseSensitivity sensitivity) { bool r = false; - if (!this->isEmpty()) { r = this->removeModelWithString(addOrReplaceModel.getModelString(), sensitivity); } + if (!this->isEmpty()) + { + r = this->removeModelWithString(addOrReplaceModel.getModelString(), addOrReplaceModel.getModelLivery(), + sensitivity); + } this->push_back(addOrReplaceModel); return r; } @@ -901,6 +907,18 @@ namespace swift::misc::simulation return ms; } + QStringList CAircraftModelList::getModelStringAndLiveryList(bool sort) const + { + QStringList ms; + for (const CAircraftModel &model : *this) + { + if (!model.hasModelString()) { continue; } + ms.append(model.getModelString() + "{" + model.getModelLivery() + "}"); + } + if (sort) { ms.sort(Qt::CaseInsensitive); } + return ms; + } + QSet CAircraftModelList::getModelStringSet() const { CSetBuilder ms; @@ -1725,12 +1743,12 @@ namespace swift::misc::simulation if (valid) { validModels.push_back(model); - invalidModels.removeModelWithString(model.getModelString(), Qt::CaseInsensitive); + invalidModels.removeModelWithString(model.getModelString(), model.getModelLivery(), Qt::CaseInsensitive); } else { invalidModels.push_back(model); - validModels.removeModelWithString(model.getModelString(), Qt::CaseInsensitive); + validModels.removeModelWithString(model.getModelString(), model.getModelLivery(), Qt::CaseInsensitive); } } diff --git a/src/misc/simulation/aircraftmodellist.h b/src/misc/simulation/aircraftmodellist.h index a429e27cd..b1ab05a18 100644 --- a/src/misc/simulation/aircraftmodellist.h +++ b/src/misc/simulation/aircraftmodellist.h @@ -334,7 +334,8 @@ namespace swift::misc //! Remove those models with given model strings //! \return number of elements removed - bool removeModelWithString(const QString &modelString, Qt::CaseSensitivity sensitivity); + bool removeModelWithString(const QString &modelString, const QString &modelLivery, + Qt::CaseSensitivity sensitivity); //! Remove those models with given model strings //! \return number of elements removed @@ -393,6 +394,9 @@ namespace swift::misc //! Model strings QStringList getModelStringList(bool sort = true) const; + //! Model strings and Livery codes + QStringList getModelStringAndLiveryList(bool sort = true) const; + //! Model strings as set QSet getModelStringSet() const; diff --git a/src/misc/simulation/aircraftmodelloader.cpp b/src/misc/simulation/aircraftmodelloader.cpp index a7bf4ccbf..c8cba5bcf 100644 --- a/src/misc/simulation/aircraftmodelloader.cpp +++ b/src/misc/simulation/aircraftmodelloader.cpp @@ -129,8 +129,17 @@ namespace swift::misc::simulation return; } - // really load from disk? - const QStringList modelDirs = this->getInitializedModelDirectories(modelDirectories, simulator); + // TODO TZ 2024: skip loading for MSFS2024? + QStringList modelDirs = { "", "" }; + // if (simulator.isMSFS2024()) + //{ + // emit this->loadingFinished(m_loadingMessages, simulator, LoadingSkipped); + // return; + // } + // else + //{ + // really load from disk? + modelDirs = this->getInitializedModelDirectories(modelDirectories, simulator); if (m_skipLoadingEmptyModelDir && modelDirs.isEmpty()) { const CStatusMessage status = CStatusMessage(this, CStatusMessage::SeverityWarning, @@ -141,6 +150,7 @@ namespace swift::misc::simulation emit this->loadingFinished(m_loadingMessages, simulator, LoadingSkipped); return; } + //} this->setObjectInfo(simulator); this->startLoadingFromDisk(mode, modelConsolidation, modelDirs); diff --git a/src/misc/simulation/aircraftmodelloaderprovider.cpp b/src/misc/simulation/aircraftmodelloaderprovider.cpp index f81c7e196..ebf5c169e 100644 --- a/src/misc/simulation/aircraftmodelloaderprovider.cpp +++ b/src/misc/simulation/aircraftmodelloaderprovider.cpp @@ -13,6 +13,7 @@ #include "misc/mixin/mixincompare.h" #include "misc/simulation/flightgear/aircraftmodelloaderflightgear.h" #include "misc/simulation/fscommon/aircraftcfgparser.h" +#include "misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h" #include "misc/simulation/xplane/aircraftmodelloaderxplane.h" #include "misc/simulation/xplane/xplaneutil.h" @@ -22,6 +23,7 @@ using namespace swift::misc::simulation::settings; using namespace swift::misc::simulation::fscommon; using namespace swift::misc::simulation::flightgear; using namespace swift::misc::simulation::xplane; +using namespace swift::misc::simulation::msfs2024; namespace swift::misc::simulation { @@ -31,6 +33,7 @@ namespace swift::misc::simulation Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Single simulator"); if (simulator.isXPlane()) { return new CAircraftModelLoaderXPlane(parent); } if (simulator.isFG()) { return new CAircraftModelLoaderFlightgear(parent); } + if (simulator.isMSFS2024()) { return new CAircraftModelLoaderMsfs2024(parent); } return CAircraftCfgParser::createModelLoader(simulator, parent); } diff --git a/src/misc/simulation/aircraftmodelutils.cpp b/src/misc/simulation/aircraftmodelutils.cpp index 7682dc04e..be8a908b0 100644 --- a/src/misc/simulation/aircraftmodelutils.cpp +++ b/src/misc/simulation/aircraftmodelutils.cpp @@ -167,9 +167,13 @@ namespace swift::misc::simulation QStringLiteral("Some of the excluded models are: '%1'").arg(ms), true)); } - // specific checks for FSX/XPlane/FG + // specific checks for FSX/XPlane/FG/MSFS CStatusMessageList specificTests; - if (simulator.isMicrosoftOrPrepare3DSimulator() || models.isLikelyFsFamilyModelList()) + if (simulator.isMSFS2024()) + { + // Placeholder, nothing to do + } + else if (simulator.isMicrosoftOrPrepare3DSimulator() || models.isLikelyFsFamilyModelList()) { const CStatusMessageList specificTests1 = fscommon::CFsCommonUtil::validateAircraftConfigFiles( models, validModels, invalidModels, ignoreEmpty, stopAtFailedFiles, wasStopped); @@ -193,12 +197,6 @@ namespace swift::misc::simulation models, validModels, invalidModels, ignoreEmpty, stopAtFailedFiles, wasStopped, simulatorDir); specificTests.push_back(specificTests2); } - else if (simulator.isMSFS2024()) - { - const CStatusMessageList specificTests2 = fscommon::CFsCommonUtil::validateMSFS2024SimObjectsPath( - models, validModels, invalidModels, ignoreEmpty, stopAtFailedFiles, wasStopped, simulatorDir); - specificTests.push_back(specificTests2); - } } else if (simulator.isXPlane() || models.isLikelyXPlaneModelList()) { diff --git a/src/misc/simulation/fscommon/aircraftcfgparser.cpp b/src/misc/simulation/fscommon/aircraftcfgparser.cpp index 3a7c23ad1..dc03414c1 100644 --- a/src/misc/simulation/fscommon/aircraftcfgparser.cpp +++ b/src/misc/simulation/fscommon/aircraftcfgparser.cpp @@ -153,11 +153,9 @@ namespace swift::misc::simulation::fscommon // set directory with name filters, get aircraft.cfg and sub directories static const QString NoNameFilter; QDir dir(directory, NoNameFilter, QDir::Name, QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); - // TODO TZ: still have to figure out how msfs2024 handles this + // for MSFS2020 we only need aircraft.cfg - // MSFS2024 has aircraft.cfg only in communityfolder - // a solution for the aircraft from the marketplace may be prepared by ASOBO - dir.setNameFilters(fileNameFilters(getSimulator().isMSFS(), getSimulator().isMSFS2024())); + dir.setNameFilters(fileNameFilters(getSimulator().isMSFS())); if (!dir.exists()) { return {}; // can happen if there are shortcuts or linked dirs not available @@ -467,10 +465,9 @@ namespace swift::misc::simulation::fscommon return content; } - // TODO TZ: MSFS2024 currently has aircraft.cfg only in the community folder - const QStringList &CAircraftCfgParser::fileNameFilters(bool isMSFS, bool isMSFS2024) + const QStringList &CAircraftCfgParser::fileNameFilters(bool isMSFS) { - if (CBuildConfig::buildWordSize() == 32 || isMSFS || isMSFS2024) + if (CBuildConfig::buildWordSize() == 32 || isMSFS) { static const QStringList f({ "aircraft.cfg" }); return f; diff --git a/src/misc/simulation/fscommon/aircraftcfgparser.h b/src/misc/simulation/fscommon/aircraftcfgparser.h index 9850f74bf..6ff000f36 100644 --- a/src/misc/simulation/fscommon/aircraftcfgparser.h +++ b/src/misc/simulation/fscommon/aircraftcfgparser.h @@ -89,7 +89,7 @@ namespace swift::misc static QString getFixedIniLineContent(const QString &line); //! Files to be used - static const QStringList &fileNameFilters(bool isMSFS, bool isMSFS2024); + static const QStringList &fileNameFilters(bool isMSFS); //! Exclude the sub directories not to be parsed static bool isExcludedSubDirectory(const QString &excludeDirectory); diff --git a/src/misc/simulation/fscommon/fscommonutil.cpp b/src/misc/simulation/fscommon/fscommonutil.cpp index e8c8d1e0d..bd14ae5eb 100644 --- a/src/misc/simulation/fscommon/fscommonutil.cpp +++ b/src/misc/simulation/fscommon/fscommonutil.cpp @@ -230,17 +230,6 @@ namespace swift::misc::simulation::fscommon stopAtFailedFiles, stopped); } - CStatusMessageList CFsCommonUtil::validateMSFS2024SimObjectsPath( - const CAircraftModelList &models, CAircraftModelList &validModels, CAircraftModelList &invalidModels, - bool ignoreEmptyFileNames, int stopAtFailedFiles, std::atomic_bool &stopped, const QString &simulatorDir) - { - Q_UNUSED(simulatorDir) - const QStringList simObjectPaths = CFsDirectories::msfs2024SimObjectsDirPath(); - return CFsCommonUtil::validateSimObjectsPath(QSet(simObjectPaths.begin(), simObjectPaths.end()), - models, validModels, invalidModels, ignoreEmptyFileNames, - stopAtFailedFiles, stopped); - } - CStatusMessageList CFsCommonUtil::validateSimObjectsPath(const QSet &simObjectDirs, const CAircraftModelList &models, CAircraftModelList &validModels, CAircraftModelList &invalidModels, diff --git a/src/misc/simulation/fscommon/fscommonutil.h b/src/misc/simulation/fscommon/fscommonutil.h index 07bfa55fc..7cde5deb1 100644 --- a/src/misc/simulation/fscommon/fscommonutil.h +++ b/src/misc/simulation/fscommon/fscommonutil.h @@ -67,15 +67,6 @@ namespace swift::misc::simulation::fscommon bool ignoreEmptyFileNames, int stopAtFailedFiles, std::atomic_bool &wasStopped, const QString &simulatorDir); - //! Validate if known SimObjects path are used - //! \remark only for MSFS2024 - static CStatusMessageList validateMSFS2024SimObjectsPath(const CAircraftModelList &models, - CAircraftModelList &validModels, - CAircraftModelList &invalidModels, - bool ignoreEmptyFileNames, int stopAtFailedFiles, - std::atomic_bool &wasStopped, - const QString &simulatorDir); - private: //! Validate if known SimObjects path are used //! \remark only for P3D/FSX diff --git a/src/misc/simulation/fscommon/fsdirectories.cpp b/src/misc/simulation/fscommon/fsdirectories.cpp index 020e32d7d..c2e883d23 100644 --- a/src/misc/simulation/fscommon/fsdirectories.cpp +++ b/src/misc/simulation/fscommon/fsdirectories.cpp @@ -331,6 +331,8 @@ namespace swift::misc::simulation::fscommon "OneStore/microsoft-discovery", "landingchallenge", "tutorials", + "point-of-interest", + "airport", }; return exclude; @@ -338,9 +340,7 @@ namespace swift::misc::simulation::fscommon const QStringList &CFsDirectories::msfs2024SimObjectsExcludeDirectoryPatterns() { - static const QStringList exclude { - "landingchallenge", - "tutorials", + static const QStringList exclude { "PassiveAircraft", "STUB", "ZZZZ" }; return exclude; diff --git a/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.cpp b/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.cpp new file mode 100644 index 000000000..890a2c365 --- /dev/null +++ b/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.cpp @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +// #include + +#include "misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h" + +#include + +#include "misc/simulation/aircraftmodel.h" +#include "misc/simulation/settings/simulatorsettings.h" + +using namespace swift::config; +using namespace swift::misc; +using namespace swift::misc::aviation; +using namespace swift::misc::physical_quantities; +using namespace swift::misc::geo; +using namespace swift::misc::network; +using namespace swift::misc::simulation; +using namespace swift::misc::simulation::settings; +// using namespace swift::misc::math; +using namespace swift::misc::simulation::data; +// using namespace swift::misc::simulation::msfs2024; +// using namespace swift::misc::simulation::settings; + +namespace swift::misc::simulation::msfs2024 +{ + + bool CAircraftModelLoaderMsfs2024::isLoadingFinished() const + { + return !m_parserWorker || m_parserWorker->isFinished(); + + } + + CAircraftModelLoaderMsfs2024::CAircraftModelLoaderMsfs2024(QObject *parent) + : simulation::IAircraftModelLoader(simulation::CSimulatorInfo::msfs2024(), parent) + {} + + CAircraftModelLoaderMsfs2024::~CAircraftModelLoaderMsfs2024() + { + // that should be safe as long as the worker uses deleteLater (which it does) + if (m_parserWorker) { m_parserWorker->waitForFinished(); } + } + + void CAircraftModelLoaderMsfs2024::updateInstalledModels(const CAircraftModelList &models) + { + this->setModelsForSimulator(models, CSimulatorInfo::msfs2024()); + + const CStatusMessage m = + CStatusMessage(this, CStatusMessage::SeverityInfo, u"MSFS2024 found and updated '%1' models") + << models.size(); + m_loadingMessages.push_back(m); + } + + CAircraftModelList CAircraftModelLoaderMsfs2024::performParsing() + { + CAircraftModelList allModels; + + // TODO TZ Implement model queries via SimConnect if possible + // misc shut not include simconnect headers or plugins directly + // still no idea how to do that + const CSimulatorInfo simulatorInfo = CSimulatorInfo::msfs2024(); + allModels = + CCentralMultiSimulatorModelCachesProvider::modelCachesInstance().getSynchronizedCachedModels(simulatorInfo); + + return allModels; + } + + // for msfs2024, the model information is read from the SimConnect interface and no longer from the directories via + // the aircraft.cfg + void CAircraftModelLoaderMsfs2024::startLoadingFromDisk( + IAircraftModelLoader::LoadMode mode, const IAircraftModelLoader::ModelConsolidationCallback &modelConsolidation, + const QStringList &modelDirectories) + { + + const CSimulatorInfo simulator = CSimulatorInfo::msfs2024(); + const QStringList modelDirs = this->getInitializedModelDirectories(modelDirectories, simulator); + const QStringList excludedDirectoryPatterns( + m_settings.getModelExcludeDirectoryPatternsOrDefault(simulator)); // copy + + if (mode.testFlag(LoadInBackground)) + { + if (m_parserWorker && !m_parserWorker->isFinished()) { return; } + emit this->diskLoadingStarted(simulator, mode); + + // TODO TZ need help: simplify, we don't need directories in this->performParsing for MSFS2024 + m_parserWorker = CWorker::fromTask(this, "CAircraftModelLoaderMsfs2024::performParsing", + [this, modelConsolidation]() { + auto models = this->performParsing(); + if (modelConsolidation) { modelConsolidation(models, true); } + return models; + }); + m_parserWorker->thenWithResult(this, [=](const auto &models) { + this->updateInstalledModels(models); + m_loadingMessages.freezeOrder(); + emit this->loadingFinished(m_loadingMessages, simulator, ParsedData); + }); + } + else if (mode.testFlag(LoadDirectly)) + { + emit this->diskLoadingStarted(simulator, mode); + CAircraftModelList models(this->performParsing()); + this->updateInstalledModels(models); + } + } + +} // namespace swift::misc::simulation::msfs2024 diff --git a/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h b/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h new file mode 100644 index 000000000..a7dd37cc0 --- /dev/null +++ b/src/misc/simulation/msfs2024/aircraftmodelloadermsfs2024.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +#ifndef SWIFT_MISC_SIMULATION_MSFS2024_AIRCRAFTMODELLOADERMSFS2024_H +#define SWIFT_MISC_SIMULATION_MSFS2024_AIRCRAFTMODELLOADERMSFS2024_H + +#include + +#include "misc/simulation/aircraftmodelloader.h" + +namespace swift::misc::simulation::msfs2024 +{ + //! Msfs2024 aircraft model loader + class CAircraftModelLoaderMsfs2024 : public IAircraftModelLoader + { + Q_OBJECT + + public: + //! Constructor + CAircraftModelLoaderMsfs2024(QObject *parent = nullptr); + + //! Virtual destructor + ~CAircraftModelLoaderMsfs2024() override; + + //! Parsed or injected models + void updateInstalledModels(const CAircraftModelList &models); + + //! \copydoc IAircraftModelLoader::isLoadingFinished + bool isLoadingFinished() const override; + + protected: + //! \copydoc IAircraftModelLoader::startLoadingFromDisk + void startLoadingFromDisk(LoadMode mode, const ModelConsolidationCallback &modelConsolidation, + const QStringList &modelDirectories) override; + + private: + QPointer m_parserWorker; + // CAircraftModelList performParsing(const QStringList &rootDirectories, const QStringList &excludeDirectories); + CAircraftModelList performParsing(); + }; + +} // namespace swift::misc::simulation::msfs2024 + +#endif // SWIFT_MISC_SIMULATION_MSFS2024_AIRCRAFTMODELLOADERMSFS2024_H diff --git a/src/misc/simulation/msfs2024/simconnectutilities.cpp b/src/misc/simulation/msfs2024/simconnectutilities.cpp new file mode 100644 index 000000000..2f57cb08f --- /dev/null +++ b/src/misc/simulation/msfs2024/simconnectutilities.cpp @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +#include "misc/simulation/msfs2024/simconnectutilities.h" + +#include +#include +#include +#include + +#include "misc/stringutils.h" + +using namespace swift::misc; +using namespace swift::misc::aviation; + +namespace swift::misc::simulation::msfs2024 +{ + QString CSimConnectUtilities::resolveEnumToString(const DWORD id, const char *enumName) + { + const int i = CSimConnectUtilities::staticMetaObject.indexOfEnumerator(enumName); + if (i < 0) { return QStringLiteral("No enumerator for %1").arg(enumName); } + const QMetaEnum m = CSimConnectUtilities::staticMetaObject.enumerator(i); + const char *k = m.valueToKey(static_cast(id)); + return (k) ? QLatin1String(k) : QStringLiteral("Id '%1' not found for %2").arg(id).arg(enumName); + } + + QString CSimConnectUtilities::simConnectExceptionToString(const DWORD id) + { + return CSimConnectUtilities::resolveEnumToString(id, "SIMCONNECT_EXCEPTION"); + } + + QString CSimConnectUtilities::simConnectSurfaceTypeToString(const DWORD type, bool beautify) + { + QString sf = CSimConnectUtilities::resolveEnumToString(type, "SIMCONNECT_SURFACE"); + return beautify ? sf.replace('_', ' ') : sf; + } + + QString CSimConnectUtilities::simConnectReceiveIdToString(DWORD type) + { + const QString ri = CSimConnectUtilities::resolveEnumToString(type, "SIMCONNECT_RECV_ID"); + return ri; + } + + int CSimConnectUtilities::lightsToLightStates(const CAircraftLights &lights) + { + int lightMask = 0; + if (lights.isBeaconOn()) { lightMask |= Beacon; } + if (lights.isLandingOn()) { lightMask |= Landing; } + if (lights.isLogoOn()) { lightMask |= Logo; } + if (lights.isNavOn()) { lightMask |= Nav; } + if (lights.isStrobeOn()) { lightMask |= Strobe; } + if (lights.isTaxiOn()) { lightMask |= Taxi; } + return lightMask; + } + + void CSimConnectUtilities::registerMetadata() + { + qRegisterMetaType(); + qRegisterMetaType(); + } + + CWinDllUtils::DLLInfo CSimConnectUtilities::simConnectDllInfo() + { + const QList modules = CWinDllUtils::getModules(-1, "simconnect"); + if (modules.isEmpty()) + { + CWinDllUtils::DLLInfo info; + info.errorMsg = "No SimConnect.dll loaded"; + return info; + } + return CWinDllUtils::getDllInfo(modules.first().executable); + } +} // namespace swift::misc::simulation::msfs2024 diff --git a/src/misc/simulation/msfs2024/simconnectutilities.h b/src/misc/simulation/msfs2024/simconnectutilities.h new file mode 100644 index 000000000..f2c2cbb95 --- /dev/null +++ b/src/misc/simulation/msfs2024/simconnectutilities.h @@ -0,0 +1,204 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +//! \file + +#ifndef SWIFT_MISC_SIMULATION_MSFS2024_SIMCONNECTUTILITIES_H +#define SWIFT_MISC_SIMULATION_MSFS2024_SIMCONNECTUTILITIES_H + +#include +#include +#include +#include + +#include "misc/aviation/aircraftlights.h" +#include "misc/simulation/simulatorinfo.h" +#include "misc/swiftmiscexport.h" +#include "misc/windllutils.h" + +// Apart from the below definitions, the following code is OS independent, +// though it does not make sense to be used on non WIN machines. +// But it allows such parts to compile on all platforms. +#ifdef Q_OS_WIN +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include +#else +using DWORD = unsigned long; //!< Fake Windows DWORD +#endif + +namespace swift::misc::simulation::msfs2024 +{ + //! Utilities for SimConnect + //! \remark not using the simconnect.h headers as Misc classes are not driver aware + class SWIFT_MISC_EXPORT CSimConnectUtilities : public QObject + { + Q_OBJECT + + public: + //! Resolve SimConnect exception (based on Qt metadata). + //! \param id enum element + //! \return enum element's name + static QString simConnectExceptionToString(const DWORD id); + + //! Resolve SimConnect surface (based on Qt metadata). + //! \param type enum element + //! \param beautify remove "_" + static QString simConnectSurfaceTypeToString(const DWORD type, bool beautify = true); + + //! SimConnect surfaces. + //! \sa http://msdn.microsoft.com/en-us/library/cc526981.aspx#AircraftFlightInstrumentationData + enum SIMCONNECT_SURFACE + { + Concrete, + Grass, + Water, + Grass_bumpy, + Asphalt, + Short_grass, + Long_grass, + Hard_turf, + Snow, + Ice, + Urban, + Forest, + Dirt, + Coral, + Gravel, + Oil_treated, + Steel_mats, + Bituminus, + Brick, + Macadam, + Planks, + Sand, + Shale, + Tarmac, + Wright_flyer_track + }; + Q_ENUM(SIMCONNECT_SURFACE) + + //! SimConnect exceptions. + enum SIMCONNECT_EXCEPTION + { + SIMCONNECT_EXCEPTION_NONE, + SIMCONNECT_EXCEPTION_ERROR, + SIMCONNECT_EXCEPTION_SIZE_MISMATCH, + SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID, + SIMCONNECT_EXCEPTION_UNOPENED, + SIMCONNECT_EXCEPTION_VERSION_MISMATCH, + SIMCONNECT_EXCEPTION_TOO_MANY_GROUPS, + SIMCONNECT_EXCEPTION_NAME_UNRECOGNIZED, + SIMCONNECT_EXCEPTION_TOO_MANY_EVENT_NAMES, + SIMCONNECT_EXCEPTION_EVENT_ID_DUPLICATE, + SIMCONNECT_EXCEPTION_TOO_MANY_MAPS, + SIMCONNECT_EXCEPTION_TOO_MANY_OBJECTS, + SIMCONNECT_EXCEPTION_TOO_MANY_REQUESTS, + SIMCONNECT_EXCEPTION_WEATHER_INVALID_PORT, + SIMCONNECT_EXCEPTION_WEATHER_INVALID_METAR, + SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_GET_OBSERVATION, + SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_CREATE_STATION, + SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_REMOVE_STATION, + SIMCONNECT_EXCEPTION_INVALID_DATA_TYPE, + SIMCONNECT_EXCEPTION_INVALID_DATA_SIZE, + SIMCONNECT_EXCEPTION_DATA_ERROR, + SIMCONNECT_EXCEPTION_INVALID_ARRAY, + SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED, + SIMCONNECT_EXCEPTION_LOAD_FLIGHTPLAN_FAILED, + SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE, + SIMCONNECT_EXCEPTION_ILLEGAL_OPERATION, + SIMCONNECT_EXCEPTION_ALREADY_SUBSCRIBED, + SIMCONNECT_EXCEPTION_INVALID_ENUM, + SIMCONNECT_EXCEPTION_DEFINITION_ERROR, + SIMCONNECT_EXCEPTION_DUPLICATE_ID, + SIMCONNECT_EXCEPTION_DATUM_ID, + SIMCONNECT_EXCEPTION_OUT_OF_BOUNDS, + SIMCONNECT_EXCEPTION_ALREADY_CREATED, + SIMCONNECT_EXCEPTION_OBJECT_OUTSIDE_REALITY_BUBBLE, + SIMCONNECT_EXCEPTION_OBJECT_CONTAINER, + SIMCONNECT_EXCEPTION_OBJECT_AI, + SIMCONNECT_EXCEPTION_OBJECT_ATC, + SIMCONNECT_EXCEPTION_OBJECT_SCHEDULE + }; + Q_ENUM(SIMCONNECT_EXCEPTION) + + //! Lights for FSX/P3D "LIGHT ON STATES" + //! \sa http://www.prepar3d.com/SDKv2/LearningCenter/utilities/variables/simulation_variables.html + enum LIGHT_STATES + { + Nav = 0x0001, + Beacon = 0x0002, + Landing = 0x0004, + Taxi = 0x0008, + Strobe = 0x0010, + Panel = 0x0020, + Recognition = 0x0040, + Wing = 0x0080, + Logo = 0x0100, + Cabin = 0x0200 + }; + + //! Receive IDs for SimConnect + enum SIMCONNECT_RECV_ID + { + SIMCONNECT_RECV_ID_NULL, + SIMCONNECT_RECV_ID_EXCEPTION, + SIMCONNECT_RECV_ID_OPEN, + SIMCONNECT_RECV_ID_QUIT, + SIMCONNECT_RECV_ID_EVENT, + SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE, + SIMCONNECT_RECV_ID_EVENT_FILENAME, + SIMCONNECT_RECV_ID_EVENT_FRAME, + SIMCONNECT_RECV_ID_SIMOBJECT_DATA, + SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE, + SIMCONNECT_RECV_ID_WEATHER_OBSERVATION, + SIMCONNECT_RECV_ID_CLOUD_STATE, + SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID, + SIMCONNECT_RECV_ID_RESERVED_KEY, + SIMCONNECT_RECV_ID_CUSTOM_ACTION, + SIMCONNECT_RECV_ID_SYSTEM_STATE, + SIMCONNECT_RECV_ID_CLIENT_DATA, + SIMCONNECT_RECV_ID_EVENT_WEATHER_MODE, + SIMCONNECT_RECV_ID_AIRPORT_LIST, + SIMCONNECT_RECV_ID_VOR_LIST, + SIMCONNECT_RECV_ID_NDB_LIST, + SIMCONNECT_RECV_ID_WAYPOINT_LIST, + SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_SERVER_STARTED, + SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_CLIENT_STARTED, + SIMCONNECT_RECV_ID_EVENT_MULTIPLAYER_SESSION_ENDED, + SIMCONNECT_RECV_ID_EVENT_RACE_END, + SIMCONNECT_RECV_ID_EVENT_RACE_LAP, + }; + Q_ENUM(SIMCONNECT_RECV_ID) + + //! Receive id to string + static QString simConnectReceiveIdToString(DWORD type); + + //! Lights to states + static int lightsToLightStates(const aviation::CAircraftLights &lights); + + //! Get info about SimConnect DLL + static swift::misc::CWinDllUtils::DLLInfo simConnectDllInfo(); + + //! Register metadata + static void registerMetadata(); + + private: + //! + //! Resolve enum value to its cleartext (based on Qt metadata). + //! \param id enum element + //! \param enumName name of the resolved enum + //! \return enum element's name + static QString resolveEnumToString(const DWORD id, const char *enumName); + + //! Hidden constructor + CSimConnectUtilities(); + }; +} // namespace swift::misc::simulation::msfs2024 + +Q_DECLARE_METATYPE(swift::misc::simulation::msfs2024::CSimConnectUtilities::SIMCONNECT_EXCEPTION) +Q_DECLARE_METATYPE(swift::misc::simulation::msfs2024::CSimConnectUtilities::SIMCONNECT_SURFACE) +Q_DECLARE_METATYPE(swift::misc::simulation::msfs2024::CSimConnectUtilities::SIMCONNECT_RECV_ID) + +#endif // SWIFT_MISC_SIMULATION_MSFS2024_SIMCONNECTUTILITIES_H diff --git a/src/misc/simulation/settings/simulatorsettings.cpp b/src/misc/simulation/settings/simulatorsettings.cpp index 1f1c3e553..70fefa7b3 100644 --- a/src/misc/simulation/settings/simulatorsettings.cpp +++ b/src/misc/simulation/settings/simulatorsettings.cpp @@ -152,6 +152,16 @@ namespace swift::misc::simulation::settings } } + void CSimulatorSettings::setPropertyModelSet(bool value) { m_modelSet = value; } + void CSimulatorSettings::setPropertyWithDbEntry(bool value) + { + m_withDbEntry = value; + } + void CSimulatorSettings::setPropertyDistributorFiltered(bool value) + { + m_distributorFiltered = value; + } + void CSimulatorSettings::setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant) { if (index.isMyself()) @@ -617,7 +627,7 @@ namespace swift::misc::simulation::settings return m_genericSettings.getModelDirectories(); } - const QStringList &CSpecializedSimulatorSettings::getDefaultModelExcludeDirectoryPatterns() const + const QStringList &CSpecializedSimulatorSettings::getDefaultModelExcludePatterns() const { return CSpecializedSimulatorSettings::defaultModelExcludeDirectoryPatterns(m_simulator); } @@ -692,9 +702,9 @@ namespace swift::misc::simulation::settings } case CSimulatorInfo::MSFS2024: { - static const QString msfs2024 = - CFileUtils::normalizeFilePathToQtStandard(CFsDirectories::msfs2024PackagesDir()); - if (msfs2024.isEmpty()) { return e; } + // msfs2024 uses no model directories but uses the field "packages directory" for filtering modelstrings + // Asterix stands for everything == no filtering + static const QString msfs2024 = "*"; static const QStringList md { msfs2024 }; return md; } diff --git a/src/misc/simulation/settings/simulatorsettings.h b/src/misc/simulation/settings/simulatorsettings.h index 056635ef7..0b38e267d 100644 --- a/src/misc/simulation/settings/simulatorsettings.h +++ b/src/misc/simulation/settings/simulatorsettings.h @@ -120,6 +120,11 @@ namespace swift::misc::simulation::settings //! Record GND values with radius bool setRecordedGndRadius(const swift::misc::physical_quantities::CLength &radius); + //! Reads the settings for automatic loading when starting swiftgui + bool getPropertyWithDbEntry() const { return m_withDbEntry; } + bool getPropertyModelSet() const { return m_modelSet; } + bool getPropertyDistributorFiltered() const { return m_distributorFiltered; } + //! Reset the paths void resetPaths(); @@ -135,6 +140,10 @@ namespace swift::misc::simulation::settings //! \copydoc swift::misc::mixin::Index::setPropertyByIndex void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant); + void setPropertyModelSet(bool value); + void setPropertyWithDbEntry(bool value); + void setPropertyDistributorFiltered(bool value); + private: QString m_simulatorDirectory; //!< Simulator directory QStringList m_modelDirectories; //!< Model directory @@ -142,6 +151,11 @@ namespace swift::misc::simulation::settings bool m_comIntegration = false; //!< COM integration bool m_recordGnd = false; //!< Record GND values (of own aircraft) int m_cgSource = static_cast(CGFromSimulatorFirst); //!< CG source + bool m_modelSet = false; //!< Reads models from simulator via SimConnect (msfs2024 only) + bool m_withDbEntry = false; //!< Only models with swift database entry are used after reading (msfs2024 only) + bool m_distributorFiltered = + false; //!< Only the distributors from the configuration file are used (msfs2024 only) + physical_quantities::CLength m_recordedGndRadius { 250.0, physical_quantities::CLengthUnit::m() }; SWIFT_METACLASS( @@ -152,7 +166,10 @@ namespace swift::misc::simulation::settings SWIFT_METAMEMBER(comIntegration), SWIFT_METAMEMBER(cgSource), SWIFT_METAMEMBER(recordGnd), - SWIFT_METAMEMBER(recordedGndRadius)); + SWIFT_METAMEMBER(recordedGndRadius), + SWIFT_METAMEMBER(modelSet), + SWIFT_METAMEMBER(withDbEntry), + SWIFT_METAMEMBER(distributorFiltered)); }; //! Some P3D/FSX settings @@ -240,7 +257,7 @@ namespace swift::misc::simulation::settings const QStringList &getModelDirectoriesIfNotDefault() const; //! Default model exclude patterns - const QStringList &getDefaultModelExcludeDirectoryPatterns() const; + const QStringList &getDefaultModelExcludePatterns() const; //! First model directoy QString getFirstModelDirectoryOrDefault() const; diff --git a/src/misc/simulation/simulatedaircraft.h b/src/misc/simulation/simulatedaircraft.h index 0ecf5ba99..91d7d666b 100644 --- a/src/misc/simulation/simulatedaircraft.h +++ b/src/misc/simulation/simulatedaircraft.h @@ -406,6 +406,12 @@ namespace swift::misc //! Get model string const QString &getModelString() const { return m_models[CurrentModel].getModelString(); } + //! Get model Livery MSFS2024 + const QString &getLiveryString() const { return m_models[CurrentModel].getModelLivery(); } + + //! Get short model string (without livery msfs2024) + const QString getShortModelString() const { return m_models[CurrentModel].getShortModelString(); } + //! Set model string void setModelString(const QString &modelString); @@ -483,6 +489,9 @@ namespace swift::misc //! \copydoc swift::misc::mixin::Icon::toIcon() CIcons::IconIndex toIcon() const { return m_callsign.toIcon(); } + //! Get model type + int getModelType() const { return m_models[CurrentModel].getModelType(); } + private: static constexpr int CurrentModel = 0; //!< m_models static constexpr int NetworkModel = 1; //!< m_models diff --git a/src/plugins/simulator/CMakeLists.txt b/src/plugins/simulator/CMakeLists.txt index 4572f5131..17c1c4039 100644 --- a/src/plugins/simulator/CMakeLists.txt +++ b/src/plugins/simulator/CMakeLists.txt @@ -22,7 +22,7 @@ if(SWIFT_BUILD_FS9_PLUGIN OR SWIFT_BUILD_FSX_PLUGIN OR SWIFT_BUILD_P3D_PLUGIN OR add_subdirectory(fscommon) endif() -if(SWIFT_BUILD_FSX_PLUGIN OR SWIFT_BUILD_P3D_PLUGIN OR SWIFT_BUILD_MSFS_PLUGIN OR SWIFT_BUILD_MSFS2024_PLUGIN) +if(SWIFT_BUILD_FSX_PLUGIN OR SWIFT_BUILD_P3D_PLUGIN OR SWIFT_BUILD_MSFS_PLUGIN) add_subdirectory(fsxcommon) endif() diff --git a/src/plugins/simulator/fsxcommon/simconnectdatadefinition.cpp b/src/plugins/simulator/fsxcommon/simconnectdatadefinition.cpp index 1ee57e99f..1139facdb 100644 --- a/src/plugins/simulator/fsxcommon/simconnectdatadefinition.cpp +++ b/src/plugins/simulator/fsxcommon/simconnectdatadefinition.cpp @@ -82,7 +82,6 @@ namespace swift::simplugin::fsxcommon hr += initSimulatorEnvironment(hSimConnect); hr += initSbDataArea(hSimConnect); if (simInfo.isMSFS()) { hr += initMSFSTransponder(hSimConnect); } - if (simInfo.isMSFS2024()) { hr += initMSFS2024Transponder(hSimConnect); } return hr; } @@ -423,22 +422,6 @@ namespace swift::simplugin::fsxcommon return hr; } - HRESULT CSimConnectDefinitions::initMSFS2024Transponder(const HANDLE hSimConnect) - { - HRESULT hr = s_ok(); - hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS, - "TRANSPONDER STATE:1", "Enum"); - hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS, - "TRANSPONDER IDENT:1", "Bool"); - if (isFailure(hr)) - { - CLogMessage(static_cast(nullptr)) - .error(u"SimConnect error: MSFS2024 transponder data definitions %1") - << hr; - } - return hr; - } - DataDefinitionRemoteAircraftPartsWithoutLights::DataDefinitionRemoteAircraftPartsWithoutLights() { this->resetToInvalid(); diff --git a/src/plugins/simulator/fsxcommon/simconnectdatadefinition.h b/src/plugins/simulator/fsxcommon/simconnectdatadefinition.h index d23a3497c..642b62479 100644 --- a/src/plugins/simulator/fsxcommon/simconnectdatadefinition.h +++ b/src/plugins/simulator/fsxcommon/simconnectdatadefinition.h @@ -262,7 +262,6 @@ namespace swift::simplugin::fsxcommon ClientAreaSquawkBox }; - // TODO TZ: are there any changes in MSFS2024 //! Handles SimConnect data definitions class FSXCOMMON_EXPORT CSimConnectDefinitions { @@ -345,9 +344,6 @@ namespace swift::simplugin::fsxcommon //! Initialize data definition for MSFS transponder static HRESULT initMSFSTransponder(const HANDLE hSimConnect); - - //! Initialize data definition for MSFS transponder - static HRESULT initMSFS2024Transponder(const HANDLE hSimConnect); }; } // namespace swift::simplugin::fsxcommon diff --git a/src/plugins/simulator/fsxcommon/simconnectsymbols.cpp b/src/plugins/simulator/fsxcommon/simconnectsymbols.cpp index c2f12ef4b..3857f565d 100644 --- a/src/plugins/simulator/fsxcommon/simconnectsymbols.cpp +++ b/src/plugins/simulator/fsxcommon/simconnectsymbols.cpp @@ -351,37 +351,6 @@ bool loadAndResolveMSFSimConnect() } } -bool loadAndResolveMSFS2024SimConnect() -{ - // Check if already loaded - if (gSymbols.SimConnect_Open) { return true; } - - QString simConnectFileName(QStringLiteral("SimConnect.MSFS2024")); - - QLibrary simConnectDll(simConnectFileName); - simConnectDll.setLoadHints(QLibrary::PreventUnloadHint); - if (simConnectDll.load()) - { - const bool resolvedCommon = resolveCommonSimConnectSymbols(simConnectDll); - if (!resolvedCommon) - { - CLogMessage(CLogCategories::driver()).error(u"Failed to resolve common symbols from SimConnect.dll: '%1'") - << simConnectFileName; - return false; - } - - CLogMessage(CLogCategories::driver()).info(u"Loaded and resolved MSFS2024 symbols from SimConnect.dll: '%1'") - << simConnectFileName; - return resolvedCommon; - } - else - { - CLogMessage(CLogCategories::driver()).error(u"Failed to load SimConnect.dll: '%1' '%2'") - << simConnectFileName << simConnectDll.errorString(); - return false; - } -} - #else bool loadAndResolveFsxSimConnect(bool manifestProbing) { diff --git a/src/plugins/simulator/fsxcommon/simconnectsymbols.h b/src/plugins/simulator/fsxcommon/simconnectsymbols.h index f8fe6c997..01eb193a5 100644 --- a/src/plugins/simulator/fsxcommon/simconnectsymbols.h +++ b/src/plugins/simulator/fsxcommon/simconnectsymbols.h @@ -39,8 +39,6 @@ inline bool loadAndResolveP3DSimConnectByString(const QString &version) FSXCOMMON_EXPORT bool loadAndResolveMSFSimConnect(); -FSXCOMMON_EXPORT bool loadAndResolveMSFS2024SimConnect(); - #else //! Load and resolve FSX SimConnect. diff --git a/src/plugins/simulator/fsxcommon/simconnectwindows.h b/src/plugins/simulator/fsxcommon/simconnectwindows.h index 541c1cc0f..de21426cc 100644 --- a/src/plugins/simulator/fsxcommon/simconnectwindows.h +++ b/src/plugins/simulator/fsxcommon/simconnectwindows.h @@ -18,6 +18,8 @@ // clang-format off #include #include +//#include "../third_party/externals/common/include/simconnect/P3D-v4/SimConnect.h" + // clang-format on #include diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp index e130562a6..5eb82a438 100644 --- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp +++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp @@ -509,8 +509,7 @@ namespace swift::simplugin::fsxcommon const CFsxP3DSettings settings = m_detailsSettings.getSettings(this->getSimulatorInfo()); m_useAddSimulatedObj = settings.isAddingAsSimulatedObjectEnabled(); m_useSbOffsets = settings.isSbOffsetsEnabled(); - if (this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS() || - this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS2024()) + if (this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS()) { m_useSbOffsets = false; // Always disable SbOffsets for MSFS. Using new transponder mode property directly } @@ -528,9 +527,7 @@ namespace swift::simplugin::fsxcommon SIMCONNECT_PERIOD_SECOND, SIMCONNECT_DATA_REQUEST_FLAG_CHANGED), "Cannot request title", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); - // TODO TZ use MSFS2024 FSUIPC? - if (!this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS() && - !this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS2024()) + if (!this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS()) { // Request the data from SB only when its changed and only ONCE so we don't have to run a 1sec event to // get/set this info ;) there was a bug with SIMCONNECT_CLIENT_DATA_PERIOD_ON_SET, see @@ -3016,16 +3013,6 @@ namespace swift::simplugin::fsxcommon return connectedSimName.contains("fsx") || connectedSimName.contains("microsoft") || connectedSimName.contains("simulator x"); } - else if (pluginSim.isMSFS()) - { - // MSFS 2020 drivers only works with MSFS - return connectedSimName.contains("kittyhawk"); - } - else if (pluginSim.isMSFS2024()) - { - // MSFS2024 drivers only works with MSFS2024 - return connectedSimName.contains("sunrise"); - } return false; } diff --git a/src/plugins/simulator/msfs2024/CMakeLists.txt b/src/plugins/simulator/msfs2024/CMakeLists.txt index d04f63955..373bc79f2 100644 --- a/src/plugins/simulator/msfs2024/CMakeLists.txt +++ b/src/plugins/simulator/msfs2024/CMakeLists.txt @@ -7,17 +7,27 @@ add_library(simulatormsfs2024 SHARED simulatormsfs2024.json simulatormsfs2024factory.cpp simulatormsfs2024factory.h + simconnectsymbolsmsfs2024.cpp + simconnectsymbolsmsfs2024.h + simulatormsfs2024common.cpp + simulatormsfs2024common.h + simconnectobjectmsfs2024.cpp + simconnectobjectmsfs2024.h + simconnectdatadefinitionmsfs2024.cpp + simconnectdatadefinitionmsfs2024.h + simulatormsfs2024simconnectproc.cpp + msfs2024export.h ) set_target_properties(simulatormsfs2024 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/out/bin/plugins/simulator) set_target_properties(simulatormsfs2024 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/out/bin/plugins/simulator) target_include_directories(simulatormsfs2024 PUBLIC ${PROJECT_SOURCE_DIR}/src) +target_compile_definitions(simulatormsfs2024 PRIVATE BUILD_MSFS2024_LIB) target_link_libraries(simulatormsfs2024 PUBLIC fscommon - fsxcommon core misc Qt::Core diff --git a/src/plugins/simulator/msfs2024/msfs2024export.h b/src/plugins/simulator/msfs2024/msfs2024export.h new file mode 100644 index 000000000..da7875508 --- /dev/null +++ b/src/plugins/simulator/msfs2024/msfs2024export.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +//! \file + +#ifndef SWIFT_SIMPLUGIN_MSFS2024_SIMULATORMSFS2024_MACROS_H +#define SWIFT_SIMPLUGIN_MSFS2024_SIMULATORMSFS2024_MACROS_H + +#include + +/*! + * \def MSFS2024_EXPORT + * MSFS2024 Export Macro + */ + +#ifndef WITH_STATIC +# if defined(BUILD_MSFS2024_LIB) +# define MSFS2024_EXPORT Q_DECL_EXPORT +# else +# define MSFS2024_EXPORT Q_DECL_IMPORT +# endif +#else +# define MSFS2024_EXPORT +#endif + +#endif // SWIFT_SIMPLUGIN_MSFS2024_SIMULATORMSFS2024_MACROS_H diff --git a/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.cpp b/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.cpp new file mode 100644 index 000000000..bb0841c79 --- /dev/null +++ b/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.cpp @@ -0,0 +1,579 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +#include "simconnectdatadefinitionmsfs2024.h" + +#include + +#include + +#include "../fscommon/simulatorfscommonfunctions.h" +#include "simconnectsymbolsmsfs2024.h" + +#include "misc/aviation/aircraftenginelist.h" +#include "misc/aviation/aircraftparts.h" +#include "misc/logmessage.h" + +using namespace swift::misc; +using namespace swift::misc::aviation; +using namespace swift::misc::simulation; +using namespace swift::simplugin::fscommon; + +namespace swift::simplugin::msfs2024common +{ + const QString &CSimConnectDefinitions::requestToString(Request request) + { + static const QString ownAircraft("RequestOwnAircraft"); + static const QString title("RequestOwnAircraftTitle"); + static const QString livery("RequestOwnAircraftLivery"); + static const QString sbData("RequestSbData"); + static const QString facility("RequestFacility"); + static const QString end(""); + static const QString unknown("unknown"); + + switch (request) + { + case RequestOwnAircraft: return ownAircraft; + case RequestOwnAircraftTitle: return title; + case RequestSbData: return sbData; + case RequestFacility: return facility; + case RequestEndMarker: return end; + case RequestOwnAircraftLivery: return livery; + default: break; + } + return unknown; + } + + const QString &CSimConnectDefinitions::simObjectRequestToString(SimObjectRequest simObjectRequest) + { + static const QString baseId("base id"); + static const QString add("add"); + static const QString remove("remove"); + static const QString lights("lights"); + static const QString pos("position"); + static const QString model("model"); + static const QString misc("misc"); + static const QString end(""); + static const QString unknown("unknown"); + + switch (simObjectRequest) + { + case SimObjectBaseId: return baseId; + case SimObjectAdd: return add; + case SimObjectRemove: return remove; + case SimObjectLights: return lights; + case SimObjectPositionData: return pos; + case SimObjectModel: return model; + case SimObjectMisc: return misc; + case SimObjectEndMarker: return end; + default: break; + } + return unknown; + } + + CSimConnectDefinitions::CSimConnectDefinitions() {} + + HRESULT CSimConnectDefinitions::initDataDefinitionsWhenConnected(const HANDLE hSimConnect, + const CSimulatorInfo &simInfo) + { + Q_UNUSED(simInfo); + + HRESULT hr = s_ok(); + hr += initOwnAircraft(hSimConnect); + hr += initRemoteAircraft(hSimConnect); + hr += initRemoteAircraftSimData(hSimConnect); + hr += initRemoteAircraftSimDataSet(hSimConnect); + hr += initSimulatorEnvironment(hSimConnect); + hr += initSbDataArea(hSimConnect); + hr += initMSFS2024Transponder(hSimConnect); + hr += initOwnAircraftList(hSimConnect); + return hr; + } + + HRESULT CSimConnectDefinitions::initOwnAircraft(const HANDLE hSimConnect) + { + // MSFS2024 vars: + // https://docs.flightsimulator.com/msfs2024/html/6_Programming_APIs/SimVars/Simulation_Variables.htm + HRESULT hr = s_ok(); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PLANE LATITUDE", + "Degrees"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PLANE LONGITUDE", + "Degrees"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PLANE ALTITUDE", + "Feet"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "PLANE ALT ABOVE GROUND", "Feet"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PRESSURE ALTITUDE", + "Meters"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "STATIC CG TO GROUND", "Feet"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "PLANE HEADING DEGREES TRUE", "Degrees"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "PLANE PITCH DEGREES", "Degrees"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "PLANE BANK DEGREES", + "Degrees"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "GROUND VELOCITY", + "Knots"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "GROUND ALTITUDE", + "Feet"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "SIM ON GROUND", + "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT STROBE", + "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT LANDING", + "Bool"); + hr += + SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT TAXI", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT BEACON", + "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT NAV", "Bool"); + hr += + SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT LOGO", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT RECOGNITION", + "Bool"); + hr += + SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT CABIN", "Bool"); + hr += + SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "LIGHT WING", "Bool"); + + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "TRANSPONDER CODE:1", + nullptr); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "COM ACTIVE FREQUENCY:1", "MHz"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "COM ACTIVE FREQUENCY:2", "MHz"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "COM STANDBY FREQUENCY:1", "MHz"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "COM STANDBY FREQUENCY:2", "MHz"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM TRANSMIT:1", + "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM TRANSMIT:2", + "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM RECEIVE ALL", + "Bool"); + hr += + SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM TEST:1", "Bool"); + hr += + SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM TEST:2", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM STATUS:1", + "Enum"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "COM STATUS:2", + "Enum"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "FLAPS HANDLE PERCENT", "Percent Over 100"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "SPOILERS HANDLE POSITION", "Percent Over 100"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "GEAR HANDLE POSITION", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "NUMBER OF ENGINES", + "Number"); + // Simconnect supports index 1 - 4 + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "GENERAL ENG COMBUSTION:1", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "GENERAL ENG COMBUSTION:2", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "GENERAL ENG COMBUSTION:3", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "GENERAL ENG COMBUSTION:4", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "GENERAL ENG COMBUSTION:5", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "GENERAL ENG COMBUSTION:6", "Bool"); + + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "VELOCITY WORLD X", + "Feet per second"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "VELOCITY WORLD Y", + "Feet per second"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, "VELOCITY WORLD Z", + "Feet per second"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "ROTATION VELOCITY BODY X", "Radians per second"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "ROTATION VELOCITY BODY Y", "Radians per second"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "ROTATION VELOCITY BODY Z", "Radians per second"); + // FS2020 + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraft, + "INDICATED ALTITUDE CALIBRATED", "Feet"); + + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "TITLE", + nullptr, SIMCONNECT_DATATYPE_STRING256); + // MSFS2024 specific from here + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "LIVERY NAME", + nullptr, SIMCONNECT_DATATYPE_STRING256); + + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)).error(u"SimConnect error: initOwnAircraft %1") + << hr; + } + return hr; + } + + HRESULT CSimConnectDefinitions::initRemoteAircraft(const HANDLE hSimConnect) + { + HRESULT hr = s_ok(); + // Position + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetPosition, + "Initial Position", nullptr, SIMCONNECT_DATATYPE_INITPOSITION); + + // Hint: "Bool" and "Percent .." are units name + // default data type is SIMCONNECT_DATATYPE_FLOAT64 -> double + + // Flaps + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "LEADING EDGE FLAPS LEFT PERCENT", "Percent Over 100"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "LEADING EDGE FLAPS RIGHT PERCENT", "Percent Over 100"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "TRAILING EDGE FLAPS LEFT PERCENT", "Percent Over 100"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "TRAILING EDGE FLAPS RIGHT PERCENT", "Percent Over 100"); + + // Gear & Spoiler + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "GEAR HANDLE POSITION", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "SPOILERS HANDLE POSITION", "Percent Over 100"); + + // Engines + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "GENERAL ENG COMBUSTION:1", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "GENERAL ENG COMBUSTION:2", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "GENERAL ENG COMBUSTION:3", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "GENERAL ENG COMBUSTION:4", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "GENERAL ENG COMBUSTION:5", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + "GENERAL ENG COMBUSTION:6", "Bool"); + + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "TITLE", + nullptr, SIMCONNECT_DATATYPE_STRING256); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "LIVERY NAME", + nullptr, SIMCONNECT_DATATYPE_STRING256); + + // Lights (other definition) + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, + "LIGHT STROBE", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, + "LIGHT LANDING", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, + "LIGHT TAXI", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, + "LIGHT BEACON", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, "LIGHT NAV", + "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, + "LIGHT LOGO", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, + "LIGHT RECOGNITION", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, + "LIGHT CABIN", "Bool"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftLights, + "LIGHT WING", "Bool"); + + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: initRemoteAircraftSituation %1") + << hr; + } + return hr; + } + + HRESULT CSimConnectDefinitions::initRemoteAircraftSimData(const HANDLE hSimConnect) + { + HRESULT hr = s_ok(); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition, + "PLANE LATITUDE", "degrees"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition, + "PLANE LONGITUDE", "degrees"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition, + "PLANE ALTITUDE", "Feet"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition, + "GROUND ALTITUDE", "Feet"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftGetPosition, + "STATIC CG TO GROUND", "Feet"); + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: initRemoteAircraftSimData DataRemoteAircraftGetPosition %1") + << hr; + } + + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, + "STATIC CG TO GROUND", "Feet"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, + "ATC TYPE", nullptr, SIMCONNECT_DATATYPE_STRING32); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, + "ATC MODEL", nullptr, SIMCONNECT_DATATYPE_STRING32); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "ATC ID", + nullptr, SIMCONNECT_DATATYPE_STRING32); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, + "ATC AIRLINE", nullptr, SIMCONNECT_DATATYPE_STRING64); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, + "ATC FLIGHT NUMBER", nullptr, SIMCONNECT_DATATYPE_STRING8); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "TITLE", + nullptr, SIMCONNECT_DATATYPE_STRING256); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, + "LIVERY NAME", nullptr, SIMCONNECT_DATATYPE_STRING256); + + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: initRemoteAircraftSimData DataRemoteAircraftModelData %1") + << hr; + } + return hr; + } + + HRESULT CSimConnectDefinitions::initRemoteAircraftSimDataSet(const HANDLE hSimConnect) + { + HRESULT hr = s_ok(); + + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetData, "ATC ID", + nullptr, SIMCONNECT_DATATYPE_STRING32); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetData, + "ATC AIRLINE", nullptr, SIMCONNECT_DATATYPE_STRING64); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetData, + "ATC FLIGHT NUMBER", nullptr, SIMCONNECT_DATATYPE_STRING8); + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: initRemoteAircraftSimDataSet DataRemoteAircraftModelData %1") + << hr; + } + return hr; + } + + HRESULT CSimConnectDefinitions::initSimulatorEnvironment(const HANDLE hSimConnect) + { + HRESULT hr = s_ok(); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataSimEnvironment, "ZULU TIME", "", + SIMCONNECT_DATATYPE_INT32); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataSimEnvironment, "ZULU YEAR", "", + SIMCONNECT_DATATYPE_INT32); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataSimEnvironment, + "ZULU MONTH OF YEAR", "", SIMCONNECT_DATATYPE_INT32); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataSimEnvironment, + "ZULU DAY OF MONTH", "", SIMCONNECT_DATATYPE_INT32); + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: initSimulatorEnvironment %1") + << hr; + } + return hr; + } + + HRESULT CSimConnectDefinitions::initSbDataArea(const HANDLE hSimConnect) + { + HRESULT hr = s_ok(); + const DWORD sbSize = sizeof(DataDefinitionClientAreaSb); + + // We need to know the client area 'name' and map it to a client ID + hr += SimConnect_MapClientDataNameToID(hSimConnect, "SquawkBox Data", ClientAreaSquawkBox); + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: SimConnect_MapClientDataNameToID %1") + << hr; + return hr; + } + + // Mapping needs to be first + hr += SimConnect_CreateClientData(hSimConnect, ClientAreaSquawkBox, sbSize, + SIMCONNECT_CREATE_CLIENT_DATA_FLAG_DEFAULT); + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: SimConnect_CreateClientData %1") + << hr; + return hr; + } + + // data definitions + hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSb, 0, + sbSize); // whole area + hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSbStandby, 17, + 1); // standby + hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSbIdent, 19, + 1); // ident + hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSbConnected, 1, + 1); // network connected + hr += SimConnect_AddToClientDataDefinition(hSimConnect, CSimConnectDefinitions::DataClientAreaSbRunning, 0, + 1); // SB running + + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: SB data area data definitions %1") + << hr; + return hr; + } + + // write a default client area so we are not suddenly squawking ident or so + DataDefinitionClientAreaSb sbArea; + byte sbRunning = 1; + sbArea.setDefaultValues(); + hr += SimConnect_SetClientData(hSimConnect, ClientAreaSquawkBox, CSimConnectDefinitions::DataClientAreaSb, + SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, sbSize, &sbArea); + hr += + SimConnect_SetClientData(hSimConnect, ClientAreaSquawkBox, CSimConnectDefinitions::DataClientAreaSbRunning, + SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, 1, &sbRunning); + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: SimConnect_SetClientData %1") + << hr; + } + return hr; + } + + HRESULT CSimConnectDefinitions::initMSFS2024Transponder(const HANDLE hSimConnect) + { + HRESULT hr = s_ok(); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS, + "TRANSPONDER STATE:1", "Enum"); + hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS, + "TRANSPONDER IDENT:1", "Bool"); + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: MSFS2024 transponder data definitions %1") + << hr; + } + return hr; + } + + HRESULT CSimConnectDefinitions::initOwnAircraftList(const HANDLE hSimConnect) + { + // MSFS2024 vars: + // https://docs.flightsimulator.com/msfs2024/html/6_Programming_APIs/SimVars/Simulation_Variables.htm + HRESULT hr = s_ok(); + hr += SimConnect_EnumerateSimObjectsAndLiveries(hSimConnect, CSimConnectDefinitions::REQUEST_AIRPLANE, + SIMCONNECT_SIMOBJECT_TYPE_AIRCRAFT); + hr += SimConnect_EnumerateSimObjectsAndLiveries(hSimConnect, CSimConnectDefinitions::REQUEST_HELICOPTER, + SIMCONNECT_SIMOBJECT_TYPE_HELICOPTER); + // hr += SimConnect_EnumerateSimObjectsAndLiveries(hSimConnect, CSimConnectDefinitions::REQUEST_HOT_AIR, + // SIMCONNECT_SIMOBJECT_TYPE_HOT_AIR_BALLOON); + + if (isFailure(hr)) + { + CLogMessage(static_cast(nullptr)) + .error(u"SimConnect error: initOwnAircraftList %1") + << hr; + } + return hr; + } + + DataDefinitionRemoteAircraftPartsWithoutLights::DataDefinitionRemoteAircraftPartsWithoutLights() + { + this->resetToInvalid(); + } + + DataDefinitionRemoteAircraftPartsWithoutLights::DataDefinitionRemoteAircraftPartsWithoutLights( + const CAircraftParts &parts) + { + this->initFromParts(parts); + } + + bool DataDefinitionRemoteAircraftPartsWithoutLights::operator==( + const DataDefinitionRemoteAircraftPartsWithoutLights &rhs) const + { + return std::tie(flapsLeadingEdgeLeftPercent, flapsLeadingEdgeRightPercent, flapsTrailingEdgeLeftPercent, + flapsTrailingEdgeRightPercent, gearHandlePosition, spoilersHandlePosition, engine1Combustion, + engine2Combustion, engine3Combustion, engine4Combustion, engine5Combustion, + engine6Combustion) == + std::tie(rhs.flapsLeadingEdgeLeftPercent, rhs.flapsLeadingEdgeRightPercent, + rhs.flapsTrailingEdgeLeftPercent, rhs.flapsTrailingEdgeRightPercent, rhs.gearHandlePosition, + rhs.spoilersHandlePosition, rhs.engine1Combustion, rhs.engine2Combustion, rhs.engine3Combustion, + rhs.engine4Combustion, rhs.engine5Combustion, rhs.engine6Combustion); + } + + void DataDefinitionRemoteAircraftPartsWithoutLights::setAllEngines(bool on) + { + engine1Combustion = on ? 1 : 0; + engine2Combustion = on ? 1 : 0; + engine3Combustion = on ? 1 : 0; + engine4Combustion = on ? 1 : 0; + engine5Combustion = on ? 1 : 0; + engine6Combustion = on ? 1 : 0; + } + + void DataDefinitionRemoteAircraftPartsWithoutLights::setEngine(int number1based, bool on) + { + double v = on ? 1.0 : 0.0; + switch (number1based) + { + case 1: engine1Combustion = v; break; + case 2: engine2Combustion = v; break; + case 3: engine3Combustion = v; break; + case 4: engine4Combustion = v; break; + case 5: engine5Combustion = v; break; + case 6: engine6Combustion = v; break; + default: break; + } + } + + void DataDefinitionRemoteAircraftPartsWithoutLights::resetAllFlaps() + { + flapsLeadingEdgeLeftPercent = 0.0; + flapsLeadingEdgeRightPercent = 0.0; + flapsTrailingEdgeLeftPercent = 0.0; + flapsTrailingEdgeRightPercent = 0.0; + } + + void DataDefinitionRemoteAircraftPartsWithoutLights::resetSpoilers() { spoilersHandlePosition = 0.0; } + + void DataDefinitionRemoteAircraftPartsWithoutLights::resetToInvalid() + { + flapsLeadingEdgeLeftPercent = -1; + flapsLeadingEdgeRightPercent = -1; + flapsTrailingEdgeLeftPercent = -1; + flapsTrailingEdgeRightPercent = -1; + gearHandlePosition = -1; + spoilersHandlePosition = -1; + engine1Combustion = -1; + engine2Combustion = -1; + engine3Combustion = -1; + engine4Combustion = -1; + engine5Combustion = -1; + engine6Combustion = -1; + } + + void DataDefinitionRemoteAircraftPartsWithoutLights::initFromParts(const CAircraftParts &parts) + { + gearHandlePosition = parts.isFixedGearDown() ? 1.0 : 0.0; + const double trail = parts.getFlapsPercent() / 100.0; + const double lead = trail; + flapsTrailingEdgeLeftPercent = trail; + flapsTrailingEdgeRightPercent = trail; + flapsLeadingEdgeLeftPercent = lead; + flapsLeadingEdgeRightPercent = lead; + spoilersHandlePosition = parts.isSpoilersOut() ? 1.0 : 0.0; + this->setAllEngines(false); // init + + int e = 1; + for (const CAircraftEngine &engine : parts.getEngines()) { this->setEngine(e++, engine.isOn()); } + } + + CAircraftLights DataDefinitionRemoteAircraftLights::toLights() const + { + return CAircraftLights(dtb(lightStrobe), dtb(lightLanding), dtb(lightTaxi), dtb(lightBeacon), dtb(lightNav), + dtb(lightLogo), dtb(lightRecognition), dtb(lightCabin), dtb(lightWing)); + } + + QString DataDefinitionClientAreaSb::toQString() const + { + return u"0 (running): " % QString::number(data[0]) % u" 1 (connected): " % QString::number(data[1]) % + u" 17 (standby): " % QString::number(data[17]) % u" 19 (ident): " % QString::number(data[19]); + } + +} // namespace swift::simplugin::msfs2024common diff --git a/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h b/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h new file mode 100644 index 000000000..c1d1e730e --- /dev/null +++ b/src/plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h @@ -0,0 +1,391 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +//! \file + +#ifndef SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECT_DATADEFINITION_H +#define SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECT_DATADEFINITION_H + +#include + +#include +#include + +#include "misc/aviation/aircraftlights.h" +#include "misc/simulation/simulatorinfo.h" +#include "plugins/simulator/msfs2024/msfs2024export.h" +#include "plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h" +#include "plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h" + +namespace swift::misc::aviation +{ + class CAircraftParts; +} +namespace swift::simplugin::msfs2024common +{ + //! Data struct of our own aircraft + //! \sa SimConnect variables http://msdn.microsoft.com/en-us/library/cc526981.aspx + //! \sa SimConnect events http://msdn.microsoft.com/en-us/library/cc526980.aspx + struct DataDefinitionOwnAircraft + { + double latitudeDeg; //!< Latitude (deg) + double longitudeDeg; //!< Longitude (deg) + double altitudeFt; //!< Altitude (ft) + double altitudeAGLFt; //!< Altitude above ground (ft) + double pressureAltitudeM; //!< Pressure altitude (m) + double cgToGroundFt; //!< Static CG to ground (ft) + double trueHeadingDeg; //!< True heading (deg) + double pitchDeg; //!< Pitch (deg) + double bankDeg; //!< Bank (deg) + double velocity; //!< Ground velocity + double elevationFt; //!< Elevation (ft) + double simOnGround; //!< Is aircraft on ground? + // 12 + double lightStrobe; //!< Is strobe light on? + double lightLanding; //!< Is landing light on? + double lightTaxi; //!< Is taxi light on? + double lightBeacon; //!< Is beacon light on? + double lightNav; //!< Is nav light on? + double lightLogo; //!< Is logo light on? + double lightRecognition; + double lightCabin; //!< Is cabin light on? + double lightWing; //!< Is wing light on? + // 21 + double transponderCode; //!< Transponder Code + double com1ActiveMHz; //!< COM1 active frequency + double com2ActiveMHz; //!< COM2 active frequency + double com1StandbyMHz; //!< COM1 standby frequency + double com2StandbyMHz; //!< COM2 standby frequency + double comTransmit1; //!< COM1 transmit, means also receiving + double comTransmit2; //!< COM2 transmit, means also receiving + double comReceiveAll; //!< all COMs receiving, or COM:x transmitting or receiving + double comTest1; //!< COM1 test + double comTest2; //!< COM2 test + double comStatus1; //!< COM1 status + double comStatus2; //!< COM2 status + // 33 + double flapsHandlePosition; //!< Flaps handle position in percent + double spoilersHandlePosition; //!< Spoilers out? (flag) + double gearHandlePosition; //!< Gear handle position (flag) + // 36 + double numberOfEngines; //!< Number of engines + double engine1Combustion; //!< Engine 1 combustion flag + double engine2Combustion; //!< Engine 2 combustion flag + double engine3Combustion; //!< Engine 3 combustion flag + double engine4Combustion; //!< Engine 4 combustion flag + double engine5Combustion; //!< Engine 5 combustion flag + double engine6Combustion; //!< Engine 6 combustion flag + // 43 + double velocityWorldX; //!< Velocity World X + double velocityWorldY; //!< Velocity World Y + double velocityWorldZ; //!< Velocity World Z + double rotationVelocityBodyX; //!< Rotation Velocity Body X + double rotationVelocityBodyY; //!< Rotation Velocity Body Y + double rotationVelocityBodyZ; //!< Rotation Velocity Body Z + // 49 + double altitudeCalibratedFt; //!< Altitude without temperature effect (ft, FS2020) + // 50 + }; + + //! Data struct of aircraft position + struct DataDefinitionOwnAircraftModel + { + char title[256]; //!< Aircraft model string + char livery[256]; //!< Aircraft livery string msfs2024 + }; + + //! Data struct of aircraft model data + + // struct DataDefinitionOwnAircraftLivery + //{ + // char livery[256]; //!< Aircraft model string + // }; + + ////! Data struct of aircraft model livery + + struct DataDefinitionRemoteAircraftModel + { + double cgToGroundFt; //!< Static CG to ground (ft) + char atcType[32]; //!< type + char atcModel[32]; //!< model + char atcId[32]; //!< id + char atcAirlineNumber[64]; //!< airline number + char atcFlightNumber[8]; //!< flight number (168) + char title[256]; //!< Aircraft model string + char livery[256]; //!< Aircraft livery string msfs2024 + }; + + //! Data struct of aircraft data (setable) + struct DataDefinitionRemoteAtc + { + // length here is from SimConnect_AddToDataDefinition + char atcId[32]; //!< ID used by ATC + char atcAirline[64]; //!< Airline used by ATC + char atcFlightNumber[8]; //!< Flight Number used by ATC + + //! @{ + //! Copy the strings, length from docu + void copyAtcId(const char *c) + { + strncpy_s(atcId, c, 10); + atcId[9] = 0; + } + void copyAtcAirline(const char *c) + { + strncpy_s(atcAirline, c, 50); + atcAirline[49] = 0; + } + void copyFlightNumber(const char *c) + { + strncpy_s(atcFlightNumber, c, 6); + atcFlightNumber[5] = 0; + } + //! @} + + //! Set default values + void setDefaultValues() + { + std::fill(atcId, atcId + 10, static_cast(0)); + std::fill(atcAirline, atcAirline + 50, static_cast(0)); + std::fill(atcFlightNumber, atcFlightNumber + 6, static_cast(0)); + } + }; + + //! Data struct of remote aircraft parts + struct MSFS2024_EXPORT DataDefinitionRemoteAircraftPartsWithoutLights + { + double flapsLeadingEdgeLeftPercent; //!< Leading edge left in percent 0..1 + double flapsLeadingEdgeRightPercent; //!< Leading edge right in percent 0..1 + double flapsTrailingEdgeLeftPercent; //!< Trailing edge left in percent 0..1 + double flapsTrailingEdgeRightPercent; //!< Trailing edge right in percent 0..1 + double gearHandlePosition; //!< Gear handle position + double spoilersHandlePosition; //!< Spoilers out? + double engine1Combustion; //!< Engine 1 combustion flag + double engine2Combustion; //!< Engine 2 combustion flag + double engine3Combustion; //!< Engine 3 combustion flag + double engine4Combustion; //!< Engine 4 combustion flag + double engine5Combustion; //!< Engine 5 combustion flag + double engine6Combustion; //!< Engine 6 combustion flag + + //! Ctor + DataDefinitionRemoteAircraftPartsWithoutLights(); + + //! Ctor + DataDefinitionRemoteAircraftPartsWithoutLights(const swift::misc::aviation::CAircraftParts &parts); + + //! Equal to other parts + bool operator==(const DataDefinitionRemoteAircraftPartsWithoutLights &rhs) const; + + //! All engines on/off + void setAllEngines(bool on); + + //! Set given engine + void setEngine(int number1based, bool on); + + //! Reset all flaps + void resetAllFlaps(); + + //! Reset spoilers + void resetSpoilers(); + + //! Reset to invalid values + void resetToInvalid(); + + //! Init from parts + void initFromParts(const swift::misc::aviation::CAircraftParts &parts); + }; + + //! Data for aircraft lighs + struct MSFS2024_EXPORT DataDefinitionRemoteAircraftLights + { + double lightStrobe; //!< Is strobe light on? + double lightLanding; //!< Is landing light on? + double lightTaxi; //!< Is taxi light on? + double lightBeacon; //!< Is beacon light on? + double lightNav; //!< Is nav light on? + double lightLogo; //!< Is logo light on? + double lightRecognition; //!< Is recognition light on + double lightCabin; //!< Is cabin light on + double lightWing; //!< Is cabin light on + //! Convert to lights + swift::misc::aviation::CAircraftLights toLights() const; + }; + + //! Data for AI object and probe sent back from simulator + struct DataDefinitionPosData + { + double latitudeDeg; //!< Latitude (deg) + double longitudeDeg; //!< Longitude (deg) + double altitudeFt; //!< Altitude (ft) + double elevationFt; //!< Elevation (ft) + double cgToGroundFt; //!< Static CG to ground (ft) + + //! Above ground ft + double aboveGroundFt() const { return altitudeFt - elevationFt; } + + //! Above ground ft + bool isOnGround() const { return this->aboveGroundFt() < 1.0; } + }; + + //! The whole SB data area + //! \remark vPilot SB area https://forums.vatsim.net/viewtopic.php?p=519580 + //! \remark SB offsets http://www.squawkbox.ca/doc/sdk/fsuipc.php + struct DataDefinitionClientAreaSb + { + byte data[128] {}; //!< 128 bytes of data, offsets + + //! Standby = 1, else 0 + byte getTransponderMode() const { return data[17]; } + + //! Ident = 1, else 0 + byte getIdent() const { return data[19]; } + + //! Ident? + bool isIdent() const { return getIdent() != 0; } + + //! Standby + bool isStandby() const { return getTransponderMode() != 0; } + + //! SB is running + void setRunning(bool running) { data[0] = running ? 1 : 0; } + + //! Mark as connected with network + void setConnected(bool connected) { data[1] = connected ? 1 : 0; } + + //! Set default values + void setDefaultValues() + { + std::fill(data, data + 128, static_cast(0)); + data[0] = 1; // SB running, indicates the client is running as external app, 0..not running, 1..external + // app, 2..FS module + data[1] = 0; // SB connected to FSD, 0..not connected, 1..connected + data[17] = 1; // 1..standby, 0..mode C + data[19] = 0; // no ident + } + + //! Values + QString toQString() const; + }; + + //! Data structure for MSFS transponder mode information + struct DataDefinitionMSFSTransponderMode + { + double transponderMode = 1; //!< transponder state simvar + double ident = 0; //!< ident + }; + + //! Client areas + enum ClientAreaId + { + ClientAreaSquawkBox + }; + + //! Handles SimConnect data definitions for MSFS2024 + class MSFS2024_EXPORT CSimConnectDefinitions + { + public: + //! SimConnect definiton IDs + enum DataDefiniton + { + DataOwnAircraft, + DataOwnAircraftTitle, + DataRemoteAircraftLights, + DataRemoteAircraftPartsWithoutLights, + DataRemoteAircraftSetPosition, //!< the position which will be set + DataRemoteAircraftGetPosition, //!< get position to evaluate altitude / AGL + DataRemoteAircraftModelData, //!< model data eventually used and reported back from simulator + DataRemoteAircraftSetData, //!< set model data such as airline + DataSimEnvironment, + DataTransponderModeMSFS, + DataClientAreaSb, //!< whole SB area, see http://squawkbox.ca/doc/sdk/fsuipc.php + DataClientAreaSbIdent, //!< SB ident single value 0x7b93/19 + DataClientAreaSbStandby, //!< SB standby 0x7b91/17 + DataClientAreaSbConnected, //!< SB connected with network 0x7b81/1 + DataClientAreaSbRunning //!< SB running 0x7b80/0 + }; + + //! SimConnect request IDs + enum Request + { + RequestOwnAircraft, + RequestOwnAircraftTitle, + RequestOwnAircraftLivery, + RequestSbData, //!< SB client area / XPDR mode + RequestMSFSTransponder, //!< MSFS XPDR mode/ident + RequestFacility, + RequestEndMarker, //!< free request ids can start here + + }; + + //! SimObject requests used for AI aircraft and probes + enum SimObjectRequest + { + SimObjectBaseId, //!< base id without specific request + SimObjectAdd, + SimObjectRemove, + SimObjectPositionData, + SimObjectLights, + SimObjectModel, + SimObjectMisc, + SimObjectEndMarker //!< end marker, do NOT remove, also means invalid + }; + + enum REQUEST_ID + { + REQUEST_NONE = 0, + REQUEST_POSITION_USER = 50, + REQUEST_CREATE = 100, + REQUEST_ALL = 1000, + REQUEST_USER, + REQUEST_AIRPLANE, + REQUEST_HELICOPTER, + REQUEST_GROUND, + REQUEST_ANIMAL, + REQUEST_HOT_AIR, + REQUEST_BOAT, + }; + + //! Request to string + static const QString &requestToString(Request request); + + //! Request to string + static const QString &simObjectRequestToString(SimObjectRequest simObjectRequest); + + //! Constructor + CSimConnectDefinitions(); + + //! Initialize all data definitions + static HRESULT initDataDefinitionsWhenConnected(const HANDLE hSimConnect, + const swift::misc::simulation::CSimulatorInfo &simInfo); + + //! Initialize data retrieval for model list + static HRESULT initOwnAircraftList(const HANDLE hSimConnect); + + private: + //! Initialize data definition for our own aircraft + static HRESULT initOwnAircraft(const HANDLE hSimConnect); + + //! Initialize data definition for remote aircraft + static HRESULT initRemoteAircraft(const HANDLE hSimConnect); + + //! Initialize data for setting remote aircraft airline etc. + static HRESULT initRemoteAircraftSimData(const HANDLE hSimConnect); + + //! Initialize data for remote aircraft queried from simulator + static HRESULT initRemoteAircraftSimDataSet(const HANDLE hSimConnect); + + //! Initialize data definition for Simulator environment + static HRESULT initSimulatorEnvironment(const HANDLE hSimConnect); + + //! Initialize the SB data are + static HRESULT initSbDataArea(const HANDLE hSimConnect); + + //! Initialize data definition for MSFS transponder + // static HRESULT initMSFSTransponder(const HANDLE hSimConnect); + + //! Initialize data definition for MSFS transponder + static HRESULT initMSFS2024Transponder(const HANDLE hSimConnect); + }; +} // namespace swift::simplugin::msfs2024common + +#endif // SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECT_DATADEFINITION_H diff --git a/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.cpp b/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.cpp new file mode 100644 index 000000000..77f20d06f --- /dev/null +++ b/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.cpp @@ -0,0 +1,520 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +#include "simconnectobjectmsfs2024.h" + +#include "simulatormsfs2024common.h" + +#include "config/buildconfig.h" +#include "core/simulator.h" +#include "misc/simulation/interpolation/interpolatormulti.h" +#include "misc/stringutils.h" + +using namespace swift::config; +using namespace swift::misc; +using namespace swift::misc::aviation; +using namespace swift::misc::simulation; +using namespace swift::core; + +namespace swift::simplugin::msfs2024common +{ + CSimConnectObject::CSimConnectObject() { this->resetCameraPositions(); } + + CSimConnectObject::CSimConnectObject(CSimConnectObject::SimObjectType type) : m_type(type) + { + this->resetCameraPositions(); + } + + CSimConnectObject::CSimConnectObject(const CSimulatedAircraft &aircraft, DWORD requestId, + ISimulationEnvironmentProvider *simEnvProvider, + IInterpolationSetupProvider *setupProvider, + IRemoteAircraftProvider *remoteAircraftProvider, CInterpolationLogger *logger) + : m_aircraft(aircraft), m_requestId(requestId), m_validRequestId(true), + m_interpolator(QSharedPointer::create(aircraft.getCallsign(), simEnvProvider, + setupProvider, remoteAircraftProvider, logger)) + { + this->resetCameraPositions(); + m_type = AircraftNonAtc; + m_interpolator->initCorrespondingModel(aircraft.getModel()); + m_callsignByteArray = aircraft.getCallsignAsString().toLatin1(); + } + + void CSimConnectObject::setAircraft(const CSimulatedAircraft &aircraft) + { + m_aircraft = aircraft; + m_callsignByteArray = aircraft.getCallsignAsString().toLatin1(); + m_type = AircraftNonAtc; + } + + void CSimConnectObject::setAircraftModelString(const QString &modelString) + { + if (modelString.isEmpty()) { return; } + m_aircraft.setModelString(modelString); + } + + void CSimConnectObject::setAircraftCG(const physical_quantities::CLength &cg) + { + if (cg.isNull()) { return; } + m_aircraft.setCG(cg); + } + + void CSimConnectObject::setRequestId(DWORD id) + { + m_requestId = id; + m_validRequestId = true; + const SimObjectType type = requestIdToType(id); + this->setType(type); + } + + DWORD CSimConnectObject::getRequestId(CSimConnectDefinitions::SimObjectRequest offset) const + { + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + const SimObjectType type = requestIdToType(m_requestId); + const bool same = CSimConnectObject::isSameTypeGroup(type, this->getType()); + Q_ASSERT_X(same, Q_FUNC_INFO, "Type mismatch"); + } + + DWORD os = 0; + switch (this->getType()) + { + case AircraftNonAtc: + case AircraftSimulatedObject: + default: os = static_cast(CSimulatorMsfs2024::offsetSimObjAircraft(offset)); break; + } + return os + m_requestId; + } + + void CSimConnectObject::setObjectId(DWORD id) + { + m_objectId = id; + m_validObjectId = true; + } + + bool CSimConnectObject::isPendingAdded() const { return !this->hasValidRequestAndObjectId() || !m_confirmedAdded; } + + bool CSimConnectObject::isOutdatedPendingAdded(qint64 thresholdMs, qint64 currentMsSinceEpoch) const + { + if (!this->isPendingAdded()) { return false; } + if (currentMsSinceEpoch < 0) { currentMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); } + if (m_tsCreated < 0) { return true; } // no valid timestamp + const qint64 delta = currentMsSinceEpoch - m_tsCreated; + return delta > thresholdMs; + } + + bool CSimConnectObject::isConfirmedAdded() const + { + Q_ASSERT_X(!m_confirmedAdded || this->hasValidRequestAndObjectId(), Q_FUNC_INFO, "confirmed but invalid ids"); + return m_confirmedAdded; + } + + void CSimConnectObject::setConfirmedAdded(bool confirm) + { + m_confirmedAdded = confirm; + m_removedWhileAdding = false; + m_addedWhileRemoving = false; + m_aircraft.setRendered(true); + } + + void CSimConnectObject::setAddedWhileRemoving(bool addedWileRemoved) { m_addedWhileRemoving = addedWileRemoved; } + + void CSimConnectObject::setRemovedWhileAdding(bool removedWhileAdding) + { + m_removedWhileAdding = removedWhileAdding; + } + + bool CSimConnectObject::isReadyToSend() const + { + return !this->isPending() && !m_addedWhileRemoving && !m_removedWhileAdding; + } + + void CSimConnectObject::setPendingRemoved(bool pending) + { + m_pendingRemoved = pending; + m_removedWhileAdding = false; + m_addedWhileRemoving = false; + m_aircraft.setRendered(false); + } + + void CSimConnectObject::resetCameraPositions() + { + m_cameraPosition.x = 0; + m_cameraPosition.y = 0; + m_cameraPosition.z = 0; + m_cameraRotation.Pitch = 0; + m_cameraRotation.Bank = 0; + m_cameraRotation.Heading = 0; + } + + void CSimConnectObject::resetState() + { + m_pendingRemoved = false; + m_confirmedAdded = false; + m_removedWhileAdding = false; + m_addedWhileRemoving = false; + m_camera = false; + m_currentLightsInSim = CAircraftLights(); + m_lightsAsSent = CAircraftLights(); + m_requestId = 0; + m_objectId = 0; + m_addingExceptions = 0; + m_validRequestId = false; + m_validObjectId = false; + m_tsCreated = -1; + this->resetCameraPositions(); + } + + void CSimConnectObject::resetToAddAgain() + { + const CSimConnectObject old(*this); + this->resetState(); + this->copyAddingFailureCounters(old); + } + + bool CSimConnectObject::hasValidRequestAndObjectId() const + { + return this->hasValidRequestId() && this->hasValidObjectId(); + } + + void CSimConnectObject::copyAddingFailureCounters(const CSimConnectObject &otherObject) + { + m_addingExceptions = otherObject.m_addingExceptions; + m_addingDirectlyRemoved = otherObject.m_addingDirectlyRemoved; + } + + QString CSimConnectObject::getInterpolatorInfo(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const + { + Q_ASSERT(m_interpolator); + return m_interpolator->getInterpolatorInfo(mode); + } + + void CSimConnectObject::attachInterpolatorLogger(CInterpolationLogger *logger) const + { + Q_ASSERT(m_interpolator); + m_interpolator->attachLogger(logger); + } + + CInterpolationResult CSimConnectObject::getInterpolation(qint64 currentTimeSinceEpoch, + const CInterpolationAndRenderingSetupPerCallsign &setup, + uint32_t aircraftNumber) const + { + if (!m_interpolator) { return {}; } + return m_interpolator->getInterpolation(currentTimeSinceEpoch, setup, aircraftNumber); + } + + const CAircraftSituation & + CSimConnectObject::getLastInterpolatedSituation(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const + { + if (!m_interpolator) { return CAircraftSituation::null(); } + return m_interpolator->getLastInterpolatedSituation(mode); + } + + const CStatusMessageList & + CSimConnectObject::getInterpolationMessages(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const + { + static const CStatusMessageList empty; + if (!m_interpolator) { return empty; } + return m_interpolator->getInterpolationMessages(mode); + } + + QString CSimConnectObject::toQString() const + { + static const QString s( + "CS: '%1' obj: %2 req: %3 conf.added: %4 pend.rem.: %5 rwa: %6 awr: %7 aEx: %8 aRem: %9"); + return s.arg(this->getCallsign().asString()) + .arg(m_objectId) + .arg(m_requestId) + .arg(boolToYesNo(m_confirmedAdded), boolToYesNo(m_pendingRemoved), boolToYesNo(m_removedWhileAdding), + boolToYesNo(m_addedWhileRemoving)) + .arg(m_addingExceptions) + .arg(m_addingDirectlyRemoved); + } + + CSimConnectObject::SimObjectType CSimConnectObject::requestIdToType(DWORD requestId) + { + if (CSimulatorMsfs2024::isRequestForSimObjAircraft(requestId)) { return AircraftNonAtc; } + Q_ASSERT_X(false, Q_FUNC_INFO, "Wrong range"); + return AircraftNonAtc; + } + + const QString &CSimConnectObject::typeToString(CSimConnectObject::SimObjectType type) + { + static const QString a1("aircraft (non ATC)"); + static const QString a2("aircraft (sim.object)"); + // static const QString p("probe"); + static const QString u("unknown"); + switch (type) + { + case AircraftNonAtc: return a1; + case AircraftSimulatedObject: return a2; + default: break; + } + return u; + } + + bool CSimConnectObject::isSameTypeGroup(CSimConnectObject::SimObjectType t1, CSimConnectObject::SimObjectType t2) + { + if (t1 == t2) { return true; } + return isAircraft(t1) && isAircraft(t2); + } + + bool CSimConnectObject::isAircraft(CSimConnectObject::SimObjectType type) + { + return CSimConnectObject::AircraftNonAtc == type || CSimConnectObject::AircraftSimulatedObject; + } + + bool CSimConnectObjects::insert(const CSimConnectObject &simObject, bool updateTimestamp) + { + if (!simObject.hasCallsign()) { return false; } + if (updateTimestamp) + { + CSimConnectObject simObj(simObject); + simObj.resetTimestampToNow(); + (*this)[simObj.getCallsign()] = simObj; + } + else { (*this)[simObject.getCallsign()] = simObject; } + return true; + } + + bool CSimConnectObjects::setSimConnectObjectIdForRequestId(DWORD requestId, DWORD objectId) + { + // First check, if this request id belongs to us + auto it = std::find_if(this->begin(), this->end(), + [requestId](const CSimConnectObject &obj) { return obj.getRequestId() == requestId; }); + if (it == this->end()) { return false; } + + // belongs to us + it->setObjectId(objectId); + return true; + } + + CCallsign CSimConnectObjects::getCallsignForObjectId(DWORD objectId) const + { + return this->getSimObjectForObjectId(objectId).getCallsign(); + } + + // TODO TZ optimize? + CCallsignSet CSimConnectObjects::getAllCallsigns(bool withoutProbes) const + { + if (this->isEmpty()) { return CCallsignSet(); } + if (!withoutProbes) { return CCallsignSet(this->keys()); } + CCallsignSet callsigns; + for (const CSimConnectObject &simObject : *this) + { + if (simObject.isAircraft()) { callsigns.insert(simObject.getCallsign().asString()); } + } + return callsigns; + } + + // TODO TZ optimize? + QStringList CSimConnectObjects::getAllCallsignStrings(bool sorted, bool withoutProbes) const + { + return this->getAllCallsigns(withoutProbes).getCallsignStrings(sorted); + } + + QString CSimConnectObjects::getAllCallsignStringsAsString(bool sorted, const QString &separator) const + { + return this->getAllCallsignStrings(sorted).join(separator); + } + + CSimConnectObject CSimConnectObjects::getSimObjectForObjectId(DWORD objectId) const + { + for (const CSimConnectObject &simObject : *this) + { + if (simObject.getObjectId() == objectId) { return simObject; } + } + return CSimConnectObject(); + } + + CSimConnectObject CSimConnectObjects::getOldestObject() const + { + if (this->isEmpty()) { return CSimConnectObject(); } + CSimConnectObject oldestSimObj = *this->begin(); + for (const CSimConnectObject &simObj : *this) + { + if (!simObj.hasCreatedTimestamp()) { continue; } + if (!oldestSimObj.hasCreatedTimestamp() || + oldestSimObj.getCreatedTimestamp() > simObj.getCreatedTimestamp()) + { + oldestSimObj = simObj; + } + } + return oldestSimObj; + } + + CSimConnectObject CSimConnectObjects::getSimObjectForRequestId(DWORD requestId) const + { + for (const CSimConnectObject &simObject : *this) + { + if (simObject.getRequestId() == requestId) { return simObject; } + } + return CSimConnectObject(); + } + + CSimConnectObject CSimConnectObjects::getSimObjectForOtherSimObject(const CSimConnectObject &otherSimObj) const + { + if (otherSimObj.hasValidObjectId()) + { + CSimConnectObject obj = this->getSimObjectForObjectId(otherSimObj.getObjectId()); + if (!obj.isInvalid()) { return obj; } + } + if (!otherSimObj.hasValidRequestId()) { return CSimConnectObject(); } + return this->getSimObjectForRequestId(otherSimObj.getRequestId()); + } + + bool CSimConnectObjects::isKnownSimObjectId(DWORD objectId) const + { + const CSimConnectObject simObject(this->getSimObjectForObjectId(objectId)); + return simObject.hasValidRequestAndObjectId() && objectId == simObject.getObjectId(); + } + + bool CSimConnectObjects::removeByObjectId(DWORD objectId) + { + const CSimConnectObject simObject(this->getSimObjectForObjectId(objectId)); + const int c = this->remove(simObject.getCallsign()); + return c > 0; + } + + bool CSimConnectObjects::removeByOtherSimObject(const CSimConnectObject &otherSimObj) + { + const int c = this->remove(otherSimObj.getCallsign()); + return c > 0; + } + + // int CSimConnectObjects::removeAllProbes() + //{ + // const QList probes = this->getProbes(); + // int c = 0; + // for (const CSimConnectObject &probe : probes) + // { + // this->remove(probe.getCallsign()); + // c++; + // } + // return c; + // } + + bool CSimConnectObjects::containsPendingAdded() const + { + for (const CSimConnectObject &simObject : *this) + { + if (simObject.isPendingAdded()) { return true; } + } + return false; + } + + bool CSimConnectObjects::containsPendingRemoved() const + { + for (const CSimConnectObject &simObject : *this) + { + if (simObject.isPendingRemoved()) { return true; } + } + return false; + } + + int CSimConnectObjects::countPendingAdded() const + { + int c = 0; + for (const CSimConnectObject &simObject : *this) + { + if (simObject.isPendingAdded()) { c++; } + } + return c; + } + + int CSimConnectObjects::countPendingRemoved() const + { + int c = 0; + for (const CSimConnectObject &simObject : *this) + { + if (simObject.isPendingRemoved()) { c++; } + } + return c; + } + + int CSimConnectObjects::countConfirmedAdded() + { + int c = 0; + for (const CSimConnectObject &simObject : std::as_const(*this)) + { + if (simObject.isConfirmedAdded()) { c++; } + } + return c; + } + + CCallsignSet CSimConnectObjects::getPendingAddedCallsigns() const + { + CCallsignSet callsigns; + for (const CSimConnectObject &simObject : *this) + { + if (simObject.isPendingAdded()) { callsigns.push_back(simObject.getCallsign()); } + } + return callsigns; + } + + CCallsignSet CSimConnectObjects::getPendingRemovedCallsigns() const + { + CCallsignSet callsigns; + for (const CSimConnectObject &simObject : *this) + { + if (simObject.isPendingRemoved()) { callsigns.push_back(simObject.getCallsign()); } + } + return callsigns; + } + + QList CSimConnectObjects::getByType(CSimConnectObject::SimObjectType type) const + { + QList objs; + for (const CSimConnectObject &simObject : *this) + { + if (simObject.getType() == type) { objs.push_back(simObject); } + } + return objs; + } + + QList CSimConnectObjects::getAircraft() const + { + QList l = this->getByType(CSimConnectObject::AircraftNonAtc); + l.append(this->getByType(CSimConnectObject::AircraftSimulatedObject)); + return l; + } + + bool CSimConnectObjects::containsType(CSimConnectObject::SimObjectType type) const + { + for (const CSimConnectObject &simObject : *this) + { + if (simObject.getType() == type) { return true; } + } + return false; + } + + bool CSimConnectObjects::containsAircraft() const + { + return this->containsType(CSimConnectObject::AircraftNonAtc) || + this->containsType(CSimConnectObject::AircraftSimulatedObject); + } + + int CSimConnectObjects::removeCallsigns(const CCallsignSet &callsigns) + { + int c = 0; + for (const CCallsign &cs : callsigns) { c += this->remove(cs); } + return c; + } + + CSimConnectObjects CSimConnectObjects::removeOutdatedPendingAdded(CSimConnectObject::SimObjectType type) + { + CCallsignSet removeCallsigns; + CSimConnectObjects removedObjects; + + const qint64 ts = QDateTime::currentMSecsSinceEpoch(); + for (const CSimConnectObject &simObject : std::as_const(*this)) + { + // verification takes at least a second, so we need some time before outdating + if (type != CSimConnectObject::AllTypes && simObject.getType() != type) { continue; } + if (!simObject.isOutdatedPendingAdded(5000, ts)) { continue; } + removedObjects.insert(simObject); + removeCallsigns.insert(simObject.getCallsign()); + } + if (!removeCallsigns.isEmpty()) { this->removeCallsigns(removeCallsigns); } + return removedObjects; + } +} // namespace swift::simplugin::msfs2024common diff --git a/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.h b/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.h new file mode 100644 index 000000000..b94048c25 --- /dev/null +++ b/src/plugins/simulator/msfs2024/simconnectobjectmsfs2024.h @@ -0,0 +1,440 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +//! \file + +#ifndef SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTOBJECT_H +#define SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTOBJECT_H + +#include +#include +#include + +#include "misc/simulation/interpolation/interpolatormulti.h" +#include "misc/simulation/simulatedaircraft.h" +#include "plugins/simulator/msfs2024/msfs2024export.h" +#include "plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h" +#include "plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h" + +namespace swift::simplugin::msfs2024common +{ + //! Class representing a SimConnect object + class MSFS2024_EXPORT CSimConnectObject + { + public: + //! Type + enum SimObjectType + { + AircraftNonAtc, + AircraftSimulatedObject, + AllTypes + }; + + //! Constructor + CSimConnectObject(); + + //! Constructor + CSimConnectObject(SimObjectType type); + + //! Constructor providing initial situation/parts + CSimConnectObject(const swift::misc::simulation::CSimulatedAircraft &aircraft, DWORD requestId, + swift::misc::simulation::ISimulationEnvironmentProvider *simEnvProvider, + swift::misc::simulation::IInterpolationSetupProvider *setupProvider, + swift::misc::simulation::IRemoteAircraftProvider *remoteAircraftProvider, + swift::misc::simulation::CInterpolationLogger *logger); + + //! Get callsign + const swift::misc::aviation::CCallsign &getCallsign() const { return m_aircraft.getCallsign(); } + + //! Get callsign + const QString &getCallsignAsString() const { return m_aircraft.getCallsign().asString(); } + + //! Callsign? + bool hasCallsign() const { return !this->getCallsign().isEmpty(); } + + //! Simulated aircraft (as added) + const swift::misc::simulation::CSimulatedAircraft &getAircraft() const { return m_aircraft; } + + //! Simulated aircraft model + const swift::misc::simulation::CAircraftModel &getAircraftModel() const { return m_aircraft.getModel(); } + + //! Simulated aircraft model string + const QString &getAircraftModelString() const { return m_aircraft.getModelString(); } + + //! Object type + SimObjectType getType() const { return m_type; } + + //! Type as string + const QString &getTypeAsString() const { return typeToString(m_type); } + + //! Aircraft? + bool isAircraft() const + { + return this->getType() == AircraftNonAtc || this->getType() == AircraftSimulatedObject; + } + + //! Aircraft simulated object? + bool isAircraftSimulatedObject() const { return this->getType() == AircraftSimulatedObject; } + + //! Aircraft NON ATC? + bool isAircraftNonAtc() const { return this->getType() == AircraftNonAtc; } + + //! Set the type + void setType(SimObjectType type) { m_type = type; } + + //! Set the aircraft + void setAircraft(const swift::misc::simulation::CSimulatedAircraft &aircraft); + + //! Set model string + void setAircraftModelString(const QString &modelString); + + //! Set CG + void setAircraftCG(const swift::misc::physical_quantities::CLength &cg); + + //! Get current lights (requested from simulator) + const swift::misc::aviation::CAircraftLights &getCurrentLightsInSimulator() const + { + return m_currentLightsInSim; + } + + //! Received lights in simulator + bool hasCurrentLightsInSimulator() const { return !m_currentLightsInSim.isNull(); } + + //! Set current lights when received from simulator + void setCurrentLightsInSimulator(const swift::misc::aviation::CAircraftLights &lights) + { + m_currentLightsInSim = lights; + } + + //! Pretend to have received lights from simulator + void fakeCurrentLightsInSimulator() { m_currentLightsInSim.setNull(false); } + + //! Lights as sent to simulator + const swift::misc::aviation::CAircraftLights &getLightsAsSent() const { return m_lightsAsSent; } + + //! Lights as sent to simulator + void setLightsAsSent(const swift::misc::aviation::CAircraftLights &lights) { m_lightsAsSent = lights; } + + //! How often do we request data from simulator for this remote aircraft + SIMCONNECT_PERIOD getSimDataPeriod() const { return m_requestSimDataPeriod; } + + //! How often do we request data from simulator for this remote aircraft + void setSimDataPeriod(SIMCONNECT_PERIOD period) { m_requestSimDataPeriod = period; } + + //! Set Simconnect request id + void setRequestId(DWORD id); + + //! Get SimConnect request id + DWORD getRequestId() const { return m_requestId; } + + //! Get SimConnect with offset + DWORD getRequestId(CSimConnectDefinitions::SimObjectRequest offset) const; + + //! Set Simconnect object id + void setObjectId(DWORD id); + + //! Get SimConnect object id + DWORD getObjectId() const { return m_objectId; } + + //! Get SimConnect object id + QString getObjectIdAsString() const { return QString::number(this->getObjectId()); } + + //! Valid request id? + bool hasValidRequestId() const { return m_validRequestId; } + + //! Valid object id? + bool hasValidObjectId() const { return m_validObjectId; } + + //! Object is requested in simulator, not yet confirmed added + bool isPendingAdded() const; + + //! Still pending + bool isOutdatedPendingAdded(qint64 thresholdMs = 5000, qint64 currentMsSinceEpoch = -1) const; + + //! Adding is confirmed + bool isConfirmedAdded() const; + + //! Marked as confirmed, means the simulator has "confirmed" the objectId as added and not instantly removed the + //! object + void setConfirmedAdded(bool confirm); + + //! @{ + //! Special states + bool isAddedWhileRemoving() { return m_addedWhileRemoving; } + void setAddedWhileRemoving(bool addedWileRemoved); + bool isRemovedWhileAdding() const { return m_removedWhileAdding; } + void setRemovedWhileAdding(bool removedWhileAdding); + //! @} + + //! Removing is pending + bool isPendingRemoved() const { return m_pendingRemoved; } + + //! Object which can be used for sending, not pending and valid ids + bool isReadyToSend() const; + + //! Marked as pending for removal + void setPendingRemoved(bool pending); + + //! Pending added or removed? + bool isPending() const { return this->isPendingAdded() || this->isPendingRemoved(); } + + //! Has camera? + bool hasCamera() const { return m_camera; } + + //! Reset camera positions + void resetCameraPositions(); + + //! Camera position + const SIMCONNECT_DATA_XYZ &cameraPosition() const { return m_cameraPosition; } + + //! Camera rotation; + const SIMCONNECT_DATA_PBH &cameraRotation() const { return m_cameraRotation; } + + //! Camera position/rotation + void setCameraPositionAndRotation(const SIMCONNECT_DATA_XYZ &position, const SIMCONNECT_DATA_PBH &rotation) + { + m_cameraPosition = position; + m_cameraRotation = rotation; + } + + //! Camera GUID + GUID getCameraGUID() const { return m_cameraGuid; } + + //! Set camera GUID + void setCameraGUID(GUID guid) + { + m_cameraGuid = guid; + m_camera = true; + } + + //! No camera anymore + void removeCamera() { m_camera = false; } + + //! Set observer + void setObserverName(const QString &observer) { m_observerName = observer; } + + //! Observer name + const QString &getObserverName() const { return m_observerName; } + + //! Reset the state (like it was a new onject) without affecting interpolator and aircraft + void resetState(); + + //! Reset so it can be added again + void resetToAddAgain(); + + //! Reset the timestamp + void resetTimestampToNow() { m_tsCreated = QDateTime::currentMSecsSinceEpoch(); } + + //! VTOL? + bool isVtol() const { return m_aircraft.isVtol(); } + + //! Valid? + bool isValid() const { return !this->isInvalid(); } + + //! Invalid? + bool isInvalid() const { return !this->hasValidObjectId() && !this->hasValidRequestId(); } + + //! Created timestamp? + bool hasCreatedTimestamp() const { return m_tsCreated >= 0; } + + //! Created timestamp + qint64 getCreatedTimestamp() const { return m_tsCreated; } + + //! Engine count + int getEngineCount() const { return m_aircraft.getEnginesCount(); } + + //! Was the object really added to simulator + bool hasValidRequestAndObjectId() const; + + //! Adding has been failed before + int getAddingExceptions() const { return m_addingExceptions; } + + //! Set adding failed before + void setAddingExceptions(int number) { m_addingExceptions = number; } + + //! Increase adding exception + void increaseAddingExceptions() { m_addingExceptions++; } + + //! Decrease adding exception + void decreaseAddingExceptions() + { + if (m_addingExceptions > 0) { m_addingExceptions--; } + } + + //! Adding and directly removed + int getAddingDirectlyRemoved() const { return m_addingDirectlyRemoved; } + + //! Set adding and directly removed + void setAddingDirectlyRemoved(int number) { m_addingDirectlyRemoved = number; } + + //! Increase adding and directly removed + void increaseAddingDirectlyRemoved() { m_addingDirectlyRemoved++; } + + //! Copy the counters from another object + void copyAddingFailureCounters(const CSimConnectObject &otherObject); + + //! Callsign as LATIN1 + const QByteArray &getCallsignByteArray() const { return m_callsignByteArray; } + + //! \copydoc swift::misc::simulation::CInterpolator::getInterpolatorInfo + QString + getInterpolatorInfo(swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const; + + //! \copydoc swift::misc::simulation::CInterpolator::attachLogger + void attachInterpolatorLogger(swift::misc::simulation::CInterpolationLogger *logger) const; + + //! \copydoc swift::misc::simulation::CInterpolator::getInterpolation + swift::misc::simulation::CInterpolationResult + getInterpolation(qint64 currentTimeSinceEpoch, + const swift::misc::simulation::CInterpolationAndRenderingSetupPerCallsign &setup, + uint32_t aircraftNumber) const; + + //! \copydoc swift::misc::simulation::CInterpolator::getLastInterpolatedSituation + const swift::misc::aviation::CAircraftSituation &getLastInterpolatedSituation( + swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const; + + //! \copydoc swift::misc::simulation::CInterpolator::getInterpolationMessages + const swift::misc::CStatusMessageList &getInterpolationMessages( + swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const; + + //! Interpolator + swift::misc::simulation::CInterpolatorMulti *getInterpolator() const { return m_interpolator.data(); } + + //! SimObject as string + QString toQString() const; + + //! Type of id + static SimObjectType requestIdToType(DWORD requestId); + + //! Type to string + static const QString &typeToString(SimObjectType type); + + //! Same type + static bool isSameTypeGroup(SimObjectType t1, SimObjectType t2); + + //! Aircraft? + static bool isAircraft(SimObjectType type); + + private: + swift::misc::simulation::CSimulatedAircraft m_aircraft; //!< corresponding aircraft + SimObjectType m_type = AircraftNonAtc; + DWORD m_requestId = 0; + DWORD m_objectId = 0; + bool m_validRequestId = false; + bool m_validObjectId = false; + bool m_confirmedAdded = false; + bool m_pendingRemoved = false; + bool m_camera = false; + bool m_removedWhileAdding = false; + bool m_addedWhileRemoving = false; + int m_addingExceptions = 0; //!< exception when added + int m_addingDirectlyRemoved = 0; //!< added, but removed directly afterwards + qint64 m_tsCreated = -1; + GUID m_cameraGuid; + SIMCONNECT_DATA_XYZ m_cameraPosition; + SIMCONNECT_DATA_PBH m_cameraRotation; + QByteArray m_callsignByteArray; + QString m_observerName; + swift::misc::aviation::CAircraftLights m_currentLightsInSim { + nullptr + }; //!< current lights to know state for toggling + swift::misc::aviation::CAircraftLights m_lightsAsSent { nullptr }; //!< lights as sent to simulator + SIMCONNECT_PERIOD m_requestSimDataPeriod = SIMCONNECT_PERIOD_NEVER; //!< how often do we query ground elevation + QSharedPointer + m_interpolator; //!< shared pointer because CSimConnectObject can be copied + }; + + //! Simulator objects (aka AI aircraft) + class CSimConnectObjects : public QHash + { + public: + //! Insert + bool insert(const CSimConnectObject &simObject, bool updateTimestamp = false); + + //! Set ID of a SimConnect object, so far we only have an request id in the object + bool setSimConnectObjectIdForRequestId(DWORD requestId, DWORD objectId); + + //! Find which callsign belongs to the object id + swift::misc::aviation::CCallsign getCallsignForObjectId(DWORD objectId) const; + + //! Get object per object id + CSimConnectObject getSimObjectForObjectId(DWORD objectId) const; + + //! Get object per request id + CSimConnectObject getSimObjectForRequestId(DWORD requestId) const; + + //! Get by request or object id, just as possible + CSimConnectObject getSimObjectForOtherSimObject(const CSimConnectObject &otherSimObj) const; + + //! Get the oldest object + CSimConnectObject getOldestObject() const; + + //! Is the object id one of our AI objects? + bool isKnownSimObjectId(DWORD objectId) const; + + //! Remove by id + bool removeByObjectId(DWORD objectId); + + //! Remove by object id or request id + bool removeByOtherSimObject(const CSimConnectObject &otherSimObj); + + //! Remove all the probes + int removeAllProbes(); + + //! Remove callsigns + int removeCallsigns(const swift::misc::aviation::CCallsignSet &callsigns); + + //! Remove all pending added objects + CSimConnectObjects removeOutdatedPendingAdded(CSimConnectObject::SimObjectType type); + + //! Pending add condition + bool containsPendingAdded() const; + + //! Pending removed condition + bool containsPendingRemoved() const; + + //! Number of pending added + int countPendingAdded() const; + + //! Number of pending removed + int countPendingRemoved() const; + + //! Objects not pending + int countConfirmedAdded(); + + //! Get all callsigns + swift::misc::aviation::CCallsignSet getAllCallsigns(bool withoutProbes = true) const; + + //! Get all callsign strings + QStringList getAllCallsignStrings(bool sorted = false, bool withoutProbes = true) const; + + //! Get all callsign strings as string + QString getAllCallsignStringsAsString(bool sorted = false, const QString &separator = ", ") const; + + //! Callsigns of pending added callsigns + swift::misc::aviation::CCallsignSet getPendingAddedCallsigns() const; + + //! Callsigns of pending removed callsigns + swift::misc::aviation::CCallsignSet getPendingRemovedCallsigns() const; + + //! Get by type + QList getByType(CSimConnectObject::SimObjectType type) const; + + //! All aircraft + QList getAircraft() const; + + //! Get a non pending probe + CSimConnectObject getNotPendingProbe() const; + + //! Get a non pending probe + CSimConnectObject getOldestNotPendingProbe() const; + + //! Contains object of type + bool containsType(CSimConnectObject::SimObjectType type) const; + + //! Aircraft? + bool containsAircraft() const; + }; +} // namespace swift::simplugin::msfs2024common + +#endif // SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTOBJECT_H diff --git a/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.cpp b/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.cpp new file mode 100644 index 000000000..68aaadfe0 --- /dev/null +++ b/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.cpp @@ -0,0 +1,585 @@ +// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +#include "simconnectsymbolsmsfs2024.h" + +// clang-format off +#include +//#include +#include "../third_party/externals/common/include/simconnect/MSFS2024/SimConnect.h" +// clang-format on + +#include + +#include + +#include "misc/logcategories.h" +#include "misc/logmessage.h" +#include "misc/stringutils.h" + +// clazy:excludeall=function-args-by-ref + +using namespace swift::misc; + +bool loadAndResolveSimConnect(bool manifestProbing) +{ + Q_UNUSED(manifestProbing); + return true; +} + +// MSFS2024 API: +// https://docs.flightsimulator.com/msfs2024/html/6_Programming_APIs/SimConnect/SimConnect_API_Reference.htm +using PfnSimConnect_Open = HRESULT(__stdcall *)(HANDLE *, LPCSTR, HWND, DWORD, HANDLE, DWORD); +using PfnSimConnect_Close = HRESULT(__stdcall *)(HANDLE); +using PfnSimConnect_AddToDataDefinition = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_DEFINITION_ID, const char *, + const char *, SIMCONNECT_DATATYPE, float, DWORD); +using PfnSimConnect_Text = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_TEXT_TYPE, float, SIMCONNECT_CLIENT_EVENT_ID, DWORD, + void *); +using PfnSimConnect_CallDispatch = HRESULT(__stdcall *)(HANDLE, DispatchProc, void *); +using PfnSimConnect_WeatherSetModeCustom = HRESULT(__stdcall *)(HANDLE); +using PfnSimConnect_WeatherSetModeGlobal = HRESULT(__stdcall *)(HANDLE); +using PfnSimConnect_WeatherSetObservation = HRESULT(__stdcall *)(HANDLE, DWORD, const char *); +using PfnSimConnect_TransmitClientEvent = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_OBJECT_ID, SIMCONNECT_CLIENT_EVENT_ID, + DWORD, SIMCONNECT_NOTIFICATION_GROUP_ID, + SIMCONNECT_EVENT_FLAG); +using PfnSimConnect_SetClientData = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_DATA_ID, + SIMCONNECT_CLIENT_DATA_DEFINITION_ID, + SIMCONNECT_CLIENT_DATA_SET_FLAG, DWORD, DWORD, void *); +using PfnSimConnect_RequestDataOnSimObject = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_REQUEST_ID, + SIMCONNECT_DATA_DEFINITION_ID, SIMCONNECT_OBJECT_ID, + SIMCONNECT_PERIOD, SIMCONNECT_DATA_REQUEST_FLAG, + DWORD, DWORD, DWORD); +using PfnSimConnect_RequestDataOnSimObjectType = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_REQUEST_ID, + SIMCONNECT_DATA_DEFINITION_ID, DWORD, + SIMCONNECT_SIMOBJECT_TYPE); +using PfnSimConnect_RequestClientData = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_DATA_ID, + SIMCONNECT_DATA_REQUEST_ID, + SIMCONNECT_CLIENT_DATA_DEFINITION_ID, + SIMCONNECT_CLIENT_DATA_PERIOD, + SIMCONNECT_CLIENT_DATA_REQUEST_FLAG, DWORD, DWORD, DWORD); +using PfnSimConnect_SubscribeToSystemEvent = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_EVENT_ID, const char *); +using PfnSimConnect_MapClientEventToSimEvent = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_EVENT_ID, const char *); +using PfnSimConnect_SubscribeToFacilities = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_FACILITY_LIST_TYPE, + SIMCONNECT_DATA_REQUEST_ID); +using PfnSimConnect_GetLastSentPacketID = HRESULT(__stdcall *)(HANDLE, DWORD *); +using PfnSimConnect_AIRemoveObject = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_REQUEST_ID); +using PfnSimConnect_SetDataOnSimObject = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_DEFINITION_ID, + SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_SET_FLAG, DWORD, + DWORD, void *); +using PfnSimConnect_AIReleaseControl = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_REQUEST_ID); +using PfnSimConnect_AICreateNonATCAircraft = HRESULT(__stdcall *)(HANDLE, const char *, const char *, + SIMCONNECT_DATA_INITPOSITION, + SIMCONNECT_DATA_REQUEST_ID); +using PfnSimConnect_AICreateNonATCAircraft_EX1 = HRESULT(__stdcall *)(HANDLE, const char *, const char *, const char *, + SIMCONNECT_DATA_INITPOSITION, + SIMCONNECT_DATA_REQUEST_ID); + +using PfnSimConnect_AICreateEnrouteATCAircraft = HRESULT(__stdcall *)(HANDLE, const char *, const char *, int, + const char *, double, BOOL, + SIMCONNECT_DATA_REQUEST_ID); +using PfnSimConnect_AICreateParkedATCAircraft = HRESULT(__stdcall *)(HANDLE, const char *, const char *, const char *, + SIMCONNECT_DATA_REQUEST_ID); +using PfnSimConnect_AICreateSimulatedObject = HRESULT(__stdcall *)(HANDLE, const char *, SIMCONNECT_DATA_INITPOSITION, + SIMCONNECT_DATA_REQUEST_ID); + +using PfnSimConnect_MapClientDataNameToID = HRESULT(__stdcall *)(HANDLE, const char *, SIMCONNECT_CLIENT_DATA_ID); +using PfnSimConnect_CreateClientData = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_DATA_ID, DWORD, + SIMCONNECT_CREATE_CLIENT_DATA_FLAG); +using PfnSimConnect_AddToClientDataDefinition = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_CLIENT_DATA_DEFINITION_ID, + DWORD, DWORD, float, DWORD); + +#ifdef Q_OS_WIN64 +// using PfnSimConnect_RequestGroundInfo = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_REQUEST_ID, double, double, +// double, +// double, double, double, DWORD, DWORD, +// SIMCONNECT_GROUND_INFO_LATLON_FORMAT, +// SIMCONNECT_GROUND_INFO_ALT_FORMAT, +// SIMCONNECT_GROUND_INFO_SOURCE_FLAG); +using PfnSimConnect_ChangeView = HRESULT(__stdcall *)(HANDLE, const char *); +using PfnSimConnect_AIReleaseControlEx = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_REQUEST_ID, + BOOL); +using PfnSimConnect_CloseView = HRESULT(__stdcall *)(HANDLE, const char *); +using PfnSimConnect_OpenView = HRESULT(__stdcall *)(HANDLE, const char *, const char *); +using PfnSimConnect_ChangeView = HRESULT(__stdcall *)(HANDLE, const char *); +using PfnSimConnect_CreateCameraInstance = HRESULT(__stdcall *)(HANDLE, const GUID, const char *, SIMCONNECT_OBJECT_ID, + SIMCONNECT_DATA_REQUEST_ID); +using PfnSimConnect_DeleteCameraInstance = HRESULT(__stdcall *)(HANDLE, const GUID, UINT32); +// using PfnSimConnect_CreateCameraDefinition = HRESULT(__stdcall *)(HANDLE, const GUID, SIMCONNECT_CAMERA_TYPE, +// const char *, SIMCONNECT_DATA_XYZ, +// SIMCONNECT_DATA_PBH); +using PfnSimConnect_ObserverAttachToEntityOn = HRESULT(__stdcall *)(HANDLE, const char *, DWORD, SIMCONNECT_DATA_XYZ); +// using PfnSimConnect_CreateObserver = HRESULT(__stdcall *)(HANDLE, const char *, SIMCONNECT_DATA_OBSERVER); +using PfnSimConnect_SetObserverLookAt = HRESULT(__stdcall *)(HANDLE, const char *, SIMCONNECT_DATA_LATLONALT); +using PfnSimConnect_SimConnect_EnumerateSimObjectsAndLiveries = HRESULT(__stdcall *)(HANDLE, SIMCONNECT_DATA_REQUEST_ID, + SIMCONNECT_SIMOBJECT_TYPE); +using PfnSimConnect_AICreateSimulatedObject_EX1 = HRESULT(__stdcall *)(HANDLE, const char *, const char *, + SIMCONNECT_DATA_INITPOSITION, + SIMCONNECT_DATA_REQUEST_ID); + +#endif + +//! The SimConnect Symbols +//! \private +struct SimConnectSymbolsMsfs2024 +{ + PfnSimConnect_Open SimConnect_Open = nullptr; + PfnSimConnect_Close SimConnect_Close = nullptr; + PfnSimConnect_AddToDataDefinition SimConnect_AddToDataDefinition = nullptr; + PfnSimConnect_Text SimConnect_Text = nullptr; + PfnSimConnect_CallDispatch SimConnect_CallDispatch = nullptr; + PfnSimConnect_WeatherSetModeCustom SimConnect_WeatherSetModeCustom = nullptr; + PfnSimConnect_WeatherSetModeGlobal SimConnect_WeatherSetModeGlobal = nullptr; + PfnSimConnect_WeatherSetObservation SimConnect_WeatherSetObservation = nullptr; + PfnSimConnect_TransmitClientEvent SimConnect_TransmitClientEvent = nullptr; + PfnSimConnect_SetClientData SimConnect_SetClientData = nullptr; + PfnSimConnect_RequestDataOnSimObject SimConnect_RequestDataOnSimObject = nullptr; + PfnSimConnect_RequestDataOnSimObjectType SimConnect_RequestDataOnSimObjectType = nullptr; + PfnSimConnect_RequestClientData SimConnect_RequestClientData = nullptr; + PfnSimConnect_SubscribeToSystemEvent SimConnect_SubscribeToSystemEvent = nullptr; + PfnSimConnect_MapClientEventToSimEvent SimConnect_MapClientEventToSimEvent = nullptr; + PfnSimConnect_SubscribeToFacilities SimConnect_SubscribeToFacilities = nullptr; + PfnSimConnect_GetLastSentPacketID SimConnect_GetLastSentPacketID = nullptr; + PfnSimConnect_AIRemoveObject SimConnect_AIRemoveObject = nullptr; + PfnSimConnect_SetDataOnSimObject SimConnect_SetDataOnSimObject = nullptr; + PfnSimConnect_AIReleaseControl SimConnect_AIReleaseControl = nullptr; + PfnSimConnect_AICreateNonATCAircraft SimConnect_AICreateNonATCAircraft = nullptr; + + PfnSimConnect_AICreateParkedATCAircraft SimConnect_AICreateParkedATCAircraft = nullptr; + PfnSimConnect_AICreateEnrouteATCAircraft SimConnect_AICreateEnrouteATCAircraft = nullptr; + PfnSimConnect_AICreateSimulatedObject SimConnect_AICreateSimulatedObject = nullptr; + + PfnSimConnect_MapClientDataNameToID SimConnect_MapClientDataNameToID = nullptr; + PfnSimConnect_CreateClientData SimConnect_CreateClientData = nullptr; + PfnSimConnect_AddToClientDataDefinition SimConnect_AddToClientDataDefinition = nullptr; +#ifdef Q_OS_WIN64 + // PfnSimConnect_RequestGroundInfo SimConnect_RequestGroundInfo = nullptr; + PfnSimConnect_ChangeView SimConnect_ChangeView = nullptr; + PfnSimConnect_AIReleaseControlEx SimConnect_AIReleaseControlEx = nullptr; + // PfnSimConnect_CreateCameraDefinition SimConnect_CreateCameraDefinition = nullptr; + PfnSimConnect_ObserverAttachToEntityOn SimConnect_ObserverAttachToEntityOn = nullptr; + // PfnSimConnect_CreateObserver SimConnect_CreateObserver = nullptr; + PfnSimConnect_CreateCameraInstance SimConnect_CreateCameraInstance = nullptr; + PfnSimConnect_DeleteCameraInstance SimConnect_DeleteCameraInstance = nullptr; + PfnSimConnect_SetObserverLookAt SimConnect_SetObserverLookAt = nullptr; + PfnSimConnect_OpenView SimConnect_OpenView = nullptr; + PfnSimConnect_CloseView SimConnect_CloseView = nullptr; + PfnSimConnect_SimConnect_EnumerateSimObjectsAndLiveries SimConnect_EnumerateSimObjectsAndLiveries = nullptr; + PfnSimConnect_AICreateNonATCAircraft_EX1 SimConnect_AICreateNonATCAircraft_EX1 = nullptr; + PfnSimConnect_AICreateSimulatedObject_EX1 SimConnect_AICreateSimulatedObject_EX1 = nullptr; +#endif +}; + +static SimConnectSymbolsMsfs2024 gSymbols; + +template +bool resolveSimConnectSymbol(QLibrary &library, FuncPtr &funcPtr, const char *funcName) +{ + funcPtr = reinterpret_cast(library.resolve(funcName)); + if (!funcPtr) + { + CLogMessage(CLogCategories::driver()).error(u"Failed to resolve %1: %2") << funcName << library.errorString(); + return false; + } + return true; +} + +bool resolveMsfs2024SimConnectSymbols(QLibrary &simConnectDll) +{ + bool resolveSuccess = true; + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_Open, "SimConnect_Open"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_Close, "SimConnect_Close"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AddToDataDefinition, + "SimConnect_AddToDataDefinition"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_Text, "SimConnect_Text"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_CallDispatch, + "SimConnect_CallDispatch"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_WeatherSetModeCustom, + "SimConnect_WeatherSetModeCustom"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_WeatherSetModeGlobal, + "SimConnect_WeatherSetModeGlobal"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_WeatherSetObservation, + "SimConnect_WeatherSetObservation"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_TransmitClientEvent, + "SimConnect_TransmitClientEvent"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SetClientData, + "SimConnect_SetClientData"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_RequestDataOnSimObject, + "SimConnect_RequestDataOnSimObject"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_RequestDataOnSimObjectType, + "SimConnect_RequestDataOnSimObjectType"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_RequestClientData, + "SimConnect_RequestClientData"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SubscribeToSystemEvent, + "SimConnect_SubscribeToSystemEvent"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_MapClientEventToSimEvent, + "SimConnect_MapClientEventToSimEvent"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SubscribeToFacilities, + "SimConnect_SubscribeToFacilities"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_GetLastSentPacketID, + "SimConnect_GetLastSentPacketID"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AIRemoveObject, + "SimConnect_AIRemoveObject"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SetDataOnSimObject, + "SimConnect_SetDataOnSimObject"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AIReleaseControl, + "SimConnect_AIReleaseControl"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateNonATCAircraft, + "SimConnect_AICreateNonATCAircraft"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateNonATCAircraft_EX1, + "SimConnect_AICreateNonATCAircraft_EX1"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateEnrouteATCAircraft, + "SimConnect_AICreateEnrouteATCAircraft"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateParkedATCAircraft, + "SimConnect_AICreateParkedATCAircraft"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateSimulatedObject, + "SimConnect_AICreateSimulatedObject"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AICreateSimulatedObject_EX1, + "SimConnect_AICreateSimulatedObject_EX1"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_MapClientDataNameToID, + "SimConnect_MapClientDataNameToID"); + resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_CreateClientData, + "SimConnect_CreateClientData"); + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AddToClientDataDefinition, + "SimConnect_AddToClientDataDefinition"); + + resolveSuccess = + resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_EnumerateSimObjectsAndLiveries, + "SimConnect_EnumerateSimObjectsAndLiveries"); + + return resolveSuccess; +} + +#ifdef Q_OS_WIN64 +// bool resolveP3DSimConnectSymbols(QLibrary &simConnectDll) +//{ +// bool resolveSuccess = true; +// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_RequestGroundInfo, +// "SimConnect_RequestGroundInfo"); +// resolveSuccess = resolveSuccess & +// resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_ChangeView, "SimConnect_ChangeView"); +// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_AIReleaseControlEx, +// "SimConnect_AIReleaseControlEx"); +// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, +// gSymbols.SimConnect_CreateCameraDefinition, +// "SimConnect_CreateCameraDefinition"); +// resolveSuccess = +// resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_ObserverAttachToEntityOn, +// "SimConnect_ObserverAttachToEntityOn"); +// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_CreateObserver, +// "SimConnect_CreateObserver"); +// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, +// gSymbols.SimConnect_CreateCameraInstance, +// "SimConnect_CreateCameraInstance"); +// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, +// gSymbols.SimConnect_DeleteCameraInstance, +// "SimConnect_DeleteCameraInstance"); +// resolveSuccess = resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_SetObserverLookAt, +// "SimConnect_SetObserverLookAt"); +// resolveSuccess = +// resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_OpenView, "SimConnect_OpenView"); +// resolveSuccess = +// resolveSuccess & resolveSimConnectSymbol(simConnectDll, gSymbols.SimConnect_CloseView, +// "SimConnect_CloseView"); +// return resolveSuccess; +// } + +bool loadAndResolveMSFS2024SimConnect() +{ + // Check if already loaded + if (gSymbols.SimConnect_Open) { return true; } + + QString simConnectFileName(QStringLiteral("SimConnect.MSFS2024")); + + QLibrary simConnectDll(simConnectFileName); + simConnectDll.setLoadHints(QLibrary::PreventUnloadHint); + if (simConnectDll.load()) + { + const bool resolvedCommon = resolveMsfs2024SimConnectSymbols(simConnectDll); + if (!resolvedCommon) + { + CLogMessage(CLogCategories::driver()).error(u"Failed to resolve common symbols from SimConnect.dll: '%1'") + << simConnectFileName; + return false; + } + + CLogMessage(CLogCategories::driver()).info(u"Loaded and resolved MSFS2024 symbols from SimConnect.dll: '%1'") + << simConnectFileName; + return resolvedCommon; + } + else + { + CLogMessage(CLogCategories::driver()).error(u"Failed to load SimConnect.dll: '%1' '%2'") + << simConnectFileName << simConnectDll.errorString(); + return false; + } +} + +#endif + +SIMCONNECTAPI SimConnect_Open(HANDLE *phSimConnect, LPCSTR szName, HWND hWnd, DWORD UserEventWin32, HANDLE hEventHandle, + DWORD ConfigIndex) +{ + return gSymbols.SimConnect_Open(phSimConnect, szName, hWnd, UserEventWin32, hEventHandle, ConfigIndex); +} + +SIMCONNECTAPI SimConnect_Close(HANDLE hSimConnect) { return gSymbols.SimConnect_Close(hSimConnect); } + +SIMCONNECTAPI SimConnect_AddToDataDefinition(HANDLE hSimConnect, SIMCONNECT_DATA_DEFINITION_ID DefineID, + const char *DatumName, const char *UnitsName, + SIMCONNECT_DATATYPE DatumType, float fEpsilon, DWORD DatumID) +{ + return gSymbols.SimConnect_AddToDataDefinition(hSimConnect, DefineID, DatumName, UnitsName, DatumType, fEpsilon, + DatumID); +} + +SIMCONNECTAPI SimConnect_EnumerateSimObjectsAndLiveries(HANDLE hSimConnect, SIMCONNECT_DATA_REQUEST_ID RequestID, + SIMCONNECT_SIMOBJECT_TYPE Type) +{ + return gSymbols.SimConnect_EnumerateSimObjectsAndLiveries(hSimConnect, RequestID, Type); +} + +SIMCONNECTAPI SimConnect_Text(HANDLE hSimConnect, SIMCONNECT_TEXT_TYPE type, float fTimeSeconds, + SIMCONNECT_CLIENT_EVENT_ID EventID, DWORD cbUnitSize, void *pDataSet) +{ + return gSymbols.SimConnect_Text(hSimConnect, type, fTimeSeconds, EventID, cbUnitSize, pDataSet); +} + +SIMCONNECTAPI SimConnect_CallDispatch(HANDLE hSimConnect, DispatchProc pfcnDispatch, void *pContext) +{ + return gSymbols.SimConnect_CallDispatch(hSimConnect, pfcnDispatch, pContext); +} + +SIMCONNECTAPI SimConnect_WeatherSetModeCustom(HANDLE hSimConnect) +{ + return gSymbols.SimConnect_WeatherSetModeCustom(hSimConnect); +} + +SIMCONNECTAPI SimConnect_WeatherSetModeGlobal(HANDLE hSimConnect) +{ + return gSymbols.SimConnect_WeatherSetModeGlobal(hSimConnect); +} + +SIMCONNECTAPI SimConnect_WeatherSetObservation(HANDLE hSimConnect, DWORD Seconds, const char *szMETAR) +{ + return gSymbols.SimConnect_WeatherSetObservation(hSimConnect, Seconds, szMETAR); +} + +SIMCONNECTAPI SimConnect_TransmitClientEvent(HANDLE hSimConnect, SIMCONNECT_OBJECT_ID ObjectID, + SIMCONNECT_CLIENT_EVENT_ID EventID, DWORD dwData, + SIMCONNECT_NOTIFICATION_GROUP_ID GroupID, SIMCONNECT_EVENT_FLAG Flags) +{ + return gSymbols.SimConnect_TransmitClientEvent(hSimConnect, ObjectID, EventID, dwData, GroupID, Flags); +} + +SIMCONNECTAPI SimConnect_SetClientData(HANDLE hSimConnect, SIMCONNECT_CLIENT_DATA_ID ClientDataID, + SIMCONNECT_CLIENT_DATA_DEFINITION_ID DefineID, + SIMCONNECT_CLIENT_DATA_SET_FLAG Flags, DWORD dwReserved, DWORD cbUnitSize, + void *pDataSet) +{ + return gSymbols.SimConnect_SetClientData(hSimConnect, ClientDataID, DefineID, Flags, dwReserved, cbUnitSize, + pDataSet); +} + +SIMCONNECTAPI SimConnect_RequestDataOnSimObject(HANDLE hSimConnect, SIMCONNECT_DATA_REQUEST_ID RequestID, + SIMCONNECT_DATA_DEFINITION_ID DefineID, SIMCONNECT_OBJECT_ID ObjectID, + SIMCONNECT_PERIOD Period, SIMCONNECT_DATA_REQUEST_FLAG Flags, + DWORD origin, DWORD interval, DWORD limit) +{ + return gSymbols.SimConnect_RequestDataOnSimObject(hSimConnect, RequestID, DefineID, ObjectID, Period, Flags, origin, + interval, limit); +} + +SIMCONNECTAPI SimConnect_RequestDataOnSimObjectType(HANDLE hSimConnect, SIMCONNECT_DATA_REQUEST_ID RequestID, + SIMCONNECT_DATA_DEFINITION_ID DefineID, DWORD dwRadiusMeters, + SIMCONNECT_SIMOBJECT_TYPE type) +{ + return gSymbols.SimConnect_RequestDataOnSimObjectType(hSimConnect, RequestID, DefineID, dwRadiusMeters, type); +} + +SIMCONNECTAPI SimConnect_RequestClientData(HANDLE hSimConnect, SIMCONNECT_CLIENT_DATA_ID ClientDataID, + SIMCONNECT_DATA_REQUEST_ID RequestID, + SIMCONNECT_CLIENT_DATA_DEFINITION_ID DefineID, + SIMCONNECT_CLIENT_DATA_PERIOD Period, + SIMCONNECT_CLIENT_DATA_REQUEST_FLAG Flags, DWORD origin, DWORD interval, + DWORD limit) +{ + return gSymbols.SimConnect_RequestClientData(hSimConnect, ClientDataID, RequestID, DefineID, Period, Flags, origin, + interval, limit); +} + +SIMCONNECTAPI SimConnect_SubscribeToSystemEvent(HANDLE hSimConnect, SIMCONNECT_CLIENT_EVENT_ID EventID, + const char *SystemEventName) +{ + return gSymbols.SimConnect_SubscribeToSystemEvent(hSimConnect, EventID, SystemEventName); +} + +SIMCONNECTAPI SimConnect_MapClientEventToSimEvent(HANDLE hSimConnect, SIMCONNECT_CLIENT_EVENT_ID EventID, + const char *EventName) +{ + return gSymbols.SimConnect_MapClientEventToSimEvent(hSimConnect, EventID, EventName); +} + +SIMCONNECTAPI SimConnect_SubscribeToFacilities(HANDLE hSimConnect, SIMCONNECT_FACILITY_LIST_TYPE type, + SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_SubscribeToFacilities(hSimConnect, type, RequestID); +} + +SIMCONNECTAPI SimConnect_GetLastSentPacketID(HANDLE hSimConnect, DWORD *pdwError) +{ + return gSymbols.SimConnect_GetLastSentPacketID(hSimConnect, pdwError); +} + +SIMCONNECTAPI SimConnect_AIRemoveObject(HANDLE hSimConnect, SIMCONNECT_OBJECT_ID ObjectID, + SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_AIRemoveObject(hSimConnect, ObjectID, RequestID); +} + +SIMCONNECTAPI SimConnect_SetDataOnSimObject(HANDLE hSimConnect, SIMCONNECT_DATA_DEFINITION_ID DefineID, + SIMCONNECT_OBJECT_ID ObjectID, SIMCONNECT_DATA_SET_FLAG Flags, + DWORD ArrayCount, DWORD cbUnitSize, void *pDataSet) +{ + return gSymbols.SimConnect_SetDataOnSimObject(hSimConnect, DefineID, ObjectID, Flags, ArrayCount, cbUnitSize, + pDataSet); +} + +SIMCONNECTAPI SimConnect_AIReleaseControl(HANDLE hSimConnect, SIMCONNECT_OBJECT_ID ObjectID, + SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_AIReleaseControl(hSimConnect, ObjectID, RequestID); +} + +SIMCONNECTAPI SimConnect_AICreateNonATCAircraft(HANDLE hSimConnect, const char *szContainerTitle, + const char *szTailNumber, SIMCONNECT_DATA_INITPOSITION InitPos, + SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_AICreateNonATCAircraft(hSimConnect, szContainerTitle, szTailNumber, InitPos, RequestID); +} + +SIMCONNECTAPI SimConnect_AICreateNonATCAircraft_EX1(HANDLE hSimConnect, const char *szContainerTitle, + const char *szContainerLivery, const char *szTailNumber, + SIMCONNECT_DATA_INITPOSITION InitPos, + SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_AICreateNonATCAircraft_EX1(hSimConnect, szContainerTitle, szContainerLivery, + szTailNumber, InitPos, RequestID); +} + +SIMCONNECTAPI SimConnect_AICreateEnrouteATCAircraft(HANDLE hSimConnect, const char *szContainerTitle, + const char *szTailNumber, int iFlightNumber, + const char *szFlightPlanPath, double dFlightPlanPosition, + BOOL bTouchAndGo, SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_AICreateEnrouteATCAircraft(hSimConnect, szContainerTitle, szTailNumber, iFlightNumber, + szFlightPlanPath, dFlightPlanPosition, bTouchAndGo, + RequestID); +} + +SIMCONNECTAPI SimConnect_AICreateParkedATCAircraft(HANDLE hSimConnect, const char *szContainerTitle, + const char *szTailNumber, const char *szAirportID, + SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_AICreateParkedATCAircraft(hSimConnect, szContainerTitle, szTailNumber, szAirportID, + RequestID); +} + +SIMCONNECTAPI SimConnect_AICreateSimulatedObject_EX1(HANDLE hSimConnect, const char *szContainerTitle, + const char *szContainerLivery, + SIMCONNECT_DATA_INITPOSITION InitPos, + SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_AICreateSimulatedObject_EX1(hSimConnect, szContainerTitle, szContainerLivery, InitPos, + RequestID); +} + +SIMCONNECTAPI SimConnect_AICreateSimulatedObject(HANDLE hSimConnect, const char *szContainerTitle, + SIMCONNECT_DATA_INITPOSITION InitPos, + SIMCONNECT_DATA_REQUEST_ID RequestID) +{ + return gSymbols.SimConnect_AICreateSimulatedObject(hSimConnect, szContainerTitle, InitPos, RequestID); +} + +SIMCONNECTAPI SimConnect_MapClientDataNameToID(HANDLE hSimConnect, const char *szClientDataName, + SIMCONNECT_CLIENT_DATA_ID ClientDataID) +{ + return gSymbols.SimConnect_MapClientDataNameToID(hSimConnect, szClientDataName, ClientDataID); +} + +SIMCONNECTAPI SimConnect_CreateClientData(HANDLE hSimConnect, SIMCONNECT_CLIENT_DATA_ID ClientDataID, DWORD dwSize, + SIMCONNECT_CREATE_CLIENT_DATA_FLAG Flags) +{ + return gSymbols.SimConnect_CreateClientData(hSimConnect, ClientDataID, dwSize, Flags); +} + +SIMCONNECTAPI SimConnect_AddToClientDataDefinition(HANDLE hSimConnect, SIMCONNECT_CLIENT_DATA_DEFINITION_ID DefineID, + DWORD dwOffset, DWORD dwSizeOrType, float fEpsilon, DWORD DatumID) +{ + return gSymbols.SimConnect_AddToClientDataDefinition(hSimConnect, DefineID, dwOffset, dwSizeOrType, fEpsilon, + DatumID); +} + +#ifdef Q_OS_WIN64 +// SIMCONNECTAPI SimConnect_RequestGroundInfo(HANDLE hSimConnect, SIMCONNECT_DATA_REQUEST_ID RequestID, double minLat, +// double minLon, double minAlt, double maxLat, double maxLon, double maxAlt, +// DWORD dwGridWidth, DWORD dwGridHeight, +// SIMCONNECT_GROUND_INFO_LATLON_FORMAT eLatLonFormat, +// SIMCONNECT_GROUND_INFO_ALT_FORMAT eAltFormat, +// SIMCONNECT_GROUND_INFO_SOURCE_FLAG eSourceFlags) +//{ +// return gSymbols.SimConnect_RequestGroundInfo(hSimConnect, RequestID, minLat, minLon, minAlt, maxLat, maxLon, +// maxAlt, +// dwGridWidth, dwGridHeight, eLatLonFormat, eAltFormat, eSourceFlags); +// } + +SIMCONNECTAPI SimConnect_ChangeView(HANDLE hSimConnect, const char *szName) +{ + return gSymbols.SimConnect_ChangeView(hSimConnect, szName); +} + +SIMCONNECTAPI SimConnect_AIReleaseControlEx(HANDLE hSimConnect, SIMCONNECT_OBJECT_ID ObjectID, + SIMCONNECT_DATA_REQUEST_ID RequestID, BOOL destroyAI) +{ + return gSymbols.SimConnect_AIReleaseControlEx(hSimConnect, ObjectID, RequestID, destroyAI); +} + +SIMCONNECTAPI SimConnect_ObserverAttachToEntityOn(HANDLE hSimConnect, const char *szName, DWORD dwObjectID, + SIMCONNECT_DATA_XYZ Offset) +{ + return gSymbols.SimConnect_ObserverAttachToEntityOn(hSimConnect, szName, dwObjectID, Offset); +} + +// SIMCONNECTAPI SimConnect_CreateObserver(HANDLE hSimConnect, const char *szName, SIMCONNECT_DATA_OBSERVER +// ObserverData) +//{ +// return gSymbols.SimConnect_CreateObserver(hSimConnect, szName, ObserverData); +// } + +SIMCONNECTAPI SimConnect_OpenView(HANDLE hSimConnect, const char *szName, const char *szTitle) +{ + return gSymbols.SimConnect_OpenView(hSimConnect, szName, szTitle); +} + +SIMCONNECTAPI SimConnect_CloseView(HANDLE hSimConnect, const char *szName) +{ + return gSymbols.SimConnect_CloseView(hSimConnect, szName); +} + +SIMCONNECTAPI SimConnect_SetObserverLookAt(HANDLE hSimConnect, const char *szName, + SIMCONNECT_DATA_LATLONALT TargetPosition) +{ + return gSymbols.SimConnect_SetObserverLookAt(hSimConnect, szName, TargetPosition); +} + +#endif diff --git a/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h b/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h new file mode 100644 index 000000000..805701ff7 --- /dev/null +++ b/src/plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +//! \file + +#ifndef SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTSYMBOLS_H +#define SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTSYMBOLS_H + +#include + +#include "plugins/simulator/msfs2024/msfs2024export.h" + +MSFS2024_EXPORT bool loadAndResolveMSFS2024SimConnect(); + +#endif // SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMCONNECTSYMBOLS_H diff --git a/src/plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h b/src/plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h new file mode 100644 index 000000000..b5c84a94c --- /dev/null +++ b/src/plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +// in P3Dv4 the simconnect.h does not include windows.h +// here we include windows.h first + +#ifndef SWIFT_SIMPLUGIN_MSFS2024_SIMCONNECTWINDOWS_H +#define SWIFT_SIMPLUGIN_MSFS2024_SIMCONNECTWINDOWS_H + +#ifndef NOMINMAX +# define NOMINMAX +#endif + +// clash with struct interface in objbase.h used to happen +#pragma push_macro("interface") +#undef interface + +// clang-format off +#include +//#include +#include "../third_party/externals/common/include/simconnect/MSFS2024/SimConnect.h" +// clang-format on + +#include + +#pragma pop_macro("interface") + +#endif // SWIFT_SIMPLUGIN_MSFS2024_SIMCONNECTWINDOWS_H diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp b/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp index fa9497ed3..10ff7061c 100644 --- a/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp +++ b/src/plugins/simulator/msfs2024/simulatormsfs2024.cpp @@ -3,7 +3,7 @@ #include "simulatorMsFs2024.h" -#include "../fsxcommon/simconnectsymbols.h" +#include "../msfs2024/simconnectsymbolsmsfs2024.h" using namespace swift::misc; using namespace swift::misc::aviation; @@ -13,23 +13,23 @@ using namespace swift::misc::network; using namespace swift::misc::simulation; using namespace swift::misc::simulation::fscommon; using namespace swift::core; -using namespace swift::simplugin::fsxcommon; +using namespace swift::simplugin::msfs2024common; namespace swift::simplugin::msfs2024 { CSimulatorMsFs2024::CSimulatorMsFs2024(const CSimulatorPluginInfo &info, IOwnAircraftProvider *ownAircraftProvider, IRemoteAircraftProvider *remoteAircraftProvider, IClientProvider *clientProvider, QObject *parent) - : CSimulatorFsxCommon(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, parent) + : CSimulatorMsfs2024(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, parent) { - this->setDefaultModel({ "Airbus A320 Neo Asobo", CAircraftModel::TypeModelMatchingDefaultModel, + this->setDefaultModel({ "A320NEO V2", CAircraftModel::TypeModelMatchingDefaultModel, "Airbus A320 default model", CAircraftIcaoCode("A320", "L2J") }); } bool CSimulatorMsFs2024::connectTo() { if (!loadAndResolveMSFS2024SimConnect()) { return false; } - return CSimulatorFsxCommon::connectTo(); + return CSimulatorMsfs2024::connectTo(); } void CSimulatorMsFs2024::setTrueAltitude(CAircraftSituation &aircraftSituation, @@ -44,7 +44,7 @@ namespace swift::simplugin::msfs2024 void CSimulatorMsFs2024Listener::startImpl() { if (!loadAndResolveMSFS2024SimConnect()) { return; } - CSimulatorFsxCommonListener::startImpl(); + CSimulatorMsfs2024Listener::startImpl(); } } // namespace swift::simplugin::msfs2024 diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024.h b/src/plugins/simulator/msfs2024/simulatormsfs2024.h index 2ca045eee..579d04a41 100644 --- a/src/plugins/simulator/msfs2024/simulatormsfs2024.h +++ b/src/plugins/simulator/msfs2024/simulatormsfs2024.h @@ -6,12 +6,12 @@ #ifndef SWIFT_SIMPLUGIN_MSFS_SIMULATORMSFS2024_H #define SWIFT_SIMPLUGIN_MSFS_SIMULATORMSFS2024_H -#include "../fsxcommon/simulatorfsxcommon.h" +#include "../msfs2024/simulatormsfs2024common.h" namespace swift::simplugin::msfs2024 { - //! FSX simulator implementation - class CSimulatorMsFs2024 : public swift::simplugin::fsxcommon::CSimulatorFsxCommon + //! MSFS2024 simulator implementation + class CSimulatorMsFs2024 : public swift::simplugin::msfs2024common::CSimulatorMsfs2024 { Q_OBJECT @@ -27,19 +27,19 @@ namespace swift::simplugin::msfs2024 virtual bool connectTo() override; //! @} - virtual void - setTrueAltitude(swift::misc::aviation::CAircraftSituation &aircraftSituation, - const swift::simplugin::fsxcommon::DataDefinitionOwnAircraft &simulatorOwnAircraft) override; + virtual void setTrueAltitude( + swift::misc::aviation::CAircraftSituation &aircraftSituation, + const swift::simplugin::msfs2024common::DataDefinitionOwnAircraft &simulatorOwnAircraft) override; }; //! Listener for MSFS2024 - class CSimulatorMsFs2024Listener : public fsxcommon::CSimulatorFsxCommonListener + class CSimulatorMsFs2024Listener : public swift::simplugin::msfs2024common::CSimulatorMsfs2024Listener { Q_OBJECT public: //! Constructor - using CSimulatorFsxCommonListener::CSimulatorFsxCommonListener; + using CSimulatorMsfs2024Listener::CSimulatorMsfs2024Listener; protected: virtual void startImpl() override; diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024common.cpp b/src/plugins/simulator/msfs2024/simulatormsfs2024common.cpp new file mode 100644 index 000000000..a7c7e984d --- /dev/null +++ b/src/plugins/simulator/msfs2024/simulatormsfs2024common.cpp @@ -0,0 +1,3226 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +#include "simulatormsfs2024common.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "../fscommon/simulatorfscommonfunctions.h" +#include "simconnectsymbolsmsfs2024.h" + +#include "config/buildconfig.h" +#include "core/application.h" +#include "core/modelsetbuilder.h" +#include "core/webdataservices.h" +#include "core/webdataservicesms.h" +#include "gui/components/dbmappingcomponent.h" +#include "gui/guiapplication.h" +#include "gui/models/distributorlistmodel.h" +#include "misc/aviation/airportlist.h" +#include "misc/country.h" +#include "misc/geo/elevationplane.h" +#include "misc/logmessage.h" +#include "misc/math/mathutils.h" +#include "misc/network/textmessage.h" +#include "misc/simulation/aircraftmodel.h" +#include "misc/simulation/data/modelcaches.h" +#include "misc/simulation/distributorlist.h" +#include "misc/simulation/fscommon/aircraftcfgparser.h" +#include "misc/simulation/fscommon/bcdconversions.h" +#include "misc/simulation/fscommon/fscommonutil.h" +#include "misc/simulation/interpolation/interpolatormulti.h" +#include "misc/simulation/msfs2024/simconnectutilities.h" +#include "misc/simulation/settings/simulatorsettings.h" +#include "misc/simulation/simulatorplugininfo.h" +#include "misc/statusmessagelist.h" +#include "misc/threadutils.h" +#include "misc/verify.h" +#include "misc/worker.h" + +using namespace swift::config; +using namespace swift::misc; +using namespace swift::misc::aviation; +using namespace swift::misc::physical_quantities; +using namespace swift::misc::geo; +using namespace swift::misc::network; +using namespace swift::misc::math; +using namespace swift::misc::simulation; +using namespace swift::misc::simulation::data; +using namespace swift::misc::simulation::fscommon; +using namespace swift::misc::simulation::msfs2024; +using namespace swift::misc::simulation::settings; +using namespace swift::core; +using namespace swift::core::db; +using namespace swift::simplugin::fscommon; +using namespace swift::gui; +using namespace swift::gui::models; +using namespace swift::gui::components; + +namespace swift::simplugin::msfs2024common +{ + + CSimulatorMsfs2024::CSimulatorMsfs2024(const CSimulatorPluginInfo &info, IOwnAircraftProvider *ownAircraftProvider, + IRemoteAircraftProvider *remoteAircraftProvider, + IClientProvider *clientProvider, QObject *parent) + : CSimulatorFsCommon(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, parent) + { + Q_ASSERT_X(ownAircraftProvider, Q_FUNC_INFO, "Missing provider"); + Q_ASSERT_X(remoteAircraftProvider, Q_FUNC_INFO, "Missing provider"); + Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing global object"); + + m_simObjectTimer.setInterval(AddPendingAircraftIntervalMs); + // default model will be set in derived class + + CSimulatorMsfs2024::registerHelp(); + connect(&m_simObjectTimer, &QTimer::timeout, this, &CSimulatorMsfs2024::timerBasedObjectAddOrRemove); + + // m_distributorPreferences = std::make_shared(); + } + + CSimulatorMsfs2024::~CSimulatorMsfs2024() { this->disconnectFrom(); } + + bool CSimulatorMsfs2024::isConnected() const { return m_simConnected && m_hSimConnect; } + + bool CSimulatorMsfs2024::isSimulating() const { return m_simSimulating && this->isConnected(); } + + bool CSimulatorMsfs2024::connectTo() + { + if (this->isConnected()) { return true; } + this->reset(); + + const HRESULT hr = SimConnect_Open(&m_hSimConnect, sApp->swiftVersionChar(), nullptr, 0, nullptr, 0); + if (isFailure(hr)) + { + // reset state as expected for unconnected + this->reset(); + return false; + } + + // set structures and move on + this->triggerAutoTraceSendId(); // we trace the init phase, so in case something goes wrong there + this->initEvents(); + this->initEventsP3D(); + this->initDataDefinitionsWhenConnected(); + + m_timerId = this->startTimer(DispatchIntervalMs); + // do not start m_addPendingAircraftTimer here, it will be started when object was added + return true; + } + + bool CSimulatorMsfs2024::disconnectFrom() + { + if (!m_simConnected) { return true; } + m_simSimulating = false; // thread as stopped, just setting the flag here avoids overhead of on onSimStopped + m_traceAutoUntilTs = -1; + m_traceSendId = false; + this->reset(); // mark as disconnected and reset all values + + if (m_hSimConnect) + { + SimConnect_Close(m_hSimConnect); + m_hSimConnect = nullptr; + m_simConnected = false; + } + + // emit status + return CSimulatorFsCommon::disconnectFrom(); + } + + bool CSimulatorMsfs2024::physicallyAddRemoteAircraft(const CSimulatedAircraft &newRemoteAircraft) + { + this->logAddingAircraftModel(newRemoteAircraft); + return this->physicallyAddRemoteAircraftImpl(newRemoteAircraft, ExternalCall); + } + + bool CSimulatorMsfs2024::updateCOMFromSwiftToSimulator(const CFrequency &newFreq, const CFrequency &lastSimFreq, + CFrequency &last25kHzSimFreq, EventIds id) + { + if (newFreq == lastSimFreq) { return false; } + + if (CComSystem::isExclusiveWithin8_33kHzChannel(newFreq) && last25kHzSimFreq.isNull()) + { + // Switch from 25 to 8.33 + // Store last 25 kHz frequency and do not send to simulator + last25kHzSimFreq = lastSimFreq; + return false; + } + + if (CComSystem::isWithin25kHzChannel(newFreq)) + { + // Send to simulator + last25kHzSimFreq.setNull(); + SimConnect_TransmitClientEvent(m_hSimConnect, 0, id, CBcdConversions::comFrequencyToBcdHz(newFreq), + SIMCONNECT_GROUP_PRIORITY_HIGHEST, + SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY); + return true; + } + + // Already 8.33 -> nothing to do + return false; + } + + bool CSimulatorMsfs2024::updateOwnSimulatorCockpit(const CSimulatedAircraft &ownAircraft, + const CIdentifier &originator) + { + if (originator == this->identifier()) { return false; } + if (!this->isSimulating()) { return false; } + + // actually those data should be the same as ownAircraft + const CComSystem newCom1 = ownAircraft.getCom1System(); + const CComSystem newCom2 = ownAircraft.getCom2System(); + const CTransponder newTransponder = ownAircraft.getTransponder(); + + bool changed = false; + + changed |= updateCOMFromSwiftToSimulator(newCom1.getFrequencyActive(), m_simCom1.getFrequencyActive(), + m_lastCom1Active, EventSetCom1Active); + changed |= updateCOMFromSwiftToSimulator(newCom1.getFrequencyStandby(), m_simCom1.getFrequencyStandby(), + m_lastCom1Standby, EventSetCom1Standby); + changed |= updateCOMFromSwiftToSimulator(newCom2.getFrequencyActive(), m_simCom2.getFrequencyActive(), + m_lastCom2Active, EventSetCom2Active); + changed |= updateCOMFromSwiftToSimulator(newCom2.getFrequencyStandby(), m_simCom2.getFrequencyStandby(), + m_lastCom2Standby, EventSetCom2Standby); + + if (newTransponder.getTransponderMode() != m_simTransponder.getTransponderMode()) + { + if (m_useSbOffsets) + { + byte ident = newTransponder.isIdentifying() ? 1U : 0U; // 1 is ident + byte standby = newTransponder.isInStandby() ? 1U : 0U; // 1 is standby + HRESULT hr = s_ok(); + + hr += SimConnect_SetClientData(m_hSimConnect, ClientAreaSquawkBox, + CSimConnectDefinitions::DataClientAreaSbIdent, + SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, 1, &ident); + hr += SimConnect_SetClientData(m_hSimConnect, ClientAreaSquawkBox, + CSimConnectDefinitions::DataClientAreaSbStandby, + SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, 1, &standby); + if (isFailure(hr)) + { + this->triggerAutoTraceSendId(); + CLogMessage(this).warning(u"Setting transponder mode failed (SB offsets)"); + } + else + { + if (m_logSbOffsets) + { + const QString lm = + "SB sent: ident " % QString::number(ident) % u" standby " % QString::number(standby); + CLogMessage(this).info(lm); + } + } + changed = true; + } + else if (this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS2024()) + { + DataDefinitionMSFSTransponderMode t; + t.transponderMode = (newTransponder.isInStandby() ? 1 : 4); + t.ident = newTransponder.isIdentifying(); + + HRESULT hr = s_ok(); + + hr += SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataTransponderModeMSFS, + SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(DataDefinitionMSFSTransponderMode), &t); + + if (isFailure(hr)) { CLogMessage(this).warning(u"Setting transponder mode failed (MSFS2024)"); } + + changed = true; + } + } + + // avoid changes of cockpit back to old values due to an outdated read back value + if (changed) { m_skipCockpitUpdateCycles = SkipUpdateCyclesForCockpit; } + + // bye + return changed; + } + + bool CSimulatorMsfs2024::updateOwnSimulatorSelcal(const CSelcal &selcal, const CIdentifier &originator) + { + if (originator == this->identifier()) { return false; } + if (!this->isSimulating()) { return false; } + + //! KB 2018-11 that would need to go to updateOwnAircraftFromSimulator if the simulator ever supports SELCAL + //! KB 2018-11 als we would need to send the value to FS9/FSX (currently we only deal with it on FS9/FSX level) + m_selcal = selcal; + return false; + } + + void CSimulatorMsfs2024::displayStatusMessage(const CStatusMessage &message) const + { + QByteArray m = message.getMessage().toLatin1().constData(); + m.append('\0'); + + SIMCONNECT_TEXT_TYPE type = SIMCONNECT_TEXT_TYPE_PRINT_BLACK; + switch (message.getSeverity()) + { + case CStatusMessage::SeverityDebug: return; + case CStatusMessage::SeverityInfo: type = SIMCONNECT_TEXT_TYPE_PRINT_GREEN; break; + case CStatusMessage::SeverityWarning: type = SIMCONNECT_TEXT_TYPE_PRINT_YELLOW; break; + case CStatusMessage::SeverityError: type = SIMCONNECT_TEXT_TYPE_PRINT_RED; break; + } + const HRESULT hr = + SimConnect_Text(m_hSimConnect, type, 7.5, EventTextMessage, static_cast(m.size()), m.data()); + Q_UNUSED(hr) + } + + void CSimulatorMsfs2024::displayTextMessage(const CTextMessage &message) const + { + QByteArray m = message.asString(true, true).toLatin1().constData(); + m.append('\0'); + + SIMCONNECT_TEXT_TYPE type = SIMCONNECT_TEXT_TYPE_PRINT_BLACK; + if (message.isSupervisorMessage()) { type = SIMCONNECT_TEXT_TYPE_PRINT_RED; } + else if (message.isPrivateMessage()) { type = SIMCONNECT_TEXT_TYPE_PRINT_YELLOW; } + else if (message.isRadioMessage()) { type = SIMCONNECT_TEXT_TYPE_PRINT_GREEN; } + + const HRESULT hr = + SimConnect_Text(m_hSimConnect, type, 7.5, EventTextMessage, static_cast(m.size()), m.data()); + Q_UNUSED(hr) + } + + bool CSimulatorMsfs2024::isPhysicallyRenderedAircraft(const CCallsign &callsign) const + { + return m_simConnectObjects.contains(callsign); + } + + CCallsignSet CSimulatorMsfs2024::physicallyRenderedAircraft() const + { + CCallsignSet callsigns(m_simConnectObjects.keys()); + callsigns.push_back( + m_addAgainAircraftWhenRemoved.getCallsigns()); // not really rendered right now, but very soon + callsigns.push_back( + m_addPendingAircraft.keys()); // not really rendered, but for the logic it should look like it is + return CCallsignSet(m_simConnectObjects.keys()); + } + + CStatusMessageList CSimulatorMsfs2024::debugVerifyStateAfterAllAircraftRemoved() const + { + CStatusMessageList msgs; + if (!CBuildConfig::isLocalDeveloperDebugBuild()) { return msgs; } + msgs = CSimulatorFsCommon::debugVerifyStateAfterAllAircraftRemoved(); + if (!m_simConnectObjects.isEmpty()) + { + msgs.push_back(CStatusMessage(this).error(u"m_simConnectObjects not empty: '%1'") + << m_simConnectObjects.getAllCallsignStringsAsString(true)); + } + if (!m_simConnectObjectsPositionAndPartsTraces.isEmpty()) + { + msgs.push_back(CStatusMessage(this).error(u"m_simConnectObjectsPositionAndPartsTraces not empty: '%1'") + << m_simConnectObjectsPositionAndPartsTraces.getAllCallsignStringsAsString(true)); + } + if (!m_addAgainAircraftWhenRemoved.isEmpty()) + { + msgs.push_back(CStatusMessage(this).error(u"m_addAgainAircraftWhenRemoved not empty: '%1'") + << m_addAgainAircraftWhenRemoved.getCallsignStrings(true).join(", ")); + } + return msgs; + } + + QString CSimulatorMsfs2024::getStatisticsSimulatorSpecific() const + { + static const QString specificInfo( + "dispatch #: %1 %2 times (cur/max): %3ms (%4ms) %5ms (%6ms) %7 %8 simData#: %9"); + return specificInfo.arg(m_dispatchProcCount) + .arg(m_dispatchProcEmptyCount) + .arg(m_dispatchTimeMs) + .arg(m_dispatchProcTimeMs) + .arg(m_dispatchMaxTimeMs) + .arg(m_dispatchProcMaxTimeMs) + .arg(CSimConnectUtilities::simConnectReceiveIdToString(static_cast(m_dispatchReceiveIdMaxTime)), + requestIdToString(m_dispatchRequestIdMaxTime)) + .arg(m_requestSimObjectDataCount); + } + + void CSimulatorMsfs2024::CacheSimObjectAndLiveries(const SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *msg) + { + for (unsigned i = 0; i < msg->dwArraySize; ++i) + { + const SIMCONNECT_ENUMERATE_SIMOBJECT_LIVERY &element = msg->rgData[i]; + + // combine aircraft title and livery name for database search + // const QString combined = QString(element.AircraftTitle) + " " + QString(element.LiveryName); + + vSimObjectsAndLiveries.push_back( + { QString("%1 %2").arg(element.AircraftTitle, element.LiveryName).trimmed(), + QString::fromUtf8(element.AircraftTitle), QString::fromUtf8(element.LiveryName) }); + } + + if (!sSimmobjectLoadedState.bLoadStarted) + { + sSimmobjectLoadedState.bLoadStarted = true; + CLogMessage(this).info(u"Start loading SimObjects and liverys from simulator"); + + const CStatusMessage m = CStatusMessage(this, CStatusMessage::SeverityInfo, + u"Start loading SimObjects and liverys from simulator"); + } + + // Check the result set to detect the end of the transmission + switch (msg->dwRequestID) + { + case CSimConnectDefinitions::REQUEST_AIRPLANE: + if (msg->dwArraySize < 79) sSimmobjectLoadedState.bAirplaneLoaded = true; + break; + case CSimConnectDefinitions::REQUEST_HELICOPTER: + if (msg->dwArraySize < 79) sSimmobjectLoadedState.bHelicopterLoaded = true; + break; + case CSimConnectDefinitions::REQUEST_HOT_AIR: + if (msg->dwArraySize < 79) sSimmobjectLoadedState.bHotAirLoaded = true; + break; + + default: break; + } + + // sSimmobjectLoadedState.bHotAirLoaded + if (sSimmobjectLoadedState.bAirplaneLoaded && sSimmobjectLoadedState.bHelicopterLoaded) + { + sSimmobjectLoadedState.bLoadStarted = false; + size_t countmodels = vSimObjectsAndLiveries.size(); + CLogMessage(this).info(u"%1 SimObjects and Liveries loaded from SimConnect") << countmodels; + + // now we try to create a new temporary model list + setSimObjectAndLiveries(); + + CLogMessage(this).info(u"finished new model set"); + } + } + + void CSimulatorMsfs2024::setSimObjectAndLiveries() + { + CLogMessage(this).info(u"%1 SimObjects and Liveries in vSimObjectsAndLiveries") + << vSimObjectsAndLiveries.size(); + + auto *worker = CWorker::fromTask(this, "createNewModelList", [=]() { + this->createNewModelList(); + return QVariant(); // void-Result + }); + + // TODO TZ need help: Where can a message be placed indicating that loading is complete? + worker->then(this, [=] { CLogMessage(this).info(u"SimObjects and Liveries in vSimObjectsAndLiveries ready"); }); + } + + void CSimulatorMsfs2024::createNewModelList() + { + + const CSpecializedSimulatorSettings settings = this->getSimulatorSettings(); + CSimulatorSettings m_generic = settings.getGenericSettings(); + QStringList excludePatterns = m_generic.getModelExcludeDirectoryPatterns(); + QStringList filterList = m_generic.getModelDirectories(); + + bool gui_application = true; + bool useFilterList = true; + bool matchFilter = false; + + QString guiName = sGui->getApplicationName(); + if (guiName.contains("mapping")) gui_application = false; + + const CAircraftMatcherSetup setup = m_matchingSettings.get(); + bool skipExcluded = setup.getMatchingMode().testFlag(CAircraftMatcherSetup::ExcludeNoExcluded); + + CAircraftModelList newModels; + + for (int i = 0; i < static_cast(vSimObjectsAndLiveries.size()); ++i) + { + const sSimObjectLivery &modelLivery = vSimObjectsAndLiveries[i]; + + CAircraftModel model; + CAircraftModel modelFromDb = + sGui->getWebDataServices()->getModelForModelString(modelLivery.szSimObjectCombinedTitle.trimmed()); + + // model is marked as excluded in the database, so skip it + if (modelFromDb.getModelMode() == CAircraftModel::Exclude && skipExcluded && gui_application) { continue; } + + // If the model is in the database, there is a DbKey + int modelkey = modelFromDb.getDbKey(); + if (modelkey) model = modelFromDb; // copy all data from db + + // we need the combined string of the model for further processing + model.setModelString(modelLivery.szSimObjectCombinedTitle); + model.setModelLivery(modelLivery.szLiveryName); + model.setModelType(CAircraftModel::TypeOwnSimulatorModel); + if (!modelkey) model.setModelType(CAircraftModel::TypeManuallySet); + model.setSimulator(this->getSimulatorInfo()); + + bool excluded = false; + for (const QString &rawPattern : excludePatterns) + { + const QString pattern = rawPattern.trimmed(); + if (pattern.isEmpty()) continue; + if (model.getModelString().contains(pattern, Qt::CaseInsensitive)) + { + excluded = true; + break; + } + } + if (excluded) continue; // skip adding this model + + if (useFilterList) + { + matchFilter = false; + for (const QString &rawFilter : filterList) + { + if (rawFilter.trimmed().contains("*")) + { + // wildcard found, disable filter list + useFilterList = false; + continue; + } + const QString filter = rawFilter.trimmed(); + if (model.getModelString().contains(filter, Qt::CaseInsensitive)) + { + matchFilter = true; + break; + } + } + } + if (useFilterList && !matchFilter) continue; // skip adding this model + + newModels.replaceOrAddModelWithString(model, Qt::CaseInsensitive); + } + + CAircraftModelList newModelList; + CAircraftModelList currentSet; + CAircraftModelList NewSet; + + // store the model in the new list + newModelList.replaceOrAddModelsWithString(newModels, Qt::CaseInsensitive); + + CLogMessage(this).info(u"%1 SimObjects and Liveries in newModelList") << newModelList.size(); + + if (!newModelList.isEmpty()) + { + + m_simulatorInfo = this->getSimulatorInfo(); + + bool givenDistributorsOnly = false; + bool dbDataOnly = false; + bool dbIcaoOnly = false; + bool incremnental = false; + bool sortByDistributor = true; + bool consolidateWithDb = false; + bool ShowAllInstalledModels = true; // msfs20424 always show all installed models + + if (gui_application) + { + givenDistributorsOnly = m_generic.getPropertyDistributorFiltered(); + dbDataOnly = m_generic.getPropertyWithDbEntry(); + incremnental = false; + consolidateWithDb = true; + ShowAllInstalledModels = true; + } + + // CDistributorList distributorList; + // for (const QString &name : distributorNames) { distributorList.push_back(CDistributor(name)); } + CDistributorList distributorList = sGui->getWebDataServices()->getDistributors(); + + CModelSetBuilder builder(nullptr); + CModelSetBuilder::Builder options = + givenDistributorsOnly ? CModelSetBuilder::GivenDistributorsOnly : CModelSetBuilder::NoOptions; + if (dbDataOnly) { options |= CModelSetBuilder::OnlyDbData; } + if (dbIcaoOnly) { options |= CModelSetBuilder::OnlyDbIcaoCodes; } + if (incremnental) { options |= CModelSetBuilder::Incremental; } + if (sortByDistributor) { options |= CModelSetBuilder::SortByDistributors; } + if (consolidateWithDb) { options |= CModelSetBuilder::ConsolidateWithDb; } + if (ShowAllInstalledModels) { options |= CModelSetBuilder::ShowAllInstalledModels; } + const CSimulatorInfo &simulator = this->getSimulatorInfo(); + + CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().synchronizeCache(simulator); + currentSet = CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().getCachedModels(simulator); + + NewSet = builder.buildModelSet(simulator, newModelList, currentSet, options, distributorList); + + CAircraftMatcher matcher; + swift::misc::simulation::CAircraftMatcherSetup mSetup = matcher.getSetup(); + + NewSet.setSimulatorInfo(simulator); + matcher.setModelSet(NewSet, m_simulatorInfo, true); + + const QDateTime latestDbModelsTs = + NewSet.isEmpty() ? sApp->getWebDataServices()->getCacheTimestamp(CEntityFlags::ModelEntity) : + NewSet.latestTimestamp(); + if (!latestDbModelsTs.isValid()) { return; } + + // for swiftgui it is enough to set the cache here + if (gui_application) + CCentralMultiSimulatorModelSetCachesProvider::modelCachesInstance().setModelsForSimulator(NewSet, + simulator); + + CCentralMultiSimulatorModelCachesProvider::modelCachesInstance().setCachedModels(NewSet, simulator); + + const CStatusMessage m = CStatusMessage(this, CStatusMessage::SeverityInfo, + u"Loading SimObjects and Liveries from the Simulator completed"); + + CLogMessage(this).info(u"%1 SimObjects and Liveries in DbModelList") << NewSet.size(); + + // TODO TZ can used only for debugging + // int cstoremodels = writeSimObjectsAndLiveriesToFile(NewSet); + } + } + + int CSimulatorMsfs2024::writeSimObjectsAndLiveriesToFile(CAircraftModelList Modelset) + { + + int counter = 0; + const std::string dateiname = "aircraftlist.txt"; + std::ofstream datei(dateiname, std::ios::out); + if (!datei) return 0; + for (int i = 0; i < static_cast(Modelset.size()); ++i) + { + datei << Modelset[i].getShortModelString().toStdString() + << "::" << Modelset[i].getModelLivery().toStdString() << "::" << Modelset[i].getDbKey() << std::endl; + ++counter; + } + datei.close(); + return counter; + } + + bool CSimulatorMsfs2024::isTracingSendId() const + { + if (m_traceSendId) { return true; } // explicit + if (m_traceAutoUntilTs < 0) { return false; } // no auto + const qint64 ts = QDateTime::currentMSecsSinceEpoch(); + const bool trace = ts <= m_traceAutoUntilTs; + return trace; + } + + void CSimulatorMsfs2024::setTractingSendId(bool trace) + { + m_traceSendId = trace; + m_traceAutoUntilTs = -1; + } + + void CSimulatorMsfs2024::setAddingAsSimulatedObjectEnabled(bool enabled) + { + m_useAddSimulatedObj = enabled; + const CSimulatorInfo sim = this->getSimulatorInfo(); + CFsxP3DSettings settings = m_detailsSettings.getSettings(sim); + settings.setAddingAsSimulatedObjectEnabled(enabled); + m_detailsSettings.setSettings(settings, sim); + } + + void CSimulatorMsfs2024::setUsingSbOffsetValues(bool enabled) + { + m_useSbOffsets = enabled; + const CSimulatorInfo sim = this->getSimulatorInfo(); + CFsxP3DSettings settings = m_detailsSettings.getSettings(sim); + settings.setSbOffsetsEnabled(enabled); + m_detailsSettings.setSettings(settings, sim); + } + + void CSimulatorMsfs2024::resetAircraftStatistics() + { + m_dispatchProcCount = 0; + m_dispatchProcEmptyCount = 0; + m_dispatchMaxTimeMs = -1; + m_dispatchProcMaxTimeMs = -1; + m_dispatchTimeMs = -1; + m_dispatchProcTimeMs = -1; + m_requestSimObjectDataCount = 0; + m_dispatchReceiveIdLast = SIMCONNECT_RECV_ID_NULL; + m_dispatchReceiveIdMaxTime = SIMCONNECT_RECV_ID_NULL; + m_dispatchRequestIdLast = CSimConnectDefinitions::RequestEndMarker; + m_dispatchRequestIdMaxTime = CSimConnectDefinitions::RequestEndMarker; + CSimulatorPluginCommon::resetAircraftStatistics(); + } + + void CSimulatorMsfs2024::setFlightNetworkConnected(bool connected) + { + // toggled? + if (connected == !this->isFlightNetworkConnected()) + { + // toggling, we trace for a while to better monitor those "critical" phases + this->triggerAutoTraceSendId(); + } + + // update SB area network connected + byte sbNetworkConnected = connected ? 1u : 0u; + const HRESULT hr = SimConnect_SetClientData(m_hSimConnect, ClientAreaSquawkBox, + CSimConnectDefinitions::DataClientAreaSbConnected, + SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, 1, &sbNetworkConnected); + if (isFailure(hr)) { CLogMessage(this).warning(u"Setting network connected failed (SB offsets)"); } + + ISimulator::setFlightNetworkConnected(connected); + } + + CStatusMessageList CSimulatorMsfs2024::getInterpolationMessages(const CCallsign &callsign) const + { + if (!m_simConnectObjects.contains(callsign)) { return CStatusMessageList(); } + const CInterpolationAndRenderingSetupPerCallsign setup = + this->getInterpolationSetupConsolidated(callsign, false); + return (m_simConnectObjects[callsign]).getInterpolationMessages(setup.getInterpolatorMode()); + } + + bool CSimulatorMsfs2024::testSendSituationAndParts(const CCallsign &callsign, const CAircraftSituation &situation, + const CAircraftParts &parts) + { + if (!m_simConnectObjects.contains(callsign)) { return false; } + CSimConnectObject simObject = m_simConnectObjects.value(callsign); + int u = 0; + if (!parts.isNull()) + { + this->sendRemoteAircraftPartsToSimulator(simObject, parts); + u++; + } + // TODO TZ check: The function call destroys the configured simulation. + // if (!situation.isNull()) + //{ + // SIMCONNECT_DATA_INITPOSITION position = this->aircraftSituationToPosition(situation, true); + // const bool traceSendId = this->isTracingSendId(); + // const HRESULT hr = this->logAndTraceSendId( + // SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetPosition, + // static_cast(simObject.getObjectId()), 0, 0, + // sizeof(SIMCONNECT_DATA_INITPOSITION), &position), + // traceSendId, simObject, "Failed to set position", Q_FUNC_INFO, "SimConnect_SetDataOnSimObject"); + // if (hr == S_OK) { u++; } + //} + return u > 0; + } + + CSimConnectDefinitions::SimObjectRequest CSimulatorMsfs2024::requestToSimObjectRequest(DWORD requestId) + { + DWORD v = static_cast(CSimConnectDefinitions::SimObjectEndMarker); + if (isRequestForSimObjAircraft(requestId)) { v = (requestId - RequestSimObjAircraftStart) / MaxSimObjAircraft; } + Q_ASSERT_X(v <= CSimConnectDefinitions::SimObjectEndMarker, Q_FUNC_INFO, "Invalid value"); + return static_cast(v); + } + + bool CSimulatorMsfs2024::stillDisplayReceiveExceptions() + { + m_receiveExceptionCount++; + return m_receiveExceptionCount < IgnoreReceiveExceptions; + } + + CSimConnectObject CSimulatorMsfs2024::getSimObjectForObjectId(DWORD objectId) const + { + return this->getSimConnectObjects().getSimObjectForObjectId(objectId); + } + + void CSimulatorMsfs2024::setSimConnected() + { + m_simConnected = true; + this->initSimulatorInternals(); + this->emitSimulatorCombinedStatus(); + + // Internals depends on simulator data which take a while to be read + // this is a trick and I re-init again after a while (which is not really expensive) + const QPointer myself(this); + QTimer::singleShot(2500, this, [myself] { + if (!myself) { return; } + myself->initSimulatorInternals(); + }); + } + + void CSimulatorMsfs2024::onSimRunning() + { + const QPointer myself(this); + QTimer::singleShot(DeferSimulatingFlagMs, this, [=] { + if (!myself) { return; } + m_simulatingChangedTs = QDateTime::currentMSecsSinceEpoch(); + this->onSimRunningDeferred(m_simulatingChangedTs); + }); + } + + void CSimulatorMsfs2024::onSimRunningDeferred(qint64 referenceTs) + { + if (m_simSimulating) { return; } // already simulatig + if (referenceTs != m_simulatingChangedTs) { return; } // changed, so no longer valid + m_simSimulating = true; // only place where this should be set to true + m_simConnected = true; + + const CFsxP3DSettings settings = m_detailsSettings.getSettings(this->getSimulatorInfo()); + m_useAddSimulatedObj = settings.isAddingAsSimulatedObjectEnabled(); + m_useSbOffsets = settings.isSbOffsetsEnabled(); + if (this->getSimulatorPluginInfo().getSimulatorInfo().isMSFS2024()) + { + m_useSbOffsets = false; // Always disable SbOffsets for MSFS. Using new transponder mode property directly + } + + HRESULT hr = s_ok(); + hr += this->logAndTraceSendId( + SimConnect_RequestDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::RequestOwnAircraft, + CSimConnectDefinitions::DataOwnAircraft, SIMCONNECT_OBJECT_ID_USER, + SIMCONNECT_PERIOD_VISUAL_FRAME), + "Cannot request own aircraft data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + + hr += this->logAndTraceSendId( + SimConnect_RequestDataOnSimObjectType(m_hSimConnect, CSimConnectDefinitions::RequestOwnAircraftTitle, + CSimConnectDefinitions::DataOwnAircraftTitle, 0, + SIMCONNECT_SIMOBJECT_TYPE_USER), + "Cannot request title and livery", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObjectType"); + + hr += this->logAndTraceSendId(SimConnect_RequestDataOnSimObject( + m_hSimConnect, CSimConnectDefinitions::RequestMSFSTransponder, + CSimConnectDefinitions::DataTransponderModeMSFS, SIMCONNECT_OBJECT_ID_USER, + SIMCONNECT_PERIOD_VISUAL_FRAME, SIMCONNECT_DATA_REQUEST_FLAG_CHANGED), + "Cannot request MSFS transponder data", Q_FUNC_INFO, + "SimConnect_RequestDataOnSimObject"); + + if (isFailure(hr)) { return; } + this->emitSimulatorCombinedStatus(); // force sending status + } + + void CSimulatorMsfs2024::onSimStopped() + { + // stopping events in FSX: Load menu, weather and season + CLogMessage(this).info(u"Simulator stopped: %1") << this->getSimulatorDetails(); + const SimulatorStatus oldStatus = this->getSimulatorStatus(); + m_simSimulating = false; + m_simulatingChangedTs = QDateTime::currentMSecsSinceEpoch(); + this->emitSimulatorCombinedStatus(oldStatus); + } + + void CSimulatorMsfs2024::onSimFrame() + { + if (m_updateRemoteAircraftInProgress) { return; } + QPointer myself(this); + QTimer::singleShot(0, this, [=] { + // run decoupled from simconnect event queue + if (!myself) { return; } + myself->updateRemoteAircraft(); + }); + } + + void CSimulatorMsfs2024::onSimExit() + { + CLogMessage(this).info(u"Simulator exit: %1") << this->getSimulatorDetails(); + + // reset complete state, we are going down + m_simulatingChangedTs = QDateTime::currentMSecsSinceEpoch(); + this->safeKillTimer(); + + // if called from dispatch function, avoid that SimConnectProc disconnects itself while in SimConnectProc + QPointer myself(this); + QTimer::singleShot(0, this, [=] { + if (!myself) { return; } + this->disconnectFrom(); + }); + } + + SIMCONNECT_DATA_REQUEST_ID CSimulatorMsfs2024::obtainRequestIdForSimObjAircraft() + { + const SIMCONNECT_DATA_REQUEST_ID id = m_requestIdSimObjAircraft++; + if (id > RequestSimObjAircraftEnd) { m_requestIdSimObjAircraft = RequestSimObjAircraftStart; } + return id; + } + + bool CSimulatorMsfs2024::releaseAIControl(const CSimConnectObject &simObject, SIMCONNECT_DATA_REQUEST_ID requestId) + { + const SIMCONNECT_OBJECT_ID objectId = simObject.getObjectId(); + const HRESULT hr1 = + this->logAndTraceSendId(SimConnect_AIReleaseControl(m_hSimConnect, objectId, requestId), simObject, + "Release control", Q_FUNC_INFO, "SimConnect_AIReleaseControl"); + const HRESULT hr2 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFreezeLatLng, 1, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, + SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + simObject, "EventFreezeLatLng", Q_FUNC_INFO, "SimConnect_TransmitClientEvent"); + const HRESULT hr3 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFreezeAlt, 1, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, + SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + simObject, "EventFreezeAlt", Q_FUNC_INFO, "SimConnect_TransmitClientEvent"); + const HRESULT hr4 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFreezeAtt, 1, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, + SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + simObject, "EventFreezeAtt", Q_FUNC_INFO, "SimConnect_TransmitClientEvent"); + + return isOk(hr1, hr2, hr3, hr4); + } + + bool CSimulatorMsfs2024::isValidSimObjectNotPendingRemoved(const CSimConnectObject &simObject) const + { + if (!simObject.hasValidRequestAndObjectId()) { return false; } + if (simObject.isPendingRemoved()) { return false; } + if (!m_simConnectObjects.contains(simObject.getCallsign())) { return false; } // removed in meantime + return true; + } + + CSimConnectObject CSimulatorMsfs2024::getSimObjectForTrace(const TraceFsxSendId &trace) const + { + return m_simConnectObjects.getSimObjectForOtherSimObject(trace.simObject); + } + + bool CSimulatorMsfs2024::removeSimObjectForTrace(const TraceFsxSendId &trace) + { + return m_simConnectObjects.removeByOtherSimObject(trace.simObject); + } + + // TODO TZ check if nessesary in MSFS2024 + void CSimulatorMsfs2024::removeCamera(CSimConnectObject &simObject) + { + // not in FSX + Q_UNUSED(simObject) + } + + // TODO TZ check if nessesary in MSFS2024 + void CSimulatorMsfs2024::removeObserver(CSimConnectObject &simObject) + { + // not in FSX + Q_UNUSED(simObject) + } + + bool CSimulatorMsfs2024::triggerAutoTraceSendId(qint64 traceTimeMs) + { + if (m_traceSendId) { return false; } // no need + if (this->isShuttingDownOrDisconnected()) { return false; } + const qint64 ts = QDateTime::currentMSecsSinceEpoch(); + const qint64 traceUntil = traceTimeMs + ts; + if (traceUntil <= m_traceAutoUntilTs) { return false; } + m_traceAutoUntilTs = traceUntil; + + static const QString format("hh:mm:ss.zzz"); + const QString untilString = QDateTime::fromMSecsSinceEpoch(traceUntil).toString(format); + CLogMessage(this).info(u"Triggered MSFS2024 auto trace until %1") << untilString; + const QPointer myself(this); + QTimer::singleShot(traceTimeMs * 1.2, this, [=] { + // triggered by mself (ts check), otherwise ignore + if (!myself) { return; } + if (m_traceAutoUntilTs > QDateTime::currentMSecsSinceEpoch()) { return; } + if (m_traceAutoUntilTs < 0) { return; } // alread off + CLogMessage(this).info(u"Auto trace id off"); + m_traceAutoUntilTs = -1; + }); + return true; + } + + void CSimulatorMsfs2024::setTrueAltitude(CAircraftSituation &aircraftSituation, + const DataDefinitionOwnAircraft &simulatorOwnAircraft) + { + aircraftSituation.setAltitude( + CAltitude(simulatorOwnAircraft.altitudeFt, CAltitude::MeanSeaLevel, CLengthUnit::ft())); + } + + void CSimulatorMsfs2024::updateOwnAircraftFromSimulator(const DataDefinitionOwnAircraft &simulatorOwnAircraft) + { + const qint64 ts = QDateTime::currentMSecsSinceEpoch(); + + CSimulatedAircraft myAircraft(getOwnAircraft()); + CCoordinateGeodetic position; + position.setLatitude(CLatitude(simulatorOwnAircraft.latitudeDeg, CAngleUnit::deg())); + position.setLongitude(CLongitude(simulatorOwnAircraft.longitudeDeg, CAngleUnit::deg())); + + if (simulatorOwnAircraft.pitchDeg < -90.0 || simulatorOwnAircraft.pitchDeg >= 90.0) + { + CLogMessage(this).warning(u"MSFS2024: Pitch value (own aircraft) out of limits: %1") + << simulatorOwnAircraft.pitchDeg; + } + CAircraftSituation aircraftSituation; + aircraftSituation.setMSecsSinceEpoch(ts); + aircraftSituation.setPosition(position); + // MSFS2024 has inverted pitch and bank angles + aircraftSituation.setPitch(CAngle(-simulatorOwnAircraft.pitchDeg, CAngleUnit::deg())); + aircraftSituation.setBank(CAngle(-simulatorOwnAircraft.bankDeg, CAngleUnit::deg())); + aircraftSituation.setHeading(CHeading(simulatorOwnAircraft.trueHeadingDeg, CHeading::True, CAngleUnit::deg())); + aircraftSituation.setGroundSpeed(CSpeed(simulatorOwnAircraft.velocity, CSpeedUnit::kts())); + aircraftSituation.setGroundElevation( + CAltitude(simulatorOwnAircraft.elevationFt, CAltitude::MeanSeaLevel, CLengthUnit::ft()), + CAircraftSituation::FromProvider); + setTrueAltitude(aircraftSituation, simulatorOwnAircraft); + aircraftSituation.setPressureAltitude(CAltitude(simulatorOwnAircraft.pressureAltitudeM, CAltitude::MeanSeaLevel, + CAltitude::PressureAltitude, CLengthUnit::m())); + // set on ground also in situation for consistency and future usage + // it is duplicated in parts + aircraftSituation.setOnGroundInfo( + { dtb(simulatorOwnAircraft.simOnGround) ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround, + COnGroundInfo::OutOnGroundOwnAircraft }); + + CAircraftVelocity aircraftVelocity( + simulatorOwnAircraft.velocityWorldX, simulatorOwnAircraft.velocityWorldY, + simulatorOwnAircraft.velocityWorldZ, CSpeedUnit::ft_s(), simulatorOwnAircraft.rotationVelocityBodyX, + simulatorOwnAircraft.rotationVelocityBodyZ, simulatorOwnAircraft.rotationVelocityBodyY, CAngleUnit::rad(), + CTimeUnit::s()); + aircraftSituation.setVelocity(aircraftVelocity); + + const CAircraftLights lights(dtb(simulatorOwnAircraft.lightStrobe), dtb(simulatorOwnAircraft.lightLanding), + dtb(simulatorOwnAircraft.lightTaxi), dtb(simulatorOwnAircraft.lightBeacon), + dtb(simulatorOwnAircraft.lightNav), dtb(simulatorOwnAircraft.lightLogo), + dtb(simulatorOwnAircraft.lightRecognition), dtb(simulatorOwnAircraft.lightCabin), + dtb(simulatorOwnAircraft.lightWing) + + ); + + CAircraftEngineList engines; + const QList helperList { + dtb(simulatorOwnAircraft.engine1Combustion), dtb(simulatorOwnAircraft.engine2Combustion), + dtb(simulatorOwnAircraft.engine3Combustion), dtb(simulatorOwnAircraft.engine4Combustion), + dtb(simulatorOwnAircraft.engine5Combustion), dtb(simulatorOwnAircraft.engine6Combustion) + }; + + for (int index = 0; index < simulatorOwnAircraft.numberOfEngines; ++index) + { + engines.push_back(CAircraftEngine(index + 1, helperList.value(index, true))); + } + + const CAircraftParts parts(lights, dtb(simulatorOwnAircraft.gearHandlePosition), + qRound(simulatorOwnAircraft.flapsHandlePosition * 100), + dtb(simulatorOwnAircraft.spoilersHandlePosition), engines, + dtb(simulatorOwnAircraft.simOnGround), ts); + + // set values + this->updateOwnSituationAndGroundElevation(aircraftSituation); + this->updateOwnParts(parts); + + // When I change cockpit values in the sim (from GUI to simulator, not originating from simulator) + // it takes a little while before these values are set in the simulator. + // To avoid jitters, I wait some update cylces to stabilize the values + if (m_skipCockpitUpdateCycles < 1) + { + // defaults + CComSystem com1(myAircraft.getCom1System()); // set defaults + CComSystem com2(myAircraft.getCom2System()); + + // updates: + // https://www.fsdeveloper.com/forum/threads/com-unit-receiving-status-com-transmit-x-com-test-1-and-volume.445187/ + // COM: If you're set to transmit on a unit, you WILL receive that unit. + // Otherwise if you're NOT set to transmit on a unit, then it will only receive if COM RECEIVE ALL is + // true. There is no control of COM volume. + com1.setFrequencyActive(CFrequency(simulatorOwnAircraft.com1ActiveMHz, CFrequencyUnit::MHz())); + com1.setFrequencyStandby(CFrequency(simulatorOwnAircraft.com1StandbyMHz, CFrequencyUnit::MHz())); + const bool comReceiveAll = dtb(simulatorOwnAircraft.comReceiveAll); + const bool com1Test = dtb(simulatorOwnAircraft.comTest1); + const bool com1Transmit = dtb(simulatorOwnAircraft.comTransmit1); + const int com1Status = + qRound(simulatorOwnAircraft.comStatus1); // Radio status flag : -1 =Invalid 0 = OK 1 = + // Does not exist 2 = No electricity 3 = Failed + com1.setTransmitEnabled(com1Status == 0 && com1Transmit); + com1.setReceiveEnabled(com1Status == 0 && (comReceiveAll || com1Transmit)); + + const bool changedCom1Active = + myAircraft.getCom1System().getFrequencyActive() != com1.getFrequencyActive() && + com1.getFrequencyActive() != m_lastCom1Active; + const bool changedCom1Standby = + myAircraft.getCom1System().getFrequencyStandby() != com1.getFrequencyStandby() && + com1.getFrequencyStandby() != m_lastCom1Standby; + + // Avoid overwrite of 8.33 kHz frequency with data from simulator + if (!changedCom1Active) { com1.setFrequencyActive(myAircraft.getCom1System().getFrequencyActive()); } + else { m_lastCom1Active.setNull(); } + + if (!changedCom1Standby) { com1.setFrequencyStandby(myAircraft.getCom1System().getFrequencyStandby()); } + else { m_lastCom1Standby.setNull(); } + + const bool changedCom1 = myAircraft.getCom1System() != com1; + + m_simCom1 = com1; + Q_UNUSED(com1Test) + + com2.setFrequencyActive(CFrequency(simulatorOwnAircraft.com2ActiveMHz, CFrequencyUnit::MHz())); + com2.setFrequencyStandby(CFrequency(simulatorOwnAircraft.com2StandbyMHz, CFrequencyUnit::MHz())); + const bool com2Test = dtb(simulatorOwnAircraft.comTest2); + const bool com2Transmit = dtb(simulatorOwnAircraft.comTransmit2); + const int com2Status = + qRound(simulatorOwnAircraft.comStatus2); // Radio status flag : -1 =Invalid 0 = OK 1 = + // Does not exist 2 = No electricity 3 = Failed + com2.setTransmitEnabled(com2Status == 0 && com2Transmit); + com2.setReceiveEnabled(com2Status == 0 && (comReceiveAll || com2Transmit)); + const bool changedCom2Active = + myAircraft.getCom2System().getFrequencyActive() != com2.getFrequencyActive() && + com2.getFrequencyActive() != m_lastCom2Active; + const bool changedCom2Standby = + myAircraft.getCom2System().getFrequencyStandby() != com2.getFrequencyStandby() && + com2.getFrequencyStandby() != m_lastCom2Standby; + + // Avoid overwrite of 8.33 kHz frequency with data from simulator + if (!changedCom2Active) { com2.setFrequencyActive(myAircraft.getCom2System().getFrequencyActive()); } + else { m_lastCom2Active.setNull(); } + + if (!changedCom2Standby) { com2.setFrequencyStandby(myAircraft.getCom2System().getFrequencyStandby()); } + else { m_lastCom2Standby.setNull(); } + + const bool changedCom2 = myAircraft.getCom2System() != com2; + + m_simCom2 = com2; + Q_UNUSED(com2Test) + + CTransponder transponder(myAircraft.getTransponder()); + transponder.setTransponderCode(qRound(simulatorOwnAircraft.transponderCode)); + m_simTransponder = transponder; + + // if the simulator ever sends SELCAL, add it here. + // m_selcal SELCAL sync.would go here + + const bool changedXpr = (myAircraft.getTransponderCode() != transponder.getTransponderCode()); + + if (changedCom1 || changedCom2 || changedXpr) + { + // set in own aircraft provider + this->updateCockpit(com1, com2, transponder, identifier()); + } + } + else { --m_skipCockpitUpdateCycles; } + + // TODO TZ check this entire function for msfs2024 + // slower updates + if (m_ownAircraftUpdateCycles % 10 == 0) + { + // SB3 offsets updating + m_simulatorInternals.setValue(QStringLiteral("fsx/sb3"), boolToEnabledDisabled(m_useSbOffsets)); + m_simulatorInternals.setValue(QStringLiteral("fsx/sb3packets"), m_useSbOffsets ? + QString::number(m_sbDataReceived) : + QStringLiteral("disabled")); + + // CG + const CLength cg(simulatorOwnAircraft.cgToGroundFt, CLengthUnit::ft()); + this->updateOwnCG(cg); + + // Simulated objects instead of NON ATC + m_simulatorInternals.setValue(QStringLiteral("fsx/addAsSimulatedObject"), + boolToEnabledDisabled(m_useAddSimulatedObj)); + + } // slow updates + + m_ownAircraftUpdateCycles++; // with 50 updates/sec long enough even for 32bit + } + + void CSimulatorMsfs2024::triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, + const DataDefinitionPosData &remoteAircraftData) + { + if (this->isShuttingDownOrDisconnected()) { return; } + QPointer myself(this); + QTimer::singleShot(0, this, [=] { + if (!myself) { return; } + myself->updateRemoteAircraftFromSimulator(simObject, remoteAircraftData); + }); + } + + void CSimulatorMsfs2024::triggerUpdateRemoteAircraftFromSimulator( + const CSimConnectObject &simObject, const DataDefinitionRemoteAircraftModel &remoteAircraftModel) + { + if (this->isShuttingDownOrDisconnected()) { return; } + QPointer myself(this); + QTimer::singleShot(0, this, [=] { + if (!myself) { return; } + myself->updateRemoteAircraftFromSimulator(simObject, remoteAircraftModel); + }); + } + + // called decoupled from simconnect event queue very fast + void CSimulatorMsfs2024::updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, + const DataDefinitionPosData &remoteAircraftData) + { + if (this->isShuttingDownOrDisconnected()) { return; } + + // Near ground we use faster updates + const CCallsign cs(simObject.getCallsign()); + CAircraftSituation lastSituation = m_lastSentSituations[cs]; + const bool moving = lastSituation.isMoving(); + const bool onGround = remoteAircraftData.isOnGround(); + + // CElevationPlane: deg, deg, feet + // we only remember near ground + const CElevationPlane elevation = + CElevationPlane(remoteAircraftData.latitudeDeg, remoteAircraftData.longitudeDeg, + remoteAircraftData.elevationFt, CElevationPlane::singlePointRadius()); + if (remoteAircraftData.aboveGroundFt() < 250) + { + const CLength cg(remoteAircraftData.cgToGroundFt, CLengthUnit::ft()); + this->rememberElevationAndSimulatorCG(cs, simObject.getAircraftModel(), onGround, elevation, cg); + } + + const bool log = this->isLogCallsign(cs); + if (log) + { + // update lat/lng/alt with real data from sim + const CAltitude alt(remoteAircraftData.altitudeFt, CAltitude::MeanSeaLevel, CAltitude::TrueAltitude, + CLengthUnit::ft()); + lastSituation.setPosition(elevation); + lastSituation.setAltitude(alt); + lastSituation.setGroundElevation(elevation, CAircraftSituation::FromProvider); + this->addLoopbackSituation(lastSituation); + } + + if (moving && remoteAircraftData.aboveGroundFt() <= 100.0) + { + // switch to fast updates + if (simObject.getSimDataPeriod() != SIMCONNECT_PERIOD_VISUAL_FRAME) + { + this->requestPositionDataForSimObject(simObject, SIMCONNECT_PERIOD_VISUAL_FRAME); + } + } + else + { + // switch to slow updates + if (simObject.getSimDataPeriod() != SIMCONNECT_PERIOD_SECOND) + { + this->requestPositionDataForSimObject(simObject, SIMCONNECT_PERIOD_SECOND); + } + } + } + + // called decoupled from simconnect event queue + void + CSimulatorMsfs2024::updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, + const DataDefinitionRemoteAircraftModel &remoteAircraftModel) + { + const CCallsign cs(simObject.getCallsign()); + if (!m_simConnectObjects.contains(cs)) { return; } // no longer existing + CSimConnectObject &so = m_simConnectObjects[cs]; + if (so.isPendingRemoved()) { return; } + + QString combinedModelstring = + QString::fromUtf8(remoteAircraftModel.title) + " " + QString::fromUtf8(remoteAircraftModel.livery); + const QString modelString(combinedModelstring.trimmed()); + + const CLength cg(remoteAircraftModel.cgToGroundFt, CLengthUnit::ft()); + so.setAircraftCG(cg); + so.setAircraftModelString(modelString); + + // update in 2 providers + this->rememberElevationAndSimulatorCG(cs, simObject.getAircraftModel(), false, CElevationPlane::null(), + cg); // env. provider + this->updateCGAndModelString(cs, cg, modelString); // remote aircraft provider + } + + void CSimulatorMsfs2024::updateProbeFromSimulator(const CCallsign &callsign, + const DataDefinitionPosData &remoteAircraftData) + { + const CElevationPlane elevation(remoteAircraftData.latitudeDeg, remoteAircraftData.longitudeDeg, + remoteAircraftData.elevationFt, CElevationPlane::singlePointRadius()); + this->callbackReceivedRequestedElevation(elevation, callsign, false); + } + + void CSimulatorMsfs2024::updateOwnAircraftFromSimulator(const DataDefinitionClientAreaSb &sbDataArea) + { + if (m_skipCockpitUpdateCycles > 0) { return; } + + // log SB offset + if (m_logSbOffsets) { CLogMessage(this).info(u"SB from sim: " % sbDataArea.toQString()); } + + // SB XPDR mode + CTransponder::TransponderMode newMode = CTransponder::StateIdent; + if (!sbDataArea.isIdent()) + { + newMode = sbDataArea.isStandby() ? CTransponder::StateStandby : CTransponder::ModeC; + } + const CSimulatedAircraft myAircraft(this->getOwnAircraft()); + const bool changed = (myAircraft.getTransponderMode() != newMode); + if (!changed) { return; } + CTransponder xpdr = myAircraft.getTransponder(); + xpdr.setTransponderMode(newMode); + this->updateCockpit(myAircraft.getCom1System(), myAircraft.getCom2System(), xpdr, this->identifier()); + } + + void CSimulatorMsfs2024::updateTransponderMode(const CTransponder::TransponderMode xpdrMode) + { + if (m_skipCockpitUpdateCycles > 0) { return; } + const CSimulatedAircraft myAircraft(this->getOwnAircraft()); + const bool changed = (myAircraft.getTransponderMode() != xpdrMode); + if (!changed) { return; } + CTransponder myXpdr = myAircraft.getTransponder(); + myXpdr.setTransponderMode(xpdrMode); + this->updateCockpit(myAircraft.getCom1System(), myAircraft.getCom2System(), myXpdr, this->identifier()); + } + + void CSimulatorMsfs2024::updateMSFS2024TransponderMode(const DataDefinitionMSFSTransponderMode transponderMode) + { + auto mode = CTransponder::StateIdent; + if (!transponderMode.ident) + { + qRound(transponderMode.transponderMode) >= 3 ? mode = CTransponder::ModeC : + mode = CTransponder::StateStandby; + } + this->updateTransponderMode(mode); + } + + bool CSimulatorMsfs2024::simulatorReportedObjectAdded(DWORD objectId) + { + if (this->isShuttingDownOrDisconnected()) { return true; } // pretend everything is fine + const CSimConnectObject simObject = m_simConnectObjects.getSimObjectForObjectId(objectId); + const CCallsign callsign(simObject.getCallsign()); + if (!simObject.hasValidRequestAndObjectId() || callsign.isEmpty()) { return false; } + + // we know the object has been created. But it can happen it is directly removed afterwards + const CSimulatedAircraft verifyAircraft(simObject.getAircraft()); + const QPointer myself(this); + QTimer::singleShot(1000, this, [=] { + // verify aircraft and also triggers new add if required + // do not do this in the event loop, so we do this deferred + if (!myself || this->isShuttingDownOrDisconnected()) { return; } + this->verifyAddedRemoteAircraft(verifyAircraft); + }); + return true; + } + + void CSimulatorMsfs2024::verifyAddedRemoteAircraft(const CSimulatedAircraft &remoteAircraftIn) + { + if (this->isShuttingDownOrDisconnected()) { return; } + + CStatusMessage msg; + CSimulatedAircraft remoteAircraft = remoteAircraftIn; + const CCallsign callsign(remoteAircraft.getCallsign()); + + do { + // no callsign + if (callsign.isEmpty()) + { + msg = CLogMessage(this).error(u"Cannot confirm AI object, empty callsign"); + break; + } + + // removed in meantime + const bool aircraftStillInRange = this->isAircraftInRange(callsign); + if (!m_simConnectObjects.contains(callsign)) + { + if (aircraftStillInRange) + { + msg = CLogMessage(this).warning( + u"Callsign '%1' removed in meantime from AI objects, but still in range") + << callsign.toQString(); + } + else + { + this->removeFromAddPendingAndAddAgainAircraft(callsign); + msg = CLogMessage(this).info(u"Callsign '%1' removed in meantime and no longer in range") + << callsign.toQString(); + } + break; + } + + CSimConnectObject &simObject = m_simConnectObjects[callsign]; + remoteAircraft = simObject.getAircraft(); // update, if something has changed + + if (!simObject.hasValidRequestAndObjectId() || simObject.isPendingRemoved()) + { + msg = CStatusMessage(this).warning(u"Object for callsign '%1'/id: %2 removed in meantime/invalid") + << callsign.toQString() << simObject.getObjectId(); + break; + } + + // P3D also has SimConnect_AIReleaseControlEx which also allows to destroy the aircraft + const SIMCONNECT_DATA_REQUEST_ID requestReleaseId = this->obtainRequestIdForSimObjAircraft(); + const bool released = this->releaseAIControl(simObject, requestReleaseId); + + if (!released) + { + msg = CStatusMessage(this).error(u"Cannot confirm model '%1' %2") + << remoteAircraft.getModelString() << simObject.toQString(); + break; + } + + // confirm as added, this is also required to request light, etc + Q_ASSERT_X(simObject.isPendingAdded(), Q_FUNC_INFO, "Already confirmed, this should be the only place"); + simObject.setConfirmedAdded(true); // aircraft + + // request data on object + this->requestPositionDataForSimObject(simObject); + this->requestLightsForSimObject(simObject); + this->requestModelInfoForSimObject(simObject); + + this->removeFromAddPendingAndAddAgainAircraft(callsign); // no longer try to add + const bool updated = this->updateAircraftRendered(callsign, true); + if (updated) + { + static const QString debugMsg("CS: '%1' model: '%2' verified, request/object id: %3 %4"); + if (this->showDebugLogMessage()) + { + this->debugLogMessage(Q_FUNC_INFO, + debugMsg.arg(callsign.toQString(), remoteAircraft.getModelString()) + .arg(simObject.getRequestId()) + .arg(simObject.getObjectId())); + } + + this->sendRemoteAircraftAtcDataToSimulator(simObject); + emit this->aircraftRenderingChanged(simObject.getAircraft()); + } + else + { + CLogMessage(this).warning( + u"Verified aircraft '%1' model '%2', request/object id: %3 %4 was already marked rendered") + << callsign.asString() << remoteAircraft.getModelString() << simObject.getRequestId() + << simObject.getObjectId(); + } + + if (simObject.isConfirmedAdded() && simObject.getType() == CSimConnectObject::AircraftSimulatedObject) + { + CLogMessage(this).warning(u"Confirm added model '%1' '%2', but as '%3'") + << remoteAircraft.getCallsignAsString() << remoteAircraft.getModelString() + << simObject.getTypeAsString(); + this->triggerAutoTraceSendId(); // trace for some time (issues regarding this workaround?) + simObject.decreaseAddingExceptions(); // if previously increased and now working, reset + } + } + while (false); + + // log errors and emit signal + if (!msg.isEmpty() && msg.isWarningOrAbove()) + { + CLogMessage::preformatted(msg); + emit this->physicallyAddingRemoteModelFailed(CSimulatedAircraft(), false, false, msg); + } + + // trigger adding pending aircraft if there are any + if (!m_addPendingAircraft.isEmpty()) { this->addPendingAircraftAfterAdded(); } + } + + void CSimulatorMsfs2024::addingAircraftFailed(const CSimConnectObject &simObject) + { + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + Q_ASSERT_X(simObject.isAircraft(), Q_FUNC_INFO, "Need aircraft"); + } + if (!simObject.isAircraft()) { return; } + + // clean up + m_simConnectObjects.removeByOtherSimObject(simObject); + this->removeFromAddPendingAndAddAgainAircraft(simObject.getCallsign()); + + CLogMessage(this).warning(u"Model failed to be added: '%1' details: %2") + << simObject.getAircraftModelString() << simObject.getAircraft().toQString(true); + // CStatusMessage verifyMsg; + // const bool verifiedAircraft = this->verifyFailedAircraftInfo(simObject, verifyMsg); // aircraft.cfg existing? + const bool verifiedAircraft = true; + // if (!verifyMsg.isEmpty()) { CLogMessage::preformatted(verifyMsg); } + + CSimConnectObject simObjAddAgain(simObject); + simObjAddAgain.increaseAddingExceptions(); + if (!simObject.hasCallsign()) + { + SWIFT_VERIFY_X(false, Q_FUNC_INFO, "Missing callsign"); + return; + } + + if (!verifiedAircraft || simObjAddAgain.getAddingExceptions() > ThresholdAddException) + { + const CStatusMessage msg = + verifiedAircraft ? + CLogMessage(this).warning(u"Model '%1' %2 failed %3 time(s) before and will be disabled") + << simObjAddAgain.getAircraftModelString() << simObjAddAgain.toQString() + << simObjAddAgain.getAddingExceptions() : + CLogMessage(this).warning(u"Model '%1' %2 failed verification and will be disabled") + << simObjAddAgain.getAircraftModelString() << simObjAddAgain.toQString(); + + this->updateAircraftEnabled(simObjAddAgain.getCallsign(), false); // disable + emit this->physicallyAddingRemoteModelFailed(simObjAddAgain.getAircraft(), true, true, + msg); // verify failed + } + else + { + CLogMessage(this).info(u"Will try '%1' again, aircraft: %2") + << simObject.getAircraftModelString() << simObject.getAircraft().toQString(true); + QPointer myself(this); + QTimer::singleShot(2000, this, [=] { + if (!myself) { return; } + if (this->isShuttingDownOrDisconnected()) { return; } + m_addPendingAircraft.insert(simObjAddAgain, true); // add failed object + }); + } + } + + bool CSimulatorMsfs2024::verifyFailedAircraftInfo(const CSimConnectObject &simObject, CStatusMessage &details) const + { + CAircraftModel model = simObject.getAircraftModel(); + + const CSpecializedSimulatorSettings settings = this->getSimulatorSettings(); + const bool fileExists = CFsCommonUtil::adjustFileDirectory(model, settings.getModelDirectoriesOrDefault()); + bool canBeUsed = true; + + CStatusMessageList messages; + if (fileExists) + { + // we can access the aircraft.cfg file + bool parsed = false; + const CAircraftCfgEntriesList entries = + CAircraftCfgParser::performParsingOfSingleFile(model.getFileName(), parsed, messages); + if (parsed) + { + if (entries.containsTitle(model.getModelString())) + { + messages.push_back(CStatusMessage(this).info(u"Model '%1' exists in re-parsed file '%2'.") + << model.getModelString() << model.getFileName()); + canBeUsed = true; // all OK + } + else + { + messages.push_back( + CStatusMessage(this).warning(u"Model '%1' no longer in re-parsed file '%2'. Models are: %3.") + << model.getModelString() << model.getFileName() << entries.getTitlesAsString(true)); + canBeUsed = false; // absolute no chance to use that one + } + } + else + { + messages.push_back(CStatusMessage(this).warning(u"CS: '%1' Cannot parse file: '%2' (existing: %3)") + << model.getCallsign().asString() << model.getFileName() + << boolToYesNo(model.hasExistingCorrespondingFile())); + } + } + else + { + // the file cannot be accessed right now, but the pilot client necessarily has access to them + // so we just carry on + messages = model.verifyModelData(); + } + + // as single message + details = messages.toSingleMessage(); + + // status + return canBeUsed; + } + + bool CSimulatorMsfs2024::logVerifyFailedAircraftInfo(const CSimConnectObject &simObject) const + { + CStatusMessage m; + const bool r = verifyFailedAircraftInfo(simObject, m); + if (!m.isEmpty()) { CLogMessage::preformatted(m); } + return r; + } + + void CSimulatorMsfs2024::timerBasedObjectAddOrRemove() + { + this->addPendingAircraft(AddByTimer); + if (!this->isTestMode()) { this->physicallyRemoveAircraftNotInProvider(); } + } + + void CSimulatorMsfs2024::addPendingAircraftAfterAdded() + { + this->addPendingAircraft(AddAfterAdded); // addPendingAircraft is already "non blocking" + } + + void CSimulatorMsfs2024::addPendingAircraft(AircraftAddMode mode) + { + if (m_addPendingAircraft.isEmpty()) { return; } + const CCallsignSet aircraftCallsignsInRange(this->getAircraftInRangeCallsigns()); + CSimulatedAircraftList toBeAddedAircraft; // aircraft still to be added + CCallsignSet toBeRemovedCallsigns; + + for (const CSimConnectObject &pendingSimObj : std::as_const(m_addPendingAircraft)) + { + SWIFT_VERIFY_X(pendingSimObj.hasCallsign(), Q_FUNC_INFO, "missing callsign"); + if (!pendingSimObj.hasCallsign()) { continue; } + else { toBeRemovedCallsigns.push_back(pendingSimObj.getCallsign()); } + } + + // no longer required to be added + m_addPendingAircraft.removeCallsigns(toBeRemovedCallsigns); + m_addAgainAircraftWhenRemoved.removeByCallsigns(toBeRemovedCallsigns); + + // add aircraft, but "non blocking" + if (!toBeAddedAircraft.isEmpty()) + { + const CSimConnectObject oldestSimObject = m_addPendingAircraft.getOldestObject(); + const CSimulatedAircraft nextPendingAircraft = oldestSimObject.getAircraft(); + if (nextPendingAircraft.hasModelString()) + { + const QPointer myself(this); + QTimer::singleShot(100, this, [=] { + if (!myself) { return; } + this->physicallyAddRemoteAircraftImpl(nextPendingAircraft, mode, oldestSimObject); + }); + } + else + { + CLogMessage(this).warning(u"Pending aircraft without model string will be removed"); + m_addPendingAircraft.removeByOtherSimObject(oldestSimObject); + } + } + } + + CSimConnectObject CSimulatorMsfs2024::removeFromAddPendingAndAddAgainAircraft(const CCallsign &callsign) + { + CSimConnectObject simObjectOld; + if (callsign.isEmpty()) { return simObjectOld; } + + m_addAgainAircraftWhenRemoved.removeByCallsign(callsign); + if (m_addPendingAircraft.contains(callsign)) + { + simObjectOld = m_addPendingAircraft[callsign]; + m_addPendingAircraft.remove(callsign); + } + return simObjectOld; + } + + bool CSimulatorMsfs2024::simulatorReportedObjectRemoved(DWORD objectID) + { + if (this->isShuttingDownOrDisconnected()) { return false; } + CSimConnectObject simObject = m_simConnectObjects.getSimObjectForObjectId(objectID); + if (!simObject.hasValidRequestAndObjectId()) { return false; } // object id from somewhere else + + const CCallsign callsign(simObject.getCallsign()); + Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Missing callsign for removed object"); + + if (simObject.isPendingRemoved()) + { + // good case, object has been removed + // we can remove the simulator object + } + else + { + // object was removed, but removal was not requested by us + // this means we are out of the reality bubble or something else went wrong + // Possible reasons: + // 1) out of reality bubble, because we move to another airport or other reasons + // 2) wrong position (in ground etc.) + // 3) Simulator not running (ie in stopped mode) + CStatusMessage msg; + if (!simObject.getAircraftModelString().isEmpty() && + simObject.getAddingDirectlyRemoved() < ThresholdAddedAndDirectlyRemoved) + { + simObject.increaseAddingDirectlyRemoved(); + m_addPendingAircraft.insert(simObject, true); // insert removed objects and update ts + m_simConnectObjects.removeByOtherSimObject( + simObject); // we have it in pending now, no need to keep it in this list + + const CInterpolationAndRenderingSetupPerCallsign setup = + this->getInterpolationSetupPerCallsignOrDefault(callsign); + msg = CLogMessage(this).warning(u"Aircraft removed, '%1' '%2' object id '%3' out of reality bubble or " + u"other reason. Interpolator: '%4'") + << callsign.toQString() << simObject.getAircraftModelString() << objectID + << simObject.getInterpolatorInfo(setup.getInterpolatorMode()); + } + else if (simObject.getAddingDirectlyRemoved() < ThresholdAddedAndDirectlyRemoved) + { + const CStatusMessage m = + CLogMessage(this).warning( + u"Aircraft removed again multiple times and will be disabled, '%1' '%2' object id '%3'") + << callsign.toQString() << simObject.getAircraftModelString() << objectID; + this->updateAircraftEnabled(simObject.getCallsign(), false); + emit this->physicallyAddingRemoteModelFailed(simObject.getAircraft(), true, true, + m); // directly removed again + } + else + { + msg = CLogMessage(this).warning( + u"Removed '%1' from simulator, but was not initiated by us (swift): %1 '%2' object id %3") + << callsign.toQString() << simObject.getAircraftModelString() << objectID; + } + + // in all cases add verification details + this->logVerifyFailedAircraftInfo(simObject); + + // relay messages + if (!msg.isEmpty()) { emit this->driverMessages(msg); } + } + + // in all cases we remove the object + const int c = m_simConnectObjects.remove(callsign); + const bool removedAny = (c > 0); + const bool updated = this->updateAircraftRendered(simObject.getCallsign(), false); + if (updated) { emit this->aircraftRenderingChanged(simObject.getAircraft()); } + + // models we have to add again after removing + if (m_addAgainAircraftWhenRemoved.containsCallsign(callsign)) + { + const CSimulatedAircraft aircraftAddAgain = m_addAgainAircraftWhenRemoved.findFirstByCallsign(callsign); + m_addAgainAircraftWhenRemoved.removeByCallsign(callsign); + QPointer myself(this); + QTimer::singleShot(2500, this, [=] { + if (!myself) { return; } + if (this->isShuttingDownOrDisconnected()) { return; } + myself->physicallyAddRemoteAircraftImpl(aircraftAddAgain, AddedAfterRemoved); + }); + } + return removedAny; + } + + bool CSimulatorMsfs2024::setSimConnectObjectId(DWORD requestId, DWORD objectId) + { + return m_simConnectObjects.setSimConnectObjectIdForRequestId(requestId, objectId); + } + + bool CSimulatorMsfs2024::setCurrentLights(const CCallsign &callsign, const CAircraftLights &lights) + { + if (!m_simConnectObjects.contains(callsign)) { return false; } + m_simConnectObjects[callsign].setCurrentLightsInSimulator(lights); + return true; + } + + bool CSimulatorMsfs2024::setLightsAsSent(const CCallsign &callsign, const CAircraftLights &lights) + { + if (!m_simConnectObjects.contains(callsign)) { return false; } + m_simConnectObjects[callsign].setLightsAsSent(lights); + return true; + } + + void CSimulatorMsfs2024::timerEvent(QTimerEvent *event) + { + Q_UNUSED(event) + if (this->isShuttingDown()) { return; } + this->dispatch(); + } + + HRESULT CSimulatorMsfs2024::initEventsP3D() { return s_ok(); } + + bool CSimulatorMsfs2024::parseDetails(const CSimpleCommandParser &parser) + { + // .driver sendid on|off + if (parser.matchesPart(1, "sendid") && parser.hasPart(2)) + { + const bool trace = parser.toBool(2); + this->setTraceSendId(trace); + CLogMessage(this, CLogCategories::cmdLine()).info(u"Tracing %1 driver sendIds is '%2'") + << this->getSimulatorPluginInfo().getIdentifier() << boolToOnOff(trace); + return true; + } + + // .driver sboffsets on|off + if (parser.matchesPart(1, "sboffsets") && parser.hasPart(2)) + { + const bool on = parser.toBool(2); + this->setUsingSbOffsetValues(on); + CLogMessage(this, CLogCategories::cmdLine()).info(u"SB offsets is '%1'") << boolToOnOff(on); + return true; + } + + // .driver sblog on|off + if (parser.matchesPart(1, "sblog") && parser.hasPart(2)) + { + const bool on = parser.toBool(2); + m_logSbOffsets = on; + CLogMessage(this, CLogCategories::cmdLine()).info(u"SB log. offsets is '%1'") << boolToOnOff(on); + return true; + } + + return CSimulatorFsCommon::parseDetails(parser); + } + + void CSimulatorMsfs2024::registerHelp() + { + if (CSimpleCommandParser::registered( + "swift::simplugin::msfs2024common::CSimulatorMsfs2024::CSimulatorMsfs2024")) + { + return; + } + CSimpleCommandParser::registerCommand({ ".drv", "alias: .driver .plugin" }); + CSimpleCommandParser::registerCommand({ ".drv sendid on|off", "Trace simConnect sendId on|off" }); + CSimpleCommandParser::registerCommand({ ".drv sboffsets on|off", "SB offsets via simConnect on|off" }); + CSimpleCommandParser::registerCommand({ ".drv sblog on|off", "SB offsets logging on|off" }); + } + + // CCallsign CSimulatorMsfs2024::getCallsignForPendingProbeRequests(DWORD requestId, bool remove) + //{ + // const CCallsign cs = m_pendingProbeRequests.value(requestId); + // if (remove) { m_pendingProbeRequests.remove(requestId); } + // return cs; + // } + + const QString &CSimulatorMsfs2024::modeToString(CSimulatorMsfs2024::AircraftAddMode mode) + { + static const QString e("external call"); + static const QString pt("add pending by timer"); + static const QString oa("add pending after object added"); + static const QString ar("add again after removed"); + static const QString dontKnow("???"); + + switch (mode) + { + case ExternalCall: return e; + case AddByTimer: return pt; + case AddAfterAdded: return oa; + case AddedAfterRemoved: return ar; + default: break; + } + return dontKnow; + } + + void CSimulatorMsfs2024::dispatch() + { + // call CSimulatorMsfs2024::SimConnectProc + Q_ASSERT_X(m_dispatchProc, Q_FUNC_INFO, "Missing DispatchProc"); + + // statistics + m_dispatchReceiveIdLast = SIMCONNECT_RECV_ID_NULL; + m_dispatchRequestIdLast = CSimConnectDefinitions::RequestEndMarker; + const qint64 start = QDateTime::currentMSecsSinceEpoch(); + + // process + const HRESULT hr = SimConnect_CallDispatch(m_hSimConnect, m_dispatchProc, this); + + // statistics + const qint64 end = QDateTime::currentMSecsSinceEpoch(); + m_dispatchTimeMs = end - start; + if (m_dispatchMaxTimeMs < m_dispatchTimeMs) + { + m_dispatchMaxTimeMs = m_dispatchTimeMs; + m_dispatchReceiveIdMaxTime = m_dispatchReceiveIdLast; + m_dispatchRequestIdMaxTime = m_dispatchRequestIdLast; + } + + // error handling + if (isFailure(hr)) + { + // on FSX we normally receive this one here when simulator goes down, and NOT onSimExit + // in that case sim status is Connected, but not PAUSED or SIMULATING + const SimulatorStatus simStatus = this->getSimulatorStatus(); + const bool disconnectedOrNotSimulating = + simStatus.testFlag(Disconnected) || !simStatus.testFlag(Simulating); + + m_dispatchErrors++; + this->triggerAutoTraceSendId(); + if (m_dispatchErrors == 2) + { + // 2nd time, an error / avoid multiple messages + // idea: if it happens once ignore + const QString msg = + QStringLiteral(u"%1: Dispatch error, sim.status: %2") + .arg(this->getSimulatorPluginInfo().getIdentifier(), ISimulator::statusToString(simStatus)); + CLogMessage(this).log( + disconnectedOrNotSimulating ? CStatusMessage::SeverityWarning : CStatusMessage::SeverityError, msg); + } + else if (m_dispatchErrors > 5) + { + // this normally happens during a FSX crash or shutdown with simconnect + const QString msg = + QStringLiteral(u"%1: Multiple dispatch errors, disconnecting. Sim.status: %2") + .arg(this->getSimulatorPluginInfo().getIdentifier(), ISimulator::statusToString(simStatus)); + CLogMessage(this).log( + disconnectedOrNotSimulating ? CStatusMessage::SeverityWarning : CStatusMessage::SeverityError, msg); + this->disconnectFrom(); + } + return; + } + m_dispatchErrors = 0; + } + + bool CSimulatorMsfs2024::physicallyAddRemoteAircraftImpl(const CSimulatedAircraft &newRemoteAircraft, + CSimulatorMsfs2024::AircraftAddMode addMode, + const CSimConnectObject &correspondingSimObject) + { + const CCallsign callsign(newRemoteAircraft.getCallsign()); + + // entry checks + Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "thread"); + Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "empty callsign"); + Q_ASSERT_X(newRemoteAircraft.hasModelString(), Q_FUNC_INFO, "missing model string"); + + // reset timer + m_simObjectTimer.start(AddPendingAircraftIntervalMs); // restart + + // remove outdated objects + const CSimConnectObjects outdatedAdded = + m_simConnectObjects.removeOutdatedPendingAdded(CSimConnectObject::AllTypes); + if (!outdatedAdded.isEmpty()) + { + const CCallsignSet callsigns = outdatedAdded.getAllCallsigns(false); + CLogMessage(this).warning(u"Removed %1 outdated object(s) pending for added: %2") + << outdatedAdded.size() << callsigns.getCallsignsAsString(true); + this->updateMultipleAircraftEnabled(callsigns, false); + + static const QString msgText("%1 outdated adding, %2"); + for (const CSimConnectObject &simObjOutdated : outdatedAdded) + { + const CStatusMessage msg = CStatusMessage(this).warning( + msgText.arg(simObjOutdated.getCallsign().asString(), simObjOutdated.toQString())); + emit this->physicallyAddingRemoteModelFailed(simObjOutdated.getAircraft(), true, true, + msg); // outdated + } + + // if this aircraft is also outdated, ignore + if (callsigns.contains(newRemoteAircraft.getCallsign())) { return false; } + } + + const bool hasPendingAdded = m_simConnectObjects.containsPendingAdded(); + bool canAdd = this->isSimulating() && !hasPendingAdded; + + Q_ASSERT_X(!hasPendingAdded || m_simConnectObjects.countPendingAdded() < 2, Q_FUNC_INFO, + "There must be only 0..1 pending objects"); + if (this->showDebugLogMessage()) + { + this->debugLogMessage(Q_FUNC_INFO, QStringLiteral("CS: '%1' mode: '%2' model: '%3'") + .arg(newRemoteAircraft.getCallsignAsString(), modeToString(addMode), + newRemoteAircraft.getModelString())); + this->debugLogMessage( + Q_FUNC_INFO, QStringLiteral("CS: '%1' pending callsigns: '%2', pending objects: '%3'") + .arg(newRemoteAircraft.getCallsignAsString(), + m_addPendingAircraft.getAllCallsignStrings(true).join(", "), + m_simConnectObjects.getPendingAddedCallsigns().getCallsignStrings().join(", "))); + } + + // do we need to remove/add again because something has changed? + // this handles changed model strings or an update of the model + if (m_simConnectObjects.contains(callsign)) + { + const CSimConnectObject simObject = m_simConnectObjects[callsign]; + const QString newModelString(newRemoteAircraft.getModelString()); + const QString simObjModelString(simObject.getAircraftModelString()); + const bool sameModel = + (simObjModelString == + newModelString); // compare on string only (other attributes might change such as mode) + + // same model, nothing will change, otherwise add again when removed + if (sameModel) + { + CLogMessage(this).info(u"CS: '%1' re-added same model '%2'") + << newRemoteAircraft.getCallsignAsString() << newModelString; + + // we restore rendered flag in case we are sure we are rendered + // this is used with rematching + const bool rendered = simObject.isConfirmedAdded() && simObject.isPending(); + if (rendered) { this->updateAircraftRendered(callsign, rendered); } + return true; + } + + this->physicallyRemoveRemoteAircraft(newRemoteAircraft.getCallsign()); + m_addAgainAircraftWhenRemoved.replaceOrAddByCallsign(newRemoteAircraft); + if (this->showDebugLogMessage()) + { + this->debugLogMessage(Q_FUNC_INFO, + QStringLiteral("CS: '%1' re-added changed model '%2', will be added again") + .arg(newRemoteAircraft.getCallsignAsString(), newModelString)); + } + return false; + } + + // situation check + CAircraftSituation situation(newRemoteAircraft.getSituation()); + if (canAdd && situation.isPositionOrAltitudeNull()) + { + // invalid position because position or altitude is null + const CAircraftSituationList situations(this->remoteAircraftSituations(callsign)); + if (situations.isEmpty()) + { + CLogMessage(this).warning(u"No valid situations for '%1', will be added as pending") + << callsign.asString(); + } + else + { + CLogMessage(this).warning(u"Invalid aircraft situation for new aircraft '%1', use closest situation") + << callsign.asString(); + situation = situations.findClosestTimeDistanceAdjusted(QDateTime::currentMSecsSinceEpoch()); + Q_ASSERT_X(!situation.isPositionOrAltitudeNull(), Q_FUNC_INFO, "Invalid situation for new aircraft"); + } + + // still invalid? + canAdd = situation.isPositionOrAltitudeNull(); + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + SWIFT_VERIFY_X(canAdd, Q_FUNC_INFO, "Expect valid situation"); + CLogMessage(this).warning(u"Invalid situation for '%1'") << callsign; + } + } + + // check if we can add, do not add if simulator is stopped or other objects pending + if (!canAdd) + { + CSimConnectObject &addPendingObj = m_addPendingAircraft[newRemoteAircraft.getCallsign()]; + addPendingObj.setAircraft(newRemoteAircraft); + addPendingObj.resetTimestampToNow(); + return false; + } + + // remove from pending and keep for later to remember fail counters + const CSimConnectObject removedPendingObj = this->removeFromAddPendingAndAddAgainAircraft(callsign); + + // create AI after crosschecking it + // if (!probe && !this->isAircraftInRangeOrTestMode(callsign)) + if (!this->isAircraftInRangeOrTestMode(callsign)) + { + CLogMessage(this).info(u"Skipping adding of '%1' since it is no longer in range") << callsign.asString(); + return false; + } + + // setup + const CInterpolationAndRenderingSetupPerCallsign setup = + this->getInterpolationSetupConsolidated(callsign, true); + const bool sendGround = setup.isSendingGndFlagToSimulator(); + + bool adding = false; // will be added flag + const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimObjAircraft(); + + // Initial situation, if possible from interpolation + CAircraftSituation initialSituation = newRemoteAircraft.getSituation(); // default + { + // Dummy CSimConnectObject just for interpolation + const CSimConnectObject dummyObject = CSimConnectObject( + newRemoteAircraft, 0, this, this, this->getRemoteAircraftProvider(), &m_interpolationLogger); + const CInterpolationResult result = + dummyObject.getInterpolation(QDateTime::currentMSecsSinceEpoch(), setup, 0); + if (result.getInterpolationStatus().isInterpolated()) + { + initialSituation = result.getInterpolatedSituation(); + } + } + + // TODO TZ handle underflow properly + CStatusMessage underflowStatus; + const SIMCONNECT_DATA_INITPOSITION initialPosition = + CSimulatorMsfs2024::aircraftSituationToPosition(initialSituation, sendGround, true, &underflowStatus); + + QString modelString(newRemoteAircraft.getShortModelString()); + const QString modelLiveryString(newRemoteAircraft.getLiveryString()); + + if (this->showDebugLogMessage()) + { + this->debugLogMessage(Q_FUNC_INFO, + QStringLiteral("CS: '%1' model: '%2' livery: %3 request: %4, init pos: %5") + .arg(callsign.toQString(), modelString, modelLiveryString) + .arg(requestId) + .arg(fsxPositionToString(initialPosition))); + } + + const QByteArray modelStringBa = toFsxChar(modelString).trimmed(); + const QByteArray modelLiveryBa = toFsxChar(modelLiveryString).trimmed(); + const QByteArray csBa = toFsxChar(callsign.toQString().left(12)); + CSimConnectObject::SimObjectType type = CSimConnectObject::AircraftNonAtc; + HRESULT hr = S_OK; + + if (this->isAddingAsSimulatedObjectEnabled() && correspondingSimObject.hasCallsign() && + correspondingSimObject.getAddingExceptions() > 0 && + correspondingSimObject.getType() == CSimConnectObject::AircraftNonAtc) + { + CStatusMessage(this).warning( + u"Model '%1' for '%2' failed %3 time(s) before, using SimConnect_AICreateSimulatedObject_EX1 now") + << newRemoteAircraft.getModelString() << callsign.toQString() + << correspondingSimObject.getAddingExceptions(); + + hr = SimConnect_AICreateSimulatedObject_EX1(m_hSimConnect, modelStringBa.constData(), + modelLiveryBa.constData(), initialPosition, requestId); + + type = CSimConnectObject::AircraftSimulatedObject; + } + else + { + hr = SimConnect_AICreateNonATCAircraft_EX1(m_hSimConnect, modelStringBa.constData(), + modelLiveryBa.constData(), csBa.constData(), initialPosition, + requestId); + + type = CSimConnectObject::AircraftNonAtc; + } + + if (!underflowStatus.isEmpty()) + { + CStatusMessage(this).warning(u"Underflow detecion for '%1', details '%2'") + << callsign.asString() << underflowStatus.getMessage(); + } + + if (isFailure(hr)) + { + const CStatusMessage msg = CStatusMessage(this).error(u"SimConnect, can not create AI traffic: '%1' '%2'") + << callsign.toQString() << modelString; + CLogMessage::preformatted(msg); + emit this->physicallyAddingRemoteModelFailed(newRemoteAircraft, true, true, msg); // SimConnect error + } + else + { + // we will request a new aircraft by request ID, later we will receive its object id + // so far this object id is 0 (DWORD) + const CSimConnectObject simObject = + this->insertNewSimConnectObject(newRemoteAircraft, requestId, type, removedPendingObj); + this->traceSendId(simObject, Q_FUNC_INFO, + QStringLiteral("mode: %1").arg(CSimulatorMsfs2024::modeToString(addMode)), true); + adding = true; + } + return adding; + } + + bool CSimulatorMsfs2024::physicallyRemoveRemoteAircraft(const CCallsign &callsign) + { + // only remove from sim + Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "wrong thread"); + if (callsign.isEmpty()) { return false; } // can happen if an object is not an aircraft + + // clean up anyway + this->removeFromAddPendingAndAddAgainAircraft(callsign); + + // really remove from simulator + if (!m_simConnectObjects.contains(callsign)) { return false; } // already fully removed or not yet added + CSimConnectObject &simObject = m_simConnectObjects[callsign]; + if (simObject.isPendingRemoved()) { return true; } + + // check for pending objects + m_addPendingAircraft.remove(callsign); // just in case still in list of pending aircraft + const bool pendingAdded = simObject.isPendingAdded(); // already added in simulator, but not yet confirmed + const bool stillWaitingForLights = !simObject.hasCurrentLightsInSimulator(); + if (!simObject.isRemovedWhileAdding() && (pendingAdded || stillWaitingForLights)) + { + // problem: we try to delete an aircraft just requested to be added + // best solution so far, call remove again with a delay + CLogMessage(this).warning(u"'%1' requested to be removed, but pending added (%2) / or pending lights(%3). " + u"Object will be removed again: %4") + << callsign.asString() << boolToYesNo(pendingAdded) << boolToYesNo(stillWaitingForLights) + << simObject.toQString(); + simObject.setRemovedWhileAdding(true); // next time kill + QPointer myself(this); + QTimer::singleShot(2000, this, [=] { + if (!myself) { return; } + CLogMessage(this).info(u"Next trial to remove '%1'") << callsign.asString(); + myself->physicallyRemoveRemoteAircraft(callsign); + }); + return false; // not yet deleted + } + + // no more data from simulator + this->stopRequestingDataForSimObject(simObject); + + // mark as removed + simObject.setPendingRemoved(true); + if (this->showDebugLogMessage()) + { + this->debugLogMessage(Q_FUNC_INFO, QStringLiteral("CS: '%1' request/object id: %2/%3") + .arg(callsign.toQString()) + .arg(simObject.getRequestId()) + .arg(simObject.getObjectId())); + } + + // call in SIM + const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectRemove); + this->removeCamera(simObject); + this->removeObserver(simObject); + const HRESULT result = SimConnect_AIRemoveObject( + m_hSimConnect, static_cast(simObject.getObjectId()), requestId); + if (isOk(result)) + { + if (this->isTracingSendId()) { this->traceSendId(simObject, Q_FUNC_INFO); } + } + else { CLogMessage(this).warning(u"Removing aircraft '%1' from simulator failed") << callsign.asString(); } + + // mark in provider + const bool updated = this->updateAircraftRendered(callsign, false); + if (updated) + { + CSimulatedAircraft aircraft(simObject.getAircraft()); + aircraft.setRendered(false); + emit this->aircraftRenderingChanged(aircraft); + } + + // cleanup function, actually this should not be needed + this->physicallyRemoveAircraftNotInProviderAsync(); + + // bye + return CSimulatorPluginCommon::physicallyRemoveRemoteAircraft(callsign); + } + + int CSimulatorMsfs2024::physicallyRemoveAllRemoteAircraft() + { + // make sure they are not added again + // cleaning here is somewhat redundant, but double checks + m_addPendingAircraft.clear(); + m_addAgainAircraftWhenRemoved.clear(); + + // remove one by one + int r = 0; + const CCallsignSet callsigns = m_simConnectObjects.getAllCallsigns(); + for (const CCallsign &cs : callsigns) + { + if (this->physicallyRemoveRemoteAircraft(cs)) { r++; } + } + + CSimulatorFsCommon::physicallyRemoveAllRemoteAircraft(); + return r; + } + + HRESULT CSimulatorMsfs2024::initEvents() + { + HRESULT hr = s_ok(); + // System events, see + // http://msdn.microsoft.com/en-us/library/cc526983.aspx#SimConnect_SubscribeToSystemEvent + hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventSimStatus, "Sim"); + hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventObjectAdded, "ObjectAdded"); + hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventObjectRemoved, "ObjectRemoved"); + hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventFrame, "Frame"); + hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventPause, "Pause"); + hr += SimConnect_SubscribeToSystemEvent(m_hSimConnect, SystemEventFlightLoaded, "FlightLoaded"); + if (isFailure(hr)) + { + CLogMessage(this).error(u"MSFS2024 plugin error: %1") << "SimConnect_SubscribeToSystemEvent failed"; + return hr; + } + + // Mapped events, see event ids here: + // http://msdn.microsoft.com/en-us/library/cc526980.aspx + // http://www.prepar3d.com/SDKv2/LearningCenter/utilities/variables/event_ids.html + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventPauseToggle, "PAUSE_TOGGLE"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, SystemEventSlewToggle, "SLEW_TOGGLE"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventFreezeLatLng, + "FREEZE_LATITUDE_LONGITUDE_SET"); // FSX old standard + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventFreezeAlt, + "FREEZE_ALTITUDE_SET"); // FSX old standard + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventFreezeAtt, + "FREEZE_ATTITUDE_SET"); // FSX old standard + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetCom1Active, "COM_RADIO_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetCom1Standby, "COM_STBY_RADIO_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetCom2Active, "COM2_RADIO_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetCom2Standby, "COM2_STBY_RADIO_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTransponderCode, "XPNDR_SET"); + + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluYear, "ZULU_YEAR_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluDay, "ZULU_DAY_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluHours, "ZULU_HOURS_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventSetTimeZuluMinutes, "ZULU_MINUTES_SET"); + + // hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLandingLightsOff, "LANDING_LIGHTS_OFF"); + // hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLandinglightsOn, "LANDING_LIGHTS_ON"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLandingLightsSet, "LANDING_LIGHTS_SET"); + // hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLandingLightsToggle, "LANDING_LIGHTS_TOGGLE"); + + // hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventPanelLightsOff, "PANEL_LIGHTS_OFF"); + // hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventPanelLightsOn, "PANEL_LIGHTS_ON"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventPanelLightsSet, "PANEL_LIGHTS_SET"); + + // hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventStrobesOff, "STROBES_OFF"); + // hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventStrobesOn, "STROBES_ON"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventStrobesSet, "STROBES_SET"); + // hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventStrobesToggle, "STROBES_TOGGLE"); + + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventBeaconLightsSet, "BEACON_LIGHTS_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventCabinLightsSet, "CABIN_LIGHTS_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventLogoLightsSet, "LOGO_LIGHTS_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventNavLightsSet, "NAV_LIGHTS_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventRecognitionLightsSet, "RECOGNITION_LIGHTS_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventTaxiLightsSet, "TAXI_LIGHTS_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventWingLightsSet, "WING_LIGHTS_SET"); + + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventGearSet, "GEAR_SET"); + hr += SimConnect_MapClientEventToSimEvent(m_hSimConnect, EventFlapsSet, "FLAPS_SET"); + + if (isFailure(hr)) + { + CLogMessage(this).error(u"MSFS2024 plugin error: %1") << "SimConnect_MapClientEventToSimEvent failed"; + return hr; + } + + // facility + SIMCONNECT_DATA_REQUEST_ID requestId = + static_cast(CSimConnectDefinitions::RequestFacility); + hr += SimConnect_SubscribeToFacilities(m_hSimConnect, SIMCONNECT_FACILITY_LIST_TYPE_AIRPORT, requestId); + if (isFailure(hr)) + { + CLogMessage(this).error(u"FSX plugin error: %1") << "SimConnect_SubscribeToFacilities failed"; + return hr; + } + return hr; + } + + HRESULT CSimulatorMsfs2024::initDataDefinitionsWhenConnected() + { + + return CSimConnectDefinitions::initDataDefinitionsWhenConnected( + m_hSimConnect, this->getSimulatorPluginInfo().getSimulatorInfo()); + } + + HRESULT CSimulatorMsfs2024::initWhenConnected() + { + // called when connected + + HRESULT hr = this->initEvents(); + if (isFailure(hr)) + { + CLogMessage(this).error(u"MSFS2024 plugin: initEvents failed"); + return hr; + } + + // init data definitions and SB data area + hr += this->initDataDefinitionsWhenConnected(); + if (isFailure(hr)) + { + CLogMessage(this).error(u"MSFS2024 plugin: initDataDefinitionsWhenConnected failed"); + return hr; + } + + return hr; + } + + void CSimulatorMsfs2024::updateRemoteAircraft() + { + static_assert(sizeof(DataDefinitionRemoteAircraftPartsWithoutLights) == sizeof(double) * 12, + "DataDefinitionRemoteAircraftPartsWithoutLights has an incorrect size."); + Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "thread"); + + // nothing to do, reset request id and exit + const int remoteAircraftNo = this->getAircraftInRangeCount(); + if (remoteAircraftNo < 1) + { + m_statsUpdateAircraftRuns = 0; + return; + } + + // values used for position and parts + const qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch(); + if (this->isUpdateAircraftLimitedWithStats(currentTimestamp)) + { + this->finishUpdateRemoteAircraftAndSetStatistics(currentTimestamp, true); + return; + } + m_updateRemoteAircraftInProgress = true; + + // interpolation for all remote aircraft + const QList simObjects(m_simConnectObjects.values()); + + uint32_t simObjectNumber = 0; + const bool traceSendId = this->isTracingSendId(); + const bool updateAllAircraft = this->isUpdateAllRemoteAircraft(currentTimestamp); + for (const CSimConnectObject &simObject : simObjects) + { + // happening if aircraft is not yet added to simulator or to be deleted + if (!simObject.isReadyToSend()) { continue; } + if (!simObject.hasCurrentLightsInSimulator()) { continue; } // wait until we have light state + + const CCallsign callsign(simObject.getCallsign()); + const bool hasCs = !callsign.isEmpty(); + const bool hasValidIds = simObject.hasValidRequestAndObjectId(); + SWIFT_VERIFY_X(hasCs, Q_FUNC_INFO, "missing callsign"); + SWIFT_AUDIT_X(hasValidIds, Q_FUNC_INFO, "Missing ids"); + if (!hasCs || !hasValidIds) { continue; } // not supposed to happen + const DWORD objectId = simObject.getObjectId(); + + // setup + const CInterpolationAndRenderingSetupPerCallsign setup = + this->getInterpolationSetupConsolidated(callsign, updateAllAircraft); + const bool sendGround = setup.isSendingGndFlagToSimulator(); + + // Interpolated situation + // simObjectNumber is passed to equally distributed steps like guessing parts + const bool slowUpdate = (((m_statsUpdateAircraftRuns + simObjectNumber) % 40) == 0); + const CInterpolationResult result = simObject.getInterpolation(currentTimestamp, setup, simObjectNumber++); + const bool forceUpdate = slowUpdate || updateAllAircraft || setup.isForcingFullInterpolation(); + if (result.getInterpolationStatus().hasValidSituation()) + { + // update situation + if (forceUpdate || !this->isEqualLastSent(result.getInterpolatedSituation())) + { + // adjust altitude to compensate for FS2020 temperature effect + CAircraftSituation situation = result; + const CLength relativeAltitude = + situation.geodeticHeight() - getOwnAircraftPosition().geodeticHeight(); + const double altitudeDeltaWeight = + 2 - qBound(3000.0, relativeAltitude.abs().value(CLengthUnit::ft()), 6000.0) / 3000; + situation.setAltitude({ situation.getAltitude() + m_altitudeDelta * altitudeDeltaWeight, + situation.getAltitude().getReferenceDatum() }); + + SIMCONNECT_DATA_INITPOSITION position = this->aircraftSituationToPosition(situation, sendGround); + const HRESULT hr = this->logAndTraceSendId( + SimConnect_SetDataOnSimObject(m_hSimConnect, + CSimConnectDefinitions::DataRemoteAircraftSetPosition, + static_cast(objectId), 0, 0, + sizeof(SIMCONNECT_DATA_INITPOSITION), &position), + traceSendId, simObject, "Failed to set position", Q_FUNC_INFO, "SimConnect_SetDataOnSimObject"); + if (isOk(hr)) + { + this->rememberLastSent(result); // remember situation + } + } + } + else + { + // already logged in interpolator + continue; + } + + // Interpolated parts + const bool updatedParts = this->updateRemoteAircraftParts(simObject, result, forceUpdate); + Q_UNUSED(updatedParts) + + } // all callsigns + + // stats + this->finishUpdateRemoteAircraftAndSetStatistics(currentTimestamp); + } + + bool CSimulatorMsfs2024::updateRemoteAircraftParts(const CSimConnectObject &simObject, + const CInterpolationResult &result, bool forcedUpdate) + { + if (!simObject.hasValidRequestAndObjectId()) { return false; } + if (!simObject.isConfirmedAdded()) { return false; } + + const CAircraftParts parts = result; + if (parts.isNull()) { return false; } + if (parts.getPartsDetails() != CAircraftParts::GuessedParts && !result.getPartsStatus().isSupportingParts()) + { + return false; + } + + const CCallsign cs = simObject.getCallsign(); + if (!forcedUpdate && (result.getPartsStatus().isReusedParts() || this->isEqualLastSent(parts, cs))) + { + return true; + } + + const bool ok = this->sendRemoteAircraftPartsToSimulator(simObject, parts); + if (ok) { this->rememberLastSent(parts, cs); } + return ok; + } + + // TODO TZ under investigation, flaps retracting to 0 again and other issues + bool CSimulatorMsfs2024::sendRemoteAircraftPartsToSimulator(const CSimConnectObject &simObject, + const CAircraftParts &parts) + { + Q_ASSERT(m_hSimConnect); + if (!simObject.isReadyToSend()) { return false; } + + const DWORD objectId = simObject.getObjectId(); + const bool traceId = this->isTracingSendId(); + + DataDefinitionRemoteAircraftPartsWithoutLights ddRemoteAircraftPartsWithoutLights(parts); + const CAircraftLights lights = parts.getAdjustedLights(); + + // in case we sent, we sent everything + const bool simObjectAircraftType = simObject.isAircraftSimulatedObject(); // no real aircraft type + const HRESULT hr1 = + simObjectAircraftType ? + S_OK : + this->logAndTraceSendId( + SimConnect_SetDataOnSimObject( + m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftPartsWithoutLights, + static_cast(objectId), SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(DataDefinitionRemoteAircraftPartsWithoutLights), &ddRemoteAircraftPartsWithoutLights), + traceId, simObject, "Failed so set parts", Q_FUNC_INFO, + "SimConnect_SetDataOnSimObject::ddRemoteAircraftPartsWithoutLights"); + + // Sim variable version, not working, setting the value, but flaps retracting to 0 again + // Sets flap handle to closest increment (0 to 16383) + const DWORD flapsDw = static_cast(qMin(16383, qRound((parts.getFlapsPercent() / 100.0) * 16383))); + const HRESULT hr2 = this->logAndTraceSendId( + SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFlapsSet, flapsDw, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, + SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed so set flaps", Q_FUNC_INFO, "SimConnect_TransmitClientEvent::EventFlapsSet"); + + const HRESULT hr3 = this->logAndTraceSendId( + SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventGearSet, parts.isFixedGearDown() ? 1.0 : 0.0, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, + SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed so set gears", Q_FUNC_INFO, "SimConnect_TransmitClientEvent::EventGearSet"); + + const CAircraftLights lightsIsState = simObject.getCurrentLightsInSimulator(); + if (lights == lightsIsState) { return isOk(hr1, hr2, hr3); } + + // lights we can set directly + const HRESULT hr4 = this->logAndTraceSendId( + SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventLandingLightsSet, + lights.isLandingOn() ? 1.0 : 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST, + SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed so set landing lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventLandingLightsSet"); + + const HRESULT hr5 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent( + m_hSimConnect, objectId, EventStrobesSet, lights.isStrobeOn() ? 1.0 : 0.0, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed to set strobe lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventStrobesSet"); + + const HRESULT hr6 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent( + m_hSimConnect, objectId, EventTaxiLightsSet, lights.isTaxiOn() ? 1.0 : 0.0, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed to set taxi lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventTaxiLightsSet"); + + const HRESULT hr7 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent( + m_hSimConnect, objectId, EventNavLightsSet, lights.isNavOn() ? 1.0 : 0.0, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed to set nav lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventNavLightsSet"); + + const HRESULT hr8 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent( + m_hSimConnect, objectId, EventLogoLightsSet, lights.isLogoOn() ? 1.0 : 0.0, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed to set logo lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventLogoLightsSet"); + + const HRESULT hr9 = this->logAndTraceSendId( + SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventRecognitionLightsSet, + lights.isRecognitionOn() ? 1.0 : 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST, + SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed to set recognitione lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventRecognitionLightsSet"); + + const HRESULT hr10 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent( + m_hSimConnect, objectId, EventCabinLightsSet, lights.isCabinOn() ? 1.0 : 0.0, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed to set cabin lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventCabinLightsSet"); + + const HRESULT hr11 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent( + m_hSimConnect, objectId, EventBeaconLightsSet, lights.isBeaconOn() ? 1.0 : 0.0, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed to set beacon lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventBeaconLightsSet"); + + const HRESULT hr12 = + this->logAndTraceSendId(SimConnect_TransmitClientEvent( + m_hSimConnect, objectId, EventWingLightsSet, lights.isWingOn() ? 1.0 : 0.0, + SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + traceId, simObject, "Failed to set wing lights", Q_FUNC_INFO, + "SimConnect_TransmitClientEvent::EventWingLightsSet"); + + // done + return isOk(hr1, hr2, hr3, (hr4 & hr5 & hr6 & hr7 & hr8 & hr9 & hr10 & hr11 & hr12)); + } + + bool CSimulatorMsfs2024::sendRemoteAircraftAtcDataToSimulator(const CSimConnectObject &simObject) + { + if (!simObject.isReadyToSend()) { return false; } + if (simObject.getType() != CSimConnectObject::AircraftNonAtc) { return false; } // otherwise errors + + const DWORD objectId = simObject.getObjectId(); + const bool traceId = this->isTracingSendId(); + + DataDefinitionRemoteAtc ddAtc; + ddAtc.setDefaultValues(); + const QByteArray csBa = simObject.getCallsignByteArray(); + const QByteArray airlineBa = simObject.getAircraft().getAirlineIcaoCode().getName().toLatin1(); + const QByteArray flightNumberBa = QString::number(simObject.getObjectId()).toLatin1(); + + ddAtc.copyAtcId(csBa.constData()); + ddAtc.copyAtcAirline(airlineBa.constData()); + ddAtc.copyFlightNumber(flightNumberBa.constData()); + + // in case we sent, we sent everything + const HRESULT hr = this->logAndTraceSendId( + SimConnect_SetDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::DataRemoteAircraftSetData, + static_cast(objectId), SIMCONNECT_DATA_SET_FLAG_DEFAULT, + 0, sizeof(DataDefinitionRemoteAtc), &ddAtc), + traceId, simObject, "Failed so aircraft ATC data", Q_FUNC_INFO, "SimConnect_SetDataOnSimObject"); + // done + return isOk(hr); + } + + // TODO TZ under investigation, toggling lights seems to be unreliable. The settings mentioned above can be used + // directly. void CSimulatorMsfs2024::sendToggledLightsToSimulator(const CSimConnectObject &simObj, + // const CAircraftLights &lightsWanted, bool force) + //{ + // if (!simObj.isReadyToSend()) { return; } // stale + + // const CAircraftLights lightsIsState = simObj.getCurrentLightsInSimulator(); + // if (lightsWanted == lightsIsState) { return; } + // if (!force && lightsWanted == simObj.getLightsAsSent()) { return; } + // const CCallsign callsign(simObj.getCallsign()); + + // // Update data + // if (m_simConnectObjects.contains(callsign)) + // { + // CSimConnectObject &simObjToUpdate = m_simConnectObjects[callsign]; + // simObjToUpdate.setLightsAsSent(lightsWanted); + // } + + // // state available, then I can toggle + // if (!lightsIsState.isNull()) + // { + // const DWORD objectId = simObj.getObjectId(); + // const bool trace = this->isTracingSendId(); + + // if (lightsWanted.isTaxiOn() != lightsIsState.isTaxiOn()) + // { + // this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleTaxiLights, + // 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST, + // SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + // trace, simObj, "Toggle taxi lights", Q_FUNC_INFO, "EventToggleTaxiLights"); + // } + // if (lightsWanted.isNavOn() != lightsIsState.isNavOn()) + // { + // this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleNavLights, + // 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST, + // SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + // trace, simObj, "Toggle nav.lights", Q_FUNC_INFO, "EventToggleNavLights"); + // } + // if (lightsWanted.isBeaconOn() != lightsIsState.isBeaconOn()) + // { + // this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, + // EventToggleBeaconLights, + // 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST, + // SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + // trace, simObj, "Toggle becon lights", Q_FUNC_INFO, "EventToggleBeaconLights"); + // } + // if (lightsWanted.isLogoOn() != lightsIsState.isLogoOn()) + // { + // this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleLogoLights, + // 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST, + // SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + // trace, simObj, "Toggle logo lights", Q_FUNC_INFO, "EventToggleLogoLights"); + // } + // if (lightsWanted.isRecognitionOn() != lightsIsState.isRecognitionOn()) + // { + // this->logAndTraceSendId( + // SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventToggleRecognitionLights, 0.0, + // SIMCONNECT_GROUP_PRIORITY_HIGHEST, + // SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + // trace, simObj, "Toggle recognition lights", Q_FUNC_INFO, "EventToggleRecognitionLights"); + // } + // if (lightsWanted.isCabinOn() != lightsIsState.isCabinOn()) + // { + // this->logAndTraceSendId(SimConnect_TransmitClientEvent(m_hSimConnect, objectId, + // EventToggleCabinLights, + // 0.0, SIMCONNECT_GROUP_PRIORITY_HIGHEST, + // SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY), + // trace, simObj, "Toggle cabin lights", Q_FUNC_INFO, "EventToggleCabinLights"); + // } + // return; + // } + + // // missing lights info from simulator so far + // if (this->showDebugLogMessage()) + // { + // this->debugLogMessage(Q_FUNC_INFO, QStringLiteral("Missing light state in simulator for '%1', model '%2'") + // .arg(callsign.asString(), simObj.getAircraftModelString())); + // } + + // const QPointer myself(this); + // QTimer::singleShot(DeferResendingLights, this, [=] { + // if (!myself) { return; } + // if (!m_simConnectObjects.contains(callsign)) { return; } + // const CSimConnectObject currentSimObject = m_simConnectObjects[callsign]; + // if (!currentSimObject.isReadyToSend()) { return; } // stale + // if (lightsWanted != currentSimObject.getLightsAsSent()) + // { + // return; + // } // changed in between, so another call sendToggledLightsToSimulator is pending + // if (this->showDebugLogMessage()) + // { + // this->debugLogMessage(Q_FUNC_INFO, QStringLiteral("Resending light state for '%1', model '%2'") + // .arg(callsign.asString(), simObj.getAircraftModelString())); + // } + // this->sendToggledLightsToSimulator(currentSimObject, lightsWanted, true); + // }); + //} + + SIMCONNECT_DATA_INITPOSITION + CSimulatorMsfs2024::aircraftSituationToPosition(const CAircraftSituation &situation, bool sendGnd, + bool forceUnderflowDetection, CStatusMessage *details) + { + Q_ASSERT_X(!situation.isGeodeticHeightNull(), Q_FUNC_INFO, "Missing height (altitude)"); + Q_ASSERT_X(!situation.isPositionNull(), Q_FUNC_INFO, "Missing position"); + + // lat/Lng, NO PBH + CAircraftSituation::AltitudeCorrection altCorrection = CAircraftSituation::UnknownCorrection; + SIMCONNECT_DATA_INITPOSITION position = CSimulatorMsfs2024::coordinateToFsxPosition(situation); + if (forceUnderflowDetection) + { + const CAltitude alt = situation.getCorrectedAltitude(true, &altCorrection); + position.Altitude = alt.value(CLengthUnit::ft()); + } + + // MSFS has inverted pitch and bank angles + position.Pitch = -situation.getPitch().value(CAngleUnit::deg()); + position.Bank = -situation.getBank().value(CAngleUnit::deg()); + position.Heading = situation.getHeading().value(CAngleUnit::deg()); + position.OnGround = 0U; // not on ground + + const double gsKts = situation.getGroundSpeed().value(CSpeedUnit::kts()); + position.Airspeed = static_cast(qRound(gsKts)); + + // sanity check + if (gsKts < 0.0) + { + // we get negative GS for pushback and helicopters + // here we handle them her with DWORD (unsigned) + position.Airspeed = 0U; + } + else { position.Airspeed = static_cast(qRound(gsKts)); } + + // send GND flag also when underflow detection is available + if ((sendGnd || forceUnderflowDetection) && situation.isOnGroundInfoAvailable()) + { + const bool onGround = situation.isOnGround(); + position.OnGround = onGround ? 1U : 0U; + } + + // if we have no GND flag yet (gnd flag prevents underflow) + if (forceUnderflowDetection && position.OnGround == 0 && + !CAircraftSituation::isCorrectedAltitude(altCorrection)) + { + // logical resolution failed so far, likely we have no CG or elevantion + // primitive guessing + do { + if (position.Airspeed < 2) + { + position.OnGround = 1U; + if (details) + { + *details = CStatusMessage(static_cast(nullptr)) + .warning(u"Force GND flag for underflow protection"); + } + break; + } + } + while (false); + } + + // crosscheck + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + SWIFT_VERIFY_X(isValidFsxPosition(position), Q_FUNC_INFO, "Invalid MSFS2024 pos."); + } + + return position; + } + + SIMCONNECT_DATA_PBH CSimulatorMsfs2024::aircraftSituationToFsxPBH(const CAircraftSituation &situation) + { + // MSFS has inverted pitch and bank angles + SIMCONNECT_DATA_PBH pbh; + pbh.Pitch = -situation.getPitch().value(CAngleUnit::deg()); + pbh.Bank = -situation.getBank().value(CAngleUnit::deg()); + pbh.Heading = situation.getHeading().value(CAngleUnit::deg()); + return pbh; + } + + SIMCONNECT_DATA_INITPOSITION + CSimulatorMsfs2024::coordinateToFsxPosition(const ICoordinateGeodetic &coordinate) + { + SIMCONNECT_DATA_INITPOSITION position; + position.Latitude = coordinate.latitude().value(CAngleUnit::deg()); + position.Longitude = coordinate.longitude().value(CAngleUnit::deg()); + position.Altitude = coordinate.geodeticHeight().value( + CLengthUnit::ft()); // already corrected in interpolator if there is an underflow + position.Heading = 0; + position.Airspeed = 0; + position.Pitch = 0; + position.Bank = 0; + position.OnGround = 0; + return position; + } + + SIMCONNECT_DATA_LATLONALT CSimulatorMsfs2024::coordinateToFsxLatLonAlt(const ICoordinateGeodetic &coordinate) + { + SIMCONNECT_DATA_LATLONALT lla; + lla.Latitude = coordinate.latitude().value(CAngleUnit::deg()); + lla.Longitude = coordinate.longitude().value(CAngleUnit::deg()); + lla.Altitude = coordinate.geodeticHeight().value( + CLengthUnit::ft()); // already corrected in interpolator if there is an underflow + return lla; + } + + bool CSimulatorMsfs2024::isValidFsxPosition(const SIMCONNECT_DATA_INITPOSITION &fsxPos) + { + // double Latitude; // degrees | double Longitude; // degrees | double Altitude; // feet + // double Pitch; // degrees | double Bank; // degrees | double Heading; // degrees + // DWORD OnGround; // 1=force to be on the ground | DWORD Airspeed; // knots + // https://www.prepar3d.com/SDKv4/sdk/simconnect_api/references/simobject_functions.html + // examples show heaading 180 => we assume values +-180deg + if (!isValid180Deg(fsxPos.Pitch)) { return false; } + if (!isValid180Deg(fsxPos.Bank)) { return false; } + if (!isValid180Deg(fsxPos.Heading)) { return false; } + if (!isValid180Deg(fsxPos.Latitude)) { return false; } + if (!isValid180Deg(fsxPos.Longitude)) { return false; } + return true; + } + + bool CSimulatorMsfs2024::requestPositionDataForSimObject(const CSimConnectObject &simObject, + SIMCONNECT_PERIOD period) + { + if (this->isShuttingDownOrDisconnected()) { return false; } + if (!simObject.hasValidRequestAndObjectId()) { return false; } + if (simObject.isPending()) { return false; } // wait until confirmed + if (simObject.getSimDataPeriod() == period) { return true; } // already queried like this + if (!m_simConnectObjects.contains(simObject.getCallsign())) { return false; } // removed in meantime + + // always request, not only when something has changed + const SIMCONNECT_DATA_REQUEST_ID reqId = static_cast( + simObject.getRequestId(CSimConnectDefinitions::SimObjectPositionData)); + const HRESULT result = this->logAndTraceSendId( + SimConnect_RequestDataOnSimObject(m_hSimConnect, reqId, + CSimConnectDefinitions::DataRemoteAircraftGetPosition, + simObject.getObjectId(), period), + simObject, "Cannot request simulator data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + + if (isOk(result)) + { + m_requestSimObjectDataCount++; + m_simConnectObjects[simObject.getCallsign()].setSimDataPeriod(period); + return true; + } + return false; + } + + bool CSimulatorMsfs2024::requestLightsForSimObject(const CSimConnectObject &simObject) + { + if (!this->isValidSimObjectNotPendingRemoved(simObject)) { return false; } + if (!m_hSimConnect) { return false; } + + // always request, not only when something has changed + const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectLights); + const HRESULT result = this->logAndTraceSendId( + SimConnect_RequestDataOnSimObject(m_hSimConnect, requestId, + CSimConnectDefinitions::DataRemoteAircraftLights, simObject.getObjectId(), + SIMCONNECT_PERIOD_SECOND), + simObject, "Cannot request lights data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + return isOk(result); + } + + bool CSimulatorMsfs2024::requestModelInfoForSimObject(const CSimConnectObject &simObject) + { + if (!this->isValidSimObjectNotPendingRemoved(simObject)) { return false; } + if (!m_hSimConnect) { return false; } + + // always request, not only when something has changed + const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectModel); + const HRESULT result = this->logAndTraceSendId( + SimConnect_RequestDataOnSimObject(m_hSimConnect, requestId, + CSimConnectDefinitions::DataRemoteAircraftModelData, + simObject.getObjectId(), SIMCONNECT_PERIOD_ONCE), + simObject, "Cannot request model info", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + return isOk(result); + } + + bool CSimulatorMsfs2024::stopRequestingDataForSimObject(const CSimConnectObject &simObject) + { + if (!simObject.hasValidRequestAndObjectId()) { return false; } + if (!m_hSimConnect) { return false; } + + // stop by setting SIMCONNECT_PERIOD_NEVER + SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectPositionData); + const HRESULT hr1 = this->logAndTraceSendId( + SimConnect_RequestDataOnSimObject(m_hSimConnect, requestId, + CSimConnectDefinitions::DataRemoteAircraftGetPosition, + simObject.getObjectId(), SIMCONNECT_PERIOD_NEVER), + simObject, "Stopping position request", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + + requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectLights); + const HRESULT hr2 = this->logAndTraceSendId( + SimConnect_RequestDataOnSimObject(m_hSimConnect, requestId, + CSimConnectDefinitions::DataRemoteAircraftLights, simObject.getObjectId(), + SIMCONNECT_PERIOD_NEVER), + simObject, "Stopping lights request", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + return isOk(hr1, hr2); + } + + void CSimulatorMsfs2024::initSimulatorInternals() + { + CSimulatorFsCommon::initSimulatorInternals(); + m_simulatorInternals.setValue("fsx/simConnectVersion", m_simConnectVersion); + } + + void CSimulatorMsfs2024::reset() + { + this->safeKillTimer(); + + // cleared below: + // physicallyRemoveAllRemoteAircraft + // m_simConnectObjects + // m_simConnectObjectsPositionAndPartsTraces + // m_addPendingAircraft + // m_updateRemoteAircraftInProgress + CSimulatorFsCommon::reset(); // clears all pending aircraft etc + + // reset values + m_simulatingChangedTs = -1; + m_simConnected = false; + m_simSimulating = false; + m_sbDataReceived = 0; + m_requestIdSimObjAircraft = static_cast(RequestSimObjAircraftStart); + m_dispatchErrors = 0; + m_receiveExceptionCount = 0; + m_sendIdTraces.clear(); + } + + void CSimulatorMsfs2024::clearAllRemoteAircraftData() + { + // m_addAgainAircraftWhenRemoved cleared below + CSimulatorFsCommon::clearAllRemoteAircraftData(); // also removes aircraft + m_simConnectObjects.clear(); + m_addPendingAircraft.clear(); + m_simConnectObjectsPositionAndPartsTraces.clear(); + } + + void CSimulatorMsfs2024::onOwnModelChanged(const CAircraftModel &newModel) + { + m_sbDataReceived = 0; + CSimulatorFsCommon::onOwnModelChanged(newModel); + } + + QString CSimulatorMsfs2024::fsxPositionToString(const SIMCONNECT_DATA_INITPOSITION &position) + { + static const QString positionStr( + "Lat: %1deg lng: %2deg alt: %3ft pitch: %4deg bank: %5deg hdg: %6deg airspeed: %7kts onGround: %8"); + return positionStr.arg(position.Latitude) + .arg(position.Longitude) + .arg(position.Altitude) + .arg(position.Pitch) + .arg(position.Bank) + .arg(position.Heading) + .arg(position.Airspeed) + .arg(position.OnGround); + } + + CCallsignSet CSimulatorMsfs2024::getCallsignsMissingInProvider() const + { + if (m_simConnectObjects.isEmpty()) { return CCallsignSet(); } + const CCallsignSet simObjectCallsigns(m_simConnectObjects.getAllCallsigns(true)); + const CCallsignSet providerCallsigns(this->getAircraftInRangeCallsigns()); + return simObjectCallsigns.difference(providerCallsigns); + } + + void CSimulatorMsfs2024::traceSendId(const CSimConnectObject &simObject, const QString &functionName, + const QString &details, bool forceTrace) + { + if (!forceTrace && !this->isTracingSendId()) { return; } + if (MaxSendIdTraces < 1) { return; } // cppcheck-suppress knownConditionTrueFalse + DWORD dwLastId = 0; + const HRESULT hr = SimConnect_GetLastSentPacketID(m_hSimConnect, &dwLastId); + if (isFailure(hr)) { return; } + if (m_sendIdTraces.size() > MaxSendIdTraces) { m_sendIdTraces.removeLast(); } + const TraceFsxSendId trace(dwLastId, simObject, + details.isEmpty() ? functionName : details % u", " % functionName); + m_sendIdTraces.push_front(trace); + } + + HRESULT CSimulatorMsfs2024::logAndTraceSendId(HRESULT hr, const QString &warningMsg, const QString &functionName, + const QString &functionDetails) + { + const CSimConnectObject empty; + return this->logAndTraceSendId(hr, empty, warningMsg, functionName, functionDetails); + } + + HRESULT CSimulatorMsfs2024::logAndTraceSendId(HRESULT hr, const CSimConnectObject &simObject, + const QString &warningMsg, const QString &functionName, + const QString &functionDetails) + { + return this->logAndTraceSendId(hr, this->isTracingSendId(), simObject, warningMsg, functionName, + functionDetails); + } + + HRESULT CSimulatorMsfs2024::logAndTraceSendId(HRESULT hr, bool traceSendId, const CSimConnectObject &simObject, + const QString &warningMsg, const QString &functionName, + const QString &functionDetails) + { + if (traceSendId) { this->traceSendId(simObject, functionName, functionDetails); } + if (isOk(hr)) { return hr; } + if (!warningMsg.isEmpty()) { CLogMessage(this).warning(warningMsg % u" SimObject: " % simObject.toQString()); } + this->triggerAutoTraceSendId(); + return hr; + } + + QByteArray CSimulatorMsfs2024::toFsxChar(const QString &string) { return string.toLatin1(); } + + TraceFsxSendId CSimulatorMsfs2024::getSendIdTrace(DWORD sendId) const + { + for (const TraceFsxSendId &trace : m_sendIdTraces) + { + if (trace.sendId == sendId) { return trace; } + } + return TraceFsxSendId::invalid(); + } + + QString CSimulatorMsfs2024::getSendIdTraceDetails(DWORD sendId) const + { + const TraceFsxSendId trace = this->getSendIdTrace(sendId); + if (trace.sendId == sendId) { return this->getSendIdTraceDetails(trace); } + return {}; + } + + QString CSimulatorMsfs2024::getSendIdTraceDetails(const TraceFsxSendId &trace) const + { + static const QString d("Send id: %1 obj.id.: %2 SimObj: %3 | '%4'"); + if (trace.isInvalid()) { return QString(); } + + // update with latest sim object + const CSimConnectObject simObject = this->getSimObjectForTrace(trace); + return d.arg(trace.sendId).arg(simObject.getObjectId()).arg(simObject.toQString(), trace.comment); + } + + // int CSimulatorMsfs2024::removeAllProbes() + //{ + // if (!m_hSimConnect) { return 0; } // already disconnected + // const QList probes = m_simConnectObjects.getProbes(); + + // int c = 0; + // for (const CSimConnectObject &probeSimObject : probes) + // { + // if (!probeSimObject.isConfirmedAdded()) { continue; } + // const SIMCONNECT_DATA_REQUEST_ID requestId = + // probeSimObject.getRequestId(CSimConnectDefinitions::SimObjectRemove); + // const HRESULT result = SimConnect_AIRemoveObject( + // m_hSimConnect, static_cast(probeSimObject.getObjectId()), requestId); + // if (isOk(result)) { c++; } + // else + // { + // CLogMessage(this).warning(u"Removing probe '%1' from simulator failed") << + // probeSimObject.getObjectId(); + // } + // } + // m_simConnectObjects.removeAllProbes(); + // m_pendingProbeRequests.clear(); + // return c; + //} + + CSimConnectObject CSimulatorMsfs2024::insertNewSimConnectObject(const CSimulatedAircraft &aircraft, DWORD requestId, + CSimConnectObject::SimObjectType type, + const CSimConnectObject &removedPendingObject) + { + if (m_simConnectObjects.contains(aircraft.getCallsign())) + { + // error, ...? + CSimConnectObject &simObject = m_simConnectObjects[aircraft.getCallsign()]; + simObject.copyAddingFailureCounters(removedPendingObject); + simObject.resetTimestampToNow(); + return simObject; + } + + CSimConnectObject simObject; + if (m_simConnectObjectsPositionAndPartsTraces.contains(aircraft.getCallsign())) + { + // if in traces, get the object and reuse it + simObject = m_simConnectObjectsPositionAndPartsTraces[aircraft.getCallsign()]; + m_simConnectObjectsPositionAndPartsTraces.remove(aircraft.getCallsign()); + simObject.resetState(); + simObject.setRequestId(requestId); + simObject.setAircraft(aircraft); + simObject.attachInterpolatorLogger(&m_interpolationLogger); // setting a logger does not start logging + } + else + { + simObject = CSimConnectObject(aircraft, requestId, this, this, this->getRemoteAircraftProvider(), + &m_interpolationLogger); + } + simObject.copyAddingFailureCounters(removedPendingObject); + simObject.setType(type); + m_simConnectObjects.insert(simObject, true); // update timestamp + return simObject; + } + + // const CAltitude &CSimulatorMsfs2024::terrainProbeAltitude() + //{ + // static const CAltitude alt(50000, CLengthUnit::ft()); + // return alt; + // } + + QString CSimulatorMsfs2024::fsxCharToQString(const char *fsxChar, int size) + { + return QString::fromLatin1(fsxChar, size); + } + + QString CSimulatorMsfs2024::requestIdToString(DWORD requestId) + { + if (requestId <= CSimConnectDefinitions::RequestEndMarker) + { + return CSimConnectDefinitions::requestToString(static_cast(requestId)); + } + + const CSimConnectDefinitions::SimObjectRequest simRequest = requestToSimObjectRequest(requestId); + const CSimConnectObject::SimObjectType simType = CSimConnectObject::requestIdToType(requestId); + + static const QString req("%1 %2 %3"); + return req.arg(requestId) + .arg(CSimConnectObject::typeToString(simType)) + .arg(CSimConnectDefinitions::simObjectRequestToString(simRequest)); + } + + DWORD CSimulatorMsfs2024::unitTestRequestId(CSimConnectObject::SimObjectType type) + { + int start; + int end; + switch (type) + { + case CSimConnectObject::AircraftNonAtc: + case CSimConnectObject::AircraftSimulatedObject: + default: + start = RequestSimObjAircraftStart; + end = RequestSimObjAircraftEnd; + break; + } + + const int id = CMathUtils::randomInteger(start, end); + return static_cast(id); + } + + CCallsignSet CSimulatorMsfs2024::physicallyRemoveAircraftNotInProvider() + { + const CCallsignSet callsignsToBeRemoved(this->getCallsignsMissingInProvider()); + if (callsignsToBeRemoved.isEmpty()) { return callsignsToBeRemoved; } + for (const CCallsign &callsign : callsignsToBeRemoved) { this->physicallyRemoveRemoteAircraft(callsign); } + + if (this->showDebugLogMessage()) + { + this->debugLogMessage(Q_FUNC_INFO, + QStringLiteral("CS: '%1'").arg(callsignsToBeRemoved.toStringList().join(", "))); + } + return callsignsToBeRemoved; + } + + void CSimulatorMsfs2024::physicallyRemoveAircraftNotInProviderAsync() + { + const QPointer myself(this); + QTimer::singleShot(100, this, [=] { + if (!myself || this->isShuttingDown()) { return; } + CSimulatorMsfs2024::physicallyRemoveAircraftNotInProvider(); + }); + } + + CSimulatorMsfs2024Listener::CSimulatorMsfs2024Listener(const CSimulatorPluginInfo &info) : ISimulatorListener(info) + { + m_timer.setInterval(MinQueryIntervalMs); + m_timer.setObjectName(this->objectName().append(":m_timer")); + connect(&m_timer, &QTimer::timeout, this, &CSimulatorMsfs2024Listener::checkConnection); + } + + void CSimulatorMsfs2024Listener::startImpl() + { + m_simulatorVersion.clear(); + m_simConnectVersion.clear(); + m_simulatorName.clear(); + m_simulatorDetails.clear(); + + m_timer.start(); + } + + void CSimulatorMsfs2024Listener::stopImpl() + { + m_timer.stop(); + this->disconnectFromSimulator(); + } + + void CSimulatorMsfs2024Listener::checkImpl() + { + if (!m_timer.isActive()) { return; } + if (this->isShuttingDown()) { return; } + + QPointer myself(this); + QTimer::singleShot(0, this, [=] { + if (!myself || !sApp || sApp->isShuttingDown()) { return; } + this->checkConnection(); + }); + + // restart because we have just checked now + m_timer.start(); + } + + QString CSimulatorMsfs2024Listener::backendInfo() const + { + if (m_simulatorName.isEmpty()) { return ISimulatorListener::backendInfo(); } + return m_simulatorDetails; + } + + void CSimulatorMsfs2024Listener::checkConnection() + { + Q_ASSERT_X(!CThreadUtils::thisIsMainThread(), Q_FUNC_INFO, "Expect to run in background"); + + // check before we access the sim. connection + if (this->isShuttingDown() || this->thread()->isInterruptionRequested()) + { + this->stopImpl(); + return; + } + + QElapsedTimer t; + t.start(); + bool check = false; + do { + // if we can connect, but not dispatch, it can mean a previously started FSX/P3D + // blocks remote calls -> RESTART + if (!this->connectToSimulator()) { break; } + + // check if we have the right sim. + // this check on a remote FSX/P3D not running/existing might TAKE LONG! + const HRESULT result = + SimConnect_CallDispatch(m_hSimConnect, CSimulatorMsfs2024Listener::SimConnectProc, this); + + // make sure we did not stop in meantime + if (this->isShuttingDown() || this->thread()->isInterruptionRequested()) + { + this->stopImpl(); + return; + } + + if (isFailure(result)) { break; } // means serious failure + check = this->checkVersionAndSimulator(); + } + while (false); + + this->adjustTimerInterval(t.elapsed()); + if (check) { emit this->simulatorStarted(this->getPluginInfo()); } + } + + void CSimulatorMsfs2024Listener::adjustTimerInterval(qint64 checkTimeMs) + { + const QString sim = this->getPluginInfo().getSimulatorInfo().toQString(true); + CLogMessage(this).debug(u"Checked sim.'%1' connection in %2ms") << sim << checkTimeMs; + if (checkTimeMs > qRound(1.25 * MinQueryIntervalMs)) + { + const int newIntervalMs = qRound(1.2 * checkTimeMs / 1000.0) * 1000; + CLogMessage(this).debug(u"Check for simulator sim.'%1' connection in %2ms, too slow. Setting %3ms") + << sim << checkTimeMs << newIntervalMs; + if (m_timer.interval() != newIntervalMs) { m_timer.setInterval(newIntervalMs); } + } + else + { + if (m_timer.interval() != MinQueryIntervalMs) { m_timer.setInterval(MinQueryIntervalMs); } + } + + // restart + m_timer.start(); + } + + bool CSimulatorMsfs2024Listener::checkVersionAndSimulator() const + { + const CSimulatorInfo pluginSim(getPluginInfo().getIdentifier()); + const QString connectedSimName = m_simulatorName.toLower().trimmed(); + + if (connectedSimName.isEmpty()) { return false; } + if (pluginSim.isMSFS2024()) + { + // MSFS2024 drivers only works with MSFS2024 + return connectedSimName.contains("sunrise"); + } + return false; + } + + bool CSimulatorMsfs2024Listener::checkSimConnectDll() const + { + static const CWinDllUtils::DLLInfo simConnectInfo = CSimConnectUtilities::simConnectDllInfo(); + if (!simConnectInfo.errorMsg.isEmpty()) { return false; } + return true; + } + + bool CSimulatorMsfs2024Listener::connectToSimulator() + { + if (m_simConnected) { return true; } + const HRESULT result = SimConnect_Open(&m_hSimConnect, sApp->swiftVersionChar(), nullptr, 0, nullptr, 0); + const bool ok = isOk(result); + m_simConnected = ok; + return ok; + } + + bool CSimulatorMsfs2024Listener::disconnectFromSimulator() + { + if (!m_simConnected) { return false; } + SimConnect_Close(m_hSimConnect); + m_hSimConnect = nullptr; + m_simConnected = false; + return true; + } + + void CSimulatorMsfs2024Listener::SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext) + { + Q_UNUSED(cbData) + CSimulatorMsfs2024Listener *simListener = static_cast(pContext); + switch (pData->dwID) + { + case SIMCONNECT_RECV_ID_OPEN: + { + SIMCONNECT_RECV_OPEN *event = static_cast(pData); + simListener->m_simulatorVersion = QStringLiteral("%1.%2.%3.%4") + .arg(event->dwApplicationVersionMajor) + .arg(event->dwApplicationVersionMinor) + .arg(event->dwApplicationBuildMajor) + .arg(event->dwApplicationBuildMinor); + simListener->m_simConnectVersion = QStringLiteral("%1.%2.%3.%4") + .arg(event->dwSimConnectVersionMajor) + .arg(event->dwSimConnectVersionMinor) + .arg(event->dwSimConnectBuildMajor) + .arg(event->dwSimConnectBuildMinor); + simListener->m_simulatorName = CSimulatorMsfs2024::fsxCharToQString(event->szApplicationName); + simListener->m_simulatorDetails = QStringLiteral("Name: '%1' Version: %2 SimConnect: %3") + .arg(simListener->m_simulatorName, simListener->m_simulatorVersion, + simListener->m_simConnectVersion); + const CStatusMessage msg = CStatusMessage(simListener).info(u"Connect to %1: '%2'") + << simListener->getPluginInfo().getIdentifier() << simListener->backendInfo(); + + // avoid the same message over and over again + if (msg.getMessage() != simListener->m_lastMessage.getMessage()) + { + CLogMessage::preformatted(msg); + simListener->m_lastMessage = msg; + } + break; + } + case SIMCONNECT_RECV_ID_EXCEPTION: break; + default: break; + } + } +} // namespace swift::simplugin::msfs2024common diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024common.h b/src/plugins/simulator/msfs2024/simulatormsfs2024common.h new file mode 100644 index 000000000..a54d34001 --- /dev/null +++ b/src/plugins/simulator/msfs2024/simulatormsfs2024common.h @@ -0,0 +1,769 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +//! \file + +#ifndef SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMULATORMSFS2024COMMON_H +#define SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMULATORMSFS2024COMMON_H + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "config/buildconfig.h" +#include "core/simulator.h" +#include "gui/components/dbmappingcomponentaware.h" +#include "misc/aviation/airportlist.h" +#include "misc/aviation/altitude.h" +#include "misc/datacache.h" +#include "misc/network/client.h" +#include "misc/pixmap.h" +#include "misc/pq/frequency.h" +#include "misc/simulation/aircraftmodel.h" +#include "misc/simulation/data/modelcaches.h" // TODO ??? +#include "misc/simulation/interpolation/interpolatorlinear.h" +#include "misc/simulation/settings/modelmatchersettings.h" +#include "misc/simulation/settings/simulatorsettings.h" +#include "misc/simulation/simulatedaircraft.h" +#include "misc/simulation/simulatorplugininfo.h" +#include "misc/statusmessage.h" +#include "plugins/simulator/fscommon/simulatorfscommon.h" +#include "plugins/simulator/msfs2024/msfs2024export.h" +#include "plugins/simulator/msfs2024/simconnectdatadefinitionmsfs2024.h" +#include "plugins/simulator/msfs2024/simconnectobjectmsfs2024.h" +#include "plugins/simulator/msfs2024/simconnectsymbolsmsfs2024.h" +#include "plugins/simulator/msfs2024/simconnectwindowsmsfs2024.h" + +namespace swift::simplugin::msfs2024common +{ + //! SimConnect Event IDs + enum EventIds + { + SystemEventSimStatus, + SystemEventObjectAdded, + SystemEventObjectRemoved, + SystemEventSlewToggle, + SystemEventFrame, + SystemEventPause, + SystemEventFlightLoaded, + EventPauseToggle, + EventFreezeLatLng, + EventFreezeAlt, + EventFreezeAtt, + EventSetCom1Active, + EventSetCom2Active, + EventSetCom1Standby, + EventSetCom2Standby, + EventSetTransponderCode, + EventTextMessage, + EventSetTimeZuluYear, + EventSetTimeZuluDay, + EventSetTimeZuluHours, + EventSetTimeZuluMinutes, + // ------------ lights ------------- + // EventLandingLightsOff, + // EventLandinglightsOn, + EventLandingLightsSet, + // EventLandingLightsToggle, + // EventPanelLightsOff, + // EventPanelLightsOn, + EventPanelLightsSet, + // EventStrobesOff, + // EventStrobesOn, + EventStrobesSet, + // EventStrobesToggle, + EventBeaconLightsSet, + EventCabinLightsSet, + EventLogoLightsSet, + EventNavLightsSet, + EventRecognitionLightsSet, + EventTaxiLightsSet, + EventWingLightsSet, + // ------------- flaps ------------- + EventFlapsSet, + EventGearSet, + // ---------- end marker ----------- + EventFSXEndMarker + }; + + //! Struct to trace send ids + struct TraceFsxSendId + { + //! Ctor + TraceFsxSendId(DWORD sendId, const CSimConnectObject &simObject, const QString &comment) + : sendId(sendId), simObject(simObject), comment(comment) + {} + + // DWORD is unsigned + DWORD sendId = 0; //!< the send id + CSimConnectObject simObject; //!< CSimConnectObject at the time of the trace + QString comment; //!< where sent + + //! For aircraft + bool isForAircraft() const { return simObject.getType() == CSimConnectObject::AircraftNonAtc; } + + //! Invalid trace? + bool isInvalid() const { return sendId == 0 && simObject.isInvalid() == 0 && comment.isEmpty(); } + + //! Valid trace? + bool isValid() const { return !this->isInvalid(); } + + //! Invalid object + static const TraceFsxSendId &invalid() + { + static const TraceFsxSendId i(0, CSimConnectObject(), ""); + return i; + } + }; + + //! Buffer for models read from SimConnect + struct sSimObjectLivery + { + QString szSimObjectCombinedTitle; + QString szSimObjectTitle; + QString szLiveryName; + }; + + //! Flags for the load status of reading from the SimConnect + struct sSimmobjectLoaded + { + bool bLoadStarted = false; + bool bAirplaneLoaded = false; + bool bHelicopterLoaded = false; + bool bHotAirLoaded = false; + }; + + //! FSX Simulator Implementation + class MSFS2024_EXPORT CSimulatorMsfs2024 : public fscommon::CSimulatorFsCommon + { + Q_OBJECT + Q_INTERFACES(swift::core::ISimulator) + Q_INTERFACES(swift::misc::simulation::ISimulationEnvironmentProvider) + Q_INTERFACES(swift::misc::simulation::IInterpolationSetupProvider) + + public: + //! Constructor, parameters as in \sa swift::core::ISimulatorFactory::create + CSimulatorMsfs2024(const swift::misc::simulation::CSimulatorPluginInfo &info, + swift::misc::simulation::IOwnAircraftProvider *ownAircraftProvider, + swift::misc::simulation::IRemoteAircraftProvider *remoteAircraftProvider, + swift::misc::network::IClientProvider *clientProvider, QObject *parent = nullptr); + + //! Destructor + virtual ~CSimulatorMsfs2024() override; + + //! \name ISimulator implementations + //! @{ + virtual bool connectTo() override; + virtual bool disconnectFrom() override; + virtual bool + physicallyAddRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &newRemoteAircraft) override; + virtual bool physicallyRemoveRemoteAircraft(const swift::misc::aviation::CCallsign &callsign) override; + virtual int physicallyRemoveAllRemoteAircraft() override; + virtual bool updateOwnSimulatorCockpit(const swift::misc::simulation::CSimulatedAircraft &ownAircraft, + const swift::misc::CIdentifier &originator) override; + virtual bool updateOwnSimulatorSelcal(const swift::misc::aviation::CSelcal &selcal, + const swift::misc::CIdentifier &originator) override; + virtual void displayStatusMessage(const swift::misc::CStatusMessage &message) const override; + virtual void displayTextMessage(const swift::misc::network::CTextMessage &message) const override; + virtual bool isPhysicallyRenderedAircraft(const swift::misc::aviation::CCallsign &callsign) const override; + virtual swift::misc::aviation::CCallsignSet physicallyRenderedAircraft() const override; + virtual swift::misc::CStatusMessageList debugVerifyStateAfterAllAircraftRemoved() const override; + virtual QString getStatisticsSimulatorSpecific() const override; + virtual void resetAircraftStatistics() override; + virtual void setFlightNetworkConnected(bool connected) override; + virtual swift::misc::CStatusMessageList + getInterpolationMessages(const swift::misc::aviation::CCallsign &callsign) const override; + virtual bool testSendSituationAndParts(const swift::misc::aviation::CCallsign &callsign, + const swift::misc::aviation::CAircraftSituation &situation, + const swift::misc::aviation::CAircraftParts &parts) override; + //! @} + + ////! \copydoc swift::misc::simulation::ISimulationEnvironmentProvider::requestElevation + ////! \remark x86 FSX version, x64 version is overridden + ////! \sa CSimulatorFsxCommon::is + // virtual bool requestElevation(const swift::misc::geo::ICoordinateGeodetic &reference, + // const swift::misc::aviation::CCallsign &aircraftCallsign) override; + + //! saves the SimObjects received from the simulator a structure + void CacheSimObjectAndLiveries(const SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *msg); + + //! Write the sim objects and liveries to file and give back the number of objects written + int writeSimObjectsAndLiveriesToFile(const swift::misc::simulation::CAircraftModelList Modelset); + + //! creates a new model list + void createNewModelList(); + + //! resets the sim object and liveries cache + void setSimObjectAndLiveries(); + + //! Tracing right now? + bool isTracingSendId() const; + + //! Trace enable (can be auto enable also) + bool isTraceSendId() const { return m_traceSendId; } + + //! Set tracing on/off + void setTractingSendId(bool trace); + + //! Using the SB offsets? + bool isUsingSbOffsetValues() const { return m_useSbOffsets; } + + //! Use SB offset values + void setUsingSbOffsetValues(bool use); + + //! Number of received SB4 packets + //! \remark if this is increasing, SB4 is supported + int receivedSBPackets() const { return m_sbDataReceived; } + + //! Allow adding as simulated object instead of non ATC + bool isAddingAsSimulatedObjectEnabled() const { return m_useAddSimulatedObj; } + + //! Allow adding as simulated object instead of non ATC + void setAddingAsSimulatedObjectEnabled(bool enabled); + + //! Request for sim data (request in range of sim data)? + static bool isRequestForSimObjAircraft(DWORD requestId) + { + return requestId >= RequestSimObjAircraftStart && requestId <= RequestSimObjAircraftRangeEnd; + } + + //! Request for any CSimConnectObject? + static bool isRequestForSimConnectObject(DWORD requestId) { return isRequestForSimObjAircraft(requestId); } + + //! Sub request type + static CSimConnectDefinitions::SimObjectRequest requestToSimObjectRequest(DWORD requestId); + + //! Random unit text request id + //! \private + static DWORD unitTestRequestId(CSimConnectObject::SimObjectType type); + + //! Encapsulates creating QString from FSX string data + static QString fsxCharToQString(const char *fsxChar, int size = -1); + + protected: + //! SimConnect callback + //! \note all tasks called in this function (i.e, all called functions) must perform fast or shall be called + //! asynchronously + static void CALLBACK SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext); + + //! \name Interface implementations + //! @{ + virtual bool isConnected() const override; + virtual bool isSimulating() const override; + //! @} + + //! \name Base class overrides + //! @{ + virtual void reset() override; + virtual void initSimulatorInternals() override; + virtual void clearAllRemoteAircraftData() override; + virtual void onOwnModelChanged(const swift::misc::simulation::CAircraftModel &newModel) override; + //! @} + + //! Timer event (our SimConnect event loop), runs dispatch + //! \sa m_timerId + //! \sa CSimulatorFsxCommon::dispatch + virtual void timerEvent(QTimerEvent *event) override; + + //! Specific P3D events + virtual HRESULT initEventsP3D(); + + //! \ingroup swiftdotcommands + //!
+        //! .drv sendid  on|off      tracing simConnect sendId on/off
+        //! 
+ virtual bool parseDetails(const swift::misc::CSimpleCommandParser &parser) override; + + //! Trigger tracing ids for some while + //! \sa CSimulatorFsxCommon::isTracingSendId + bool triggerAutoTraceSendId(qint64 traceTimeMs = AutoTraceOffsetMs); + + //! Callsign for pending request + swift::misc::aviation::CCallsign getCallsignForPendingProbeRequests(DWORD requestId, bool remove); + + //! Get new request id, overflow safe + SIMCONNECT_DATA_REQUEST_ID obtainRequestIdForSimObjAircraft(); + + //! Release AI control + //! \remark P3D version is overridden + virtual bool releaseAIControl(const CSimConnectObject &simObject, SIMCONNECT_DATA_REQUEST_ID requestId); + + //! Valid CSimConnectObject which is NOT pendig removed + bool isValidSimObjectNotPendingRemoved(const CSimConnectObject &simObject) const; + + //! CSimConnectObject for trace + CSimConnectObject getSimObjectForTrace(const TraceFsxSendId &trace) const; + + //! Remove the CSimConnectObject linked in the trace + bool removeSimObjectForTrace(const TraceFsxSendId &trace); + + //! Remove camera if any + virtual void removeCamera(CSimConnectObject &simObject); + + //! Remove observer if any + virtual void removeObserver(CSimConnectObject &simObject); + + //! Trace if required, log errors + HRESULT logAndTraceSendId(HRESULT hr, const QString &warningMsg, const QString &functionName, + const QString &functionDetails = {}); + + //! Trace if required, log errors + HRESULT logAndTraceSendId(HRESULT hr, const CSimConnectObject &simObject, const QString &warningMsg, + const QString &functionName, const QString &functionDetails = {}); + + //! Trace if required, log errors + HRESULT logAndTraceSendId(HRESULT hr, bool traceSendId, const CSimConnectObject &simObject, + const QString &warningMsg, const QString &functionName, + const QString &functionDetails = {}); + + //! Convert to FSX char array + static QByteArray toFsxChar(const QString &string); + + //! Register help + static void registerHelp(); + + //! @{ + //! Word size + static bool is32bit() { return (swift::config::CBuildConfig::buildWordSize() == 32); } + static bool is64bit() { return (swift::config::CBuildConfig::buildWordSize() == 64); } + //! @} + + //! Format conversion + //! \note must be valid situation + static SIMCONNECT_DATA_INITPOSITION + aircraftSituationToPosition(const swift::misc::aviation::CAircraftSituation &situation, bool sendGnd = true, + bool forceUnderflowDetection = false, + swift::misc::CStatusMessage *details = nullptr); + + //! Format conversion + //! \note must be valid situation + static SIMCONNECT_DATA_PBH + aircraftSituationToFsxPBH(const swift::misc::aviation::CAircraftSituation &situation); + + //! Format conversion + static SIMCONNECT_DATA_INITPOSITION + coordinateToFsxPosition(const swift::misc::geo::ICoordinateGeodetic &coordinate); + + //! Format conversion + static SIMCONNECT_DATA_LATLONALT + coordinateToFsxLatLonAlt(const swift::misc::geo::ICoordinateGeodetic &coordinate); + + //! Valid FSX/P3D position + static bool isValidFsxPosition(const SIMCONNECT_DATA_INITPOSITION &fsxPos); + + //! Valid 180degrees value + static bool isValid180Deg(double deg) { return deg > -180.0 && deg <= 180.0; } + + static constexpr qint64 AutoTraceOffsetMs = 10 * 1000; //!< how long do we trace? + HANDLE m_hSimConnect = nullptr; //!< handle to SimConnect object + DispatchProc m_dispatchProc = &CSimulatorMsfs2024::SimConnectProc; //!< called function for dispatch, can be + //!< overriden by specialized P3D function + CSimConnectObjects m_simConnectObjects; //!< AI objects and their object and request ids + + QMap + m_pendingProbeRequests; //!< pending elevation requests: requestId/aircraft callsign + + swift::misc::physical_quantities::CLength m_altitudeDelta; //!< FS2020 effect of temperature on altitude + + private: + //! Reason for adding an aircraft + enum AircraftAddMode + { + ExternalCall, //!< normal external request to add aircraft + AddByTimer, //!< add pending aircraft by timer + AddAfterAdded, //!< add pending because object successfully added + AddedAfterRemoved //!< added again after removed + }; + + //! Mode as string + static const QString &modeToString(AircraftAddMode mode); + + //! Dispatch SimConnect messages + //! \remark very frequently called + void dispatch(); + + //! Implementation of add remote aircraft, which also handles FSX specific adding one by one + //! \remark main purpose of this function is to only add one aircraft at a time, and only if simulator is not + //! paused/stopped + bool physicallyAddRemoteAircraftImpl(const swift::misc::simulation::CSimulatedAircraft &newRemoteAircraft, + AircraftAddMode addMode, + const CSimConnectObject &correspondingSimObject = {}); + + //! Remove aircraft no longer in provider + //! \remark kind of cleanup function, in an ideal scenario this should never need to cleanup something + swift::misc::aviation::CCallsignSet physicallyRemoveAircraftNotInProvider(); + + //! ASynchronous version of physicallyRemoveAircraftNotInProvider + void physicallyRemoveAircraftNotInProviderAsync(); + + //! Verify that an object has been added in simulator + //! \remark checks if the object was really added after an "add request" and not directly removed again + //! \remark requests further data on remote aircraft (lights, ..) when correctly added + void verifyAddedRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &remoteAircraftIn); + + //! Adding an aircraft failed + void addingAircraftFailed(const CSimConnectObject &simObject); + + //! Create a detailed info about the failed aircraft + bool verifyFailedAircraftInfo(const CSimConnectObject &simObject, swift::misc::CStatusMessage &details) const; + + //! Logging version of verifyFailedAircraftInfo + bool logVerifyFailedAircraftInfo(const CSimConnectObject &simObject) const; + + //! Add next aircraft based on timer + void timerBasedObjectAddOrRemove(); + + //! Add next aircraft after another has been confirmed + void addPendingAircraftAfterAdded(); + + //! Try to add the next aircraft (one by one) + void addPendingAircraft(AircraftAddMode mode); + + //! Remove as m_addPendingAircraft and m_aircraftToAddAgainWhenRemoved + CSimConnectObject removeFromAddPendingAndAddAgainAircraft(const swift::misc::aviation::CCallsign &callsign); + + //! Call this method to declare the simulator connected + void setSimConnected(); + + //! Called when simulator has started + void onSimRunning(); + + //! Deferred version of onSimRunning to avoid jitter + void onSimRunningDeferred(qint64 referenceTs); + + //! Called every visual frame + void onSimFrame(); + + //! Called when simulator has stopped, e.g. by selecting the "select aircraft screen" + void onSimStopped(); + + //! Simulator is going down + void onSimExit(); + + //! Init when connected + HRESULT initWhenConnected(); + + //! Initialize SimConnect system events + HRESULT initEvents(); + + //! Initialize SimConnect data definitions + HRESULT initDataDefinitionsWhenConnected(); + + //! Update remote aircraft + //! \remark this is where the interpolated data are sent + void updateRemoteAircraft(); + + //! Update remote aircraft parts (send to FSX) + bool updateRemoteAircraftParts(const CSimConnectObject &simObject, + const swift::misc::simulation::CInterpolationResult &result, bool forcedUpdate); + + //! Send parts to simulator + //! \remark does not send if there is no change + bool sendRemoteAircraftPartsToSimulator(const CSimConnectObject &simObject, + const swift::misc::aviation::CAircraftParts &parts); + + //! Send ATC data (callsign etc.) to simulator + bool sendRemoteAircraftAtcDataToSimulator(const CSimConnectObject &simObject); + + //! Send lights to simulator (those which have to be toggled) + //! \remark challenge here is that I can only sent those value if I have already obtained the current light + //! state from simulator \param force send lights even if they appear to be the same + // void sendToggledLightsToSimulator(const CSimConnectObject &simObject, + // const swift::misc::aviation::CAircraftLights &lightsWanted, + // bool force = false); + + //! Call CSimulatorFsxCommon::updateRemoteAircraftFromSimulator asynchronously + //! \remark do not to send SimConnect data in event loop + void triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, + const DataDefinitionPosData &remoteAircraftData); + + //! Call CSimulatorFsxCommon::updateRemoteAircraftFromSimulator asynchronously + //! \remark do not to send SimConnect data in event loop + void triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, + const DataDefinitionRemoteAircraftModel &remoteAircraftModel); + + //! Remote aircraft data sent from simulator + void updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, + const DataDefinitionPosData &remoteAircraftData); + + //! Remote aircraft data sent from simulator + void updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, + const DataDefinitionRemoteAircraftModel &remoteAircraftModel); + + //! Probe data sent from simulator + void updateProbeFromSimulator(const swift::misc::aviation::CCallsign &callsign, + const DataDefinitionPosData &remoteAircraftData); + + //! Customization point for adjusting altitude to compensate for temperature effect + virtual void + setTrueAltitude(swift::misc::aviation::CAircraftSituation &aircraftSituation, + const swift::simplugin::msfs2024common::DataDefinitionOwnAircraft &simulatorOwnAircraft); + + //! Called when data about our own aircraft are received + void updateOwnAircraftFromSimulator(const DataDefinitionOwnAircraft &simulatorOwnAircraft); + + //! Update from SB client area + //! \threadsafe + void updateOwnAircraftFromSimulator(const DataDefinitionClientAreaSb &sbDataArea); + + //! Update transponder mode + //! \threadsafe + void updateTransponderMode(const misc::aviation::CTransponder::TransponderMode xpdrMode); + + //! Update transponder mode from MSFS + void updateMSFS2024TransponderMode(const DataDefinitionMSFSTransponderMode transponderMode); + + //! An AI aircraft was added in the simulator + bool simulatorReportedObjectAdded(DWORD objectId); + + //! Simulator reported that AI aircraft was removed + bool simulatorReportedObjectRemoved(DWORD objectID); + + //! Set ID of a SimConnect object, so far we only have an request id in the object + bool setSimConnectObjectId(DWORD requestId, DWORD objectId); + + //! Remember current lights + bool setCurrentLights(const swift::misc::aviation::CCallsign &callsign, + const swift::misc::aviation::CAircraftLights &lights); + + //! Remember lights sent + bool setLightsAsSent(const swift::misc::aviation::CCallsign &callsign, + const swift::misc::aviation::CAircraftLights &lights); + + //! Display receive exceptions? + bool stillDisplayReceiveExceptions(); + + //! The SimConnect related objects + const CSimConnectObjects &getSimConnectObjects() const { return m_simConnectObjects; } + + //! The SimConnect object for idxs + CSimConnectObject getSimObjectForObjectId(DWORD objectId) const; + + //! Request data for a CSimConnectObject (aka remote aircraft) + bool requestPositionDataForSimObject(const CSimConnectObject &simObject, + SIMCONNECT_PERIOD period = SIMCONNECT_PERIOD_SECOND); + + //! Request lights for a CSimConnectObject + bool requestLightsForSimObject(const CSimConnectObject &simObject); + + //! Model info for a CSimConnectObject + bool requestModelInfoForSimObject(const CSimConnectObject &simObject); + + //! Stop requesting data for CSimConnectObject + bool stopRequestingDataForSimObject(const CSimConnectObject &simObject); + + //! FSX position as string + static QString fsxPositionToString(const SIMCONNECT_DATA_INITPOSITION &position); + + //! Get the callsigns which are no longer in the provider, but still in m_simConnectObjects + swift::misc::aviation::CCallsignSet getCallsignsMissingInProvider() const; + + //! Set tracing on/off + void setTraceSendId(bool traceSendId) { m_traceSendId = traceSendId; } + + //! Trace the send id + void traceSendId(const CSimConnectObject &simObject, const QString &functionName, const QString &details = {}, + bool forceTrace = false); + + //! Send id trace or given send id + TraceFsxSendId getSendIdTrace(DWORD sendId) const; + + //! Get the trace details, otherwise empty string + QString getSendIdTraceDetails(const TraceFsxSendId &trace) const; + + //! Get the trace details, otherwise empty string + QString getSendIdTraceDetails(DWORD sendId) const; + + //! Remove all probes + int removeAllProbes(); + + //! Insert a new SimConnect object + CSimConnectObject insertNewSimConnectObject(const swift::misc::simulation::CSimulatedAircraft &aircraft, + DWORD requestId, CSimConnectObject::SimObjectType type, + const CSimConnectObject &removedPendingObject = {}); + + //! Update simulator COM from swift data. Returns true if simulator frequency was changed + bool updateCOMFromSwiftToSimulator(const swift::misc::physical_quantities::CFrequency &newFreq, + const swift::misc::physical_quantities::CFrequency &lastSimFreq, + swift::misc::physical_quantities::CFrequency &last25kHzSimFreq, EventIds id); + + //! Used for terrain probes + static const swift::misc::aviation::CAltitude &terrainProbeAltitude(); + + static constexpr int GuessRemoteAircraftPartsCycle = 20; //!< guess every n-th cycle + static constexpr int SkipUpdateCyclesForCockpit = 10; //!< skip x cycles before updating cockpit again + static constexpr int IgnoreReceiveExceptions = 10; //!< skip exceptions when displayed more than x times + static constexpr int MaxSendIdTraces = 10000; //!< max.traces of send id + static constexpr DWORD MaxSimObjAircraft = 10000; //!< max.number of SimObjects at the same time + static constexpr DWORD MaxSimObjProbes = 100; //!< max. probes + + // -- second chance tresholds -- + static constexpr int ThresholdAddException = 1; //!< one failure allowed + static constexpr int ThresholdAddedAndDirectlyRemoved = 2; //!< two failures allowed + + // -- range for sim data, each sim object will get its own request id and use the offset ranges + static constexpr int RequestSimObjAircraftStart = static_cast(CSimConnectDefinitions::RequestEndMarker); + static constexpr int RequestSimObjAircraftEnd = RequestSimObjAircraftStart - 1 + MaxSimObjAircraft; + static constexpr int RequestSimObjAircraftRangeEnd = + RequestSimObjAircraftStart - 1 + + static_cast(CSimConnectDefinitions::SimObjectEndMarker) * MaxSimObjAircraft; + + // times + static constexpr int AddPendingAircraftIntervalMs = 20 * 1000; + static constexpr int DispatchIntervalMs = 10; //!< how often with run the FSX event queue + static constexpr int DeferSimulatingFlagMs = + 1500; //!< simulating can jitter at startup (simulating->stopped->simulating, multiple start events), so we + //!< defer detection + static constexpr int DeferResendingLights = + 2500; //!< Resend light state when aircraft light state was not yet available + + QString m_simConnectVersion; //!< SimConnect version + bool m_simConnected = false; //!< Is simulator connected? + bool m_simSimulating = false; //!< Simulator running? + bool m_useSbOffsets = true; //!< with SB offsets + bool m_logSbOffsets = false; //!< log SB offsets + bool m_traceSendId = false; //!< trace the send ids, meant for debugging + bool m_useAddSimulatedObj = false; //!< simulated object use if AI Non ATC object fails + qint64 m_traceAutoUntilTs = -1; //!< allows to automatically trace for some time + qint64 m_simulatingChangedTs = -1; //!< timestamp, when simulating changed (used to avoid jitter) + int m_sbDataReceived = 0; //!< SB3 area data received + + // tracing dispatch performance + int m_dispatchErrors = 0; //!< number of dispatched failed, \sa dispatch + int m_dispatchProcCount = 0; //!< number of dispatchProc counts + int m_dispatchProcEmptyCount = 0; //!< number dispatchProc doing nothing + qint64 m_dispatchTimeMs = -1; //!< \sa ISimulator::getStatisticsSimulatorSpecific + qint64 m_dispatchMaxTimeMs = -1; //!< \sa ISimulator::getStatisticsSimulatorSpecific + qint64 m_dispatchProcTimeMs = -1; //!< \sa ISimulator::getStatisticsSimulatorSpecific + qint64 m_dispatchProcMaxTimeMs = -1; //!< \sa ISimulator::getStatisticsSimulatorSpecific + + SIMCONNECT_RECV_ID m_dispatchReceiveIdLast = SIMCONNECT_RECV_ID_NULL; //!< last receive id from dispatching + SIMCONNECT_RECV_ID m_dispatchReceiveIdMaxTime = + SIMCONNECT_RECV_ID_NULL; //!< receive id corresponding to max.time + DWORD m_dispatchRequestIdLast = + CSimConnectDefinitions::RequestEndMarker; //!< request id if any for last request + DWORD m_dispatchRequestIdMaxTime = + CSimConnectDefinitions::RequestEndMarker; //!< request id corresponding to max.time + + // sending via SimConnect + QList m_sendIdTraces; //!< Send id traces for debugging, latest first + int m_receiveExceptionCount = 0; //!< exceptions + int m_requestSimObjectDataCount = 0; //!< requested SimObjects + + // settings + swift::misc::simulation::settings::CMultiSimulatorDetailsSettings m_detailsSettings; + + // objects + CSimConnectObjects m_simConnectObjectsPositionAndPartsTraces; //!< position/parts received, but object not yet + //!< added, excluded, disabled etc. + CSimConnectObjects m_addPendingAircraft; //!< aircraft/probes awaiting to be added; + SIMCONNECT_DATA_REQUEST_ID m_requestIdSimObjAircraft = static_cast( + RequestSimObjAircraftStart); //!< request id, use obtainRequestIdForSimObjAircraft to get id + QTimer m_simObjectTimer; //!< updating of SimObjects awaiting to be added + + // Last selected frequencies in simulator before setting 8.33 kHz spacing frequency + swift::misc::physical_quantities::CFrequency m_lastCom1Active { + 0, swift::misc::physical_quantities::CFrequencyUnit::nullUnit() + }; //!< last COM1 active frequency + swift::misc::physical_quantities::CFrequency m_lastCom1Standby { + 0, swift::misc::physical_quantities::CFrequencyUnit::nullUnit() + }; //!< last COM1 standby frequency + swift::misc::physical_quantities::CFrequency m_lastCom2Active { + 0, swift::misc::physical_quantities::CFrequencyUnit::nullUnit() + }; //!< last COM2 active frequency + swift::misc::physical_quantities::CFrequency m_lastCom2Standby { + 0, swift::misc::physical_quantities::CFrequencyUnit::nullUnit() + }; //!< last COM2 standby frequency + + //! Request id to string + static QString requestIdToString(DWORD requestId); + + //! status of loaded sim objects + sSimmobjectLoaded sSimmobjectLoadedState; + + //! cached sim objects and liveries read frim simconnect + std::vector vSimObjectsAndLiveries; + + //! Simulator info + swift::misc::simulation::CSimulatorInfo m_simulatorInfo; + + swift::misc::CSetting m_matchingSettings { + this + }; //!< settings + + public: + //! @{ + //! Offsets + static DWORD offsetSimObjAircraft(CSimConnectDefinitions::SimObjectRequest req) + { + return MaxSimObjAircraft * static_cast(req); + } + //! @} + }; + + //! Listener for MSFS2024 + class MSFS2024_EXPORT CSimulatorMsfs2024Listener : public swift::core::ISimulatorListener + { + Q_OBJECT + + public: + //! Constructor + CSimulatorMsfs2024Listener(const swift::misc::simulation::CSimulatorPluginInfo &info); + + //! \copydoc swift::core::ISimulatorListener::backendInfo + virtual QString backendInfo() const override; + + protected: + //! \copydoc swift::core::ISimulatorListener::startImpl + virtual void startImpl() override; + + //! \copydoc swift::core::ISimulatorListener::stopImpl + virtual void stopImpl() override; + + //! \copydoc swift::core::ISimulatorListener::checkImpl + virtual void checkImpl() override; + + private: + //! Test if connection can be established + void checkConnection(); + + //! Check simulator version and type + bool checkVersionAndSimulator() const; + + //! Check the simconnect.dll + bool checkSimConnectDll() const; + + //! Connect to simulator (if not already) + bool connectToSimulator(); + + //! Disconnect from simulator + bool disconnectFromSimulator(); + + //! Adjust the timer interval + void adjustTimerInterval(qint64 checkTimeMs); + + static constexpr int MinQueryIntervalMs = 5 * 1000; // 5 seconds + + QTimer m_timer { this }; //!< timer, "this" is needed otherwise I get warnings when move to new thread + QString m_simulatorVersion; + QString m_simConnectVersion; + QString m_simulatorName; + QString m_simulatorDetails; + HANDLE m_hSimConnect; + bool m_simConnected = false; //!< SimConnect is connected, does not mean to the correct sim. + swift::misc::CStatusMessage m_lastMessage; //!< last listener message + + //! SimConnect Callback (simplified version for listener) + //! \sa CSimConnectObjects::SimConnectProc + static void CALLBACK SimConnectProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContext); + }; +} // namespace swift::simplugin::msfs2024common + +#endif // SWIFT_SIMPLUGIN_MSFS2024COMMON_SIMULATORMSFS2024COMMON_H diff --git a/src/plugins/simulator/msfs2024/simulatormsfs2024simconnectproc.cpp b/src/plugins/simulator/msfs2024/simulatormsfs2024simconnectproc.cpp new file mode 100644 index 000000000..0ccc91e19 --- /dev/null +++ b/src/plugins/simulator/msfs2024/simulatormsfs2024simconnectproc.cpp @@ -0,0 +1,421 @@ +// SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +#include + +#include + +#include "simconnectdatadefinitionmsfs2024.h" +#include "simulatormsfs2024common.h" + +#include "config/buildconfig.h" +#include "core/application.h" +#include "core/simulator.h" +#include "misc/aviation/airportlist.h" +#include "misc/logmessage.h" +#include "misc/simulation/fscommon/bcdconversions.h" +#include "misc/simulation/fsx/simconnectutilities.h" +#include "misc/simulation/settings/simulatorsettings.h" +#include "misc/simulation/simulatorplugininfo.h" + +using namespace swift::core; +using namespace swift::config; +using namespace swift::misc; +using namespace swift::misc::simulation; +using namespace swift::misc::aviation; +using namespace swift::misc::physical_quantities; +using namespace swift::misc::geo; +using namespace swift::misc::network; +using namespace swift::misc::simulation; +using namespace swift::misc::simulation::fscommon; +using namespace swift::misc::simulation::fsx; +using namespace swift::misc::simulation::settings; + +namespace swift::simplugin::msfs2024common +{ + void CALLBACK CSimulatorMsfs2024::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(); + CSimulatorMsfs2024 *simulatorMsfs2024 = static_cast(pContext); + const SIMCONNECT_RECV_ID recvId = static_cast(pData->dwID); + static const DataDefinitionOwnAircraftModel *dataDefinitionModel; + simulatorMsfs2024->m_dispatchReceiveIdLast = recvId; + simulatorMsfs2024->m_dispatchProcCount++; + const CSpecializedSimulatorSettings settings = simulatorMsfs2024->getSimulatorSettings(); + CSimulatorSettings m_generic = settings.getGenericSettings(); + + switch (recvId) + { + case SIMCONNECT_RECV_ID_OPEN: + { + SIMCONNECT_RECV_OPEN *event = static_cast(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 = CSimulatorMsfs2024::fsxCharToQString(event->szApplicationName); + const QString details = + QStringLiteral("Name: '%1' Version: %2 SimConnect: %3").arg(name, version, simConnectVersion); + simulatorMsfs2024->setSimulatorDetails(name, details, version); + simulatorMsfs2024->m_simConnectVersion = simConnectVersion; + CLogMessage(simulatorMsfs2024).info(u"Connected to %1: '%2'") + << simulatorMsfs2024->getSimulatorPluginInfo().getIdentifier() << details; + simulatorMsfs2024->setSimConnected(); + break; // SIMCONNECT_RECV_ID_OPEN + } + case SIMCONNECT_RECV_ID_EXCEPTION: + { + if (!simulatorMsfs2024->stillDisplayReceiveExceptions()) { break; } + simulatorMsfs2024->triggerAutoTraceSendId(); + + SIMCONNECT_RECV_EXCEPTION *exception = static_cast(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 = simulatorMsfs2024->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 existing + CSimConnectObject simObject = simulatorMsfs2024->getSimObjectForTrace(trace); + if (simObject.isInvalid()) { simObject = trace.simObject; } // take the one in the trace + if (simObject.isValid()) + { + if (simObject.isAircraft()) + { + simulatorMsfs2024->addingAircraftFailed(simObject); + logGenericExceptionInfo = false; + } + else + { + const bool removed = simulatorMsfs2024->m_simConnectObjects.remove(simObject.getCallsign()); + Q_UNUSED(removed); + CLogMessage(simulatorMsfs2024).warning(u"Adding probe failed: %1 %2") + << simObject.getCallsign().asString() << simObject.getAircraftModelString(); + 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(exception->dwException))); + const QString sendIdDetails = simulatorMsfs2024->getSendIdTraceDetails(sendId); + CLogMessage(simulatorMsfs2024).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: + { + simulatorMsfs2024->onSimExit(); + break; + } + case SIMCONNECT_RECV_ID_EVENT: + { + const SIMCONNECT_RECV_EVENT *event = static_cast(pData); + switch (event->uEventID) + { + case SystemEventSimStatus: + { + const bool running = event->dwData ? true : false; + if (running) { simulatorMsfs2024->onSimRunning(); } + else { simulatorMsfs2024->onSimStopped(); } + + // If the simulation stops, the model will be reloaded when it is restarted. + dataDefinitionModel = NULL; + + break; + } + case SystemEventPause: + { + const bool paused = event->dwData ? true : false; + if (simulatorMsfs2024->m_simPaused != paused) + { + simulatorMsfs2024->m_simPaused = paused; + simulatorMsfs2024->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(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 (simulatorMsfs2024->getSimConnectObjects().isKnownSimObjectId(objectId)) + { + switch (event->uEventID) + { + case SystemEventObjectRemoved: simulatorMsfs2024->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(pData); + switch (event->uEventID) + { + case SystemEventFrame: + // doing interpolation + simulatorMsfs2024->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(pData); + const DWORD requestId = event->dwRequestID; + const DWORD objectId = event->dwObjectID; + + simulatorMsfs2024->m_dispatchRequestIdLast = requestId; + + if (CSimulatorMsfs2024::isRequestForSimConnectObject(requestId)) + { + bool success = simulatorMsfs2024->setSimConnectObjectId(requestId, objectId); + if (!success) { break; } // not an request ID of ours + success = simulatorMsfs2024->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 = simulatorMsfs2024->getSimObjectForObjectId(objectId); + const CSimulatedAircraft remoteAircraft(simObject.getAircraft()); + const CStatusMessage msg = + CStatusMessage(simulatorMsfs2024).error(u"Cannot add object %1, cs: '%2' model: '%3'") + << objectId << remoteAircraft.getCallsignAsString() << remoteAircraft.getModelString(); + CLogMessage::preformatted(msg); + emit simulatorMsfs2024->physicallyAddingRemoteModelFailed(remoteAircraft, false, false, msg); + } + } + break; // SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID + } + case SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE: + { + const SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE *pObjData = + static_cast(pData); + const DWORD requestId = pObjData->dwRequestID; + + switch (pObjData->dwRequestID) + { + + case 0: + { + break; + } + case 1: + { + dataDefinitionModel = reinterpret_cast(&pObjData->dwData); + if (dataDefinitionModel->title != NULL && dataDefinitionModel->livery != NULL) + { + CAircraftModel model(dataDefinitionModel->title, dataDefinitionModel->livery, + CAircraftModel::TypeOwnSimulatorModel); + + simulatorMsfs2024->reverseLookupAndUpdateOwnAircraftModel(model.getMsfs2024ModelString()); + } + break; + } + + default: printf("Unhandled SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE received: %d\n", requestId); break; + } + break; + + break; + } + case SIMCONNECT_RECV_ID_SIMOBJECT_DATA: + { + const SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = static_cast(pData); + const DWORD requestId = pObjData->dwRequestID; + simulatorMsfs2024->m_dispatchRequestIdLast = requestId; + + switch (requestId) + { + case CSimConnectDefinitions::RequestOwnAircraft: + { + static_assert(sizeof(DataDefinitionOwnAircraft) == 50 * sizeof(double), + "DataDefinitionOwnAircraft has an incorrect size."); + const DataDefinitionOwnAircraft *ownAircaft = + reinterpret_cast(&pObjData->dwData); + simulatorMsfs2024->updateOwnAircraftFromSimulator(*ownAircaft); + break; + } + case CSimConnectDefinitions::RequestMSFSTransponder: + { + const DataDefinitionMSFSTransponderMode *transponderMode = + reinterpret_cast(&pObjData->dwData); + simulatorMsfs2024->updateMSFS2024TransponderMode(*transponderMode); + break; + } + default: + { + const DWORD objectId = pObjData->dwObjectID; + if (CSimulatorMsfs2024::isRequestForSimObjAircraft(requestId)) + { + const CSimConnectObject simObject = simulatorMsfs2024->getSimObjectForObjectId(objectId); + if (!simObject.hasValidRequestAndObjectId()) { break; } + const CSimConnectDefinitions::SimObjectRequest subRequest = + CSimulatorMsfs2024::requestToSimObjectRequest(requestId); + + if (subRequest == CSimConnectDefinitions::SimObjectPositionData) + { + static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double), + "DataDefinitionPosData has an incorrect size."); + const DataDefinitionPosData *remoteAircraftSimData = + reinterpret_cast(&pObjData->dwData); + // extra check, but ids should be the same + if (objectId == simObject.getObjectId()) + { + simulatorMsfs2024->triggerUpdateRemoteAircraftFromSimulator(simObject, + *remoteAircraftSimData); + } + } // position + else if (subRequest == CSimConnectDefinitions::SimObjectModel) + { + static_assert(sizeof(DataDefinitionRemoteAircraftModel) == sizeof(double) + 168 + 256 + 256, + "DataDefinitionRemoteAircraftModel has an incorrect size."); + const DataDefinitionRemoteAircraftModel *remoteAircraftModel = + reinterpret_cast(&pObjData->dwData); + // extra check, but ids should be the same + if (objectId == simObject.getObjectId()) + { + simulatorMsfs2024->triggerUpdateRemoteAircraftFromSimulator(simObject, + *remoteAircraftModel); + } + } // model + else if (subRequest == CSimConnectDefinitions::SimObjectLights) + { + static_assert(sizeof(DataDefinitionRemoteAircraftLights) == 9 * sizeof(double), + "DataDefinitionRemoteAircraftLights has an incorrect size."); + const DataDefinitionRemoteAircraftLights *remoteAircraftLights = + reinterpret_cast(&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 + simulatorMsfs2024->setCurrentLights(callsign, lights); + if (simObject.getLightsAsSent().isNull()) + { + // allows to compare for toggle + simulatorMsfs2024->setLightsAsSent(callsign, lights); + } + } + break; + } // lights + else + { + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + CLogMessage(simulatorMsfs2024).error(u"Unknown subrequest (aircraft): '%1' %2") + << CSimConnectDefinitions::simObjectRequestToString(subRequest) + << simObject.toQString(); + } + } + } + } + break; // default (SIMCONNECT_RECV_ID_SIMOBJECT_DATA) + } + break; // SIMCONNECT_RECV_ID_SIMOBJECT_DATA + } + case SIMCONNECT_RECV_ID_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST: // 38 + { + if (m_generic.getPropertyModelSet()) + { + SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *msg = + (SIMCONNECT_RECV_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST *)pData; + switch (msg->dwRequestID) + { + case CSimConnectDefinitions::REQUEST_AIRPLANE: + case CSimConnectDefinitions::REQUEST_HELICOPTER: + case CSimConnectDefinitions::REQUEST_HOT_AIR: simulatorMsfs2024->CacheSimObjectAndLiveries(msg); break; + } + } + + break; + } + case SIMCONNECT_RECV_ID_CLIENT_DATA: + { + if (!simulatorMsfs2024->m_useSbOffsets) { break; } + simulatorMsfs2024->m_sbDataReceived++; + const SIMCONNECT_RECV_CLIENT_DATA *clientData = static_cast(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); + simulatorMsfs2024->updateOwnAircraftFromSimulator(sbData); + } + break; // SIMCONNECT_RECV_ID_CLIENT_DATA + } + case SIMCONNECT_RECV_ID_EVENT_FILENAME: + { + const SIMCONNECT_RECV_EVENT_FILENAME *event = static_cast(pData); + switch (event->uEventID) + { + case SystemEventFlightLoaded: break; + default: break; + } + break; // SIMCONNECT_RECV_ID_EVENT_FILENAME + } + default: simulatorMsfs2024->m_dispatchProcEmptyCount++; break; + } // main switch + + // performance stats + const qint64 procTimeEnd = QDateTime::currentMSecsSinceEpoch(); + simulatorMsfs2024->m_dispatchProcTimeMs = procTimeEnd - procTimeStart; + if (simulatorMsfs2024->m_dispatchProcTimeMs > simulatorMsfs2024->m_dispatchProcMaxTimeMs) + { + simulatorMsfs2024->m_dispatchProcMaxTimeMs = simulatorMsfs2024->m_dispatchProcTimeMs; + } + } // method +} // namespace swift::simplugin::msfs2024common diff --git a/src/swiftdata/CMakeLists.txt b/src/swiftdata/CMakeLists.txt index 1dc6536e9..22ccfd48f 100644 --- a/src/swiftdata/CMakeLists.txt +++ b/src/swiftdata/CMakeLists.txt @@ -7,6 +7,8 @@ add_executable(swiftdata WIN32 swiftdata.h swiftdata.ui swiftdatamenus.cpp + swiftdataapplication.cpp + swiftdataapplication.h ) if(WIN32) diff --git a/src/swiftdata/main.cpp b/src/swiftdata/main.cpp index e8d80900a..b0ea5384a 100644 --- a/src/swiftdata/main.cpp +++ b/src/swiftdata/main.cpp @@ -11,6 +11,7 @@ #include "misc/crashhandler.h" #include "misc/icons.h" #include "swiftdata.h" +#include "swiftdataapplication.h" using namespace swift::misc; using namespace swift::core; @@ -23,19 +24,25 @@ int main(int argc, char *argv[]) Q_UNUSED(qa) CCrashHandler::instance()->init(); - CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48()); - if (!a.parseCommandLineArgsAndLoadSetup()) { return EXIT_FAILURE; } - a.splashScreen(CIcons::swiftDatabase256()); - a.initAndStartWebDataServices(swift::core::CWebReaderFlags::AllSwiftDbReaders, - CDatabaseReaderConfigList::forMappingTool()); - a.startCoreFacadeWithoutContexts(); - if (!a.start()) + + int r = 0; { - a.gracefulShutdown(); - return EXIT_FAILURE; + // CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, + // CIcons::swiftDatabase48()); + CSwiftDataApplication a; // application with contexts + if (!a.parseCommandLineArgsAndLoadSetup()) { return EXIT_FAILURE; } + a.splashScreen(CIcons::swiftDatabase256()); + a.initAndStartWebDataServices(swift::core::CWebReaderFlags::AllSwiftDbReaders, + CDatabaseReaderConfigList::forMappingTool()); + a.startCoreFacadeWithoutContexts(); + if (!a.start()) + { + a.gracefulShutdown(); + return EXIT_FAILURE; + } + CSwiftData w; + w.show(); + r = a.exec(); } - CSwiftData w; - w.show(); - int r = a.exec(); return r; } diff --git a/src/swiftdata/swiftdata.h b/src/swiftdata/swiftdata.h index 2b278cb5c..8160a4e90 100644 --- a/src/swiftdata/swiftdata.h +++ b/src/swiftdata/swiftdata.h @@ -25,7 +25,7 @@ namespace Ui namespace swift::core { class CWebDataServices; -} +} // namespace swift::core namespace swift::gui::components { class CAutoPublishDialog; diff --git a/src/swiftdata/swiftdataapplication.cpp b/src/swiftdata/swiftdataapplication.cpp new file mode 100644 index 000000000..8fb5b6a87 --- /dev/null +++ b/src/swiftdata/swiftdataapplication.cpp @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +#include "swiftdataapplication.h" + +#include + +#include "core/application.h" +#include "core/corefacadeconfig.h" +#include "core/coremodeenums.h" +#include "misc/dbusserver.h" +#include "misc/icons.h" + +using namespace swift::misc; +using namespace swift::core; + +CSwiftDataApplication::CSwiftDataApplication() + : CGuiApplication(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48()) +{ + // this->addParserOption(m_cmdFacadeMode); + // this->addDBusAddressOption(); + // this->addNetworkOptions(); + // this->addAudioOptions(); +} + +CStatusMessageList CSwiftDataApplication::startHookIn() +{ + Q_ASSERT_X(m_parsed, Q_FUNC_INFO, "Not yet parsed cmd line arguments"); + + QString dBusAddress(this->getCmdDBusAddressValue()); + const QString coreModeStr = + this->isParserOptionSet(m_cmdFacadeMode) ? this->getParserValue(m_cmdFacadeMode) : QString(); + CoreModes::CoreMode coreMode = CoreModes::stringToCoreMode(coreModeStr); + + CStatusMessageList msgs; + + CCoreFacadeConfig runtimeConfig = coreModeToCoreFacadeConfig(coreMode, dBusAddress); + const CStatusMessageList contextMsgs = this->initContextsAndStartCoreFacade(runtimeConfig); + msgs.push_back(contextMsgs); + return contextMsgs; +} + +bool CSwiftDataApplication::parsingHookIn() { return CGuiApplication::parsingHookIn(); } + +CSwiftDataApplication *CSwiftDataApplication::instance() +{ + return qobject_cast(CApplication::instance()); +} + +CCoreFacadeConfig CSwiftDataApplication::coreModeToCoreFacadeConfig(CoreModes::CoreMode coreMode, + const QString &dBusAddress) +{ + switch (coreMode) + { + case CoreModes::Distributed: return CCoreFacadeConfig(CCoreFacadeConfig::Remote, dBusAddress); + case CoreModes::Standalone: return CCoreFacadeConfig(CCoreFacadeConfig::Local, dBusAddress); break; + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Not handled core mode"); + return CCoreFacadeConfig(CCoreFacadeConfig::NotUsed, dBusAddress); + } +} diff --git a/src/swiftdata/swiftdataapplication.h b/src/swiftdata/swiftdataapplication.h new file mode 100644 index 000000000..50c516862 --- /dev/null +++ b/src/swiftdata/swiftdataapplication.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 + +//! \file + +#ifndef SWIFTDATAAPPLICATION_H +#define SWIFTDATAAPPLICATION_H + +#include +#include +#include +#include + +#include "core/coremodeenums.h" +#include "gui/guiapplication.h" + +/*! + * Specialized GUI application for swift pilot client. + * Handles parsing of some specialized CMD line argumenets and startup of core + */ +class CSwiftDataApplication : public swift::gui::CGuiApplication +{ + Q_OBJECT + +public: + //! Constructor + CSwiftDataApplication(); + + //! Single instance + CSwiftDataApplication *instance(); + +protected: + //! Parsing of special CMD args + virtual bool parsingHookIn() override; + + //! Start facade by cmd arguments + virtual swift::misc::CStatusMessageList startHookIn() override; + +private: + static swift::core::CCoreFacadeConfig coreModeToCoreFacadeConfig(swift::core::CoreModes::CoreMode, + const QString &dBusAddress); + + QCommandLineOption m_cmdFacadeMode { { "c", "core" }, + QCoreApplication::translate("main", "Core mode: (d)istributed, (s)tandalone."), + "coremode" }; //!< Facade startup mode +}; + +#endif // SWIFTDATAAPPLICATION_H