Ref T335, improve FSX/P3D probe/aircraft handling

- probes handled in same sim object list as aircraft
- init probes at beginning, probes can be disabled
- better handle SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED:
- CSimulatorFsxCommon::verifyAddedTerrainProbe
- use "SimConnect_AICreateSimulatedObject" for probes
This commit is contained in:
Klaus Basan
2018-09-07 00:11:39 +02:00
parent 2020c04f52
commit bbd6a70162
3 changed files with 287 additions and 191 deletions

View File

@@ -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<CSimulatorFsxCommon> myself(this);
QTimer::singleShot(2000, this, [ = ]
{
@@ -1290,7 +1323,7 @@ namespace BlackSimPlugin
const HRESULT result = SimConnect_AIRemoveObject(m_hSimConnect, static_cast<SIMCONNECT_OBJECT_ID>(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<DWORD>(situation.getGroundSpeed().valueInteger(CSpeedUnit::kts()));
const double gsKts = situation.getGroundSpeed().value(CSpeedUnit::kts());
position.Airspeed = static_cast<DWORD>(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<SIMCONNECT_DATA_REQUEST_ID>(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<CSimConnectObject> 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);

View File

@@ -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<DWORD, BlackMisc::Aviation::CCallsign> 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

View File

@@ -64,23 +64,63 @@ namespace BlackSimPlugin
SIMCONNECT_RECV_EXCEPTION *exception = static_cast<SIMCONNECT_RECV_EXCEPTION *>(pData);
const DWORD exceptionId = exception->dwException;
const DWORD sendId = exception->dwSendID;
const DWORD index = exception->dwIndex; // index of parameter that was source of error, 4294967295/0xFFFFFFFF means unknown, 0 means also UNKNOWN INDEX
const DWORD data = cbData;
const 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<DWORD>(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<DWORD>(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<SIMCONNECT_RECV_ASSIGNED_OBJECT_ID *>(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)
}