mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-30 11:55:35 +08:00
refs #272 added new DBus service object XBus::CTraffic providing access to traffic aircraft in the sim
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#define NOMINMAX
|
||||
#include "plugin.h"
|
||||
#include "utils.h"
|
||||
#include "traffic.h"
|
||||
#include <XPLM/XPLMPlanes.h>
|
||||
|
||||
#if ! defined(XPLM210)
|
||||
@@ -20,6 +21,8 @@ PLUGIN_API int XPluginStart(char *o_name, char *o_sig, char *o_desc)
|
||||
std::strcpy(o_name, "X-Bus");
|
||||
std::strcpy(o_sig, "net.vatsim.XBus");
|
||||
std::strcpy(o_desc, "Allows pilot client to connect to X-Plane via D-Bus");
|
||||
|
||||
XBus::CTraffic::initLegacyData();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "traffic.h"
|
||||
|
||||
#define XBUS_SERVICE_SERVICENAME "net.vatsim.xbus"
|
||||
|
||||
@@ -25,7 +27,9 @@ namespace XBus
|
||||
|
||||
m_server = new BlackCore::CDBusServer(XBUS_SERVICE_SERVICENAME, address, this);
|
||||
m_service = new CService(this);
|
||||
m_traffic = new CTraffic(this);
|
||||
m_server->addObject(CService::ObjectPath(), m_service);
|
||||
m_server->addObject(CTraffic::ObjectPath(), m_traffic);
|
||||
}
|
||||
|
||||
void CPlugin::onAircraftModelChanged()
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
#define NOMINMAX
|
||||
#include "menus.h"
|
||||
#include "service.h"
|
||||
|
||||
#pragma push_macro("interface")
|
||||
#undef interface
|
||||
@@ -26,6 +25,9 @@
|
||||
|
||||
namespace XBus
|
||||
{
|
||||
class CService;
|
||||
class CTraffic;
|
||||
|
||||
/*!
|
||||
* Main plugin class
|
||||
*/
|
||||
@@ -43,6 +45,7 @@ namespace XBus
|
||||
private:
|
||||
BlackCore::CDBusServer *m_server = nullptr;
|
||||
CService *m_service = nullptr;
|
||||
CTraffic *m_traffic = nullptr;
|
||||
CMenu m_menu;
|
||||
QVector<CMenuItem> m_startServerMenuItems;
|
||||
|
||||
|
||||
310
src/xbus/traffic.cpp
Normal file
310
src/xbus/traffic.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/* Copyright (C) 2013 VATSIM Community / contributors
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#define NOMINMAX
|
||||
#include "traffic.h"
|
||||
#include "XPMPMultiplayer.h"
|
||||
#include <XPLM/XPLMProcessing.h>
|
||||
#include <XPLM/XPLMUtilities.h>
|
||||
#include <QDateTime>
|
||||
#include <cstring>
|
||||
|
||||
namespace XBus
|
||||
{
|
||||
|
||||
CTraffic::Plane::Plane(void *id_, QString callsign_, QString aircraftIcao_, QString airlineIcao_, QString livery_)
|
||||
: id(id_), callsign(callsign_), aircraftIcao(aircraftIcao_), airlineIcao(airlineIcao_), livery(livery_)
|
||||
{
|
||||
position0.size = sizeof(position0);
|
||||
position1.size = sizeof(position1);
|
||||
surfaces.size = sizeof(surfaces);
|
||||
xpdr.size = sizeof(xpdr);
|
||||
|
||||
std::strncpy(position1.label, qPrintable(callsign), sizeof(position1.label));
|
||||
surfaces.lights.timeOffset = static_cast<quint16>(qrand() % 0xffff);
|
||||
}
|
||||
|
||||
CTraffic::CTraffic(QObject *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
CTraffic::~CTraffic()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
bool s_legacyDataOK = true;
|
||||
|
||||
void CTraffic::initLegacyData()
|
||||
{
|
||||
char xplanePath[512];
|
||||
XPLMGetSystemPath(xplanePath);
|
||||
auto dir = QString(xplanePath) + "Resources" + XPLMGetDirectorySeparator() + "plugins" + XPLMGetDirectorySeparator()
|
||||
+ "xbus" + XPLMGetDirectorySeparator() + "LegacyData" + XPLMGetDirectorySeparator();
|
||||
|
||||
auto err = XPMPMultiplayerInitLegacyData(qPrintable(dir + "CSL"), qPrintable(dir + "related.txt"),
|
||||
qPrintable(dir + "lights.png"), qPrintable(dir + "Doc8643.txt"), "C172", preferences, preferences);
|
||||
if (*err) { s_legacyDataOK = false; }
|
||||
}
|
||||
|
||||
bool CTraffic::initialize()
|
||||
{
|
||||
if (! s_legacyDataOK) { return false; }
|
||||
|
||||
auto err = XPMPMultiplayerInit(preferences, preferences);
|
||||
if (*err) { cleanup(); return false; }
|
||||
m_initialized = true;
|
||||
|
||||
err = XPMPMultiplayerEnable();
|
||||
if (*err) { cleanup(); return false; }
|
||||
m_enabled = true;
|
||||
|
||||
XPMPLoadPlanesIfNecessary();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTraffic::cleanup()
|
||||
{
|
||||
removeAllPlanes();
|
||||
|
||||
if (m_enabled)
|
||||
{
|
||||
m_enabled = false;
|
||||
XPMPMultiplayerDisable();
|
||||
}
|
||||
|
||||
if (m_initialized)
|
||||
{
|
||||
m_initialized = false;
|
||||
XPMPMultiplayerCleanup();
|
||||
}
|
||||
}
|
||||
|
||||
int CTraffic::preferences(const char *section, const char *name, int def)
|
||||
{
|
||||
// TODO [planes] max_full_count
|
||||
return def;
|
||||
}
|
||||
|
||||
float CTraffic::preferences(const char *section, const char *name, float def)
|
||||
{
|
||||
// TODO [planes] full_distance
|
||||
return def;
|
||||
}
|
||||
|
||||
bool CTraffic::loadPlanesPackage(const QString &path)
|
||||
{
|
||||
char xplanePath[512];
|
||||
XPLMGetSystemPath(xplanePath);
|
||||
auto dir = QString(xplanePath) + "Resources" + XPLMGetDirectorySeparator() + "plugins" + XPLMGetDirectorySeparator()
|
||||
+ "xbus" + XPLMGetDirectorySeparator() + "LegacyData" + XPLMGetDirectorySeparator();
|
||||
|
||||
auto err = XPMPLoadCSLPackage(qPrintable(path), qPrintable(dir + "related.txt"), qPrintable(dir + "Doc8643.txt"));
|
||||
if (*err) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTraffic::setDefaultIcao(const QString &defaultIcao)
|
||||
{
|
||||
XPMPSetDefaultPlaneICAO(qPrintable(defaultIcao));
|
||||
}
|
||||
|
||||
void CTraffic::setDrawingLabels(bool drawing)
|
||||
{
|
||||
if (drawing)
|
||||
{
|
||||
XPMPEnableAircraftLabels();
|
||||
}
|
||||
else
|
||||
{
|
||||
XPMPDisableAircraftLabels();
|
||||
}
|
||||
}
|
||||
|
||||
bool CTraffic::isDrawingLabels() const
|
||||
{
|
||||
return XPMPDrawingAircraftLabels();
|
||||
}
|
||||
|
||||
void CTraffic::addPlane(const QString &callsign, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery)
|
||||
{
|
||||
auto id = XPMPCreatePlane(qPrintable(aircraftIcao), qPrintable(airlineIcao), qPrintable(livery), getPlaneData, static_cast<void *>(this));
|
||||
auto plane = new Plane(id, callsign, aircraftIcao, airlineIcao, livery);
|
||||
m_planesByCallsign[callsign] = plane;
|
||||
m_planesById[id] = plane;
|
||||
}
|
||||
|
||||
void CTraffic::removePlane(const QString &callsign)
|
||||
{
|
||||
auto plane = m_planesByCallsign.value(callsign, nullptr);
|
||||
if (plane)
|
||||
{
|
||||
m_planesByCallsign.remove(callsign);
|
||||
m_planesById.remove(plane->id);
|
||||
XPMPDestroyPlane(plane->id);
|
||||
delete plane;
|
||||
}
|
||||
}
|
||||
|
||||
void CTraffic::removeAllPlanes()
|
||||
{
|
||||
for (auto plane : m_planesByCallsign)
|
||||
{
|
||||
XPMPDestroyPlane(plane->id);
|
||||
delete plane;
|
||||
}
|
||||
m_planesByCallsign.clear();
|
||||
m_planesById.clear();
|
||||
}
|
||||
|
||||
void CTraffic::setPlanePosition(const QString &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading)
|
||||
{
|
||||
const auto plane = m_planesByCallsign.value(callsign, nullptr);
|
||||
if (plane)
|
||||
{
|
||||
plane->time0 = plane->time1;
|
||||
plane->time1 = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
std::memcpy(reinterpret_cast<char *>(&plane->position0) + sizeof(plane->position0.size),
|
||||
reinterpret_cast<char *>(&plane->position1) + sizeof(plane->position1.size),
|
||||
sizeof(plane->position0) - sizeof(plane->position0.size));
|
||||
|
||||
plane->position1.lat = latitude;
|
||||
plane->position1.lon = longitude;
|
||||
plane->position1.elevation = altitude;
|
||||
plane->position1.pitch = static_cast<float>(pitch);
|
||||
plane->position1.roll = static_cast<float>(roll);
|
||||
plane->position1.heading = static_cast<float>(heading);
|
||||
}
|
||||
}
|
||||
|
||||
void CTraffic::setPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
|
||||
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern)
|
||||
{
|
||||
const auto plane = m_planesByCallsign.value(callsign, nullptr);
|
||||
if (plane)
|
||||
{
|
||||
plane->hasSurfaces = true;
|
||||
plane->surfaces.gearPosition = gear;
|
||||
plane->surfaces.flapRatio = flap;
|
||||
plane->surfaces.spoilerRatio = spoiler;
|
||||
plane->surfaces.speedBrakeRatio = speedBrake;
|
||||
plane->surfaces.slatRatio = slat;
|
||||
plane->surfaces.wingSweep = wingSweep;
|
||||
plane->surfaces.thrust = thrust;
|
||||
plane->surfaces.yokePitch = elevator;
|
||||
plane->surfaces.yokeHeading = rudder;
|
||||
plane->surfaces.yokeRoll = aileron;
|
||||
plane->surfaces.lights.landLights = landLight;
|
||||
plane->surfaces.lights.bcnLights = beaconLight;
|
||||
plane->surfaces.lights.strbLights = strobeLight;
|
||||
plane->surfaces.lights.navLights = navLight;
|
||||
plane->surfaces.lights.flashPattern = lightPattern;
|
||||
}
|
||||
}
|
||||
|
||||
void CTraffic::setPlaneTransponder(const QString &callsign, int code, bool modeC, bool ident)
|
||||
{
|
||||
const auto plane = m_planesByCallsign.value(callsign, nullptr);
|
||||
if (plane)
|
||||
{
|
||||
plane->hasXpdr = true;
|
||||
plane->xpdr.code = code;
|
||||
if (ident) { plane->xpdr.mode = xpmpTransponderMode_ModeC_Ident; }
|
||||
else if (modeC) { plane->xpdr.mode = xpmpTransponderMode_ModeC; }
|
||||
else { plane->xpdr.mode = xpmpTransponderMode_Standby; }
|
||||
}
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// linearly interpolate angle in degrees
|
||||
template <typename T>
|
||||
T lerpDegrees(T from, T to, double factor)
|
||||
{
|
||||
if (std::fabs(to - from) > 180)
|
||||
{
|
||||
if (to > from) { to -= 360; }
|
||||
else { to += 360; }
|
||||
}
|
||||
return from + (to - from) * static_cast<T>(factor);
|
||||
}
|
||||
|
||||
int CTraffic::getPlaneData(void *id, int dataType, void *io_data)
|
||||
{
|
||||
auto plane = m_planesById.value(id, nullptr);
|
||||
if (! plane) { return xpmpData_Unavailable; }
|
||||
|
||||
switch (dataType)
|
||||
{
|
||||
case xpmpDataType_Position:
|
||||
if (plane->time1)
|
||||
{
|
||||
const auto io_position = static_cast<XPMPPlanePosition_t *>(io_data);
|
||||
|
||||
if (plane->time0) // we have two positions between which to interpolate
|
||||
{
|
||||
const auto currentTime = QDateTime::currentMSecsSinceEpoch();
|
||||
const auto factor = static_cast<double>(currentTime - plane->time0) / (plane->time1 - plane->time0);
|
||||
io_position->lat = lerpDegrees(plane->position0.lat, plane->position1.lat, factor);
|
||||
io_position->lon = lerpDegrees(plane->position0.lon, plane->position1.lon, factor);
|
||||
io_position->pitch = lerpDegrees(plane->position0.pitch, plane->position1.pitch, factor);
|
||||
io_position->roll = lerpDegrees(plane->position0.roll, plane->position1.roll, factor);
|
||||
io_position->heading = lerpDegrees(plane->position0.heading, plane->position1.heading, factor);
|
||||
io_position->elevation = plane->position0.elevation + (plane->position1.elevation - plane->position0.elevation) * factor;
|
||||
return xpmpData_NewData;
|
||||
}
|
||||
else // we only received one position so far
|
||||
{
|
||||
if (memcmpPayload(io_position, &plane->position1))
|
||||
{
|
||||
std::memcpy(io_position, &plane->position1, sizeof(*io_position));
|
||||
return xpmpData_NewData;
|
||||
}
|
||||
else { return xpmpData_Unchanged; }
|
||||
}
|
||||
}
|
||||
else { return xpmpData_Unavailable; }
|
||||
|
||||
case xpmpDataType_Surfaces:
|
||||
if (plane->hasSurfaces)
|
||||
{
|
||||
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; }
|
||||
}
|
||||
else { return xpmpData_Unavailable; }
|
||||
|
||||
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; }
|
||||
}
|
||||
else { return xpmpData_Unavailable; }
|
||||
|
||||
default: return xpmpData_Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
131
src/xbus/traffic.h
Normal file
131
src/xbus/traffic.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/* Copyright (C) 2013 VATSIM Community / contributors
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BLACKSIM_XBUS_TRAFFIC_H
|
||||
#define BLACKSIM_XBUS_TRAFFIC_H
|
||||
|
||||
//! \file
|
||||
|
||||
#include "datarefs.h"
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include "XPMPMultiplayer.h"
|
||||
|
||||
//! \cond PRIVATE
|
||||
#define XBUS_TRAFFIC_INTERFACENAME "net.vatsim.xbus.traffic"
|
||||
#define XBUS_TRAFFIC_OBJECTPATH "/xbus/traffic"
|
||||
//! \endcond
|
||||
|
||||
namespace XBus
|
||||
{
|
||||
|
||||
/*!
|
||||
* XBus service object for traffic aircraft which is accessible through DBus
|
||||
*/
|
||||
class CTraffic : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", XBUS_TRAFFIC_INTERFACENAME)
|
||||
|
||||
public:
|
||||
//! Constructor
|
||||
CTraffic(QObject *parent);
|
||||
|
||||
//! Destructor
|
||||
~CTraffic();
|
||||
|
||||
//! DBus interface name
|
||||
static const QString &InterfaceName()
|
||||
{
|
||||
static QString s(XBUS_TRAFFIC_INTERFACENAME);
|
||||
return s;
|
||||
}
|
||||
|
||||
//! DBus object path
|
||||
static const QString &ObjectPath()
|
||||
{
|
||||
static QString s(XBUS_TRAFFIC_OBJECTPATH);
|
||||
return s;
|
||||
}
|
||||
|
||||
//! Called by XPluginStart
|
||||
static void initLegacyData();
|
||||
|
||||
public slots:
|
||||
//! Initialize the multiplayer planes rendering and return true if successful
|
||||
bool initialize();
|
||||
|
||||
//! Reverse the actions of initialize().
|
||||
void cleanup();
|
||||
|
||||
//! Load a collection of planes from the given directory and return true if successful
|
||||
bool loadPlanesPackage(const QString &path);
|
||||
|
||||
//! Set the ICAO code to use for aircraft without a model match
|
||||
void setDefaultIcao(const QString &defaultIcao);
|
||||
|
||||
//! Set whether the plugin draws type and callsign labels above aircraft
|
||||
void setDrawingLabels(bool drawing);
|
||||
|
||||
//! Get whether the plugin draws type and callsign labels above aircraft
|
||||
bool isDrawingLabels() const;
|
||||
|
||||
//! Introduce a new traffic aircraft
|
||||
void addPlane(const QString &callsign, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery);
|
||||
|
||||
//! Remove a traffic aircraft
|
||||
void removePlane(const QString &callsign);
|
||||
|
||||
//! Remove all traffic aircraft
|
||||
void removeAllPlanes();
|
||||
|
||||
//! Set the position of a traffic aircraft
|
||||
void setPlanePosition(const QString &callsign, double latitude, double longitude, double altitude, double pitch, double roll, double heading);
|
||||
|
||||
//! Set the flight control surfaces and lights of a traffic aircraft
|
||||
void setPlaneSurfaces(const QString &callsign, double gear, double flap, double spoiler, double speedBrake, double slat, double wingSweep, double thrust,
|
||||
double elevator, double rudder, double aileron, bool landLight, bool beaconLight, bool strobeLight, bool navLight, int lightPattern);
|
||||
|
||||
//! Set the transponder of a traffic aircraft
|
||||
void setPlaneTransponder(const QString &callsign, int code, bool modeC, bool ident);
|
||||
|
||||
private:
|
||||
bool m_initialized = false;
|
||||
bool m_enabled = false;
|
||||
|
||||
static int preferences(const char *section, const char *name, int def);
|
||||
static float preferences(const char *section, const char *name, float def);
|
||||
|
||||
struct Plane
|
||||
{
|
||||
void *id = nullptr;
|
||||
QString callsign;
|
||||
QString aircraftIcao;
|
||||
QString airlineIcao;
|
||||
QString livery;
|
||||
qint64 time0 = 0;
|
||||
qint64 time1 = 0;
|
||||
bool hasSurfaces = false;
|
||||
bool hasXpdr = false;
|
||||
XPMPPlanePosition_t position0;
|
||||
XPMPPlanePosition_t position1;
|
||||
XPMPPlaneSurfaces_t surfaces;
|
||||
XPMPPlaneRadar_t xpdr;
|
||||
Plane(void *id_, QString callsign_, QString aircraftIcao_, QString airlineIcao_, QString livery_);
|
||||
};
|
||||
QHash<QString, Plane *> m_planesByCallsign;
|
||||
QHash<void *, Plane *> m_planesById;
|
||||
|
||||
int getPlaneData(void *id, int dataType, void *io_data);
|
||||
static int getPlaneData(void *id, int dataType, void *io_data, void *self)
|
||||
{
|
||||
return static_cast<CTraffic *>(self)->getPlaneData(id, dataType, io_data);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // guard
|
||||
|
||||
Reference in New Issue
Block a user