mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-22 23:05:36 +08:00
Ref T275, improved request id handling for FSX/P3D
* base reange for request IDs * all operations use offsets * we can conclude what an id is used for (debugging) plus * minor Doxygen fixes * some renamings
This commit is contained in:
@@ -23,32 +23,53 @@ namespace BlackSimPlugin
|
||||
const QString &CSimConnectDefinitions::requestToString(Request request)
|
||||
{
|
||||
static const QString ownAircraft("RequestOwnAircraft");
|
||||
static const QString removeAircraft("RequestRemoveAircraft");
|
||||
static const QString title("RequestOwnAircraftTitle");
|
||||
static const QString simEnv("RequestSimEnvironment");
|
||||
static const QString sbData("RequestSbData");
|
||||
static const QString unknown("unknown");
|
||||
static const QString facility("RequestFacility");
|
||||
static const QString end("<end>");
|
||||
static const QString simdata("range simdata");
|
||||
static const QString probe("range probe");
|
||||
static const QString lights("range lights");
|
||||
static const QString unknown("unknown");
|
||||
|
||||
switch (request)
|
||||
{
|
||||
case RequestOwnAircraft: return ownAircraft;
|
||||
case RequestRemoveAircraft: return removeAircraft;
|
||||
case RequestOwnAircraftTitle: return title;
|
||||
case RequestSimEnvironment: return simEnv;
|
||||
case RequestSbData: return sbData;
|
||||
case RequestRangeForLights: return lights;
|
||||
case RequestRangeForProbe: return probe;
|
||||
case RequestRangeForSimData: return simdata;
|
||||
case RequestFacility: return facility;
|
||||
case RequestEndMarker: return end;
|
||||
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("<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)
|
||||
@@ -132,6 +153,8 @@ namespace BlackSimPlugin
|
||||
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftParts, "GENERAL ENG COMBUSTION:4", "Bool");
|
||||
|
||||
// Lights (other definition)
|
||||
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataOwnAircraftTitle, "TITLE", NULL, SIMCONNECT_DATATYPE_STRING256);
|
||||
|
||||
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");
|
||||
@@ -157,11 +180,22 @@ namespace BlackSimPlugin
|
||||
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 (hr != S_OK)
|
||||
{
|
||||
CLogMessage(static_cast<CSimConnectDefinitions *>(nullptr)).error("SimConnect error: initRemoteAircraftSimData %1") << hr;
|
||||
CLogMessage(static_cast<CSimConnectDefinitions *>(nullptr)).error("SimConnect error: initRemoteAircraftSimData DataRemoteAircraftGetPosition %1") << hr;
|
||||
}
|
||||
|
||||
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "ATC TYPE", NULL, SIMCONNECT_DATATYPE_STRING32);
|
||||
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "ATC MODEL", NULL, SIMCONNECT_DATATYPE_STRING32);
|
||||
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "ATC ID", NULL, SIMCONNECT_DATATYPE_STRING32);
|
||||
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "ATC AIRLINE", NULL, SIMCONNECT_DATATYPE_STRING64);
|
||||
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "ATC FLIGHT NUMBER", NULL, SIMCONNECT_DATATYPE_STRING8);
|
||||
hr += SimConnect_AddToDataDefinition(hSimConnect, CSimConnectDefinitions::DataRemoteAircraftModelData, "TITLE", NULL, SIMCONNECT_DATATYPE_STRING256);
|
||||
if (hr != S_OK)
|
||||
{
|
||||
CLogMessage(static_cast<CSimConnectDefinitions *>(nullptr)).error("SimConnect error: initRemoteAircraftSimData DataRemoteAircraftModelData %1") << hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
@@ -180,7 +214,7 @@ namespace BlackSimPlugin
|
||||
HRESULT CSimConnectDefinitions::initSbDataArea(const HANDLE hSimConnect)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
DWORD sbSize = sizeof(DataDefinitionClientAreaSb);
|
||||
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);
|
||||
|
||||
@@ -130,8 +130,8 @@ namespace BlackSimPlugin
|
||||
BlackMisc::Aviation::CAircraftLights toLights() const;
|
||||
};
|
||||
|
||||
//! Data for AI object sent back from simulator
|
||||
struct DataDefinitionRemoteAircraftSimData
|
||||
//! Data for AI object and probe sent back from simulator
|
||||
struct DataDefinitionPosData
|
||||
{
|
||||
double latitudeDeg; //!< Latitude (deg)
|
||||
double longitudeDeg; //!< Longitude (deg)
|
||||
@@ -195,6 +195,7 @@ namespace BlackSimPlugin
|
||||
DataRemoteAircraftParts,
|
||||
DataRemoteAircraftSetPosition, //!< the position which will be set
|
||||
DataRemoteAircraftGetPosition, //!< get position to evaluate altitude / AGL
|
||||
DataRemoteAircraftModelData, //!< model data eventually used and reported back from simulator
|
||||
DataSimEnvironment,
|
||||
DataClientAreaSb, //!< whole SB area
|
||||
DataClientAreaSbIdent, //!< ident single value
|
||||
@@ -205,19 +206,32 @@ namespace BlackSimPlugin
|
||||
enum Request
|
||||
{
|
||||
RequestOwnAircraft,
|
||||
RequestRemoveAircraft,
|
||||
RequestOwnAircraftTitle,
|
||||
RequestSimEnvironment,
|
||||
RequestSbData, //!< SB client area / XPDR mode
|
||||
RequestRangeForSimData, //!< range for sim data
|
||||
RequestRangeForProbe, //!< range for probe
|
||||
RequestRangeForLights, //!< range for lights
|
||||
RequestEndMarker //!< free request ids can start here
|
||||
RequestSbData, //!< SB client area / XPDR mode
|
||||
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
|
||||
};
|
||||
|
||||
//! Request to string
|
||||
static const QString &requestToString(Request request);
|
||||
|
||||
//! Request to string
|
||||
static const QString &simObjectRequestToString(SimObjectRequest simObjectRequest);
|
||||
|
||||
//! Constructor
|
||||
CSimConnectDefinitions();
|
||||
|
||||
|
||||
@@ -8,9 +8,15 @@
|
||||
*/
|
||||
|
||||
#include "simconnectobject.h"
|
||||
#include "blackmisc/stringutils.h"
|
||||
#include "simulatorfsxcommon.h"
|
||||
#include "blackcore/simulator.h"
|
||||
#include "blackcore/simulator.h"
|
||||
#include "blackmisc/simulation/interpolatormulti.h"
|
||||
#include "blackconfig/buildconfig.h"
|
||||
|
||||
using namespace BlackConfig;
|
||||
using namespace BlackMisc;
|
||||
using namespace BlackMisc::Aviation;
|
||||
using namespace BlackMisc::Simulation;
|
||||
using namespace BlackCore;
|
||||
@@ -47,6 +53,36 @@ namespace BlackSimPlugin
|
||||
m_callsignByteArray = aircraft.getCallsignAsString().toLatin1();
|
||||
}
|
||||
|
||||
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);
|
||||
Q_ASSERT_X(type == this->getType(), Q_FUNC_INFO, "Type mismatch");
|
||||
}
|
||||
|
||||
DWORD os = 0;
|
||||
switch (this->getType())
|
||||
{
|
||||
case TerrainProbe:
|
||||
os = static_cast<DWORD>(CSimulatorFsxCommon::offsetSimObjTerrainProbe(offset));
|
||||
break;
|
||||
case Aircraft:
|
||||
default:
|
||||
os = static_cast<DWORD>(CSimulatorFsxCommon::offsetSimObjAircraft(offset));
|
||||
break;
|
||||
}
|
||||
return os + m_requestId;
|
||||
}
|
||||
|
||||
void CSimConnectObject::setObjectId(DWORD id)
|
||||
{
|
||||
m_objectId = id;
|
||||
@@ -130,6 +166,34 @@ namespace BlackSimPlugin
|
||||
return m_interpolator->getLastInterpolatedSituation(mode);
|
||||
}
|
||||
|
||||
QString CSimConnectObject::toQString() const
|
||||
{
|
||||
static const QString s("CS: '%1' obj: %2 req: %3 conf.added: %4 pend.rem.: %5");
|
||||
return s.arg(this->getCallsign().asString()).arg(m_objectId).arg(m_requestId).arg(boolToYesNo(m_confirmedAdded), boolToYesNo(m_pendingRemoved));
|
||||
}
|
||||
|
||||
CSimConnectObject::SimObjectType CSimConnectObject::requestIdToType(DWORD requestId)
|
||||
{
|
||||
if (CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId)) { return TerrainProbe; }
|
||||
if (CSimulatorFsxCommon::isRequestForSimObjAircraft(requestId)) { return Aircraft; }
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Wrong range");
|
||||
return Aircraft;
|
||||
}
|
||||
|
||||
const QString &CSimConnectObject::typeToString(CSimConnectObject::SimObjectType type)
|
||||
{
|
||||
static const QString a("aircraft");
|
||||
static const QString p("probe");
|
||||
static const QString u("unknown");
|
||||
switch (type)
|
||||
{
|
||||
case Aircraft: return a;
|
||||
case TerrainProbe: return p;
|
||||
default: break;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
bool CSimConnectObjects::setSimConnectObjectIdForRequestId(DWORD requestId, DWORD objectId)
|
||||
{
|
||||
// First check, if this request id belongs to us
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace BlackSimPlugin
|
||||
enum SimObjectType
|
||||
{
|
||||
Aircraft,
|
||||
Probe
|
||||
TerrainProbe
|
||||
};
|
||||
|
||||
//! Constructor
|
||||
@@ -93,18 +93,21 @@ namespace BlackSimPlugin
|
||||
void setSimDataPeriod(SIMCONNECT_PERIOD period) { m_requestSimDataPeriod = period; }
|
||||
|
||||
//! Set Simconnect request id
|
||||
void setRequestId(DWORD id) { m_requestId = id; m_validRequestId = true; }
|
||||
void setRequestId(DWORD id);
|
||||
|
||||
//! Get Simconnect request 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
|
||||
//! Get SimConnect object id
|
||||
DWORD getObjectId() const { return m_objectId; }
|
||||
|
||||
//! Get Simconnect object id
|
||||
//! Get SimConnect object id
|
||||
QString getObjectIdAsString() const { return QString::number(this->getObjectId()); }
|
||||
|
||||
//! Valid request id?
|
||||
@@ -179,6 +182,15 @@ namespace BlackSimPlugin
|
||||
//! Interpolator
|
||||
BlackMisc::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);
|
||||
|
||||
private:
|
||||
BlackMisc::Simulation::CSimulatedAircraft m_aircraft; //!< corresponding aircraft
|
||||
SimObjectType m_type = Aircraft;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "blackmisc/simulation/simulatorplugininfo.h"
|
||||
#include "blackmisc/aviation/airportlist.h"
|
||||
#include "blackmisc/geo/elevationplane.h"
|
||||
#include "blackmisc/math/mathutils.h"
|
||||
#include "blackmisc/logmessage.h"
|
||||
#include "blackmisc/statusmessagelist.h"
|
||||
#include "blackmisc/threadutils.h"
|
||||
@@ -34,6 +35,7 @@ using namespace BlackMisc::Aviation;
|
||||
using namespace BlackMisc::PhysicalQuantities;
|
||||
using namespace BlackMisc::Geo;
|
||||
using namespace BlackMisc::Network;
|
||||
using namespace BlackMisc::Math;
|
||||
using namespace BlackMisc::Simulation;
|
||||
using namespace BlackMisc::Simulation::FsCommon;
|
||||
using namespace BlackMisc::Simulation::Fsx;
|
||||
@@ -77,7 +79,7 @@ namespace BlackSimPlugin
|
||||
|
||||
bool CSimulatorFsxCommon::isSimulating() const
|
||||
{
|
||||
return m_simSimulating;
|
||||
return m_simSimulating && this->isConnected();
|
||||
}
|
||||
|
||||
bool CSimulatorFsxCommon::connectTo()
|
||||
@@ -260,10 +262,9 @@ namespace BlackSimPlugin
|
||||
{
|
||||
static const QString specificInfo("dispatch #: %1 %2 times (cur/max): %3ms (%4ms) %5ms (%6ms) %7 %8 simData#: %9");
|
||||
return specificInfo.
|
||||
arg(DispatchProc).arg(m_dispatchProcEmpty).
|
||||
arg(m_dispatchProcCount).arg(m_dispatchProcEmptyCount).
|
||||
arg(m_dispatchTimeMs).arg(m_dispatchProcTimeMs).arg(m_dispatchMaxTimeMs).arg(m_dispatchProcMaxTimeMs).
|
||||
arg(CSimConnectUtilities::simConnectReceiveIdToString(m_dispatchMaxTimeReceiveId),
|
||||
CSimConnectDefinitions::requestToString(m_dispatchMaxTimeRequest)).
|
||||
arg(CSimConnectUtilities::simConnectReceiveIdToString(m_dispatchReceiveIdMaxTime), requestIdToString(m_dispatchRequestIdMaxTime)).
|
||||
arg(m_requestSimObjectDataCount);
|
||||
}
|
||||
|
||||
@@ -318,19 +319,34 @@ namespace BlackSimPlugin
|
||||
void CSimulatorFsxCommon::resetAircraftStatistics()
|
||||
{
|
||||
m_dispatchProcCount = 0;
|
||||
m_dispatchProcEmpty = 0;
|
||||
m_dispatchProcEmptyCount = 0;
|
||||
m_dispatchMaxTimeMs = -1;
|
||||
m_dispatchProcMaxTimeMs = -1;
|
||||
m_dispatchTimeMs = -1;
|
||||
m_dispatchProcTimeMs = -1;
|
||||
m_requestSimObjectDataCount = 0;
|
||||
m_dispatchLastReceiveId = SIMCONNECT_RECV_ID_NULL;
|
||||
m_dispatchMaxTimeReceiveId = SIMCONNECT_RECV_ID_NULL;
|
||||
m_dispatchLastRequest = CSimConnectDefinitions::RequestEndMarker;
|
||||
m_dispatchMaxTimeRequest = CSimConnectDefinitions::RequestEndMarker;
|
||||
m_dispatchReceiveIdLast = SIMCONNECT_RECV_ID_NULL;
|
||||
m_dispatchReceiveIdMaxTime = SIMCONNECT_RECV_ID_NULL;
|
||||
m_dispatchRequestIdLast = CSimConnectDefinitions::RequestEndMarker;
|
||||
m_dispatchRequestIdMaxTime = CSimConnectDefinitions::RequestEndMarker;
|
||||
CSimulatorPluginCommon::resetAircraftStatistics();
|
||||
}
|
||||
|
||||
CSimConnectDefinitions::SimObjectRequest CSimulatorFsxCommon::requestToSimObjectRequest(DWORD requestId)
|
||||
{
|
||||
DWORD v = static_cast<DWORD>(CSimConnectDefinitions::SimObjectEndMarker);
|
||||
if (isRequestForSimObjAircraft(requestId))
|
||||
{
|
||||
v = (requestId - RequestSimObjAircraftStart) / MaxSimObjAircraft;
|
||||
}
|
||||
else if (isRequestForSimObjTerrainProbe(requestId))
|
||||
{
|
||||
v = (requestId - RequestSimObjTerrainProbeStart) / MaxSimObjProbes;
|
||||
}
|
||||
Q_ASSERT_X(v <= CSimConnectDefinitions::SimObjectEndMarker, Q_FUNC_INFO, "Invalid value");
|
||||
return static_cast<CSimConnectDefinitions::SimObjectRequest>(v);
|
||||
}
|
||||
|
||||
bool CSimulatorFsxCommon::stillDisplayReceiveExceptions()
|
||||
{
|
||||
m_receiveExceptionCount++;
|
||||
@@ -451,17 +467,17 @@ namespace BlackSimPlugin
|
||||
});
|
||||
}
|
||||
|
||||
SIMCONNECT_DATA_REQUEST_ID CSimulatorFsxCommon::obtainRequestIdForSimData()
|
||||
SIMCONNECT_DATA_REQUEST_ID CSimulatorFsxCommon::obtainRequestIdForSimObjAircraft()
|
||||
{
|
||||
const SIMCONNECT_DATA_REQUEST_ID id = m_requestIdSimData++;
|
||||
if (id > RequestIdSimDataEnd) { m_requestIdSimData = RequestIdSimDataStart; }
|
||||
const SIMCONNECT_DATA_REQUEST_ID id = m_requestIdSimObjAircraft++;
|
||||
if (id > RequestSimObjAircraftEnd) { m_requestIdSimObjAircraft = RequestSimObjAircraftStart; }
|
||||
return id;
|
||||
}
|
||||
|
||||
SIMCONNECT_DATA_REQUEST_ID CSimulatorFsxCommon::obtainRequestIdForProbe()
|
||||
SIMCONNECT_DATA_REQUEST_ID CSimulatorFsxCommon::obtainRequestIdForSimObjTerrainProbe()
|
||||
{
|
||||
const SIMCONNECT_DATA_REQUEST_ID id = m_requestIdProbe++;
|
||||
if (id > RequestIdTerrainProbeEnd) { m_requestIdProbe = RequestIdTerrainProbeStart; }
|
||||
const SIMCONNECT_DATA_REQUEST_ID id = m_requestIdSimObjTerrainProbe++;
|
||||
if (id > RequestSimObjTerrainProbeEnd) { m_requestIdSimObjTerrainProbe = RequestSimObjTerrainProbeStart; }
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -471,6 +487,7 @@ namespace BlackSimPlugin
|
||||
if (this->isShuttingDownOrDisconnected()) { return false; }
|
||||
const qint64 ts = QDateTime::currentMSecsSinceEpoch();
|
||||
m_traceAutoTs = ts; // auto trace on
|
||||
CLogMessage(this).warning("Triggered auto trace until %1") << ts;
|
||||
const QPointer<CSimulatorFsxCommon> myself(this);
|
||||
QTimer::singleShot(AutoTraceOffsetMs * 1.2, this, [ = ]
|
||||
{
|
||||
@@ -478,6 +495,7 @@ namespace BlackSimPlugin
|
||||
if (myself.isNull()) { return; }
|
||||
if (myself->m_traceAutoTs == ts)
|
||||
{
|
||||
CLogMessage(this).warning("Auto trace id off");
|
||||
myself->m_traceAutoTs = -1;
|
||||
}
|
||||
});
|
||||
@@ -581,7 +599,7 @@ namespace BlackSimPlugin
|
||||
}
|
||||
}
|
||||
|
||||
void CSimulatorFsxCommon::triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionRemoteAircraftSimData &remoteAircraftData)
|
||||
void CSimulatorFsxCommon::triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionPosData &remoteAircraftData)
|
||||
{
|
||||
if (this->isShuttingDownOrDisconnected()) { return; }
|
||||
QPointer<CSimulatorFsxCommon> myself(this);
|
||||
@@ -592,7 +610,7 @@ namespace BlackSimPlugin
|
||||
});
|
||||
}
|
||||
|
||||
void CSimulatorFsxCommon::updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionRemoteAircraftSimData &remoteAircraftData)
|
||||
void CSimulatorFsxCommon::updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionPosData &remoteAircraftData)
|
||||
{
|
||||
if (this->isShuttingDownOrDisconnected()) { return; }
|
||||
|
||||
@@ -627,7 +645,7 @@ namespace BlackSimPlugin
|
||||
}
|
||||
}
|
||||
|
||||
void CSimulatorFsxCommon::updateProbeFromSimulator(const CCallsign &callsign, const DataDefinitionRemoteAircraftSimData &remoteAircraftData)
|
||||
void CSimulatorFsxCommon::updateProbeFromSimulator(const CCallsign &callsign, const DataDefinitionPosData &remoteAircraftData)
|
||||
{
|
||||
const CElevationPlane elevation(remoteAircraftData.latitudeDeg, remoteAircraftData.longitudeDeg, remoteAircraftData.elevationFt, CElevationPlane::singlePointRadius());
|
||||
this->callbackReceivedRequestedElevation(elevation, callsign);
|
||||
@@ -721,8 +739,8 @@ namespace BlackSimPlugin
|
||||
simObject.setConfirmedAdded(true);
|
||||
|
||||
// P3D also has SimConnect_AIReleaseControlEx which also allows to destroy the aircraft
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimData();
|
||||
const DWORD objectId = simObject.getObjectId();
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectMisc);
|
||||
HRESULT hr = SimConnect_AIReleaseControl(m_hSimConnect, objectId, requestId);
|
||||
hr += SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFreezeLat, 1, SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY);
|
||||
hr += SimConnect_TransmitClientEvent(m_hSimConnect, objectId, EventFreezeAlt, 1, SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY);
|
||||
@@ -826,14 +844,14 @@ namespace BlackSimPlugin
|
||||
if (simObject.isPendingRemoved())
|
||||
{
|
||||
// good case, object has been removed
|
||||
// we can remove the sim object
|
||||
// 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
|
||||
// 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;
|
||||
@@ -959,8 +977,8 @@ namespace BlackSimPlugin
|
||||
Q_ASSERT_X(m_dispatchProc, Q_FUNC_INFO, "Missing DispatchProc");
|
||||
|
||||
// statistics
|
||||
m_dispatchLastReceiveId = SIMCONNECT_RECV_ID_NULL;
|
||||
m_dispatchLastRequest = CSimConnectDefinitions::RequestEndMarker;
|
||||
m_dispatchReceiveIdLast = SIMCONNECT_RECV_ID_NULL;
|
||||
m_dispatchRequestIdLast = CSimConnectDefinitions::RequestEndMarker;
|
||||
const qint64 start = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// process
|
||||
@@ -972,8 +990,8 @@ namespace BlackSimPlugin
|
||||
if (m_dispatchMaxTimeMs < m_dispatchTimeMs)
|
||||
{
|
||||
m_dispatchMaxTimeMs = m_dispatchTimeMs;
|
||||
m_dispatchMaxTimeReceiveId = m_dispatchLastReceiveId;
|
||||
m_dispatchMaxTimeRequest = m_dispatchLastRequest;
|
||||
m_dispatchReceiveIdMaxTime = m_dispatchReceiveIdLast;
|
||||
m_dispatchRequestIdMaxTime = m_dispatchRequestIdLast;
|
||||
}
|
||||
|
||||
// error handling
|
||||
@@ -1022,7 +1040,7 @@ namespace BlackSimPlugin
|
||||
m_addPendingSimObjTimer.start(AddPendingAircraftIntervalMs); // restart
|
||||
|
||||
const bool hasPendingAdded = m_simConnectObjects.containsPendingAdded();
|
||||
bool canAdd = m_simSimulating && m_simConnected && !hasPendingAdded;
|
||||
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())
|
||||
@@ -1103,7 +1121,7 @@ namespace BlackSimPlugin
|
||||
|
||||
// FSX/P3D adding
|
||||
bool adding = false; // will be added flag
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimData();
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimObjAircraft(); // add
|
||||
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))); }
|
||||
@@ -1136,7 +1154,7 @@ namespace BlackSimPlugin
|
||||
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->obtainRequestIdForProbe();
|
||||
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);
|
||||
@@ -1149,10 +1167,10 @@ namespace BlackSimPlugin
|
||||
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 simObj(CSimConnectObject::Probe);
|
||||
simObj.setRequestId(requestId);
|
||||
simObj.setAircraft(pseudoAircraft);
|
||||
m_simConnectProbes.insert(cs, simObj);
|
||||
CSimConnectObject simObject(CSimConnectObject::TerrainProbe);
|
||||
simObject.setRequestId(requestId);
|
||||
simObject.setAircraft(pseudoAircraft);
|
||||
m_simConnectProbes.insert(cs, simObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1198,7 +1216,7 @@ namespace BlackSimPlugin
|
||||
if (this->showDebugLogMessage()) { this->debugLogMessage(Q_FUNC_INFO, QString("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 = this->obtainRequestIdForSimData();
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectRemove);
|
||||
const HRESULT result = SimConnect_AIRemoveObject(m_hSimConnect, static_cast<SIMCONNECT_OBJECT_ID>(simObject.getObjectId()), requestId);
|
||||
if (result == S_OK)
|
||||
{
|
||||
@@ -1309,7 +1327,7 @@ namespace BlackSimPlugin
|
||||
}
|
||||
|
||||
// facility
|
||||
SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimData();
|
||||
SIMCONNECT_DATA_REQUEST_ID requestId = static_cast<SIMCONNECT_DATA_REQUEST_ID>(CSimConnectDefinitions::RequestFacility);
|
||||
hr += SimConnect_SubscribeToFacilities(m_hSimConnect, SIMCONNECT_FACILITY_LIST_TYPE_AIRPORT, requestId);
|
||||
if (hr != S_OK)
|
||||
{
|
||||
@@ -1397,7 +1415,7 @@ namespace BlackSimPlugin
|
||||
if (hr == S_OK)
|
||||
{
|
||||
this->rememberLastSent(result); // remember
|
||||
if (this->isTracingSendId()) { this->traceSendId(objectId, Q_FUNC_INFO); }
|
||||
if (this->isTracingSendId()) { this->traceSendId(objectId, Q_FUNC_INFO, simObject.toQString()); }
|
||||
this->removedClampedLog(callsign);
|
||||
}
|
||||
else
|
||||
@@ -1658,8 +1676,9 @@ namespace BlackSimPlugin
|
||||
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<SIMCONNECT_DATA_REQUEST_ID>(simObject.getRequestId(CSimConnectDefinitions::SimObjectPositionData));
|
||||
const HRESULT result = SimConnect_RequestDataOnSimObject(
|
||||
m_hSimConnect, simObject.getRequestId() + RequestSimDataOffset,
|
||||
m_hSimConnect, reqId,
|
||||
CSimConnectDefinitions::DataRemoteAircraftGetPosition,
|
||||
simObject.getObjectId(), period);
|
||||
|
||||
@@ -1681,20 +1700,21 @@ namespace BlackSimPlugin
|
||||
if (m_simConnectProbes.countConfirmedAdded() < 1) { return false; }
|
||||
if (!m_simConnectObjects.contains(callsign)) { return false; } // removed in meantime
|
||||
|
||||
const DWORD id = this->obtainRequestIdForProbe();
|
||||
const DWORD objectId = m_simConnectProbes.values().front().getObjectId();
|
||||
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 = SimConnect_RequestDataOnSimObject(
|
||||
m_hSimConnect, id,
|
||||
m_hSimConnect, requestId,
|
||||
CSimConnectDefinitions::DataRemoteAircraftGetPosition,
|
||||
objectId, SIMCONNECT_PERIOD_ONCE);
|
||||
|
||||
if (result == S_OK)
|
||||
{
|
||||
if (this->isTracingSendId()) { this->traceSendId(id, Q_FUNC_INFO); }
|
||||
m_pendingProbeRequests.insert(id, callsign);
|
||||
if (this->isTracingSendId()) { this->traceSendId(requestId, Q_FUNC_INFO); }
|
||||
m_pendingProbeRequests.insert(requestId, callsign);
|
||||
return true;
|
||||
}
|
||||
CLogMessage(this).error("Cannot request terrain probe data for id '%1' ''%2") << id << callsign.asString();
|
||||
CLogMessage(this).error("Cannot request terrain probe data for id '%1' ''%2") << requestId << callsign.asString();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1706,8 +1726,9 @@ namespace BlackSimPlugin
|
||||
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 = SimConnect_RequestDataOnSimObject(
|
||||
m_hSimConnect, simObject.getRequestId() + RequestLightsOffset,
|
||||
m_hSimConnect, requestId,
|
||||
CSimConnectDefinitions::DataRemoteAircraftLights, simObject.getObjectId(),
|
||||
SIMCONNECT_PERIOD_SECOND);
|
||||
if (result == S_OK)
|
||||
@@ -1725,14 +1746,16 @@ namespace BlackSimPlugin
|
||||
if (!m_hSimConnect) { return false; }
|
||||
|
||||
// stop by setting SIMCONNECT_PERIOD_NEVER
|
||||
SIMCONNECT_DATA_REQUEST_ID requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectPositionData);
|
||||
HRESULT result = SimConnect_RequestDataOnSimObject(
|
||||
m_hSimConnect, simObject.getRequestId() + RequestSimDataOffset,
|
||||
m_hSimConnect, requestId,
|
||||
CSimConnectDefinitions::DataRemoteAircraftGetPosition,
|
||||
simObject.getObjectId(), SIMCONNECT_PERIOD_NEVER);
|
||||
if (result == S_OK) { if (this->isTracingSendId()) { this->traceSendId(simObject.getObjectId(), Q_FUNC_INFO, "Position");} }
|
||||
|
||||
requestId = simObject.getRequestId(CSimConnectDefinitions::SimObjectLights);
|
||||
result = SimConnect_RequestDataOnSimObject(
|
||||
m_hSimConnect, simObject.getRequestId() + RequestLightsOffset,
|
||||
m_hSimConnect, requestId,
|
||||
CSimConnectDefinitions::DataRemoteAircraftLights, simObject.getObjectId(),
|
||||
SIMCONNECT_PERIOD_NEVER);
|
||||
if (result == S_OK) { if (this->isTracingSendId()) { this->traceSendId(simObject.getObjectId(), Q_FUNC_INFO, "Lights");} }
|
||||
@@ -1760,7 +1783,7 @@ namespace BlackSimPlugin
|
||||
m_simSimulating = false;
|
||||
m_syncDeferredCounter = 0;
|
||||
m_skipCockpitUpdateCycles = 0;
|
||||
m_requestIdSimData = static_cast<SIMCONNECT_DATA_REQUEST_ID>(RequestIdSimDataStart);
|
||||
m_requestIdSimObjAircraft = static_cast<SIMCONNECT_DATA_REQUEST_ID>(RequestSimObjAircraftStart);
|
||||
m_dispatchErrors = 0;
|
||||
m_receiveExceptionCount = 0;
|
||||
m_sendIdTraces.clear();
|
||||
@@ -1829,18 +1852,18 @@ namespace BlackSimPlugin
|
||||
{
|
||||
if (m_simConnectProbes.isEmpty()) { return 0; }
|
||||
int c = 0;
|
||||
for (const CSimConnectObject &simObject : m_simConnectProbes.values())
|
||||
for (const CSimConnectObject &probeSimObject : m_simConnectProbes.values())
|
||||
{
|
||||
if (!simObject.isConfirmedAdded()) { continue; }
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForProbe();
|
||||
const HRESULT result = SimConnect_AIRemoveObject(m_hSimConnect, static_cast<SIMCONNECT_OBJECT_ID>(simObject.getObjectId()), requestId);
|
||||
if (!probeSimObject.isConfirmedAdded()) { continue; }
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = probeSimObject.getRequestId(CSimConnectDefinitions::SimObjectRemove);
|
||||
const HRESULT result = SimConnect_AIRemoveObject(m_hSimConnect, static_cast<SIMCONNECT_OBJECT_ID>(probeSimObject.getObjectId()), requestId);
|
||||
if (result == S_OK)
|
||||
{
|
||||
c++;
|
||||
}
|
||||
else
|
||||
{
|
||||
CLogMessage(this).warning("Removing probe '%1' from simulator failed") << simObject.getObjectId();
|
||||
CLogMessage(this).warning("Removing probe '%1' from simulator failed") << probeSimObject.getObjectId();
|
||||
}
|
||||
}
|
||||
m_simConnectProbes.clear();
|
||||
@@ -1880,6 +1903,39 @@ namespace BlackSimPlugin
|
||||
return QString::fromLatin1(fsxChar, size);
|
||||
}
|
||||
|
||||
QString CSimulatorFsxCommon::requestIdToString(DWORD requestId)
|
||||
{
|
||||
if (requestId <= CSimConnectDefinitions::RequestEndMarker)
|
||||
{
|
||||
return CSimConnectDefinitions::requestToString(static_cast<CSimConnectDefinitions::Request>(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 CSimulatorFsxCommon::unitTestRequestId(CSimConnectObject::SimObjectType type)
|
||||
{
|
||||
int start;
|
||||
int end;
|
||||
switch (type)
|
||||
{
|
||||
case CSimConnectObject::TerrainProbe:
|
||||
start = RequestSimObjTerrainProbeStart; end = RequestSimObjTerrainProbeEnd;
|
||||
break;
|
||||
case CSimConnectObject::Aircraft:
|
||||
default:
|
||||
start = RequestSimObjAircraftStart; end = RequestSimObjAircraftEnd;
|
||||
break;
|
||||
}
|
||||
|
||||
const int id = CMathUtils::randomInteger(start, end);
|
||||
return static_cast<DWORD>(id);
|
||||
}
|
||||
|
||||
CCallsignSet CSimulatorFsxCommon::physicallyRemoveAircraftNotInProvider()
|
||||
{
|
||||
const CCallsignSet toBeRemoved(getCallsignsMissingInProvider());
|
||||
|
||||
@@ -145,6 +145,19 @@ namespace BlackSimPlugin
|
||||
//! \copydoc BlackCore::CSimulatorCommon::resetAircraftStatistics
|
||||
virtual void resetAircraftStatistics() override;
|
||||
|
||||
//! 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; }
|
||||
|
||||
//! Sub request type
|
||||
static CSimConnectDefinitions::SimObjectRequest requestToSimObjectRequest(DWORD requestId);
|
||||
|
||||
//! Random unit text request id
|
||||
//! \private
|
||||
static DWORD unitTestRequestId(CSimConnectObject::SimObjectType type);
|
||||
|
||||
protected:
|
||||
//! SimConnect callback
|
||||
//! \note all tasks called in this function (i.e, all called functions) must perform fast or shall be called asynchronously
|
||||
@@ -179,30 +192,21 @@ namespace BlackSimPlugin
|
||||
//! @}
|
||||
virtual bool parseDetails(const BlackMisc::CSimpleCommandParser &parser) override;
|
||||
|
||||
//! Get new request id, overflow safe
|
||||
SIMCONNECT_DATA_REQUEST_ID obtainRequestIdForSimData();
|
||||
|
||||
//! Get new request id, overflow safe
|
||||
SIMCONNECT_DATA_REQUEST_ID obtainRequestIdForProbe();
|
||||
|
||||
//! Trigger tracing ids for some while
|
||||
bool triggerAutoTraceSendId();
|
||||
|
||||
//! Request for sim data (request in range of sim data)?
|
||||
static bool isRequestForSimData(DWORD requestId) { return requestId >= (RequestIdSimDataStart + RequestSimDataOffset) && requestId < (RequestIdSimDataStart + RequestSimDataOffset + MaxSimObjects); }
|
||||
//! Callsign for pending request
|
||||
BlackMisc::Aviation::CCallsign getCallsignForPendingProbeRequests(DWORD requestId, bool remove);
|
||||
|
||||
//! Request for lights (request in range of lights)?
|
||||
static bool isRequestForLights(DWORD requestId) { return requestId >= (RequestIdSimDataStart + RequestLightsOffset) && requestId < (RequestIdSimDataStart + RequestLightsOffset + MaxSimObjects); }
|
||||
//! Get new request id, overflow safe
|
||||
SIMCONNECT_DATA_REQUEST_ID obtainRequestIdForSimObjAircraft();
|
||||
|
||||
//! Request for probe (elevation)?
|
||||
static bool isRequestForProbe(DWORD requestId) { return requestId >= RequestIdTerrainProbeStart && requestId <= RequestIdTerrainProbeEnd; }
|
||||
//! Get new request id, overflow safe
|
||||
SIMCONNECT_DATA_REQUEST_ID obtainRequestIdForSimObjTerrainProbe();
|
||||
|
||||
//! Register help
|
||||
static void registerHelp();
|
||||
|
||||
//! Callsign for pending request
|
||||
BlackMisc::Aviation::CCallsign getCallsignForPendingProbeRequests(DWORD requestId, bool remove);
|
||||
|
||||
static constexpr qint64 AutoTraceOffsetMs = 10 * 1000; //!< how long do we trace?
|
||||
HANDLE m_hSimConnect = nullptr; //!< handle to SimConnect object
|
||||
DispatchProc m_dispatchProc = &CSimulatorFsxCommon::SimConnectProc; //!< called function for dispatch, can be overriden by specialized P3D function
|
||||
@@ -309,13 +313,13 @@ namespace BlackSimPlugin
|
||||
|
||||
//! Call CSimulatorFsxCommon::updateRemoteAircraftFromSimulator asynchronously
|
||||
//! \remark can help not to send SimConnect data in event loop
|
||||
void triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionRemoteAircraftSimData &remoteAircraftData);
|
||||
void triggerUpdateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionPosData &remoteAircraftData);
|
||||
|
||||
//! Remote aircraft data sent from simulator
|
||||
void updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionRemoteAircraftSimData &remoteAircraftData);
|
||||
void updateRemoteAircraftFromSimulator(const CSimConnectObject &simObject, const DataDefinitionPosData &remoteAircraftData);
|
||||
|
||||
//! Probe data sent from simulator
|
||||
void updateProbeFromSimulator(const BlackMisc::Aviation::CCallsign &callsign, const DataDefinitionRemoteAircraftSimData &remoteAircraftData);
|
||||
void updateProbeFromSimulator(const BlackMisc::Aviation::CCallsign &callsign, const DataDefinitionPosData &remoteAircraftData);
|
||||
|
||||
//! Update from SB client area
|
||||
//! \threadsafe
|
||||
@@ -404,16 +408,23 @@ namespace BlackSimPlugin
|
||||
static QString fsxCharToQString(const char *fsxChar, int size = -1);
|
||||
|
||||
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 MaxSimObjects = 10000; //!< max.number of SimObjects at the same time
|
||||
static constexpr int MaxSendIdTraces = 10000; //!< max.traces of send id
|
||||
static constexpr int RequestIdSimDataStart = static_cast<int>(CSimConnectDefinitions::RequestEndMarker);
|
||||
static constexpr int RequestIdSimDataEnd = RequestIdSimDataStart + MaxSimObjects - 1;
|
||||
static constexpr int RequestSimDataOffset = 0 * MaxSimObjects; //!< range for sim data requests
|
||||
static constexpr int RequestLightsOffset = 1 * MaxSimObjects; //!< range for lights
|
||||
static constexpr int RequestIdTerrainProbeStart = 2 * MaxSimObjects + RequestIdSimDataEnd + 1; //!< range for terrain probe
|
||||
static constexpr int RequestIdTerrainProbeEnd = (RequestIdTerrainProbeStart + 1000) - 1;
|
||||
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 int MaxSimObjAircraft = 10000; //!< max.number of SimObjects at the same time
|
||||
static constexpr int MaxSimObjProbes = 100; //!< max. probes
|
||||
|
||||
// -- range for sim data, each sim object will get its own request id and use the offset ranges
|
||||
static constexpr int RequestSimObjAircraftStart = static_cast<int>(CSimConnectDefinitions::RequestEndMarker);
|
||||
static constexpr int RequestSimObjAircraftEnd = RequestSimObjAircraftStart - 1 + MaxSimObjAircraft;
|
||||
static constexpr int RequestSimObjAircraftRangeEnd = RequestSimObjAircraftStart - 1 + static_cast<int>(CSimConnectDefinitions::SimObjectEndMarker) * MaxSimObjAircraft;
|
||||
|
||||
// -- range for probe data, each probe object will get its own request id and use the offset ranges
|
||||
static constexpr int RequestSimObjTerrainProbeStart = RequestSimObjAircraftRangeEnd + 1 ;
|
||||
static constexpr int RequestSimObjTerrainProbeEnd = RequestSimObjTerrainProbeStart - 1 + MaxSimObjProbes;
|
||||
static constexpr int RequestSimObjTerrainProbeRangeEnd = RequestSimObjTerrainProbeStart - 1 + static_cast<int>(CSimConnectDefinitions::SimObjectEndMarker) * MaxSimObjProbes;
|
||||
|
||||
// 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
|
||||
@@ -432,16 +443,16 @@ namespace BlackSimPlugin
|
||||
// tracing dispatch performance
|
||||
int m_dispatchErrors = 0; //!< number of dispatched failed, \sa dispatch
|
||||
int m_dispatchProcCount = 0; //!< number of dispatchProc counts
|
||||
int m_dispatchProcEmpty = 0; //!< number dispatchProc doing nothing
|
||||
int m_dispatchProcEmptyCount = 0; //!< number dispatchProc doing nothing
|
||||
qint64 m_dispatchTimeMs = -1;
|
||||
qint64 m_dispatchMaxTimeMs = -1;
|
||||
qint64 m_dispatchProcTimeMs = -1;
|
||||
qint64 m_dispatchProcMaxTimeMs = -1;
|
||||
|
||||
SIMCONNECT_RECV_ID m_dispatchLastReceiveId = SIMCONNECT_RECV_ID_NULL; //!< last receive id from dispatching
|
||||
SIMCONNECT_RECV_ID m_dispatchMaxTimeReceiveId = SIMCONNECT_RECV_ID_NULL; //!< receive id corresponding to max.time
|
||||
CSimConnectDefinitions::Request m_dispatchLastRequest = CSimConnectDefinitions::RequestEndMarker; //!< request id if any
|
||||
CSimConnectDefinitions::Request m_dispatchMaxTimeRequest = CSimConnectDefinitions::RequestEndMarker; //!< request id corresponding to max.time
|
||||
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 = -1; //!< request id if any
|
||||
DWORD m_dispatchRequestIdMaxTime = -1; //!< max.time request
|
||||
|
||||
// sending via SimConnect
|
||||
QList<TraceFsxSendId> m_sendIdTraces; //!< Send id traces for debugging
|
||||
@@ -450,10 +461,19 @@ namespace BlackSimPlugin
|
||||
|
||||
// objects
|
||||
CSimConnectObjects m_simConnectObjectsPositionAndPartsTraces; //!< position/parts received, but object not yet added, excluded, disabled etc.
|
||||
SIMCONNECT_DATA_REQUEST_ID m_requestIdSimData = static_cast<SIMCONNECT_DATA_REQUEST_ID>(RequestIdSimDataStart); //!< request id, use obtainRequestIdForSimData() to get id
|
||||
SIMCONNECT_DATA_REQUEST_ID m_requestIdProbe = static_cast<SIMCONNECT_DATA_REQUEST_ID>(RequestIdTerrainProbeStart); //!< request id, use obtainRequestIdForProbe() to get id
|
||||
SIMCONNECT_DATA_REQUEST_ID m_requestIdSimObjAircraft = static_cast<SIMCONNECT_DATA_REQUEST_ID>(RequestSimObjAircraftStart); //!< request id, use obtainRequestIdForSimObjAircraft to get id
|
||||
SIMCONNECT_DATA_REQUEST_ID m_requestIdSimObjTerrainProbe = static_cast<SIMCONNECT_DATA_REQUEST_ID>(RequestSimObjTerrainProbeStart); //!< request id, use obtainRequestIdForSimObjTerrainProbe to get id
|
||||
BlackMisc::Simulation::CSimulatedAircraftList m_addPendingAircraft; //!< aircraft awaiting to be added
|
||||
QTimer m_addPendingSimObjTimer; //!< updating of SimObjects awaiting to be added
|
||||
|
||||
//! Request id to string
|
||||
static QString requestIdToString(DWORD requestId);
|
||||
|
||||
public:
|
||||
//! Offsets @{
|
||||
static DWORD offsetSimObjAircraft(CSimConnectDefinitions::SimObjectRequest req) { return MaxSimObjAircraft * req; }
|
||||
static DWORD offsetSimObjTerrainProbe(CSimConnectDefinitions::SimObjectRequest req) { return MaxSimObjProbes * req; }
|
||||
//! @}
|
||||
};
|
||||
|
||||
//! Listener for FSX
|
||||
|
||||
@@ -8,15 +8,17 @@
|
||||
*/
|
||||
|
||||
#include "simulatorfsxcommon.h"
|
||||
#include "blackcore/application.h"
|
||||
#include "simconnectdatadefinition.h"
|
||||
#include "blackcore/application.h"
|
||||
#include "blackmisc/simulation/fscommon/bcdconversions.h"
|
||||
#include "blackmisc/simulation/fsx/simconnectutilities.h"
|
||||
#include "blackmisc/simulation/simulatorplugininfo.h"
|
||||
#include "blackmisc/aviation/airportlist.h"
|
||||
#include "blackmisc/logmessage.h"
|
||||
#include "blackconfig/buildconfig.h"
|
||||
|
||||
using namespace BlackCore;
|
||||
using namespace BlackConfig;
|
||||
using namespace BlackMisc;
|
||||
using namespace BlackMisc::Simulation;
|
||||
using namespace BlackMisc::Aviation;
|
||||
@@ -39,7 +41,7 @@ namespace BlackSimPlugin
|
||||
const qint64 procTimeStart = QDateTime::currentMSecsSinceEpoch();
|
||||
CSimulatorFsxCommon *simulatorFsxP3D = static_cast<CSimulatorFsxCommon *>(pContext);
|
||||
const SIMCONNECT_RECV_ID recvId = static_cast<SIMCONNECT_RECV_ID>(pData->dwID);
|
||||
simulatorFsxP3D->m_dispatchLastReceiveId = recvId;
|
||||
simulatorFsxP3D->m_dispatchReceiveIdLast = recvId;
|
||||
simulatorFsxP3D->m_dispatchProcCount++;
|
||||
switch (recvId)
|
||||
{
|
||||
@@ -67,7 +69,7 @@ namespace BlackSimPlugin
|
||||
switch (exceptionId)
|
||||
{
|
||||
case SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE: break;
|
||||
case SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID: 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;
|
||||
}
|
||||
@@ -176,10 +178,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;
|
||||
|
||||
const DWORD objectId = event->dwObjectID;
|
||||
bool success = false;
|
||||
if (CSimulatorFsxCommon::isRequestForProbe(requestId))
|
||||
simulatorFsxP3D->m_dispatchRequestIdLast = requestId;
|
||||
|
||||
if (CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId))
|
||||
{
|
||||
success = simulatorFsxP3D->setSimConnectProbeId(requestId, objectId);
|
||||
if (!success) { break; } // not an request ID of ours
|
||||
@@ -194,7 +197,7 @@ namespace BlackSimPlugin
|
||||
CLogMessage(simulatorFsxP3D).error("Cannot add probe '%1' id: %2") << simObject.getCallsign() << objectId;
|
||||
}
|
||||
}
|
||||
else if (CSimulatorFsxCommon::isRequestForSimData(requestId))
|
||||
else if (CSimulatorFsxCommon::isRequestForSimObjAircraft(requestId))
|
||||
{
|
||||
success = simulatorFsxP3D->setSimConnectObjectId(requestId, objectId);
|
||||
if (!success) { break; } // not an request ID of ours
|
||||
@@ -219,13 +222,13 @@ namespace BlackSimPlugin
|
||||
{
|
||||
const SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA *) pData;
|
||||
const DWORD requestId = pObjData->dwRequestID;
|
||||
simulatorFsxP3D->m_dispatchRequestIdLast = requestId;
|
||||
|
||||
switch (requestId)
|
||||
{
|
||||
case CSimConnectDefinitions::RequestOwnAircraft:
|
||||
{
|
||||
static_assert(sizeof(DataDefinitionOwnAircraft) == 31 * sizeof(double), "DataDefinitionOwnAircraft has an incorrect size.");
|
||||
simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestOwnAircraft;
|
||||
const DataDefinitionOwnAircraft *ownAircaft = (DataDefinitionOwnAircraft *)&pObjData->dwData;
|
||||
simulatorFsxP3D->updateOwnAircraftFromSimulator(*ownAircaft);
|
||||
break;
|
||||
@@ -233,14 +236,12 @@ namespace BlackSimPlugin
|
||||
case CSimConnectDefinitions::RequestOwnAircraftTitle:
|
||||
{
|
||||
const DataDefinitionOwnAircraftModel *dataDefinitionModel = (DataDefinitionOwnAircraftModel *) &pObjData->dwData;
|
||||
simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestOwnAircraftTitle;
|
||||
const CAircraftModel model(dataDefinitionModel->title, CAircraftModel::TypeOwnSimulatorModel);
|
||||
simulatorFsxP3D->reverseLookupAndUpdateOwnAircraftModel(model);
|
||||
break;
|
||||
}
|
||||
case CSimConnectDefinitions::RequestSimEnvironment:
|
||||
{
|
||||
simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestSimEnvironment;
|
||||
const DataDefinitionSimEnvironment *simEnv = (DataDefinitionSimEnvironment *) &pObjData->dwData;
|
||||
if (simulatorFsxP3D->isTimeSynchronized())
|
||||
{
|
||||
@@ -257,57 +258,82 @@ namespace BlackSimPlugin
|
||||
default:
|
||||
{
|
||||
const DWORD objectId = pObjData->dwObjectID;
|
||||
if (CSimulatorFsxCommon::isRequestForSimData(requestId))
|
||||
|
||||
if (CSimulatorFsxCommon::isRequestForSimObjAircraft(requestId))
|
||||
{
|
||||
static_assert(sizeof(DataDefinitionRemoteAircraftSimData) == 5 * sizeof(double), "DataDefinitionRemoteAircraftSimData has an incorrect size.");
|
||||
simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestRangeForSimData;
|
||||
const CSimConnectObject simObject = simulatorFsxP3D->getSimObjectForObjectId(objectId);
|
||||
if (!simObject.hasValidRequestAndObjectId()) { break; }
|
||||
const DataDefinitionRemoteAircraftSimData *remoteAircraftSimData = (DataDefinitionRemoteAircraftSimData *)&pObjData->dwData;
|
||||
// extra check, but ids should be the same
|
||||
if (objectId == simObject.getObjectId())
|
||||
const CSimConnectDefinitions::SimObjectRequest subRequest = CSimulatorFsxCommon::requestToSimObjectRequest(requestId);
|
||||
|
||||
if (subRequest == CSimConnectDefinitions::SimObjectPositionData)
|
||||
{
|
||||
simulatorFsxP3D->triggerUpdateRemoteAircraftFromSimulator(simObject, *remoteAircraftSimData);
|
||||
}
|
||||
}
|
||||
else if (CSimulatorFsxCommon::isRequestForProbe(requestId))
|
||||
{
|
||||
static_assert(sizeof(DataDefinitionRemoteAircraftSimData) == 5 * sizeof(double), "DataDefinitionRemoteAircraftSimData has an incorrect size.");
|
||||
simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestRangeForProbe;
|
||||
const CSimConnectObject probeObj = simulatorFsxP3D->getProbeForObjectId(objectId);
|
||||
if (!probeObj.hasValidRequestAndObjectId()) { break; }
|
||||
const DataDefinitionRemoteAircraftSimData *probeSimData = (DataDefinitionRemoteAircraftSimData *)&pObjData->dwData;
|
||||
// extra check, but ids should be the same
|
||||
if (objectId == probeObj.getObjectId())
|
||||
{
|
||||
const CCallsign cs = simulatorFsxP3D->m_pendingProbeRequests.value(requestId);
|
||||
if (cs.isEmpty()) { break; }
|
||||
simulatorFsxP3D->updateProbeFromSimulator(cs, *probeSimData);
|
||||
}
|
||||
}
|
||||
else if (CSimulatorFsxCommon::isRequestForLights(requestId))
|
||||
{
|
||||
static_assert(sizeof(DataDefinitionRemoteAircraftLights) == 8 * sizeof(double), "DataDefinitionRemoteAircraftLights has an incorrect size.");
|
||||
simulatorFsxP3D->m_dispatchLastRequest = CSimConnectDefinitions::RequestRangeForLights;
|
||||
const CSimConnectObject simObj = simulatorFsxP3D->getSimObjectForObjectId(objectId);
|
||||
if (!simObj.hasValidRequestAndObjectId()) { break; }
|
||||
const DataDefinitionRemoteAircraftLights *remoteAircraftLights = (DataDefinitionRemoteAircraftLights *)&pObjData->dwData;
|
||||
// extra check, but ids should be the same
|
||||
if (objectId == simObj.getObjectId())
|
||||
{
|
||||
const CCallsign callsign(simObj.getCallsign());
|
||||
const CAircraftLights lights(remoteAircraftLights->toLights()); // as in simulator
|
||||
simulatorFsxP3D->setCurrentLights(callsign, lights);
|
||||
if (simObj.getLightsAsSent().isNull())
|
||||
static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double), "DataDefinitionPosData has an incorrect size.");
|
||||
const DataDefinitionPosData *remoteAircraftSimData = reinterpret_cast<const DataDefinitionPosData *>(&pObjData->dwData);
|
||||
// extra check, but ids should be the same
|
||||
if (objectId == simObject.getObjectId())
|
||||
{
|
||||
// allows to compare for toggle
|
||||
simulatorFsxP3D->setLightsAsSent(callsign, lights);
|
||||
simulatorFsxP3D->triggerUpdateRemoteAircraftFromSimulator(simObject, *remoteAircraftSimData);
|
||||
}
|
||||
} // position
|
||||
else if (subRequest == CSimConnectDefinitions::SimObjectLights)
|
||||
{
|
||||
static_assert(sizeof(DataDefinitionRemoteAircraftLights) == 8 * sizeof(double), "DataDefinitionRemoteAircraftLights has an incorrect size.");
|
||||
const DataDefinitionRemoteAircraftLights *remoteAircraftLights = reinterpret_cast<const DataDefinitionRemoteAircraftLights *>(&pObjData->dwData);
|
||||
// extra check, but ids should be the same
|
||||
if (objectId == simObject.getObjectId())
|
||||
{
|
||||
const CCallsign callsign(simObject.getCallsign());
|
||||
const CAircraftLights lights(remoteAircraftLights->toLights()); // as in simulator
|
||||
simulatorFsxP3D->setCurrentLights(callsign, lights);
|
||||
if (simObject.getLightsAsSent().isNull())
|
||||
{
|
||||
// allows to compare for toggle
|
||||
simulatorFsxP3D->setLightsAsSent(callsign, lights);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} // lights
|
||||
else
|
||||
{
|
||||
if (CBuildConfig::isLocalDeveloperDebugBuild())
|
||||
{
|
||||
CLogMessage(simulatorFsxP3D).error("Unknown subrequest (aircraft): '%1' %2")
|
||||
<< CSimConnectDefinitions::simObjectRequestToString(subRequest)
|
||||
<< simObject.toQString();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId))
|
||||
{
|
||||
const CSimConnectObject probeObj = simulatorFsxP3D->getProbeForObjectId(objectId);
|
||||
if (!probeObj.hasValidRequestAndObjectId()) { break; }
|
||||
const CSimConnectDefinitions::SimObjectRequest subRequest = CSimulatorFsxCommon::requestToSimObjectRequest(requestId);
|
||||
|
||||
if (subRequest == CSimConnectDefinitions::SimObjectPositionData)
|
||||
{
|
||||
static_assert(sizeof(DataDefinitionPosData) == 5 * sizeof(double), "DataDefinitionRemoteAircraftSimData has an incorrect size.");
|
||||
const DataDefinitionPosData *probeSimData = (DataDefinitionPosData *)&pObjData->dwData;
|
||||
// extra check, but ids should be the same
|
||||
if (objectId == probeObj.getObjectId())
|
||||
{
|
||||
const CCallsign cs = simulatorFsxP3D->m_pendingProbeRequests.value(requestId);
|
||||
if (cs.isEmpty()) { break; }
|
||||
simulatorFsxP3D->updateProbeFromSimulator(cs, *probeSimData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CBuildConfig::isLocalDeveloperDebugBuild())
|
||||
{
|
||||
CLogMessage(simulatorFsxP3D).error("Unknown subrequest (probe): '%1' %2")
|
||||
<< CSimConnectDefinitions::simObjectRequestToString(subRequest)
|
||||
<< probeObj.toQString();
|
||||
}
|
||||
}
|
||||
} // probe
|
||||
break;
|
||||
}
|
||||
break;
|
||||
break; // default (SIMCONNECT_RECV_ID_SIMOBJECT_DATA)
|
||||
}
|
||||
break; // SIMCONNECT_RECV_ID_SIMOBJECT_DATA
|
||||
}
|
||||
@@ -356,7 +382,7 @@ namespace BlackSimPlugin
|
||||
break; // SIMCONNECT_RECV_ID_EVENT_FILENAME
|
||||
}
|
||||
default:
|
||||
simulatorFsxP3D->m_dispatchProcEmpty++;
|
||||
simulatorFsxP3D->m_dispatchProcEmptyCount++;
|
||||
break;
|
||||
} // main switch
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace BlackSimPlugin
|
||||
// https://www.prepar3d.com/SDKv4/sdk/simconnect_api/references/structures_and_enumerations.html#SIMCONNECT_RECV_GROUND_INFO
|
||||
const SIMCONNECT_RECV_GROUND_INFO *pObjData = (SIMCONNECT_RECV_GROUND_INFO *) pData;
|
||||
const DWORD requestId = pObjData->dwRequestID;
|
||||
if (!CSimulatorFsxCommon::isRequestForProbe(requestId)) { break; }
|
||||
if (!CSimulatorFsxCommon::isRequestForSimObjTerrainProbe(requestId)) { break; }
|
||||
// valid elevation request
|
||||
// https://www.prepar3d.com/SDKv4/sdk/simconnect_api/references/structures_and_enumerations.html#SIMCONNECT_DATA_GROUND_INFO
|
||||
if (pObjData->dwArraySize != 1) { break; }
|
||||
@@ -100,7 +100,7 @@ namespace BlackSimPlugin
|
||||
const DWORD dwGridWidth = 1.0;
|
||||
const DWORD dwGridHeight = 1.0;
|
||||
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForProbe();
|
||||
const SIMCONNECT_DATA_REQUEST_ID requestId = this->obtainRequestIdForSimObjTerrainProbe(); // P3D we use new request id each time (no simobject)
|
||||
|
||||
// returns SIMCONNECT_RECV_GROUND_INFO -> SIMCONNECT_DATA_GROUND_INFO
|
||||
const HRESULT hr = SimConnect_RequestGroundInfo(
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace BlackSimPlugin
|
||||
//! \copydoc BlackMisc::Simulation::ISimulationEnvironmentProvider::requestElevation
|
||||
virtual bool requestElevation(const BlackMisc::Geo::ICoordinateGeodetic &reference, const BlackMisc::Aviation::CCallsign &callsign) override;
|
||||
|
||||
//! \copydoc ISimulator::followAircraft
|
||||
//! \copydoc BlackCore::ISimulator::followAircraft
|
||||
virtual bool followAircraft(const BlackMisc::Aviation::CCallsign &callsign) override;
|
||||
|
||||
protected:
|
||||
|
||||
Reference in New Issue
Block a user