Files
pilotclient/src/xswiftbus/traffic.cpp
Mat Sutcliffe acdbf9d43d Ref T470 CTraffic::getRemoteAircraftData uses NaN to represent
an unknown vertical offset.
2019-02-22 20:23:30 +00:00

964 lines
38 KiB
C++

/* Copyright (C) 2013
* swift Project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
//! \cond PRIVATE
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "traffic.h"
#include "utils.h"
#include "XPMPMultiplayer.h"
#include "XPMPPlaneRenderer.h"
#include "XPLMGraphics.h"
#include <XPLM/XPLMProcessing.h>
#include <XPLM/XPLMUtilities.h>
#include <XPLM/XPLMPlanes.h>
#include <XPLM/XPLMPlugin.h>
#include <cassert>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <limits>
// clazy:excludeall=reserve-candidates
namespace XSwiftBus
{
CTraffic::Plane::Plane(void *id_, const std::string &callsign_, const std::string &aircraftIcao_, const std::string &airlineIcao_, const std::string &livery_, const std::string &modelName_)
: id(id_), callsign(callsign_), aircraftIcao(aircraftIcao_), airlineIcao(airlineIcao_), livery(livery_), modelName(modelName_)
{
std::memset(static_cast<void *>(&surfaces), 0, sizeof(surfaces));
surfaces.lights.bcnLights = surfaces.lights.landLights = surfaces.lights.navLights = surfaces.lights.strbLights = 1;
surfaces.size = sizeof(surfaces);
xpdr.size = sizeof(xpdr);
std::strncpy(label, callsign.c_str(), sizeof(label));
std::srand(static_cast<unsigned int>(std::time(nullptr)));
surfaces.lights.timeOffset = static_cast<uint16_t>(std::rand() % 0xffff);
}
CTraffic::CTraffic() :
CDBusObject(),
m_followPlaneViewNextCommand("org/swift-project/xswiftbus/follow_next_plane", "Changes plane view to follow next plane in sequence", [this] { followNextPlane(); }),
m_followPlaneViewPreviousCommand("org/swift-project/xswiftbus/follow_previous_plane", "Changes plane view to follow previous plane in sequence", [this] { followPreviousPlane(); })
{
XPLMRegisterDrawCallback(drawCallback, xplm_Phase_Airplanes, 1, this);
XPLMRegisterKeySniffer(spaceKeySniffer, 1, this);
}
CTraffic::~CTraffic()
{
XPLMUnregisterDrawCallback(drawCallback, xplm_Phase_Airplanes, 1, this);
cleanup();
}
bool s_legacyDataOK = true;
void CTraffic::setPlaneViewMenu(const CMenu &planeViewSubMenu)
{
m_followPlaneViewSubMenu = planeViewSubMenu;
}
void CTraffic::initLegacyData()
{
initXPlanePath();
auto dir = g_xplanePath + "Resources" + g_sep + "plugins" + g_sep + "xswiftbus" + g_sep + "LegacyData" + g_sep;
std::string csl = dir + "CSL";
std::string related = dir + "related.txt";
std::string doc8643 = dir + "Doc8643.txt";
std::string lights = dir + "lights.png";
auto err = XPMPMultiplayerInitLegacyData(csl.c_str(), related.c_str(), lights.c_str(), doc8643.c_str(),
"C172", preferences, preferences);
if (*err) { s_legacyDataOK = false; }
}
bool CTraffic::initialize()
{
if (! s_legacyDataOK) { return false; }
if (! m_initialized)
{
auto err = XPMPMultiplayerInit(preferences, preferences);
if (*err) { cleanup(); }
else { m_initialized = true; }
}
return m_initialized;
}
bool CTraffic::acquireMultiplayerPlanes(std::string *owner)
{
if (! m_enabledMultiplayer)
{
auto err = XPMPMultiplayerEnable();
if (*err)
{
cleanup();
}
else
{
m_enabledMultiplayer = true;
XPMPLoadPlanesIfNecessary();
}
}
int totalAircraft;
int activeAircraft;
XPLMPluginID controller;
XPLMCountAircraft(&totalAircraft, &activeAircraft, &controller);
char pluginName[256];
XPLMGetPluginInfo(controller, pluginName, nullptr, nullptr, nullptr);
*owner = std::string(pluginName);
return m_enabledMultiplayer;
}
void CTraffic::cleanup()
{
removeAllPlanes();
if (m_enabledMultiplayer)
{
m_enabledMultiplayer = false;
XPMPMultiplayerDisable();
}
if (m_initialized)
{
m_initialized = false;
XPMPMultiplayerCleanup();
}
}
void CTraffic::emitSimFrame()
{
if (m_emitSimFrame) { sendDBusSignal("simFrame"); }
m_emitSimFrame = !m_emitSimFrame;
}
void CTraffic::emitPlaneAdded(const std::string &callsign)
{
CDBusMessage signalPlaneAdded = CDBusMessage::createSignal(XSWIFTBUS_TRAFFIC_OBJECTPATH, XSWIFTBUS_TRAFFIC_INTERFACENAME, "remoteAircraftAdded");
signalPlaneAdded.beginArgumentWrite();
signalPlaneAdded.appendArgument(callsign);
sendDBusMessage(signalPlaneAdded);
}
void CTraffic::emitPlaneAddingFailed(const std::string &callsign)
{
CDBusMessage signalPlaneAddingFailed = CDBusMessage::createSignal(XSWIFTBUS_TRAFFIC_OBJECTPATH, XSWIFTBUS_TRAFFIC_INTERFACENAME, "remoteAircraftAddingFailed");
signalPlaneAddingFailed.beginArgumentWrite();
signalPlaneAddingFailed.appendArgument(callsign);
sendDBusMessage(signalPlaneAddingFailed);
}
void CTraffic::switchToFollowPlaneView(const std::string &callsign)
{
m_followPlaneViewCallsign = callsign;
/* This is the hotkey callback. First we simulate a joystick press and
* release to put us in 'free view 1'. This guarantees that no panels
* are showing and we are an external view. */
XPLMCommandButtonPress(xplm_joy_v_fr1);
XPLMCommandButtonRelease(xplm_joy_v_fr1);
/* Now we control the camera until the view changes. */
XPLMControlCamera(xplm_ControlCameraUntilViewChanges, orbitPlaneFunc, this);
}
void CTraffic::followNextPlane()
{
if (m_planesByCallsign.empty() || m_followPlaneViewCallsign.empty()) { return; }
auto callsignIt = std::find(m_followPlaneViewSequence.begin(), m_followPlaneViewSequence.end(), m_followPlaneViewCallsign);
// If we are not at the end, increase by one
if (callsignIt != m_followPlaneViewSequence.end()) { callsignIt++; }
// If we were already at the end or reached it now, start from the beginning
if (callsignIt == m_followPlaneViewSequence.end()) { callsignIt = m_followPlaneViewSequence.begin(); }
m_followPlaneViewCallsign = *callsignIt;
}
void CTraffic::followPreviousPlane()
{
if (m_planesByCallsign.empty() || m_followPlaneViewCallsign.empty()) { return; }
auto callsignIt = std::find(m_followPlaneViewSequence.rbegin(), m_followPlaneViewSequence.rend(), m_followPlaneViewCallsign);
// If we are not at the end, increase by one
if (callsignIt != m_followPlaneViewSequence.rend()) { callsignIt++; }
// If we were already at the end or reached it now, start from the beginning
if (callsignIt == m_followPlaneViewSequence.rend()) { callsignIt = m_followPlaneViewSequence.rbegin(); }
m_followPlaneViewCallsign = *callsignIt;
}
int g_maxPlanes = 100;
float g_drawDistance = 50.0f;
int CTraffic::preferences(const char *section, const char *name, int def)
{
if (strcmp(section, "planes") == 0 && strcmp(name, "max_full_count") == 0)
{
return g_maxPlanes;
}
else if (strcmp(section, "debug") == 0 && strcmp(name, "allow_obj8_async_load") == 0)
{
return true;
}
return def;
}
float CTraffic::preferences(const char *section, const char *name, float def)
{
if (strcmp(section, "planes") == 0 && strcmp(name, "full_distance") == 0)
{
return g_drawDistance;
}
return def;
}
bool CTraffic::loadPlanesPackage(const std::string &path)
{
initXPlanePath();
auto dir = g_xplanePath + "Resources" + g_sep + "plugins" + g_sep + "xswiftbus" + g_sep + "LegacyData" + g_sep;
std::string related = dir + "related.txt";
std::string doc8643 = dir + "Doc8643.txt";
auto err = XPMPLoadCSLPackage(path.c_str(), related.c_str(), doc8643.c_str());
if (*err) { return false; }
return true;
}
void CTraffic::setDefaultIcao(const std::string &defaultIcao)
{
XPMPSetDefaultPlaneICAO(defaultIcao.c_str());
}
void CTraffic::setDrawingLabels(bool drawing)
{
if (drawing)
{
XPMPEnableAircraftLabels();
}
else
{
XPMPDisableAircraftLabels();
}
}
bool CTraffic::isDrawingLabels() const
{
return XPMPDrawingAircraftLabels();
}
void CTraffic::setMaxPlanes(int planes)
{
g_maxPlanes = planes;
}
void CTraffic::setMaxDrawDistance(double nauticalMiles)
{
g_drawDistance = static_cast<float>(nauticalMiles);
}
void CTraffic::addPlane(const std::string &callsign, const std::string &modelName, const std::string &aircraftIcao, const std::string &airlineIcao, const std::string &livery)
{
auto planeIt = m_planesByCallsign.find(callsign);
if (planeIt != m_planesByCallsign.end()) { return; }
XPMPPlaneID id = nullptr;
if (modelName.empty())
{
id = XPMPCreatePlane(aircraftIcao.c_str(), airlineIcao.c_str(), livery.c_str(), getPlaneData, planeLoaded, static_cast<void *>(this));
}
else
{
id = XPMPCreatePlaneWithModelName(modelName.c_str(), aircraftIcao.c_str(), airlineIcao.c_str(), livery.c_str(), getPlaneData, planeLoaded, static_cast<void *>(this));
}
if (!id)
{
emitPlaneAddingFailed(callsign);
return;
}
Plane *plane = new Plane(id, callsign, aircraftIcao, airlineIcao, livery, modelName);
m_planesByCallsign[callsign] = plane;
m_planesById[id] = plane;
// Create view menu item
CMenuItem planeViewMenuItem = m_followPlaneViewSubMenu.item(callsign, [this, callsign] { switchToFollowPlaneView(callsign); });
m_followPlaneViewMenuItems[callsign] = planeViewMenuItem;
m_followPlaneViewSequence.push_back(callsign);
}
void CTraffic::removePlane(const std::string &callsign)
{
auto menuItemIt = m_followPlaneViewMenuItems.find(callsign);
if (menuItemIt != m_followPlaneViewMenuItems.end())
{
m_followPlaneViewSubMenu.removeItem(menuItemIt->second);
m_followPlaneViewMenuItems.erase(menuItemIt);
}
auto planeIt = m_planesByCallsign.find(callsign);
if (planeIt == m_planesByCallsign.end()) { return; }
m_followPlaneViewSequence.erase(std::remove(m_followPlaneViewSequence.begin(), m_followPlaneViewSequence.end(), callsign),
m_followPlaneViewSequence.end());
Plane *plane = planeIt->second;
m_planesByCallsign.erase(callsign);
m_planesById.erase(plane->id);
XPMPDestroyPlane(plane->id);
delete plane;
}
void CTraffic::removeAllPlanes()
{
for (const auto &kv : m_planesByCallsign)
{
Plane *plane = kv.second;
assert(plane);
XPMPDestroyPlane(plane->id);
delete plane;
}
for (const auto &kv : m_followPlaneViewMenuItems)
{
CMenuItem item = kv.second;
m_followPlaneViewSubMenu.removeItem(item);
}
m_planesByCallsign.clear();
m_planesById.clear();
m_followPlaneViewMenuItems.clear();
m_followPlaneViewSequence.clear();
}
void CTraffic::setPlanesPositions(const std::vector<std::string> &callsigns, std::vector<double> latitudesDeg, std::vector<double> longitudesDeg, std::vector<double> altitudesFt,
std::vector<double> pitchesDeg, std::vector<double> rollsDeg, std::vector<double> headingsDeg, const std::vector<bool> &onGrounds)
{
(void)onGrounds;
for (size_t i = 0; i < callsigns.size(); i++)
{
auto planeIt = m_planesByCallsign.find(callsigns.at(i));
if (planeIt == m_planesByCallsign.end()) { return; }
Plane *plane = planeIt->second;
if (!plane) { return; }
plane->position.lat = latitudesDeg.at(i);
plane->position.lon = longitudesDeg.at(i);
plane->position.elevation = altitudesFt.at(i);
plane->position.pitch = static_cast<float>(pitchesDeg.at(i));
plane->position.roll = static_cast<float>(rollsDeg.at(i));
plane->position.heading = static_cast<float>(headingsDeg.at(i));
}
}
void CTraffic::setPlanesSurfaces(const std::vector<std::string> &callsigns, const std::vector<double> &gears, const std::vector<double> &flaps, const std::vector<double> &spoilers,
const std::vector<double> &speedBrakes, const std::vector<double> &slats, const std::vector<double> &wingSweeps, const std::vector<double> &thrusts,
const std::vector<double> &elevators, const std::vector<double> &rudders, const std::vector<double> &ailerons, const std::vector<bool> &landLights,
const std::vector<bool> &beaconLights, const std::vector<bool> &strobeLights, const std::vector<bool> &navLights, const std::vector<int> &lightPatterns)
{
for (size_t i = 0; i < callsigns.size(); i++)
{
auto planeIt = m_planesByCallsign.find(callsigns.at(i));
if (planeIt == m_planesByCallsign.end()) { return; }
Plane *plane = planeIt->second;
if (!plane) { return; }
plane->hasSurfaces = true;
plane->targetGearPosition = static_cast<float>(gears.at(i));
plane->surfaces.flapRatio = static_cast<float>(flaps.at(i));
plane->surfaces.spoilerRatio = static_cast<float>(spoilers.at(i));
plane->surfaces.speedBrakeRatio = static_cast<float>(speedBrakes.at(i));
plane->surfaces.slatRatio = static_cast<float>(slats.at(i));
plane->surfaces.wingSweep = static_cast<float>(wingSweeps.at(i));
plane->surfaces.thrust = static_cast<float>(thrusts.at(i));
plane->surfaces.yokePitch = static_cast<float>(elevators.at(i));
plane->surfaces.yokeHeading = static_cast<float>(rudders.at(i));
plane->surfaces.yokeRoll = static_cast<float>(ailerons.at(i));
plane->surfaces.lights.landLights = landLights.at(i);
plane->surfaces.lights.bcnLights = beaconLights.at(i);
plane->surfaces.lights.strbLights = strobeLights.at(i);
plane->surfaces.lights.navLights = navLights.at(i);
plane->surfaces.lights.flashPattern = lightPatterns.at(i);
}
}
void CTraffic::setPlanesTransponders(const std::vector<std::string> &callsigns, const std::vector<int> &codes, const std::vector<bool> &modeCs, const std::vector<bool> &idents)
{
for (size_t i = 0; i < callsigns.size(); i++)
{
auto planeIt = m_planesByCallsign.find(callsigns.at(i));
if (planeIt == m_planesByCallsign.end()) { return; }
Plane *plane = planeIt->second;
if (!plane) { return; }
plane->hasXpdr = true;
plane->xpdr.code = codes.at(i);
if (idents.at(i)) { plane->xpdr.mode = xpmpTransponderMode_ModeC_Ident; }
else if (modeCs.at(i)) { plane->xpdr.mode = xpmpTransponderMode_ModeC; }
else { plane->xpdr.mode = xpmpTransponderMode_Standby; }
}
}
void CTraffic::getRemoteAircraftData(std::vector<std::string> &callsigns, std::vector<double> &latitudesDeg, std::vector<double> &longitudesDeg,
std::vector<double> &elevationsM, std::vector<double> &verticalOffsets) const
{
if (callsigns.empty() || m_planesByCallsign.empty()) { return; }
const auto requestedCallsigns = callsigns;
callsigns.clear();
latitudesDeg.clear();
longitudesDeg.clear();
elevationsM.clear();
verticalOffsets.clear();
for (const auto &requestedCallsign : requestedCallsigns)
{
auto planeIt = m_planesByCallsign.find(requestedCallsign);
if (planeIt == m_planesByCallsign.end()) { continue; }
Plane *plane = planeIt->second;
assert(plane);
double latDeg = plane->position.lat;
double lonDeg = plane->position.lon;
double groundElevation = plane->terrainProbe.getElevation(latDeg, lonDeg, plane->position.elevation);
if (std::isnan(groundElevation)) { groundElevation = 0.0; }
double fudgeFactor = 3.0;
bool hasOffset = XPMPGetVerticalOffset(plane->id, &fudgeFactor);
callsigns.push_back(requestedCallsign);
latitudesDeg.push_back(latDeg);
longitudesDeg.push_back(lonDeg);
elevationsM.push_back(groundElevation);
verticalOffsets.push_back(hasOffset ? fudgeFactor : std::numeric_limits<decltype(fudgeFactor)>::quiet_NaN());
}
}
double CTraffic::getElevationAtPosition(const std::string &callsign, double latitudeDeg, double longitudeDeg, double altitudeMeters) const
{
auto planeIt = m_planesByCallsign.find(callsign);
if (planeIt != m_planesByCallsign.end())
{
Plane *plane = planeIt->second;
return plane->terrainProbe.getElevation(latitudeDeg, longitudeDeg, altitudeMeters);
}
else
{
return m_terrainProbe.getElevation(latitudeDeg, longitudeDeg, altitudeMeters);
}
}
void CTraffic::setFollowedAircraft(const std::string &callsign)
{
if (callsign != ownAircraftString())
{
auto planeIt = m_planesByCallsign.find(callsign);
if (planeIt == m_planesByCallsign.end()) { return; }
}
switchToFollowPlaneView(callsign);
}
void CTraffic::dbusDisconnectedHandler()
{
removeAllPlanes();
}
const char *introspection_traffic =
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
#include "org.swift_project.xswiftbus.traffic.xml"
;
DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage &message_)
{
CDBusMessage message(message_);
const std::string sender = message.getSender();
const dbus_uint32_t serial = message.getSerial();
const bool wantsReply = message.wantsReply();
if (message.getInterfaceName() == DBUS_INTERFACE_INTROSPECTABLE)
{
if (message.getMethodName() == "Introspect")
{
sendDBusReply(sender, serial, introspection_traffic);
}
}
else if (message.getInterfaceName() == XSWIFTBUS_TRAFFIC_INTERFACENAME)
{
if (message.getMethodName() == "acquireMultiplayerPlanes")
{
queueDBusCall([ = ]()
{
std::string owner;
bool acquired = acquireMultiplayerPlanes(&owner);
CDBusMessage reply = CDBusMessage::createReply(sender, serial);
reply.beginArgumentWrite();
reply.appendArgument(acquired);
reply.appendArgument(owner);
sendDBusMessage(reply);
});
}
else if (message.getMethodName() == "initialize")
{
sendDBusReply(sender, serial, initialize());
}
else if (message.getMethodName() == "cleanup")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
queueDBusCall([ = ]()
{
cleanup();
});
}
else if (message.getMethodName() == "loadPlanesPackage")
{
std::string path;
message.beginArgumentRead();
message.getArgument(path);
queueDBusCall([ = ]()
{
sendDBusReply(sender, serial, loadPlanesPackage(path));
});
}
else if (message.getMethodName() == "setDefaultIcao")
{
std::string defaultIcao;
message.beginArgumentRead();
message.getArgument(defaultIcao);
queueDBusCall([ = ]()
{
setDefaultIcao(defaultIcao);
});
}
else if (message.getMethodName() == "setDrawingLabels")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
bool drawing = true;
message.beginArgumentRead();
message.getArgument(drawing);
queueDBusCall([ = ]()
{
setDrawingLabels(drawing);
});
}
else if (message.getMethodName() == "isDrawingLabels")
{
queueDBusCall([ = ]()
{
sendDBusReply(sender, serial, isDrawingLabels());
});
}
else if (message.getMethodName() == "setMaxPlanes")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
int planes = 100;
message.beginArgumentRead();
message.getArgument(planes);
queueDBusCall([ = ]()
{
setMaxPlanes(planes);
});
}
else if (message.getMethodName() == "setMaxDrawDistance")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
double nauticalMiles = 100;
message.beginArgumentRead();
message.getArgument(nauticalMiles);
queueDBusCall([ = ]()
{
setMaxDrawDistance(nauticalMiles);
});
}
else if (message.getMethodName() == "addPlane")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
std::string callsign;
std::string modelName;
std::string aircraftIcao;
std::string airlineIcao;
std::string livery;
message.beginArgumentRead();
message.getArgument(callsign);
message.getArgument(modelName);
message.getArgument(aircraftIcao);
message.getArgument(airlineIcao);
message.getArgument(livery);
queueDBusCall([ = ]()
{
addPlane(callsign, modelName, aircraftIcao, airlineIcao, livery);
});
}
else if (message.getMethodName() == "removePlane")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
std::string callsign;
message.beginArgumentRead();
message.getArgument(callsign);
queueDBusCall([ = ]()
{
removePlane(callsign);
});
}
else if (message.getMethodName() == "removeAllPlanes")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
queueDBusCall([ = ]()
{
removeAllPlanes();
});
}
else if (message.getMethodName() == "setPlanesPositions")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
std::vector<std::string> callsigns;
std::vector<double> latitudes;
std::vector<double> longitudes;
std::vector<double> altitudes;
std::vector<double> pitches;
std::vector<double> rolls;
std::vector<double> headings;
std::vector<bool> onGrounds;
message.beginArgumentRead();
message.getArgument(callsigns);
message.getArgument(latitudes);
message.getArgument(longitudes);
message.getArgument(altitudes);
message.getArgument(pitches);
message.getArgument(rolls);
message.getArgument(headings);
message.getArgument(onGrounds);
queueDBusCall([ = ]()
{
setPlanesPositions(callsigns, latitudes, longitudes, altitudes, pitches, rolls, headings, onGrounds);
});
}
else if (message.getMethodName() == "setPlanesSurfaces")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
std::vector<std::string> callsigns;
std::vector<double> gears;
std::vector<double> flaps;
std::vector<double> spoilers;
std::vector<double> speedBrakes;
std::vector<double> slats;
std::vector<double> wingSweeps;
std::vector<double> thrusts;
std::vector<double> elevators;
std::vector<double> rudders;
std::vector<double> ailerons;
std::vector<bool> landLights;
std::vector<bool> beaconLights;
std::vector<bool> strobeLights;
std::vector<bool> navLights;
std::vector<int> lightPatterns;
message.beginArgumentRead();
message.getArgument(callsigns);
message.getArgument(gears);
message.getArgument(flaps);
message.getArgument(spoilers);
message.getArgument(speedBrakes);
message.getArgument(slats);
message.getArgument(wingSweeps);
message.getArgument(thrusts);
message.getArgument(elevators);
message.getArgument(rudders);
message.getArgument(ailerons);
message.getArgument(landLights);
message.getArgument(beaconLights);
message.getArgument(strobeLights);
message.getArgument(navLights);
message.getArgument(lightPatterns);
queueDBusCall([ = ]()
{
setPlanesSurfaces(callsigns, gears, flaps, spoilers, speedBrakes, slats, wingSweeps, thrusts, elevators,
rudders, ailerons, landLights, beaconLights, strobeLights, navLights, lightPatterns);
});
}
else if (message.getMethodName() == "setPlanesTransponders")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
std::vector<std::string> callsigns;
std::vector<int> codes;
std::vector<bool> modeCs;
std::vector<bool> idents;
message.beginArgumentRead();
message.getArgument(callsigns);
message.getArgument(codes);
message.getArgument(modeCs);
message.getArgument(idents);
queueDBusCall([ = ]()
{
setPlanesTransponders(callsigns, codes, modeCs, idents);
});
}
else if (message.getMethodName() == "getRemoteAircraftData")
{
std::vector<std::string> requestedcallsigns;
message.beginArgumentRead();
message.getArgument(requestedcallsigns);
queueDBusCall([ = ]()
{
std::vector<std::string> callsigns = requestedcallsigns;
std::vector<double> latitudesDeg;
std::vector<double> longitudesDeg;
std::vector<double> elevationsM;
std::vector<double> verticalOffsets;
getRemoteAircraftData(callsigns, latitudesDeg, longitudesDeg, elevationsM, verticalOffsets);
CDBusMessage reply = CDBusMessage::createReply(sender, serial);
reply.beginArgumentWrite();
reply.appendArgument(callsigns);
reply.appendArgument(latitudesDeg);
reply.appendArgument(longitudesDeg);
reply.appendArgument(elevationsM);
reply.appendArgument(verticalOffsets);
sendDBusMessage(reply);
});
}
else if (message.getMethodName() == "getElevationAtPosition")
{
std::string callsign;
double latitudeDeg;
double longitudeDeg;
double altitudeMeters;
message.beginArgumentRead();
message.getArgument(callsign);
message.getArgument(latitudeDeg);
message.getArgument(longitudeDeg);
message.getArgument(altitudeMeters);
queueDBusCall([ = ]()
{
double elevation = getElevationAtPosition(callsign, latitudeDeg, longitudeDeg, altitudeMeters);
CDBusMessage reply = CDBusMessage::createReply(sender, serial);
reply.beginArgumentWrite();
reply.appendArgument(callsign);
reply.appendArgument(elevation);
sendDBusMessage(reply);
});
}
else if (message.getMethodName() == "setFollowedAircraft")
{
maybeSendEmptyDBusReply(wantsReply, sender, serial);
std::string callsign;
message.beginArgumentRead();
message.getArgument(callsign);
queueDBusCall([ = ]()
{
setFollowedAircraft(callsign);
});
}
else
{
// Unknown message. Tell DBus that we cannot handle it
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
}
return DBUS_HANDLER_RESULT_HANDLED;
}
int CTraffic::process()
{
invokeQueuedDBusCalls();
return 1;
}
//! memcmp function which ignores the header ("size" member) and compares only the payload (the rest of the struct)
template <typename T>
int memcmpPayload(T *dst, T *src)
{
return std::memcmp(reinterpret_cast<char *>(dst) + sizeof(dst->size),
reinterpret_cast<char *>(src) + sizeof(src->size),
sizeof(*dst) - sizeof(dst->size));
}
int CTraffic::getPlaneData(void *id, int dataType, void *io_data)
{
auto planeIt = m_planesById.find(id);
assert(planeIt != m_planesById.end());
Plane *plane = planeIt->second;
if (!plane) { return xpmpData_Unavailable; }
switch (dataType)
{
case xpmpDataType_Position:
{
const auto io_position = static_cast<XPMPPlanePosition_t *>(io_data);
io_position->lat = plane->position.lat;
io_position->lon = plane->position.lon;
io_position->elevation = plane->position.elevation;
io_position->pitch = plane->position.pitch;
io_position->roll = plane->position.roll;
io_position->heading = plane->position.heading;
std::strncpy(io_position->label, plane->label, sizeof(plane->label)); // fixme don't need to copy on every frame
return xpmpData_NewData;
}
case xpmpDataType_Surfaces:
if (plane->hasSurfaces)
{
const auto now = std::chrono::system_clock::now();
static const float epsilon = std::numeric_limits<float>::epsilon();
const float f = plane->surfaces.gearPosition - plane->targetGearPosition;
if (std::abs(f) > epsilon)
{
// interpolate gear position
constexpr float gearMoveTimeMs = 5000;
const auto gearPositionDiffRemaining = plane->targetGearPosition - plane->surfaces.gearPosition;
auto diffMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - plane->prevSurfacesLerpTime);
const auto gearPositionDiffThisFrame = (diffMs.count()) / gearMoveTimeMs;
plane->surfaces.gearPosition += std::copysign(gearPositionDiffThisFrame, gearPositionDiffRemaining);
plane->surfaces.gearPosition = std::max(0.0f, std::min(plane->surfaces.gearPosition, 1.0f));
}
plane->prevSurfacesLerpTime = now;
const auto io_surfaces = static_cast<XPMPPlaneSurfaces_t *>(io_data);
if (memcmpPayload(io_surfaces, &plane->surfaces))
{
std::memcpy(io_surfaces, &plane->surfaces, sizeof(*io_surfaces));
return xpmpData_NewData;
}
else { return xpmpData_Unchanged; }
}
break;
case xpmpDataType_Radar:
if (plane->hasXpdr)
{
const auto io_xpdr = static_cast<XPMPPlaneRadar_t *>(io_data);
if (memcmpPayload(io_xpdr, &plane->xpdr))
{
std::memcpy(io_xpdr, &plane->xpdr, sizeof(*io_xpdr));
return xpmpData_NewData;
}
else { return xpmpData_Unchanged; }
}
}
return xpmpData_Unavailable;
}
int CTraffic::orbitPlaneFunc(XPLMCameraPosition_t *cameraPosition, int isLosingControl, void *refcon)
{
auto *traffic = static_cast<CTraffic *>(refcon);
if (isLosingControl == 1)
{
// traffic->m_planeViewCallsign.clear();
return 0;
}
if (cameraPosition)
{
// Ideally we would like to test against right mouse button, but X-Plane SDK does not
// allow that.
if (!traffic->m_deltaCameraPosition.isInitialized || traffic->m_isSpacePressed)
{
int w, h, x, y;
// First get the screen size and mouse location. We will use this to decide
// what part of the orbit we are in. The mouse will move us up-down and around.
// fixme: In a future update, change the orbit only while right mouse button is pressed.
XPLMGetScreenSize(&w, &h);
XPLMGetMouseLocation(&x, &y);
traffic->m_deltaCameraPosition.heading = 360.0 * static_cast<double>(x) / static_cast<double>(w);
traffic->m_deltaCameraPosition.pitch = 20.0 * ((static_cast<double>(y) / static_cast<double>(h)) * 2.0 - 1.0);
// Now calculate where the camera should be positioned to be 200
// meters from the plane and pointing at the plane at the pitch and
// heading we wanted above.
static const double PI = std::acos(-1);
traffic->m_deltaCameraPosition.dx = -50.0 * sin(traffic->m_deltaCameraPosition.heading * PI / 180.0);
traffic->m_deltaCameraPosition.dz = 50.0 * cos(traffic->m_deltaCameraPosition.heading * PI / 180.0);
traffic->m_deltaCameraPosition.dy = -50.0 * tan(traffic->m_deltaCameraPosition.pitch * PI / 180.0);
traffic->m_deltaCameraPosition.isInitialized = true;
}
double lx, ly, lz;
static const double kFtToMeters = 0.3048;
if (traffic->m_followPlaneViewCallsign == traffic->ownAircraftString())
{
lx = traffic->m_ownAircraftPositionX.get();
ly = traffic->m_ownAircraftPositionY.get();
lz = traffic->m_ownAircraftPositionZ.get();
}
else
{
auto planeIt = traffic->m_planesByCallsign.find(traffic->m_followPlaneViewCallsign);
if (planeIt == traffic->m_planesByCallsign.end()) { return 0; }
Plane *plane = planeIt->second;
XPLMWorldToLocal(plane->position.lat, plane->position.lon, plane->position.elevation * kFtToMeters, &lx, &ly, &lz);
}
// Fill out the camera position info.
cameraPosition->x = static_cast<float>(lx + traffic->m_deltaCameraPosition.dx);
cameraPosition->y = static_cast<float>(ly + traffic->m_deltaCameraPosition.dy);
cameraPosition->z = static_cast<float>(lz + traffic->m_deltaCameraPosition.dz);
cameraPosition->pitch = static_cast<float>(traffic->m_deltaCameraPosition.pitch);
cameraPosition->heading = static_cast<float>(traffic->m_deltaCameraPosition.heading);
cameraPosition->roll = 0.0;
}
// Return 1 to indicate we want to keep controlling the camera.
return 1;
}
int CTraffic::drawCallback(XPLMDrawingPhase phase, int isBefore, void *refcon)
{
(void)phase;
(void)isBefore;
CTraffic *traffic = static_cast<CTraffic *>(refcon);
// The draw callback is called several times per frame. We need this only once.
if (traffic->m_worldRenderType.get() == 0)
{
traffic->emitSimFrame();
}
return 1;
}
int CTraffic::spaceKeySniffer(char character, XPLMKeyFlags flags, char virtualKey, void *refcon)
{
(void) character;
(void) flags;
CTraffic *traffic = static_cast<CTraffic *>(refcon);
// We are only interested in Space key
if (virtualKey == XPLM_VK_SPACE)
{
if (flags & xplm_DownFlag) { traffic->m_isSpacePressed = true; }
if (flags & xplm_UpFlag) { traffic->m_isSpacePressed = false; }
}
/* Return 1 to pass the keystroke to plugin windows and X-Plane.
* Returning 0 would consume the keystroke. */
return 1;
}
}
//! \endcond