diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp index d154bbc3f..d1bcbd537 100644 --- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp +++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.cpp @@ -21,6 +21,7 @@ #include "blackmisc/aviation/airportlist.h" #include "blackmisc/geo/elevationplane.h" #include "blackmisc/math/mathutils.h" +#include "blackmisc/country.h" #include "blackmisc/logmessage.h" #include "blackmisc/statusmessagelist.h" #include "blackmisc/threadutils.h" @@ -279,16 +280,16 @@ namespace BlackSimPlugin bool CSimulatorFsxCommon::requestElevation(const ICoordinateGeodetic &reference, const CCallsign &callsign) { + // this is the 32bit FSX version, the P3D x64 is overridden! + if (this->isShuttingDownOrDisconnected()) { return false; } + if (!this->isUsingFsxTerrainProbe()) { return false; } if (reference.isNull()) { return false; } + const CSimConnectObject simObject = m_simConnectObjects.getNotPendingProbe(); + if (!simObject.isConfirmedAdded()) { return false; } - static const CAltitude alt(50000, CLengthUnit::ft()); CCoordinateGeodetic pos(reference); - pos.setGeodeticHeight(alt); - - if (m_simConnectProbes.isEmpty()) { return this->physicallyAddAITerrainProbe(pos); } - if (m_simConnectProbes.countConfirmedAdded() < 1) { return false; } // pending probes - const CSimConnectObject simObject = m_simConnectProbes.values().front(); + pos.setGeodeticHeight(terrainProbeAltitude()); SIMCONNECT_DATA_INITPOSITION position = this->coordinateToFsxPosition(pos); const HRESULT hr = this->logAndTraceSendId( @@ -299,7 +300,7 @@ namespace BlackSimPlugin if (isFailure(hr)) { return false; } - this->requestTerrainProbeData(callsign); + this->requestTerrainProbeData(simObject); emit this->requestedElevation(callsign); return true; } @@ -372,11 +373,6 @@ namespace BlackSimPlugin return this->getSimConnectObjects().getSimObjectForObjectId(objectId); } - CSimConnectObject CSimulatorFsxCommon::getProbeForObjectId(DWORD objectId) const - { - return this->getSimConnectProbes().getSimObjectForObjectId(objectId); - } - void CSimulatorFsxCommon::setSimConnected() { m_simConnected = true; @@ -413,26 +409,26 @@ namespace BlackSimPlugin HRESULT hr1 = this->logAndTraceSendId( SimConnect_RequestDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::RequestOwnAircraft, CSimConnectDefinitions::DataOwnAircraft, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME), - 0, "Cannot request own aircraft data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + "Cannot request own aircraft data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); HRESULT hr2 = this->logAndTraceSendId( SimConnect_RequestDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::RequestOwnAircraftTitle, CSimConnectDefinitions::DataOwnAircraftTitle, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_SECOND, SIMCONNECT_DATA_REQUEST_FLAG_CHANGED), - 0, "Cannot request title", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + "Cannot request title", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); HRESULT hr3 = this->logAndTraceSendId( SimConnect_RequestDataOnSimObject(m_hSimConnect, CSimConnectDefinitions::RequestSimEnvironment, CSimConnectDefinitions::DataSimEnvironment, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_SECOND, SIMCONNECT_DATA_REQUEST_FLAG_CHANGED), - 0, "Cannot request sim.env.", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + "Cannot request sim.env.", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); // 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 ;) HRESULT hr4 = this->logAndTraceSendId( SimConnect_RequestClientData(m_hSimConnect, ClientAreaSquawkBox, CSimConnectDefinitions::RequestSbData, CSimConnectDefinitions::DataClientAreaSb, SIMCONNECT_CLIENT_DATA_PERIOD_SECOND, SIMCONNECT_CLIENT_DATA_REQUEST_FLAG_CHANGED), - 0, "Cannot request client data", Q_FUNC_INFO, "SimConnect_RequestClientData"); + "Cannot request client data", Q_FUNC_INFO, "SimConnect_RequestClientData"); if (isFailure(hr1, hr2, hr3, hr4)) { return; } this->emitSimulatorCombinedStatus(); // force sending status @@ -499,6 +495,16 @@ namespace BlackSimPlugin return true; } + CSimConnectObject CSimulatorFsxCommon::getSimObjectForTrace(const TraceFsxSendId &trace) const + { + return m_simConnectObjects.getSimObjectForOtherSimObject(trace.simObject); + } + + bool CSimulatorFsxCommon::removeSimObjectForTrace(const TraceFsxSendId &trace) + { + return m_simConnectObjects.removeByOtherSimObject(trace.simObject); + } + bool CSimulatorFsxCommon::triggerAutoTraceSendId(qint64 traceTimeMs) { if (m_traceSendId) { return false; } // no need @@ -609,17 +615,28 @@ namespace BlackSimPlugin --m_skipCockpitUpdateCycles; } - if (m_isWeatherActivated) + // slower updates + if (m_ownAircraftUpdate % 10 == 0) { - const auto currentPosition = CCoordinateGeodetic { aircraftSituation.latitude(), aircraftSituation.longitude() }; - if (CWeatherScenario::isRealWeatherScenario(m_weatherScenarioSettings.get()) && - calculateGreatCircleDistance(m_lastWeatherPosition, currentPosition).value(CLengthUnit::mi()) > 20) + if (m_isWeatherActivated) { - m_lastWeatherPosition = currentPosition; - const auto weatherGrid = CWeatherGrid { { "GLOB", currentPosition } }; - requestWeatherGrid(weatherGrid, { this, &CSimulatorFsxCommon::injectWeatherGrid }); + const auto currentPosition = CCoordinateGeodetic { aircraftSituation.latitude(), aircraftSituation.longitude() }; + if (CWeatherScenario::isRealWeatherScenario(m_weatherScenarioSettings.get()) && + calculateGreatCircleDistance(m_lastWeatherPosition, currentPosition).value(CLengthUnit::mi()) > 20) + { + m_lastWeatherPosition = currentPosition; + const auto weatherGrid = CWeatherGrid { { "GLOB", currentPosition } }; + requestWeatherGrid(weatherGrid, { this, &CSimulatorFsxCommon::injectWeatherGrid }); + } + } + + if (this->m_useFsxTerrainProbe && m_addedProbes < 1) + { + this->physicallyInitAITerrainProbes(position, 2); } } + + m_ownAircraftUpdate++; // with 50updates/sec long enough even for 32bit } void CSimulatorFsxCommon::triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionPosData &remoteAircraftData) @@ -735,17 +752,14 @@ namespace BlackSimPlugin return true; } - bool CSimulatorFsxCommon::simulatorReportedProbeAdded(DWORD objectId) - { - if (this->isShuttingDownOrDisconnected()) { return true; } // pretend everything is fine - const CSimConnectObject simObject = m_simConnectProbes.markObjectAsAdded(objectId); - const bool valid(simObject.hasValidRequestAndObjectId() && simObject.isConfirmedAdded()); - return valid; - } - void CSimulatorFsxCommon::verifyAddedRemoteAircraft(const CSimulatedAircraft &remoteAircraftIn) { if (this->isShuttingDownOrDisconnected()) { return; } + if (remoteAircraftIn.isTerrainProbe()) + { + this->verifyAddedTerrainProbe(remoteAircraftIn); + return; + } CStatusMessage msg; CSimulatedAircraft remoteAircraft = remoteAircraftIn; @@ -836,6 +850,17 @@ namespace BlackSimPlugin } } + void CSimulatorFsxCommon::verifyAddedTerrainProbe(const CSimulatedAircraft &remoteAircraftIn) + { + CSimConnectObject &simObject = m_simConnectObjects[remoteAircraftIn.getCallsign()]; + simObject.setConfirmedAdded(true); + // trigger new adding from pending if any + if (!m_addPendingAircraft.isEmpty()) + { + this->addPendingAircraftAfterAdded(); + } + } + void CSimulatorFsxCommon::addPendingAircraftByTimer() { this->addPendingAircraft(AddByTimer); @@ -849,13 +874,13 @@ namespace BlackSimPlugin void CSimulatorFsxCommon::addPendingAircraft(AircraftAddMode mode) { if (m_addPendingAircraft.isEmpty()) { return; } - const CCallsignSet aircraftCallsignsInRange(getAircraftInRangeCallsigns()); + const CCallsignSet aircraftCallsignsInRange(this->getAircraftInRangeCallsigns()); CSimulatedAircraftList toBeAddedAircraft; CCallsignSet toBeRemovedCallsigns; for (const CSimulatedAircraft &aircraft : as_const(m_addPendingAircraft)) { Q_ASSERT_X(!aircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "missing callsign"); - if (aircraftCallsignsInRange.contains(aircraft.getCallsign())) + if (aircraft.isTerrainProbe() || aircraftCallsignsInRange.contains(aircraft.getCallsign())) { toBeAddedAircraft.push_back(aircraft); } @@ -956,11 +981,6 @@ namespace BlackSimPlugin return m_simConnectObjects.setSimConnectObjectIdForRequestId(requestId, objectId); } - bool CSimulatorFsxCommon::setSimConnectProbeId(DWORD requestId, DWORD objectId) - { - return m_simConnectProbes.setSimConnectObjectIdForRequestId(requestId, objectId); - } - bool CSimulatorFsxCommon::setCurrentLights(const CCallsign &callsign, const CAircraftLights &lights) { if (!m_simConnectObjects.contains(callsign)) { return false; } @@ -1092,6 +1112,7 @@ namespace BlackSimPlugin bool CSimulatorFsxCommon::physicallyAddRemoteAircraftImpl(const CSimulatedAircraft &newRemoteAircraft, CSimulatorFsxCommon::AircraftAddMode addMode) { const CCallsign callsign(newRemoteAircraft.getCallsign()); + const bool probe = newRemoteAircraft.isTerrainProbe(); // entry checks Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "thread"); @@ -1170,8 +1191,8 @@ namespace BlackSimPlugin this->removeFromAddPendingAndAddAgainAircraft(callsign); - // create AI - if (!this->isAircraftInRange(callsign)) + // create AI after crosscheking it + if (!probe && !this->isAircraftInRange(callsign)) { CLogMessage(this).info("Skipping adding of '%1' since it is no longer in range") << callsign.asString(); return false; @@ -1182,13 +1203,19 @@ namespace BlackSimPlugin const bool sendGround = setup.isSendingGndFlagToSimulator(); // FSX/P3D adding + Q_ASSERT_X(!probe || m_useFsxTerrainProbe, Q_FUNC_INFO, "Adding probe, but FSX probe mode is off"); + bool adding = false; // will be added flag - const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimObjAircraft(); // add + const SIMCONNECT_DATA_REQUEST_ID requestId = probe ? + this->obtainRequestIdForSimObjTerrainProbe() : + this->obtainRequestIdForSimObjAircraft(); const SIMCONNECT_DATA_INITPOSITION initialPosition = CSimulatorFsxCommon::aircraftSituationToFsxPosition(newRemoteAircraft.getSituation(), sendGround); const QString modelString(newRemoteAircraft.getModelString()); if (this->showDebugLogMessage()) { this->debugLogMessage(Q_FUNC_INFO, QString("CS: '%1' model: '%2' request: %3, init pos: %4").arg(callsign.toQString(), modelString).arg(requestId).arg(fsxPositionToString(initialPosition))); } - const HRESULT hr = SimConnect_AICreateNonATCAircraft(m_hSimConnect, qPrintable(modelString), qPrintable(callsign.toQString().left(12)), initialPosition, requestId); + const HRESULT hr = !probe ? + SimConnect_AICreateNonATCAircraft(m_hSimConnect, qPrintable(modelString), qPrintable(callsign.toQString().left(12)), initialPosition, requestId) : + SimConnect_AICreateSimulatedObject(m_hSimConnect, qPrintable(modelString), initialPosition, requestId); if (isFailure(hr)) { const CStatusMessage msg = CStatusMessage(this).error("SimConnect, can not create AI traffic: '%1' '%2'") << callsign.toQString() << modelString; @@ -1198,48 +1225,51 @@ namespace BlackSimPlugin else { // we will request a new aircraft by request ID, later we will receive its object id - // so far this object id is -1 + // so far this object id is 0 (DWORD) + static const QString mode("mode: %1"); const CSimConnectObject simObject = this->insertNewSimConnectObject(newRemoteAircraft, requestId); - if (this->isTracingSendId()) { this->traceSendId(simObject.getObjectId(), Q_FUNC_INFO);} + this->traceSendId(simObject, Q_FUNC_INFO, mode.arg(CSimulatorFsxCommon::modeToString(addMode)), true); adding = true; } return adding; } - bool CSimulatorFsxCommon::physicallyAddAITerrainProbe(const ICoordinateGeodetic &coordinate) + bool CSimulatorFsxCommon::physicallyAddAITerrainProbe(const ICoordinateGeodetic &coordinate, int number) { if (coordinate.isNull()) { return false; } + if (!this->isUsingFsxTerrainProbe()) { return false; } Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "thread"); - // static const QString modelString = this->getDefaultModel().getModelString(); - static const QString modelString("OrcaWhale"); - static const QString pseudoCallsign("swift pr: %1"); // max 12 chars - const int index = m_simConnectProbes.size() + 1; - const CCallsign cs(pseudoCallsign.arg(index)); - const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimObjTerrainProbe(); // add - const SIMCONNECT_DATA_INITPOSITION initialPosition = CSimulatorFsxCommon::coordinateToFsxPosition(coordinate); - const HRESULT hr = SimConnect_AICreateNonATCAircraft(m_hSimConnect, qPrintable(modelString), qPrintable(cs.asString().right(12)), initialPosition, requestId); - // const HRESULT hr = SimConnect_AICreateSimulatedObject(m_hSimConnect, qPrintable(modelString), initialPosition, requestId); - if (this->isTracingSendId()) { this->traceSendId(0, Q_FUNC_INFO, QString("Adding probe, req.id: %1").arg(requestId));} + // static const QString modelString("OrcaWhale"); + // static const QString modelString("Water Drop"); + // static const QString modelString("A321ACA"); + static const QString modelString("AI_Tracker_Object_0"); + static const QString pseudoCallsign("PROBE%1"); // max 12 chars + static const CCountry ctry("SW", "SWIFT"); + static const CAirlineIcaoCode swiftAirline("SWI", "swift probe", ctry, "SWIFT", false, false); + static const CLivery swiftLivery(CLivery::getStandardCode(swiftAirline), swiftAirline, "swift probe"); - bool ok = false; - if (isOk(hr)) + const CCallsign cs(pseudoCallsign.arg(number)); + const CAircraftModel model(modelString, CAircraftModel::TypeTerrainProbe, QStringLiteral("swift terrain probe"), CAircraftIcaoCode::unassignedIcao(), swiftLivery); + CAircraftSituation situation(cs, coordinate); + situation.setAltitude(terrainProbeAltitude()); + situation.setZeroPBH(); + const CSimulatedAircraft pseudoAircraft(cs, model, CUser("123456", "swift", cs), situation); + return this->physicallyAddRemoteAircraftImpl(pseudoAircraft, ExternalCall); + } + + int CSimulatorFsxCommon::physicallyInitAITerrainProbes(const ICoordinateGeodetic &coordinate, int number) + { + if (number < 1) { return 0; } + int c = 0; + for (int n = 1; n <= number; ++n) { - ok = true; - const CAircraftModel model(modelString, CAircraftModel::TypeTerrainProbe, QStringLiteral("swift terrain probe"), CAircraftIcaoCode::unassignedIcao()); - const CAircraftSituation situation(cs, coordinate); - const CSimulatedAircraft pseudoAircraft(cs, model, CUser("123456", "swift", cs), situation); - CSimConnectObject simObject(CSimConnectObject::TerrainProbe); - simObject.setRequestId(requestId); - simObject.setAircraft(pseudoAircraft); - m_simConnectProbes.insert(cs, simObject); + if (this->physicallyAddAITerrainProbe(coordinate, n)) { c++; } } - else - { - const CStatusMessage msg = CStatusMessage(this).error("SimConnect, can not create terrain AI: '%1'") << requestId; - CLogMessage::preformatted(msg); - } - return ok; + + CLogMessage(this).info("Adding %1 FSX terrain probes") << number; + m_addedProbes = c; + return c; } bool CSimulatorFsxCommon::physicallyRemoveRemoteAircraft(const CCallsign &callsign) @@ -1255,20 +1285,23 @@ namespace BlackSimPlugin if (!m_simConnectObjects.contains(callsign)) { return false; } // already fully removed or not yet added CSimConnectObject &simObject = m_simConnectObjects[callsign]; if (simObject.isPendingRemoved()) { return true; } + if (simObject.isTerrainProbe()) + { + return false; + } // check for pending objects m_addPendingAircraft.removeByCallsign(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 (pendingAdded || stillWaitingForLights) + 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("Object: '%1' requested to be removed, but pedning added (%2) / or pending lights(%3). CS '%4' will be removed again.") + CLogMessage(this).warning("Object: '%1' requested to be removed, but pending added (%2) / or pending lights(%3). CS '%4' will be removed again.") << simObject.toQString() << boolToYesNo(pendingAdded) << boolToYesNo(stillWaitingForLights) << callsign.asString(); - simObject.fakeCurrentLightsInSimulator(); // next time looks like we have lights - simObject.setRemovedWhileAdding(true); + simObject.setRemovedWhileAdding(true); // next time kill QPointer myself(this); QTimer::singleShot(2000, this, [ = ] { @@ -1290,7 +1323,7 @@ namespace BlackSimPlugin const HRESULT result = SimConnect_AIRemoveObject(m_hSimConnect, static_cast(simObject.getObjectId()), requestId); if (isOk(result)) { - if (this->isTracingSendId()) { this->traceSendId(simObject.getObjectId(), Q_FUNC_INFO);} + if (this->isTracingSendId()) { this->traceSendId(simObject, Q_FUNC_INFO);} } else { @@ -1673,13 +1706,15 @@ namespace BlackSimPlugin SIMCONNECT_DATA_INITPOSITION position = CSimulatorFsxCommon::coordinateToFsxPosition(situation); position.Heading = situation.getHeading().value(CAngleUnit::deg()); - position.Airspeed = static_cast(situation.getGroundSpeed().valueInteger(CSpeedUnit::kts())); + const double gsKts = situation.getGroundSpeed().value(CSpeedUnit::kts()); + position.Airspeed = static_cast(qRound(gsKts)); // sanity check - if (CBuildConfig::isLocalDeveloperDebugBuild() && situation.getGroundSpeed().isNegativeWithEpsilonConsidered()) + if (gsKts < 0.0) { - BLACK_VERIFY_X(false, Q_FUNC_INFO, "Negative speed"); - position.Airspeed = 0; + // we get negative GS for pushback and helicopters + // do do we handle them her with DWORD + position.Airspeed = 0U; } // MSFS has inverted pitch and bank angles @@ -1783,13 +1818,9 @@ namespace BlackSimPlugin return false; } - bool CSimulatorFsxCommon::requestTerrainProbeData(const CCallsign &callsign) + bool CSimulatorFsxCommon::requestTerrainProbeData(const CSimConnectObject &simObject) { - if (m_simConnectProbes.countConfirmedAdded() < 1) { return false; } - if (!m_simConnectObjects.contains(callsign)) { return false; } // removed in meantime - static const QString w("Cannot request terrain probe data for id '%1'"); - const CSimConnectObject simObject = m_simConnectProbes.values().front(); const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectPositionData); const DWORD objectId = simObject.getObjectId(); const HRESULT result = @@ -1813,7 +1844,7 @@ namespace BlackSimPlugin SimConnect_RequestDataOnSimObject( m_hSimConnect, requestId, CSimConnectDefinitions::DataRemoteAircraftLights, simObject.getObjectId(), SIMCONNECT_PERIOD_SECOND), - true, simObject, "Cannot request lights data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); + simObject, "Cannot request lights data", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); return isOk(result); } @@ -1827,8 +1858,8 @@ namespace BlackSimPlugin const HRESULT result = this->logAndTraceSendId( SimConnect_RequestDataOnSimObject( m_hSimConnect, requestId, - CSimConnectDefinitions::DataRemoteAircraftModelData, simObject.getObjectId(), - SIMCONNECT_PERIOD_ONCE), + CSimConnectDefinitions::DataRemoteAircraftModelData, + simObject.getObjectId(), SIMCONNECT_PERIOD_ONCE), simObject, "Cannot request model info", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); return isOk(result); } @@ -1851,8 +1882,8 @@ namespace BlackSimPlugin const HRESULT hr2 = this->logAndTraceSendId( SimConnect_RequestDataOnSimObject( m_hSimConnect, requestId, - CSimConnectDefinitions::DataRemoteAircraftLights, simObject.getObjectId(), - SIMCONNECT_PERIOD_NEVER), + CSimConnectDefinitions::DataRemoteAircraftLights, + simObject.getObjectId(), SIMCONNECT_PERIOD_NEVER), simObject, "Stopping lights request", Q_FUNC_INFO, "SimConnect_RequestDataOnSimObject"); return isOk(hr1, hr2); } @@ -1882,9 +1913,11 @@ namespace BlackSimPlugin m_simSimulating = false; m_syncDeferredCounter = 0; m_skipCockpitUpdateCycles = 0; + m_ownAircraftUpdate = 0; m_requestIdSimObjAircraft = static_cast(RequestSimObjAircraftStart); m_dispatchErrors = 0; m_receiveExceptionCount = 0; + m_addedProbes = 0; m_sendIdTraces.clear(); } @@ -1908,39 +1941,27 @@ namespace BlackSimPlugin CCallsignSet CSimulatorFsxCommon::getCallsignsMissingInProvider() const { - const CCallsignSet simObjectCallsigns(m_simConnectObjects.keys()); + const CCallsignSet simObjectCallsigns(m_simConnectObjects.getAllCallsigns(true)); const CCallsignSet providerCallsigns(this->getAircraftInRangeCallsigns()); return simObjectCallsigns.difference(providerCallsigns); } - void CSimulatorFsxCommon::traceSendId(DWORD simObjectId, const QString &functionName, const QString &details) + void CSimulatorFsxCommon::traceSendId(const CSimConnectObject &simObject, const QString &functionName, const QString &details, bool forceTrace) { - if (!this->isTracingSendId()) { return; } + if (!forceTrace && !this->isTracingSendId()) { return; } if (MaxSendIdTraces < 1) { return; } 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, simObjectId, details.isEmpty() ? functionName : details % QStringLiteral(", ") % functionName); + const TraceFsxSendId trace(dwLastId, simObject, details.isEmpty() ? functionName : details % QStringLiteral(", ") % functionName); m_sendIdTraces.push_front(trace); } - HRESULT CSimulatorFsxCommon::logAndTraceSendId(HRESULT hr, DWORD simObjectId, const QString &warningMsg, const QString &functionName, const QString &functionDetails) + HRESULT CSimulatorFsxCommon::logAndTraceSendId(HRESULT hr, const QString &warningMsg, const QString &functionName, const QString &functionDetails) { - return this->logAndTraceSendId(hr, this->isTracingSendId(), simObjectId, warningMsg, functionName, functionDetails); - } - - HRESULT CSimulatorFsxCommon::logAndTraceSendId(HRESULT hr, bool traceSendId, DWORD simObjectId, const QString &warningMsg, const QString &functionName, const QString &functionDetails) - { - if (traceSendId) { this->traceSendId(simObjectId, functionName, functionDetails); } - if (isOk(hr)) { return hr; } - if (!warningMsg.isEmpty()) - { - CSimConnectObject simObject = (simObjectId == 0) ? CSimConnectObject() : m_simConnectObjects.getSimObjectForObjectId(simObjectId); - CLogMessage(this).warning(warningMsg % QStringLiteral(" ") % simObject.toQString()); - } - this->triggerAutoTraceSendId(); - return hr; + const CSimConnectObject empty; + return this->logAndTraceSendId(hr, empty, warningMsg, functionName, functionDetails); } HRESULT CSimulatorFsxCommon::logAndTraceSendId(HRESULT hr, const CSimConnectObject &simObject, const QString &warningMsg, const QString &functionName, const QString &functionDetails) @@ -1950,7 +1971,7 @@ namespace BlackSimPlugin HRESULT CSimulatorFsxCommon::logAndTraceSendId(HRESULT hr, bool traceSendId, const CSimConnectObject &simObject, const QString &warningMsg, const QString &functionName, const QString &functionDetails) { - if (traceSendId) { this->traceSendId(simObject.getObjectId(), functionName, functionDetails); } + if (traceSendId) { this->traceSendId(simObject, functionName, functionDetails); } if (isOk(hr)) { return hr; } if (!warningMsg.isEmpty()) { @@ -1960,26 +1981,42 @@ namespace BlackSimPlugin return hr; } - QString CSimulatorFsxCommon::getSendIdTraceDetails(DWORD sendId) const + TraceFsxSendId CSimulatorFsxCommon::getSendIdTrace(DWORD sendId) const { for (const TraceFsxSendId &trace : m_sendIdTraces) { - if (trace.sendId == sendId) - { - static const QString d("Send id: %1 obj.id.: %2 SimObj: %3 | '%4'"); - const CSimConnectObject simObj = m_simConnectObjects.getSimObjectForObjectId(trace.simObjectId); - return d.arg(sendId).arg(trace.simObjectId).arg(simObj.toQString(), trace.comment); - } + if (trace.sendId == sendId) { return trace; } + } + return TraceFsxSendId::invalid(); + } + + QString CSimulatorFsxCommon::getSendIdTraceDetails(DWORD sendId) const + { + const TraceFsxSendId trace = this->getSendIdTrace(sendId); + if (trace.sendId == sendId) + { + return this->getSendIdTraceDetails(trace); } return ""; } + QString CSimulatorFsxCommon::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 CSimulatorFsxCommon::removeAllProbes() { if (!m_hSimConnect) { return 0; } // already disconnected - if (m_simConnectProbes.isEmpty()) { return 0; } + const QList probes = m_simConnectObjects.getProbes(); + int c = 0; - for (const CSimConnectObject &probeSimObject : m_simConnectProbes.values()) + for (const CSimConnectObject &probeSimObject : probes) { if (!probeSimObject.isConfirmedAdded()) { continue; } const SIMCONNECT_DATA_REQUEST_ID requestId = probeSimObject.getRequestId(CSimConnectDefinitions::SimObjectRemove); @@ -1993,7 +2030,7 @@ namespace BlackSimPlugin CLogMessage(this).warning("Removing probe '%1' from simulator failed") << probeSimObject.getObjectId(); } } - m_simConnectProbes.clear(); + m_simConnectObjects.removeAllProbes(); m_pendingProbeRequests.clear(); return c; } @@ -2025,6 +2062,12 @@ namespace BlackSimPlugin return simObject; } + const CAltitude &CSimulatorFsxCommon::terrainProbeAltitude() + { + static const CAltitude alt(10000, CLengthUnit::ft()); + return alt; + } + QString CSimulatorFsxCommon::fsxCharToQString(const char *fsxChar, int size) { return QString::fromLatin1(fsxChar, size); diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h index 68c2d94ee..c9a7ba87f 100644 --- a/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h +++ b/src/plugins/simulator/fsxcommon/simulatorfsxcommon.h @@ -23,6 +23,7 @@ #include "blackmisc/simulation/aircraftmodel.h" #include "blackmisc/simulation/simulatedaircraft.h" #include "blackmisc/aviation/airportlist.h" +#include "blackmisc/aviation/altitude.h" #include "blackmisc/statusmessage.h" #include "blackmisc/network/client.h" #include "blackmisc/pixmap.h" @@ -87,14 +88,29 @@ namespace BlackSimPlugin struct TraceFsxSendId { //! Ctor - TraceFsxSendId(DWORD sendId, DWORD simObjectId, const QString &comment) : - sendId(sendId), simObjectId(simObjectId), comment(comment) + TraceFsxSendId(DWORD sendId, const CSimConnectObject &simObject, const QString &comment) : + sendId(sendId), simObject(simObject), comment(comment) { } // DWORD is unsigned - DWORD sendId = 0; //!< the send id - DWORD simObjectId = 0; //!< corresponding CSimConnectObject - QString comment; //!< where sent + DWORD sendId = 0; //!< the send id + CSimConnectObject simObject; //!< CSimConnectObject at the time of the trace + QString comment; //!< where sent + + //! For probe + bool isForProbe() const { return simObject.getType() == CSimConnectObject::TerrainProbe; } + + //! For aircraft + bool isForAircraft() const { return simObject.getType() == CSimConnectObject::Aircraft; } + + //! 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; } }; //! FSX Simulator Implementation @@ -136,6 +152,8 @@ namespace BlackSimPlugin //! @} //! \copydoc BlackMisc::Simulation::ISimulationEnvironmentProvider::requestElevation + //! \remark x86 FSX version, x64 version is overridden + //! \sa CSimulatorFsxCommon::is virtual bool requestElevation(const BlackMisc::Geo::ICoordinateGeodetic &reference, const BlackMisc::Aviation::CCallsign &callsign) override; //! Tracing? @@ -144,12 +162,25 @@ namespace BlackSimPlugin //! Set tracing on/off void setTractingSendId(bool trace); + //! FSX Terrain probe + //! \remark must be off at P3D v4.2 drivers or later + bool isUsingFsxTerrainProbe() const { return m_useFsxTerrainProbe; } + + //! FSX terrain probe + void setUsingFsxTerrainProbe(bool use) { m_useFsxTerrainProbe = use; } + //! Request for sim data (request in range of sim data)? static bool isRequestForSimObjAircraft(DWORD requestId) { return requestId >= RequestSimObjAircraftStart && requestId <= RequestSimObjAircraftRangeEnd; } //! Request for probe (elevation)? static bool isRequestForSimObjTerrainProbe(DWORD requestId) { return requestId >= RequestSimObjTerrainProbeStart && requestId <= RequestSimObjTerrainProbeRangeEnd; } + //! Request for any CSimConnectObject? + static bool isRequestForSimConnectObject(DWORD requestId) + { + return isRequestForSimObjAircraft(requestId) || isRequestForSimObjTerrainProbe(requestId); + } + //! Sub request type static CSimConnectDefinitions::SimObjectRequest requestToSimObjectRequest(DWORD requestId); @@ -210,6 +241,12 @@ namespace BlackSimPlugin //! 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); + //! Register help static void registerHelp(); @@ -217,7 +254,10 @@ namespace BlackSimPlugin HANDLE m_hSimConnect = nullptr; //!< handle to SimConnect object DispatchProc m_dispatchProc = &CSimulatorFsxCommon::SimConnectProc; //!< called function for dispatch, can be overriden by specialized P3D function CSimConnectObjects m_simConnectObjects; //!< AI objects and their object and request ids - CSimConnectObjects m_simConnectProbes; //!< AI terrain probes and their object / request ids + + // probes + bool m_useFsxTerrainProbe = true; //!< Use FSX Terrain probe? + int m_addedProbes = 0; //!< added probes QMap m_pendingProbeRequests; //!< pending elevation requests private: @@ -231,7 +271,7 @@ namespace BlackSimPlugin }; //! Mode as string - const QString &modeToString(AircraftAddMode mode); + static const QString &modeToString(AircraftAddMode mode); //! Dispatch SimConnect messages //! \remark very frequently called @@ -243,7 +283,11 @@ namespace BlackSimPlugin //! Add AI object for terrain probe //! \remark experimental - bool physicallyAddAITerrainProbe(const BlackMisc::Geo::ICoordinateGeodetic &coordinate); + bool physicallyAddAITerrainProbe(const BlackMisc::Geo::ICoordinateGeodetic &coordinate, int number); + + //! Add number probes (inits the probe objects) + //! \remark experimental + int physicallyInitAITerrainProbes(const BlackMisc::Geo::ICoordinateGeodetic &coordinate, int number); //! Remove aircraft no longer in provider //! \remark kind of cleanup function, in an ideal this should never need to cleanup something @@ -254,6 +298,9 @@ namespace BlackSimPlugin //! \remark requests further data on remote aircraft (lights, ..) when correctly added void verifyAddedRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraftIn); + //! Verify the probe + void verifyAddedTerrainProbe(const BlackMisc::Simulation::CSimulatedAircraft &remoteAircraftIn); + //! Add next aircraft based on timer void addPendingAircraftByTimer(); @@ -342,18 +389,12 @@ namespace BlackSimPlugin //! An AI aircraft was added in the simulator bool simulatorReportedObjectAdded(DWORD objectId); - //! An AI probe was added in the simulator - bool simulatorReportedProbeAdded(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); - //! Set ID of a SimConnect object, so far we only have an request id in the object - bool setSimConnectProbeId(DWORD requestId, DWORD objectId); - //! Remember current lights bool setCurrentLights(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftLights &lights); @@ -369,12 +410,6 @@ namespace BlackSimPlugin //! The SimConnect object for idxs CSimConnectObject getSimObjectForObjectId(DWORD objectId) const; - //! The simconnect related probes - const CSimConnectObjects &getSimConnectProbes() const { return m_simConnectProbes; } - - //! Get probe for id - CSimConnectObject getProbeForObjectId(DWORD objectId) const; - //! Format conversion //! \note must be valid situation SIMCONNECT_DATA_INITPOSITION aircraftSituationToFsxPosition(const BlackMisc::Aviation::CAircraftSituation &situation, bool sendGnd = true); @@ -389,7 +424,7 @@ namespace BlackSimPlugin bool requestPositionDataForSimObject(const CSimConnectObject &simObject, SIMCONNECT_PERIOD period = SIMCONNECT_PERIOD_SECOND); //! Request data for the terrain probe - bool requestTerrainProbeData(const BlackMisc::Aviation::CCallsign &callsign); + bool requestTerrainProbeData(const CSimConnectObject &simObject); //! Request lights for a CSimConnectObject bool requestLightsForSimObject(const CSimConnectObject &simObject); @@ -410,13 +445,10 @@ namespace BlackSimPlugin void setTraceSendId(bool traceSendId) { m_traceSendId = traceSendId; } //! Trace the send id - void traceSendId(DWORD simObjectId, const QString &functionName, const QString &details = {}); + void traceSendId(const CSimConnectObject &simObject, const QString &functionName, const QString &details = {}, bool forceTrace = false); //! Trace if required, log errors - HRESULT logAndTraceSendId(HRESULT hr, DWORD simObjectId, const QString &warningMsg, const QString &functionName, const QString &functionDetails = {}); - - //! Trace if required, log errors - HRESULT logAndTraceSendId(HRESULT hr, bool traceSendId, DWORD simObjectId, const QString &warningMsg, const QString &functionName, const QString &functionDetails = {}); + 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 = {}); @@ -424,6 +456,12 @@ namespace BlackSimPlugin //! Trace if required, log errors HRESULT logAndTraceSendId(HRESULT hr, bool traceSendId, const CSimConnectObject &simObject, const QString &warningMsg, const QString &functionName, const QString &functionDetails = {}); + //! 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; @@ -433,6 +471,9 @@ namespace BlackSimPlugin //! Insert a new SimConnect object CSimConnectObject insertNewSimConnectObject(const BlackMisc::Simulation::CSimulatedAircraft &aircraft, DWORD requestId); + //! Used for terrain probes + static const BlackMisc::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 @@ -465,6 +506,7 @@ namespace BlackSimPlugin qint64 m_simulatingChangedTs = -1; //!< timestamp, when simulating changed (used to avoid jitter) int m_syncDeferredCounter = 0; //!< Set when synchronized, used to wait some time int m_skipCockpitUpdateCycles = 0; //!< skip some update cycles to allow changes in simulator cockpit to be set + int m_ownAircraftUpdate = 0; //!< own aircraft update // tracing dispatch performance int m_dispatchErrors = 0; //!< number of dispatched failed, \sa dispatch diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp index a79ce9994..9369d59fd 100644 --- a/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp +++ b/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp @@ -64,23 +64,63 @@ namespace BlackSimPlugin 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 DWORD index = exception->dwIndex; // index of parameter that was source of error, 4294967295/0xFFFFFFFF means unknown, 0 means also UNKNOWN INDEX + const DWORD data = cbData; + const TraceFsxSendId trace = simulatorFsxP3D->getSendIdTrace(sendId); + bool logGenericExceptionInfo = true; + switch (exceptionId) { case SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE: break; case SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID: break; // Specifies that the client event, request ID, data definition ID, or object ID was not recognized - case SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED: break; - default: break; + case SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED: + { + if (trace.isValid()) + { + // it can happen the object is not yet + CSimConnectObject simObject = simulatorFsxP3D->getSimObjectForTrace(trace); + if (simObject.isInvalid()) { simObject = trace.simObject; } // take the one in the trace + if (simObject.isValid()) + { + const bool removed = simulatorFsxP3D->m_simConnectObjects.remove(simObject.getCallsign()); + if (removed) + { + if (simObject.isAircraft()) + { + CLogMessage(simulatorFsxP3D).warning("Model failed to be added and will be disabled: '%1' details: %2") + << simObject.getAircraftModelString() + << simObject.getAircraft().toQString(true); + logGenericExceptionInfo = false; + } + else + { + CLogMessage(simulatorFsxP3D).warning("Adding probe failed: %1 %2") + << simObject.getCallsign().asString() + << simObject.getAircraftModelString(); + simulatorFsxP3D->setUsingFsxTerrainProbe(false); + logGenericExceptionInfo = false; + } + } // removed + } + } + } // SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED: + break; + default: + break; + } // switch exception id + + // generic exception warning + if (logGenericExceptionInfo) + { + QString ex; + ex.sprintf("Exception=%lu | SendID=%lu | Index=%lu | cbData=%lu", exceptionId, sendId, index, data); + const QString exceptionString(CSimConnectUtilities::simConnectExceptionToString(static_cast(exception->dwException))); + const QString sendIdDetails = simulatorFsxP3D->getSendIdTraceDetails(sendId); + CLogMessage(simulatorFsxP3D).warning("Caught simConnect exception: '%1' '%2' | send details: '%3'") + << exceptionString << ex + << (sendIdDetails.isEmpty() ? "N/A" : sendIdDetails); + simulatorFsxP3D->triggerAutoTraceSendId(); } - QString ex; - ex.sprintf("Exception=%lu | SendID=%lu | Index=%lu | cbData=%lu", exceptionId, sendId, index, data); - const QString exceptionString(CSimConnectUtilities::simConnectExceptionToString(static_cast(exception->dwException))); - const QString sendIdDetails = simulatorFsxP3D->getSendIdTraceDetails(sendId); - CLogMessage(simulatorFsxP3D).warning("Caught simConnect exception: '%1' '%2' | send details: '%3'") - << exceptionString << ex - << (sendIdDetails.isEmpty() ? "N/A" : sendIdDetails); - simulatorFsxP3D->triggerAutoTraceSendId(); break; // SIMCONNECT_RECV_ID_EXCEPTION } case SIMCONNECT_RECV_ID_QUIT: @@ -145,19 +185,6 @@ namespace BlackSimPlugin break; } } - else if (simulatorFsxP3D->getSimConnectProbes().isKnownSimObjectId(objectId)) - { - switch (event->uEventID) - { - case SystemEventObjectRemoved: - CLogMessage(simulatorFsxP3D).info("Removed probe id: %2") << objectId; - simulatorFsxP3D->m_simConnectProbes.removeByObjectId(objectId); - break; - case SystemEventObjectAdded: // added in SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID - default: - break; - } - } break; // SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE } case SIMCONNECT_RECV_ID_EVENT_FRAME: @@ -179,27 +206,11 @@ namespace BlackSimPlugin const SIMCONNECT_RECV_ASSIGNED_OBJECT_ID *event = static_cast(pData); const DWORD requestId = event->dwRequestID; const DWORD objectId = event->dwObjectID; - bool success = false; simulatorFsxP3D->m_dispatchRequestIdLast = requestId; - if (CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId)) + if (CSimulatorFsxCommon::isRequestForSimConnectObject(requestId)) { - success = simulatorFsxP3D->setSimConnectProbeId(requestId, objectId); - if (!success) { break; } // not an request ID of ours - success = simulatorFsxP3D->simulatorReportedProbeAdded(objectId); - const CSimConnectObject simObject = simulatorFsxP3D->getProbeForObjectId(objectId); - if (success) - { - CLogMessage(simulatorFsxP3D).info("Added probe '%1' id: %2") << simObject.getCallsign() << objectId; - } - else - { - CLogMessage(simulatorFsxP3D).error("Cannot add probe '%1' id: %2") << simObject.getCallsign() << objectId; - } - } - else if (CSimulatorFsxCommon::isRequestForSimObjAircraft(requestId)) - { - success = simulatorFsxP3D->setSimConnectObjectId(requestId, objectId); + bool success = simulatorFsxP3D->setSimConnectObjectId(requestId, objectId); if (!success) { break; } // not an request ID of ours success = simulatorFsxP3D->simulatorReportedObjectAdded(objectId); // trigger follow up actions if (!success) @@ -314,8 +325,9 @@ namespace BlackSimPlugin } else if (CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId)) { - const CSimConnectObject probeObj = simulatorFsxP3D->getProbeForObjectId(objectId); + const CSimConnectObject probeObj = simulatorFsxP3D->getSimObjectForObjectId(objectId); if (!probeObj.hasValidRequestAndObjectId()) { break; } + Q_ASSERT_X(probeObj.isTerrainProbe(), Q_FUNC_INFO, "No probe"); const CSimConnectDefinitions::SimObjectRequest subRequest = CSimulatorFsxCommon::requestToSimObjectRequest(requestId); if (subRequest == CSimConnectDefinitions::SimObjectPositionData) @@ -340,7 +352,6 @@ namespace BlackSimPlugin } } } // probe - break; } break; // default (SIMCONNECT_RECV_ID_SIMOBJECT_DATA) }