From 68cf4fa63d7767b8c1a30b14e311ef8ddf213d94 Mon Sep 17 00:00:00 2001 From: ltoenning Date: Sat, 19 Jan 2019 16:40:10 +0100 Subject: [PATCH] Initial FlightGear plugin commit --- default.json | 3 +- .../simulation/settings/mysettings.h | 46 + .../simulator/flightgear/flightgear.pro | 32 + .../simulator/flightgear/simulatorxplane.cpp | 1257 +++++++++++++++++ .../simulator/flightgear/simulatorxplane.h | 314 ++++ .../simulator/flightgear/simulatorxplane.json | 7 + .../simulator/flightgear/xplanempaircraft.cpp | 73 + .../simulator/flightgear/xplanempaircraft.h | 94 ++ .../flightgear/xswiftbusserviceproxy.cpp | 510 +++++++ .../flightgear/xswiftbusserviceproxy.h | 394 ++++++ .../flightgear/xswiftbustrafficproxy.cpp | 189 +++ .../flightgear/xswiftbustrafficproxy.h | 221 +++ .../flightgear/xswiftbusweatherproxy.cpp | 89 ++ .../flightgear/xswiftbusweatherproxy.h | 106 ++ .../flightgearconfig/flightgearconfig.pro | 29 + .../simulatorxplaneconfig.cpp | 30 + .../flightgearconfig/simulatorxplaneconfig.h | 50 + .../simulatorxplaneconfig.json | 3 + .../simulatorxplaneconfigwindow.cpp | 55 + .../simulatorxplaneconfigwindow.h | 50 + .../simulatorxplaneconfigwindow.ui | 70 + src/plugins/simulator/simulator.pro | 4 + 22 files changed, 3625 insertions(+), 1 deletion(-) create mode 100644 src/blackmisc/simulation/settings/mysettings.h create mode 100644 src/plugins/simulator/flightgear/flightgear.pro create mode 100644 src/plugins/simulator/flightgear/simulatorxplane.cpp create mode 100644 src/plugins/simulator/flightgear/simulatorxplane.h create mode 100644 src/plugins/simulator/flightgear/simulatorxplane.json create mode 100644 src/plugins/simulator/flightgear/xplanempaircraft.cpp create mode 100644 src/plugins/simulator/flightgear/xplanempaircraft.h create mode 100644 src/plugins/simulator/flightgear/xswiftbusserviceproxy.cpp create mode 100644 src/plugins/simulator/flightgear/xswiftbusserviceproxy.h create mode 100644 src/plugins/simulator/flightgear/xswiftbustrafficproxy.cpp create mode 100644 src/plugins/simulator/flightgear/xswiftbustrafficproxy.h create mode 100644 src/plugins/simulator/flightgear/xswiftbusweatherproxy.cpp create mode 100644 src/plugins/simulator/flightgear/xswiftbusweatherproxy.h create mode 100644 src/plugins/simulator/flightgearconfig/flightgearconfig.pro create mode 100644 src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.cpp create mode 100644 src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.h create mode 100644 src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.json create mode 100644 src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.cpp create mode 100644 src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.h create mode 100644 src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.ui diff --git a/default.json b/default.json index c82324723..1fddb5e38 100644 --- a/default.json +++ b/default.json @@ -23,7 +23,8 @@ "fsx": true, "p3d": true, "fsuipc": true, - "xplane": true + "xplane": true, + "fg": true }, "profileRelease": true, "assertsInRelease": true, diff --git a/src/blackmisc/simulation/settings/mysettings.h b/src/blackmisc/simulation/settings/mysettings.h new file mode 100644 index 000000000..aace84199 --- /dev/null +++ b/src/blackmisc/simulation/settings/mysettings.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2017 + * 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. + */ + +//! \file + +#ifndef BLACKMISC_SIMULATION_SETTINGS_XSWIFTBUSSETTINGS_H +#define BLACKMISC_SIMULATION_SETTINGS_XSWIFTBUSSETTINGS_H + +#include +#include "blackmisc/settingscache.h" +#include "blackmisc/dbusserver.h" + +namespace BlackMisc +{ + namespace Simulation + { + namespace Settings + { + /*! + * Setting for XSwiftBus. + */ + struct TXSwiftBusServer : public BlackMisc::TSettingTrait + { + //! \copydoc BlackMisc::TSettingTrait::key + static const char *key() { return "xswiftbus/server"; } + + //! \copydoc BlackCore::TSettingTrait::humanReadable + static const QString &humanReadable() { static const QString name("XSwiftBus"); return name; } + + //! \copydoc BlackMisc::TSettingTrait::defaultValue + static QString defaultValue() { return "tcp:host=127.0.0.1,port=45003"; } + + //! \copydoc BlackMisc::TSettingTrait::defaultValue + static bool isValid(const QString &dBusAddress) { return BlackMisc::CDBusServer::isSessionOrSystemAddress(dBusAddress) || BlackMisc::CDBusServer::isQtDBusAddress(dBusAddress); } + }; + } // ns + } // ns +} // ns + +#endif // guard diff --git a/src/plugins/simulator/flightgear/flightgear.pro b/src/plugins/simulator/flightgear/flightgear.pro new file mode 100644 index 000000000..1d2e680f3 --- /dev/null +++ b/src/plugins/simulator/flightgear/flightgear.pro @@ -0,0 +1,32 @@ +load(common_pre) + +QT += core dbus widgets network + +TARGET = simulatorflightgear +TEMPLATE = lib + +CONFIG += plugin shared +CONFIG += blackmisc blackcore blackgui blackconfig simulatorplugincommon + +DEPENDPATH += . $$SourceRoot/src +INCLUDEPATH += . $$SourceRoot/src + +unix:!macx { + INCLUDEPATH *= /usr/include/dbus-1.0 + INCLUDEPATH *= /usr/lib/x86_64-linux-gnu/dbus-1.0/include +} + +SOURCES += *.cpp +HEADERS += *.h +DISTFILES += simulatorxplane.json +DESTDIR = $$DestRoot/bin/plugins/simulator + +win32 { + dlltarget.path = $$PREFIX/bin/plugins/simulator + INSTALLS += dlltarget +} else { + target.path = $$PREFIX/bin/plugins/simulator + INSTALLS += target +} + +load(common_post) diff --git a/src/plugins/simulator/flightgear/simulatorxplane.cpp b/src/plugins/simulator/flightgear/simulatorxplane.cpp new file mode 100644 index 000000000..feedd1158 --- /dev/null +++ b/src/plugins/simulator/flightgear/simulatorxplane.cpp @@ -0,0 +1,1257 @@ +/* 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. + */ + +#include "simulatorxplane.h" +#include "qcompilerdetection.h" +#include "xswiftbusserviceproxy.h" +#include "xswiftbustrafficproxy.h" +#include "xswiftbusweatherproxy.h" +#include "blackcore/aircraftmatcher.h" +#include "blackmisc/simulation/aircraftmodel.h" +#include "blackmisc/simulation/simulatedaircraft.h" +#include "blackmisc/simulation/simulatedaircraftlist.h" +#include "blackmisc/weather/cloudlayer.h" +#include "blackmisc/weather/cloudlayerlist.h" +#include "blackmisc/weather/gridpoint.h" +#include "blackmisc/weather/temperaturelayer.h" +#include "blackmisc/weather/temperaturelayerlist.h" +#include "blackmisc/weather/visibilitylayer.h" +#include "blackmisc/weather/visibilitylayerlist.h" +#include "blackmisc/weather/windlayer.h" +#include "blackmisc/weather/windlayerlist.h" +#include "blackmisc/aviation/aircraftengine.h" +#include "blackmisc/aviation/aircraftenginelist.h" +#include "blackmisc/aviation/aircrafticaocode.h" +#include "blackmisc/aviation/aircraftparts.h" +#include "blackmisc/aviation/aircraftsituation.h" +#include "blackmisc/aviation/airlineicaocode.h" +#include "blackmisc/aviation/altitude.h" +#include "blackmisc/aviation/callsign.h" +#include "blackmisc/aviation/comsystem.h" +#include "blackmisc/aviation/heading.h" +#include "blackmisc/aviation/livery.h" +#include "blackmisc/aviation/transponder.h" +#include "blackmisc/network/textmessage.h" +#include "blackmisc/geo/coordinategeodetic.h" +#include "blackmisc/geo/latitude.h" +#include "blackmisc/geo/longitude.h" +#include "blackmisc/pq/angle.h" +#include "blackmisc/pq/frequency.h" +#include "blackmisc/pq/length.h" +#include "blackmisc/pq/pressure.h" +#include "blackmisc/pq/speed.h" +#include "blackmisc/pq/temperature.h" +#include "blackmisc/verify.h" +#include "blackmisc/compare.h" +#include "blackmisc/dbusserver.h" +#include "blackmisc/iterator.h" +#include "blackmisc/logmessage.h" +#include "blackconfig/buildconfig.h" + +#include "dbus/dbus.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace BlackConfig; +using namespace BlackMisc; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Network; +using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Simulation; +using namespace BlackMisc::Geo; +using namespace BlackMisc::Simulation; +using namespace BlackMisc::Weather; +using namespace BlackCore; + +namespace +{ + inline const QString &xswiftbusServiceName() + { + static const QString name("org.swift-project.xswiftbus"); + return name; + } +} + +namespace BlackSimPlugin +{ + namespace XPlane + { + CSimulatorXPlane::CSimulatorXPlane(const CSimulatorPluginInfo &info, + IOwnAircraftProvider *ownAircraftProvider, + IRemoteAircraftProvider *remoteAircraftProvider, + IWeatherGridProvider *weatherGridProvider, + IClientProvider *clientProvider, + QObject *parent) : + CSimulatorPluginCommon(info, ownAircraftProvider, remoteAircraftProvider, weatherGridProvider, clientProvider, parent) + { + m_watcher = new QDBusServiceWatcher(this); + m_watcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration); + m_watcher->addWatchedService(xswiftbusServiceName()); + m_watcher->setObjectName("QDBusServiceWatcher"); + connect(m_watcher, &QDBusServiceWatcher::serviceUnregistered, this, &CSimulatorXPlane::serviceUnregistered); + + m_fastTimer.setObjectName(this->objectName().append(":m_fastTimer")); + m_slowTimer.setObjectName(this->objectName().append(":m_slowTimer")); + m_pendingAddedTimer.setObjectName(this->objectName().append(":m_pendingAddedTimer")); + connect(&m_fastTimer, &QTimer::timeout, this, &CSimulatorXPlane::fastTimerTimeout); + connect(&m_slowTimer, &QTimer::timeout, this, &CSimulatorXPlane::slowTimerTimeout); + connect(&m_airportUpdater, &QTimer::timeout, this, &CSimulatorXPlane::updateAirportsInRange); + connect(&m_pendingAddedTimer, &QTimer::timeout, this, &CSimulatorXPlane::addNextPendingAircraft); + m_fastTimer.start(100); + m_slowTimer.start(1000); + m_airportUpdater.start(60 * 1000); + m_pendingAddedTimer.start(5000); + + this->setDefaultModel({ "Jets A320_a A320_a_Austrian_Airlines A320_a_Austrian_Airlines", CAircraftModel::TypeModelMatchingDefaultModel, + "A320 AUA", CAircraftIcaoCode("A320", "L2J")}); + this->resetXPlaneData(); + } + + CSimulatorXPlane::~CSimulatorXPlane() + { + this->unload(); + } + + void CSimulatorXPlane::unload() + { + if (!this->isConnected()) { return; } + + // will call disconnect from + CSimulatorPluginCommon::unload(); + delete m_watcher; + m_watcher = nullptr; + } + + QString CSimulatorXPlane::getStatisticsSimulatorSpecific() const + { + return QStringLiteral("Add-time: %1ms/%2ms").arg(m_statsAddCurrentTimeMs).arg(m_statsAddMaxTimeMs); + } + + void CSimulatorXPlane::resetAircraftStatistics() + { + m_statsAddMaxTimeMs = -1; + m_statsAddCurrentTimeMs = -1; + } + + CStatusMessageList CSimulatorXPlane::getInterpolationMessages(const CCallsign &callsign) const + { + if (!m_xplaneAircraftObjects.contains(callsign)) { return CStatusMessageList(); } + const CInterpolationAndRenderingSetupPerCallsign setup = this->getInterpolationSetupConsolidated(callsign, false); + return m_xplaneAircraftObjects[callsign].getInterpolationMessages(setup.getInterpolatorMode()); + } + + void CSimulatorXPlane::clearAllRemoteAircraftData() + { + m_aircraftAddedFailed.clear(); + CSimulatorPluginCommon::clearAllRemoteAircraftData(); + } + + bool CSimulatorXPlane::requestElevation(const ICoordinateGeodetic &reference, const CCallsign &callsign) + { + if (this->isShuttingDownOrDisconnected()) { return false; } + if (reference.isNull()) { return false; } + + CCoordinateGeodetic pos(reference); + if (!pos.hasMSLGeodeticHeight()) + { + // testing showed: height has an influence on the returned result + // - the most accurate value seems to be returned if the height is close to the elevation + // - in normal scenarios there is no much difference of the results if 0 is used + // - in Lukla (9200ft MSL) the difference between 0 and 9200 is around 1ft + // - in the LOWW scenario using 50000ft MSL results in around 3ft too low elevation + static const CAltitude alt(0, CAltitude::MeanSeaLevel, CLengthUnit::ft()); + pos.setGeodeticHeight(alt); + } + + using namespace std::placeholders; + auto callback = std::bind(&CSimulatorXPlane::callbackReceivedRequestedElevation, this, _1, _2); + + // Request + m_trafficProxy->getElevationAtPosition(callsign, + pos.latitude().value(CAngleUnit::deg()), + pos.longitude().value(CAngleUnit::deg()), + pos.geodeticHeight().value(CLengthUnit::m()), + callback); + emit this->requestedElevation(callsign); + return true; + } + + // convert xplane squawk mode to swift squawk mode + CTransponder::TransponderMode xpdrMode(int xplaneMode, bool ident) + { + if (ident) { return CTransponder::StateIdent; } + if (xplaneMode == 0 || xplaneMode == 1) { return CTransponder::StateStandby; } + return CTransponder::ModeC; + } + + // convert swift squawk mode to xplane squawk mode + int xpdrMode(CTransponder::TransponderMode mode) + { + return mode == CTransponder::StateStandby ? 1 : 2; + } + + void CSimulatorXPlane::fastTimerTimeout() + { + if (this->isConnected()) + { + m_serviceProxy->getOwnAircraftSituationData(&m_xplaneData); + m_serviceProxy->getCom1ActiveKhzAsync(&m_xplaneData.com1ActiveKhz); + m_serviceProxy->getCom1StandbyKhzAsync(&m_xplaneData.com1StandbyKhz); + m_serviceProxy->getCom2ActiveKhzAsync(&m_xplaneData.com2ActiveKhz); + m_serviceProxy->getCom2StandbyKhzAsync(&m_xplaneData.com2StandbyKhz); + m_serviceProxy->getTransponderCodeAsync(&m_xplaneData.xpdrCode); + m_serviceProxy->getTransponderModeAsync(&m_xplaneData.xpdrMode); + m_serviceProxy->getTransponderIdentAsync(&m_xplaneData.xpdrIdent); + m_serviceProxy->getAllWheelsOnGroundAsync(&m_xplaneData.onGroundAll); + + CAircraftSituation situation; + situation.setPosition({ m_xplaneData.latitudeDeg, m_xplaneData.longitudeDeg, 0 }); + CAltitude altitude { m_xplaneData.altitudeM, CAltitude::MeanSeaLevel, CLengthUnit::m() }; + situation.setAltitude({ m_xplaneData.altitudeM, CAltitude::MeanSeaLevel, CLengthUnit::m() }); + CPressure seaLevelPressure({ m_xplaneData.seaLevelPressureInHg, CPressureUnit::inHg() }); + CAltitude pressureAltitude(altitude.toPressureAltitude(seaLevelPressure)); + situation.setPressureAltitude(pressureAltitude); + situation.setHeading({ m_xplaneData.trueHeadingDeg, CHeading::True, CAngleUnit::deg() }); + situation.setPitch({ m_xplaneData.pitchDeg, CAngleUnit::deg() }); + situation.setBank({ m_xplaneData.rollDeg, CAngleUnit::deg() }); + situation.setGroundSpeed({ m_xplaneData.groundspeedMs, CSpeedUnit::m_s() }); + + // Updates + // Do not update ICAO codes, as this overrides reverse lookups + // updateOwnIcaoCodes(m_xplaneData.aircraftIcaoCode, CAirlineIcaoCode()); + updateOwnSituation(situation); + + // defaults + CSimulatedAircraft myAircraft(getOwnAircraft()); + CComSystem com1(myAircraft.getCom1System()); // set defaults + CComSystem com2(myAircraft.getCom2System()); + CTransponder transponder(myAircraft.getTransponder()); + + // updates + com1.setFrequencyActive(CFrequency(m_xplaneData.com1ActiveKhz, CFrequencyUnit::kHz())); + com1.setFrequencyStandby(CFrequency(m_xplaneData.com1StandbyKhz, CFrequencyUnit::kHz())); + const bool changedCom1 = myAircraft.getCom1System() != com1; + + com2.setFrequencyActive(CFrequency(m_xplaneData.com2ActiveKhz, CFrequencyUnit::kHz())); + com2.setFrequencyStandby(CFrequency(m_xplaneData.com2StandbyKhz, CFrequencyUnit::kHz())); + const bool changedCom2 = myAircraft.getCom2System() != com2; + + transponder = CTransponder::getStandardTransponder(m_xplaneData.xpdrCode, xpdrMode(m_xplaneData.xpdrMode, m_xplaneData.xpdrIdent)); + const bool changedXpr = (myAircraft.getTransponder() != transponder); + + if (changedCom1 || changedCom2 || changedXpr) + { + this->updateCockpit(com1, com2, transponder, identifier()); + } + + if (m_isWeatherActivated) + { + const auto currentPosition = CCoordinateGeodetic { situation.latitude(), situation.longitude() }; + if (CWeatherScenario::isRealWeatherScenario(m_weatherScenarioSettings.get())) + { + if (m_lastWeatherPosition.isNull() || + calculateGreatCircleDistance(m_lastWeatherPosition, currentPosition).value(CLengthUnit::mi()) > 20) + { + m_lastWeatherPosition = currentPosition; + const auto weatherGrid = CWeatherGrid { { "GLOB", currentPosition } }; + requestWeatherGrid(weatherGrid, { this, &CSimulatorXPlane::injectWeatherGrid }); + } + } + } + } + } + + void CSimulatorXPlane::slowTimerTimeout() + { + if (isConnected()) + { + m_serviceProxy->getAircraftModelPathAsync(&m_xplaneData.aircraftModelPath); // this is NOT the model string + m_serviceProxy->getAircraftIcaoCodeAsync(&m_xplaneData.aircraftIcaoCode); + m_serviceProxy->getBeaconLightsOnAsync(&m_xplaneData.beaconLightsOn); + m_serviceProxy->getLandingLightsOnAsync(&m_xplaneData.landingLightsOn); + m_serviceProxy->getNavLightsOnAsync(&m_xplaneData.navLightsOn); + m_serviceProxy->getStrobeLightsOnAsync(&m_xplaneData.strobeLightsOn); + m_serviceProxy->getTaxiLightsOnAsync(&m_xplaneData.taxiLightsOn); + m_serviceProxy->getFlapsDeployRatioAsync(&m_xplaneData.flapsReployRatio); + m_serviceProxy->getGearDeployRatioAsync(&m_xplaneData.gearReployRatio); + m_serviceProxy->getEngineN1PercentageAsync(&m_xplaneData.enginesN1Percentage); + m_serviceProxy->getSpeedBrakeRatioAsync(&m_xplaneData.speedBrakeRatio); + + CAircraftEngineList engines; + for (int engineNumber = 0; engineNumber < m_xplaneData.enginesN1Percentage.size(); ++engineNumber) + { + // Engine number start counting at 1 + // We consider the engine running when N1 is bigger than 5 % + CAircraftEngine engine {engineNumber + 1, m_xplaneData.enginesN1Percentage.at(engineNumber) > 5.0}; + engines.push_back(engine); + } + + CAircraftParts parts { { + m_xplaneData.strobeLightsOn, m_xplaneData.landingLightsOn, m_xplaneData.taxiLightsOn, + m_xplaneData.beaconLightsOn, m_xplaneData.navLightsOn, false + }, + m_xplaneData.gearReployRatio > 0, static_cast(m_xplaneData.flapsReployRatio * 100), + m_xplaneData.speedBrakeRatio > 0.5, engines, m_xplaneData.onGroundAll + }; + + this->updateOwnParts(parts); + this->requestRemoteAircraftDataFromXPlane(); + + for (CXPlaneMPAircraft &xplaneAircraft : m_xplaneAircraftObjects) + { + // Update remote aircraft to have the latest transponder modes, codes etc. + CSimulatedAircraft simulatedAircraft = this->getAircraftInRangeForCallsign(xplaneAircraft.getCallsign()); + xplaneAircraft.setSimulatedAircraft(simulatedAircraft); + } + } + } + + bool CSimulatorXPlane::isConnected() const + { + return m_serviceProxy && m_trafficProxy && m_weatherProxy; + } + + bool CSimulatorXPlane::connectTo() + { + if (isConnected()) { return true; } + QString dbusAddress = m_xswiftbusServerSetting.getThreadLocal(); + + if (BlackMisc::CDBusServer::isSessionOrSystemAddress(dbusAddress)) + { + m_dBusConnection = connectionFromString(dbusAddress); + m_dbusMode = Session; + } + else if (BlackMisc::CDBusServer::isQtDBusAddress(dbusAddress)) + { + m_dBusConnection = QDBusConnection::connectToPeer(dbusAddress, "xswiftbus"); + if (! m_dBusConnection.isConnected()) { return false; } + m_dbusMode = P2P; + } + + m_serviceProxy = new CXSwiftBusServiceProxy(m_dBusConnection, this); + m_trafficProxy = new CXSwiftBusTrafficProxy(m_dBusConnection, this); + m_weatherProxy = new CXSwiftBusWeatherProxy(m_dBusConnection, this); + + bool s = m_dBusConnection.connect(QString(), DBUS_PATH_LOCAL, DBUS_INTERFACE_LOCAL, + "Disconnected", this, SLOT(serviceUnregistered())); + Q_ASSERT(s); + if (!m_serviceProxy->isValid() || !m_trafficProxy->isValid() || !m_weatherProxy->isValid()) + { + this->disconnectFrom(); + return false; + } + + emitOwnAircraftModelChanged(m_serviceProxy->getAircraftModelPath(), m_serviceProxy->getAircraftModelFilename(), m_serviceProxy->getAircraftLivery(), + m_serviceProxy->getAircraftIcaoCode(), m_serviceProxy->getAircraftModelString(), m_serviceProxy->getAircraftName(), m_serviceProxy->getAircraftDescription()); + QString xplaneVersion = QStringLiteral("%1.%2").arg(m_serviceProxy->getXPlaneVersionMajor()).arg(m_serviceProxy->getXPlaneVersionMinor()); + setSimulatorDetails("X-Plane", {}, xplaneVersion); + connect(m_serviceProxy, &CXSwiftBusServiceProxy::aircraftModelChanged, this, &CSimulatorXPlane::emitOwnAircraftModelChanged); + connect(m_serviceProxy, &CXSwiftBusServiceProxy::airportsInRangeUpdated, this, &CSimulatorXPlane::setAirportsInRange); + m_serviceProxy->updateAirportsInRange(); + connect(m_trafficProxy, &CXSwiftBusTrafficProxy::simFrame, this, &CSimulatorXPlane::updateRemoteAircraft); + connect(m_trafficProxy, &CXSwiftBusTrafficProxy::remoteAircraftAdded, this, &CSimulatorXPlane::onRemoteAircraftAdded); + connect(m_trafficProxy, &CXSwiftBusTrafficProxy::remoteAircraftAddingFailed, this, &CSimulatorXPlane::onRemoteAircraftAddingFailed); + if (m_watcher) { m_watcher->setConnection(m_dBusConnection); } + m_trafficProxy->removeAllPlanes(); + this->loadCslPackages(); + this->emitSimulatorCombinedStatus(); + + this->initSimulatorInternals(); + return true; + } + + bool CSimulatorXPlane::disconnectFrom() + { + if (!this->isConnected()) { return true; } // avoid emit if already disconnected + this->disconnectFromDBus(); + if (m_watcher) { m_watcher->setConnection(m_dBusConnection); } + delete m_serviceProxy; + delete m_trafficProxy; + delete m_weatherProxy; + m_serviceProxy = nullptr; + m_trafficProxy = nullptr; + m_weatherProxy = nullptr; + this->emitSimulatorCombinedStatus(); + return true; + } + + void CSimulatorXPlane::serviceUnregistered() + { + if (m_dbusMode == P2P) { m_dBusConnection.disconnectFromPeer(m_dBusConnection.name()); } + m_dBusConnection = QDBusConnection { "default" }; + if (m_watcher) { m_watcher->setConnection(m_dBusConnection); } + delete m_serviceProxy; + delete m_trafficProxy; + delete m_weatherProxy; + m_serviceProxy = nullptr; + m_trafficProxy = nullptr; + m_weatherProxy = nullptr; + this->emitSimulatorCombinedStatus(); + } + + void CSimulatorXPlane::emitOwnAircraftModelChanged(const QString &path, const QString &filename, const QString &livery, + const QString &icao, const QString &modelString, const QString &name, const QString &description) + { + CAircraftModel model(modelString, CAircraftModel::TypeOwnSimulatorModel, CSimulatorInfo::XPLANE, name, description, icao); + if (!livery.isEmpty()) { model.setModelString(model.getModelString() + " " + livery); } + model.setFileName(path + "/" + filename); + + this->reverseLookupAndUpdateOwnAircraftModel(model); + } + + void CSimulatorXPlane::displayStatusMessage(const CStatusMessage &message) const + { + // No assert here as status message may come because of network problems + if (!isConnected()) { return; } + + // avoid infinite recursion in case this function is called due to a message caused by this very function + static bool isInFunction = false; + if (isInFunction) { return; } + isInFunction = true; + + QColor color = "cyan"; + /* switch (message.getSeverity()) + { + case CStatusMessage::SeverityDebug: color = "teal"; break; + case CStatusMessage::SeverityInfo: color = "cyan"; break; + case CStatusMessage::SeverityWarning: color = "orange"; break; + case CStatusMessage::SeverityError: color = "red"; break; + } */ + + m_serviceProxy->addTextMessage("swift: " + message.getMessage(), color.redF(), color.greenF(), color.blueF()); + isInFunction = false; + } + + void CSimulatorXPlane::displayTextMessage(const Network::CTextMessage &message) const + { + Q_ASSERT(isConnected()); + + QColor color; + if (message.isServerMessage()) { color = "orchid"; } + else if (message.isSupervisorMessage()) { color = "yellow"; } + else if (message.isPrivateMessage()) { color = "magenta"; } + else { color = "lime"; } + + m_serviceProxy->addTextMessage(message.getSenderCallsign().toQString() + ": " + message.getMessage(), color.redF(), color.greenF(), color.blueF()); + } + + void CSimulatorXPlane::setAirportsInRange(const QStringList &icaos, const QStringList &names, const CSequence &lats, const CSequence &lons, const CSequence &alts) + { + //! \todo restrict to maxAirportsInRange() + m_airportsInRange.clear(); + auto icaoIt = icaos.begin(); + auto nameIt = names.begin(); + auto latIt = lats.begin(); + auto lonIt = lons.begin(); + auto altIt = alts.begin(); + for (; icaoIt != icaos.end() && nameIt != names.end() && latIt != lats.end() && lonIt != lons.end() && altIt != alts.end(); ++icaoIt, ++nameIt, ++latIt, ++lonIt, ++altIt) + { + m_airportsInRange.push_back({ *icaoIt, { CLatitude(*latIt, CAngleUnit::deg()), CLongitude(*lonIt, CAngleUnit::deg()), CAltitude(*altIt, CLengthUnit::m()) }, *nameIt }); + } + } + + CAirportList CSimulatorXPlane::getAirportsInRange(bool recalculateDistance) const + { + if (!recalculateDistance) { return m_airportsInRange; } + CAirportList airports(m_airportsInRange); + airports.calculcateAndUpdateRelativeDistanceAndBearing(this->getOwnAircraftPosition()); + return airports; + } + + bool CSimulatorXPlane::setTimeSynchronization(bool enable, const PhysicalQuantities::CTime &offset) + { + Q_UNUSED(offset); + if (enable) + { + CLogMessage(this).info(u"X-Plane provides real time synchronization, use this one"); + } + return false; + } + + QDBusConnection CSimulatorXPlane::connectionFromString(const QString &str) + { + if (str == CDBusServer::sessionBusAddress()) { return QDBusConnection::sessionBus(); } + Q_UNREACHABLE(); + return QDBusConnection("NO CONNECTION"); + } + + bool CSimulatorXPlane::isPhysicallyRenderedAircraft(const CCallsign &callsign) const + { + return m_xplaneAircraftObjects.contains(callsign); + } + + bool CSimulatorXPlane::updateOwnSimulatorCockpit(const Simulation::CSimulatedAircraft &aircraft, const CIdentifier &originator) + { + Q_ASSERT(this->isConnected()); + if (originator == this->identifier()) { return false; } + auto com1 = CComSystem::getCom1System({ m_xplaneData.com1ActiveKhz, CFrequencyUnit::kHz() }, { m_xplaneData.com1StandbyKhz, CFrequencyUnit::kHz() }); + auto com2 = CComSystem::getCom2System({ m_xplaneData.com2ActiveKhz, CFrequencyUnit::kHz() }, { m_xplaneData.com2StandbyKhz, CFrequencyUnit::kHz() }); + auto xpdr = CTransponder::getStandardTransponder(m_xplaneData.xpdrCode, xpdrMode(m_xplaneData.xpdrMode, m_xplaneData.xpdrIdent)); + if (aircraft.hasChangedCockpitData(com1, com2, xpdr)) + { + m_xplaneData.com1ActiveKhz = aircraft.getCom1System().getFrequencyActive().valueInteger(CFrequencyUnit::kHz()); + m_xplaneData.com1StandbyKhz = aircraft.getCom1System().getFrequencyStandby().valueInteger(CFrequencyUnit::kHz()); + m_xplaneData.com2ActiveKhz = aircraft.getCom2System().getFrequencyActive().valueInteger(CFrequencyUnit::kHz()); + m_xplaneData.com2StandbyKhz = aircraft.getCom2System().getFrequencyStandby().valueInteger(CFrequencyUnit::kHz()); + m_xplaneData.xpdrCode = aircraft.getTransponderCode(); + m_xplaneData.xpdrMode = xpdrMode(aircraft.getTransponderMode()); + m_serviceProxy->setCom1ActiveKhz(m_xplaneData.com1ActiveKhz); + m_serviceProxy->setCom1StandbyKhz(m_xplaneData.com1StandbyKhz); + m_serviceProxy->setCom2ActiveKhz(m_xplaneData.com2ActiveKhz); + m_serviceProxy->setCom2StandbyKhz(m_xplaneData.com2StandbyKhz); + m_serviceProxy->setTransponderCode(m_xplaneData.xpdrCode); + m_serviceProxy->setTransponderMode(m_xplaneData.xpdrMode); + + m_serviceProxy->cancelAllPendingAsyncCalls(); // in case there is already a reply with some old data incoming + return true; + } + return false; + } + + bool CSimulatorXPlane::updateOwnSimulatorSelcal(const CSelcal &selcal, const CIdentifier &originator) + { + Q_ASSERT(this->isConnected()); + if (originator == this->identifier()) { return false; } + Q_UNUSED(selcal); + + //! \fixme KB 8/2017 use SELCAL?? + return false; + } + + void CSimulatorXPlane::loadCslPackages() + { + struct Prefix { QString s; }; + struct PrefixComparator + { + bool operator()(const Prefix &a, const QString &b) const { return QStringRef(&a.s) < b.leftRef(a.s.size()); } + bool operator()(const QString &a, const Prefix &b) const { return a.leftRef(b.s.size()) < QStringRef(&b.s); } + }; + QList packages; + + Q_ASSERT(isConnected()); + const CAircraftModelList models = m_modelSet.getThreadLocal(); + for (const auto &model : models) + { + const QString &modelFile = model.getFileName(); + if (modelFile.isEmpty() || ! QFile::exists(modelFile)) { continue; } + auto it = std::lower_bound(packages.begin(), packages.end(), modelFile, PrefixComparator()); + if (it != packages.end() && modelFile.startsWith(it->s)) { continue; } + QString package = findCslPackage(modelFile); + if (package.isEmpty()) { continue; } + packages.insert(it, { package.append('/') }); + } + for (auto &package : packages) + { + Q_ASSERT(package.s.endsWith('/')); + package.s.chop(1); + m_trafficProxy->loadPlanesPackage(package.s); + } + } + + QString CSimulatorXPlane::findCslPackage(const QString &modelFile) + { + //! \todo KB 2018-02 KB when I have removed the CSL dir (acciedently) there was no warning here + const QFileInfo info(modelFile); + QDir dir = info.isDir() ? QDir(modelFile) : info.dir(); + do + { + if (dir.exists(QStringLiteral("xsb_aircraft.txt"))) + { + if (dir.cdUp()) { return dir.path(); } + } + } + while (dir.cdUp()); + CLogMessage(this).warning(u"Failed to find CSL package for %1") << modelFile; + return {}; + } + + bool CSimulatorXPlane::physicallyAddRemoteAircraft(const CSimulatedAircraft &newRemoteAircraft) + { + Q_ASSERT(isConnected()); + // entry checks + Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "thread"); + Q_ASSERT_X(!newRemoteAircraft.getCallsign().isEmpty(), Q_FUNC_INFO, "empty callsign"); + Q_ASSERT_X(newRemoteAircraft.hasModelString(), Q_FUNC_INFO, "missing model string"); + + // crosscheck if still a valid aircraft + // it can happen that aircraft has been removed, timed out ... + if (!this->isAircraftInRange(newRemoteAircraft.getCallsign())) + { + // next cycle will be called by callbacks or timer + CLogMessage(this).warning(u"Aircraft '%1' no longer in range, will not add") << newRemoteAircraft.getCallsign(); + return false; + } + + if (this->canAddAircraft()) + { + // no aircraft pending, add + CLogMessage(this).info(u"Adding '%1' to XPlane") << newRemoteAircraft.getCallsign(); + const qint64 now = QDateTime::currentMSecsSinceEpoch(); + m_addingInProgressAircraft.insert(newRemoteAircraft.getCallsign(), now); + const QString callsign = newRemoteAircraft.getCallsign().asString(); + const CAircraftModel aircraftModel = newRemoteAircraft.getModel(); + const QString livery = aircraftModel.getLivery().getCombinedCode(); //! \todo livery resolution for XP + m_trafficProxy->addPlane(callsign, aircraftModel.getModelString(), + newRemoteAircraft.getAircraftIcaoCode().getDesignator(), + newRemoteAircraft.getAirlineIcaoCode().getDesignator(), + livery); + } + else + { + // add in queue + m_pendingToBeAddedAircraft.replaceOrAdd(newRemoteAircraft); + } + return true; + } + + bool CSimulatorXPlane::physicallyRemoveRemoteAircraft(const CCallsign &callsign) + { + Q_ASSERT(isConnected()); + + // only remove from sim + Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "wrong thread"); + if (callsign.isEmpty()) { return false; } // can happen if an object is not an aircraft + + // really remove from simulator + if (!m_xplaneAircraftObjects.contains(callsign) && !m_pendingToBeAddedAircraft.containsCallsign(callsign) && !m_addingInProgressAircraft.contains(callsign)) + { + // not existing aircraft + return false; + } + + // mark in provider + const bool updated = this->updateAircraftRendered(callsign, false); + if (updated) + { + if (m_xplaneAircraftObjects.contains(callsign)) + { + const CXPlaneMPAircraft &xplaneAircraft = m_xplaneAircraftObjects[callsign]; + CSimulatedAircraft aircraft(xplaneAircraft.getAircraft()); + aircraft.setRendered(false); + emit this->aircraftRenderingChanged(aircraft); + } + else if (m_pendingToBeAddedAircraft.containsCallsign(callsign)) + { + CSimulatedAircraft aircraft = m_pendingToBeAddedAircraft.findFirstByCallsign(callsign); + aircraft.setRendered(false); + emit this->aircraftRenderingChanged(aircraft); + } + else if (m_addingInProgressAircraft.contains(callsign)) + { + // we are just about to add that aircraft + QPointer myself(this); + QTimer::singleShot(TimeoutAdding, this, [ = ] + { + if (!myself) { return; } + m_addingInProgressAircraft.remove(callsign); // remove as "in progress" + this->physicallyRemoveRemoteAircraft(callsign); // and remove from sim. if it was added in the mean time + }); + } + } + + m_trafficProxy->removePlane(callsign.asString()); + m_xplaneAircraftObjects.remove(callsign); + m_pendingToBeAddedAircraft.removeByCallsign(callsign); + + // bye + return true; + } + + int CSimulatorXPlane::physicallyRemoveAllRemoteAircraft() + { + if (!this->isConnected()) { return 0; } + m_pendingToBeAddedAircraft.clear(); + m_addingInProgressAircraft.clear(); + return CSimulatorPluginCommon::physicallyRemoveAllRemoteAircraft(); + } + + CCallsignSet CSimulatorXPlane::physicallyRenderedAircraft() const + { + //! \todo XP driver, return list of callsigns really present in the simulator + return this->getAircraftInRange().findByRendered(true).getCallsigns(); // just a poor workaround + } + + bool CSimulatorXPlane::followAircraft(const CCallsign &callsign) + { + if (! m_trafficProxy || ! m_trafficProxy->isValid()) { return false; } + m_trafficProxy->setFollowedAircraft(callsign.toQString()); + return true; + } + + void CSimulatorXPlane::injectWeatherGrid(const Weather::CWeatherGrid &weatherGrid) + { + Q_ASSERT(isConnected()); + m_weatherProxy->setUseRealWeather(false); + + //! TODO: find the closest + if (weatherGrid.isEmpty()) { return; } + const CGridPoint gridPoint = weatherGrid.front(); + + // todo: find the closest + auto visibilityLayers = gridPoint.getVisibilityLayers(); + visibilityLayers.sortBy(&CVisibilityLayer::getBase); + const CVisibilityLayer visibilityLayer = visibilityLayers.frontOrDefault(); + m_weatherProxy->setVisibility(visibilityLayer.getVisibility().value(CLengthUnit::m())); + + CTemperatureLayerList temperatureLayers = gridPoint.getTemperatureLayers(); + temperatureLayers.sortBy(&CTemperatureLayer::getLevel); + const CTemperatureLayer temperatureLayer = temperatureLayers.frontOrDefault(); + m_weatherProxy->setTemperature(temperatureLayer.getTemperature().valueInteger(CTemperatureUnit::C())); + m_weatherProxy->setDewPoint(temperatureLayer.getDewPoint().valueInteger(CTemperatureUnit::C())); + m_weatherProxy->setQNH(gridPoint.getSurfacePressure().value(CPressureUnit::inHg())); + + int layerNumber = 0; + CCloudLayerList cloudLayers = gridPoint.getCloudLayers(); + auto numberOfLayers = cloudLayers.size(); + // Fill cloud layers if less then 3 + while (numberOfLayers < 3) + { + cloudLayers.push_back(CCloudLayer()); + numberOfLayers++; + } + cloudLayers.sortBy(&CCloudLayer::getBase); + // todo: Instead of truncate, find the 3 vertical closest cloud layers + cloudLayers.truncate(3); + for (const auto &cloudLayer : as_const(cloudLayers)) + { + const int base = cloudLayer.getBase().valueInteger(CLengthUnit::m()); + const int top = cloudLayer.getTop().valueInteger(CLengthUnit::m()); + + int coverage = 0; + switch (cloudLayer.getCoverage()) + { + case CCloudLayer::None: coverage = 0; break; + case CCloudLayer::Few: coverage = 2; break; + case CCloudLayer::Scattered: coverage = 3; break; + case CCloudLayer::Broken: coverage = 4; break; + case CCloudLayer::Overcast: coverage = 6; break; + default: coverage = 0; break; + } + + // Clear = 0, High Cirrus = 1, Scattered = 2, Broken = 3, Overcast = 4, Stratus = 5 + int type = 0; + switch (cloudLayer.getClouds()) + { + case CCloudLayer::NoClouds: type = 0; break; + case CCloudLayer::Cirrus: type = 1; break; + case CCloudLayer::Stratus: type = 5; break; + default: type = 0; break; + } + + m_weatherProxy->setCloudLayer(layerNumber, base, top, type, coverage); + layerNumber++; + } + + layerNumber = 0; + CWindLayerList windLayers = gridPoint.getWindLayers(); + numberOfLayers = windLayers.size(); + // Fill cloud layers if less then 3 + while (numberOfLayers < 3) + { + windLayers.push_back(CWindLayer()); + numberOfLayers++; + } + windLayers.sortBy(&CWindLayer::getLevel); + // todo: Instead of truncate, find the 3 vertical closest cloud layers + windLayers.truncate(3); + for (const auto &windLayer : windLayers) + { + const int altitudeMeter = windLayer.getLevel().valueInteger(CLengthUnit::m()); + const double directionDeg = windLayer.getDirection().value(CAngleUnit::deg()); + const int speedKts = windLayer.getSpeed().valueInteger(CSpeedUnit::kts()); + m_weatherProxy->setWindLayer(layerNumber, altitudeMeter, directionDeg, speedKts, 0, 0, 0); + layerNumber++; + } + + m_weatherProxy->setPrecipitationRatio(cloudLayers.frontOrDefault().getPrecipitationRate()); + m_weatherProxy->setThunderstormRatio(0.0); + } + + void CSimulatorXPlane::updateRemoteAircraft() + { + Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "thread"); + + const int remoteAircraftNo = this->getAircraftInRangeCount(); + if (remoteAircraftNo < 1) { return; } + + // values used for position and parts + m_updateRemoteAircraftInProgress = true; + const qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch(); + + // interpolation for all remote aircraft + PlanesPositions planesPositions; + PlanesSurfaces planesSurfaces; + PlanesTransponders planesTransponders; + + int aircraftNumber = 0; + const bool updateAllAircraft = this->isUpdateAllRemoteAircraft(currentTimestamp); + for (const CXPlaneMPAircraft &xplaneAircraft : m_xplaneAircraftObjects) + { + const CCallsign callsign(xplaneAircraft.getCallsign()); + Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "missing callsign"); + + planesTransponders.callsigns.push_back(callsign.asString()); + planesTransponders.codes.push_back(xplaneAircraft.getAircraft().getTransponderCode()); + CTransponder::TransponderMode transponderMode = xplaneAircraft.getAircraft().getTransponderMode(); + planesTransponders.idents.push_back(transponderMode == CTransponder::StateIdent); + planesTransponders.modeCs.push_back(transponderMode == CTransponder::ModeC); + + // setup + const CInterpolationAndRenderingSetupPerCallsign setup = this->getInterpolationSetupConsolidated(callsign, updateAllAircraft); + + // interpolated situation/parts + const CInterpolationResult result = xplaneAircraft.getInterpolation(currentTimestamp, setup, aircraftNumber++); + if (result.getInterpolationStatus().hasValidSituation()) + { + const CAircraftSituation interpolatedSituation(result); + + // update situation + if (updateAllAircraft || !this->isEqualLastSent(interpolatedSituation)) + { + this->rememberLastSent(interpolatedSituation); + planesPositions.callsigns.push_back(interpolatedSituation.getCallsign().asString()); + planesPositions.latitudesDeg.push_back(interpolatedSituation.latitude().value(CAngleUnit::deg())); + planesPositions.longitudesDeg.push_back(interpolatedSituation.longitude().value(CAngleUnit::deg())); + planesPositions.altitudesFt.push_back(interpolatedSituation.getAltitude().value(CLengthUnit::ft())); + planesPositions.pitchesDeg.push_back(interpolatedSituation.getPitch().value(CAngleUnit::deg())); + planesPositions.rollsDeg.push_back(interpolatedSituation.getBank().value(CAngleUnit::deg())); + planesPositions.headingsDeg.push_back(interpolatedSituation.getHeading().value(CAngleUnit::deg())); + planesPositions.onGrounds.push_back(interpolatedSituation.getOnGround() == CAircraftSituation::OnGround); + } + } + else + { + CLogMessage(this).warning(this->getInvalidSituationLogMessage(callsign, result.getInterpolationStatus())); + } + + const CAircraftParts parts(result); + if (result.getPartsStatus().isSupportingParts() || parts.getPartsDetails() == CAircraftParts::GuessedParts) + { + if (updateAllAircraft || !this->isEqualLastSent(parts, callsign)) + { + this->rememberLastSent(parts, callsign); + planesSurfaces.callsigns.push_back(xplaneAircraft.getCallsign().asString()); + planesSurfaces.gears.push_back(parts.isGearDown() ? 1 : 0); + planesSurfaces.flaps.push_back(parts.getFlapsPercent() / 100.0); + planesSurfaces.spoilers.push_back(parts.isSpoilersOut() ? 1 : 0); + planesSurfaces.speedBrakes.push_back(parts.isSpoilersOut() ? 1 : 0); + planesSurfaces.slats.push_back(parts.getFlapsPercent() / 100.0); + planesSurfaces.wingSweeps.push_back(0.0); + planesSurfaces.thrusts.push_back(parts.isAnyEngineOn() ? 0 : 0.75); + planesSurfaces.elevators.push_back(0.0); + planesSurfaces.rudders.push_back(0.0); + planesSurfaces.ailerons.push_back(0.0); + planesSurfaces.landLights.push_back(parts.getLights().isLandingOn()); + planesSurfaces.beaconLights.push_back(parts.getLights().isBeaconOn()); + planesSurfaces.strobeLights.push_back(parts.getLights().isStrobeOn()); + planesSurfaces.navLights.push_back(parts.getLights().isNavOn()); + planesSurfaces.lightPatterns.push_back(0); + } + } + + } // all callsigns + + if (!planesTransponders.isEmpty()) + { + m_trafficProxy->setPlanesTransponders(planesTransponders); + } + + if (!planesPositions.isEmpty()) + { + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + Q_ASSERT_X(planesPositions.hasSameSizes(), Q_FUNC_INFO, "Mismatching sizes"); + } + m_trafficProxy->setPlanesPositions(planesPositions); + } + + if (! planesSurfaces.isEmpty()) + { + m_trafficProxy->setPlanesSurfaces(planesSurfaces); + } + + // stats + this->finishUpdateRemoteAircraftAndSetStatistics(currentTimestamp); + } + + void CSimulatorXPlane::requestRemoteAircraftDataFromXPlane() + { + if (!isConnected()) { return; } + + // It is not required to request all elevations and CGs, but only for aircraft "near ground relevant" + // - we could use the elevation cache and CG cache to decide if we need to request + // - if an aircraft is on ground but not moving, we do not need to request elevation if we already have it (it will not change + CCallsignSet callsigns = m_xplaneAircraftObjects.getAllCallsigns(); + const CCallsignSet remove = this->getLastSentCanLikelySkipNearGroundInterpolation().getCallsigns(); + callsigns.remove(remove); + if (!callsigns.isEmpty()) { this->requestRemoteAircraftDataFromXPlane(callsigns); } + } + + void CSimulatorXPlane::requestRemoteAircraftDataFromXPlane(const CCallsignSet &callsigns) + { + if (callsigns.isEmpty()) { return; } + if (this->isShuttingDown()) { return; } + const QStringList csStrings = callsigns.getCallsignStrings(); + m_trafficProxy->getRemoteAircraftData(csStrings, [ = ](const QStringList & callsigns, const QDoubleList & latitudesDeg, const QDoubleList & longitudesDeg, const QDoubleList & elevationsMeters, const QDoubleList & verticalOffsetsMeters) + { + this->updateRemoteAircraftFromSimulator(callsigns, latitudesDeg, longitudesDeg, elevationsMeters, verticalOffsetsMeters); + }); + } + + void CSimulatorXPlane::triggerRequestRemoteAircraftDataFromXPlane(const CCallsignSet &callsigns) + { + if (callsigns.isEmpty()) { return; } + QPointer myself(this); + QTimer::singleShot(0, this, [ = ] + { + if (!myself) { return; } + this->requestRemoteAircraftDataFromXPlane(callsigns); + }); + } + + void CSimulatorXPlane::updateRemoteAircraftFromSimulator( + const QStringList &callsigns, const QDoubleList &latitudesDeg, const QDoubleList &longitudesDeg, + const QDoubleList &elevationsMeters, const QDoubleList &verticalOffsetsMeters) + { + const int size = callsigns.size(); + + // we skip if we are not near ground + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + Q_ASSERT_X(elevationsMeters.size() == size, Q_FUNC_INFO, "Wrong elevations"); + Q_ASSERT_X(latitudesDeg.size() == size, Q_FUNC_INFO, "Wrong latitudesDeg"); + Q_ASSERT_X(longitudesDeg.size() == size, Q_FUNC_INFO, "Wrong longitudesDeg"); + Q_ASSERT_X(verticalOffsetsMeters.size() == size, Q_FUNC_INFO, "Wrong CG"); + } + + const CCallsignSet logCallsigns = this->getLogCallsigns(); + for (int i = 0; i < size; i++) + { + const CCallsign cs(callsigns[i]); + if (!m_xplaneAircraftObjects.contains(cs)) { continue; } + const CXPlaneMPAircraft xpAircraft = m_xplaneAircraftObjects[cs]; + + const double cgValue = verticalOffsetsMeters[i]; // XP offset is swift CG + const CAltitude elevationAlt(elevationsMeters[i], CLengthUnit::m(), CLengthUnit::ft()); + const CElevationPlane elevation(CLatitude(latitudesDeg[i], CAngleUnit::deg()), CLongitude(longitudesDeg[i], CAngleUnit::deg()), elevationAlt, CElevationPlane::singlePointRadius()); + const CLength cg = std::isnan(cgValue) ? + CLength::null() : + CLength(cgValue, CLengthUnit::m(), CLengthUnit::ft()); + this->rememberElevationAndCG(cs, xpAircraft.getAircraftModelString(), elevation, cg); + + // loopback + if (logCallsigns.contains(cs)) + { + this->addLoopbackSituation(cs, elevation, cg); + } + } + } + + void CSimulatorXPlane::disconnectFromDBus() + { + if (m_dBusConnection.isConnected()) + { + if (m_trafficProxy) { m_trafficProxy->cleanup(); } + + if (m_dbusMode == P2P) { QDBusConnection::disconnectFromPeer(m_dBusConnection.name()); } + else { QDBusConnection::disconnectFromBus(m_dBusConnection.name()); } + } + m_dBusConnection = QDBusConnection { "default" }; + } + + void CSimulatorXPlane::updateAirportsInRange() + { + if (this->isConnected()) { m_serviceProxy->updateAirportsInRange(); } + } + + void CSimulatorXPlane::onRemoteAircraftAdded(const QString &callsign) + { + const CCallsign cs(callsign); + CSimulatedAircraft addedRemoteAircraft = this->getAircraftInRangeForCallsign(cs); + + // statistics + bool wasPending = false; + if (m_addingInProgressAircraft.contains(cs)) + { + wasPending = true; + const qint64 wasStartedMs = m_addingInProgressAircraft.value(cs); + const qint64 deltaTimeMs = QDateTime::currentMSecsSinceEpoch() - wasStartedMs; + m_statsAddCurrentTimeMs = deltaTimeMs; + if (deltaTimeMs > m_statsAddMaxTimeMs) { m_statsAddMaxTimeMs = deltaTimeMs; } + m_addingInProgressAircraft.remove(cs); + } + + if (!addedRemoteAircraft.hasCallsign()) + { + CLogMessage(this).warning(u"Aircraft '%1' no longer in range, will be removed") << callsign; + this->triggerRemoveAircraft(cs, TimeoutAdding); + return; + } + + CLogMessage(this).info(u"Added aircraft '%1'") << callsign; + if (!wasPending) + { + // timeout? + // slow adding? + CLogMessage(this).warning(u"Added callsign '%1' was not in progress anymore. Timeout?") << callsign; + } + + const bool rendered = true; + addedRemoteAircraft.setRendered(rendered); + this->updateAircraftRendered(cs, rendered); + this->triggerRequestRemoteAircraftDataFromXPlane(cs); + this->triggerAddNextPendingAircraft(); + + m_xplaneAircraftObjects.insert(addedRemoteAircraft.getCallsign(), CXPlaneMPAircraft(addedRemoteAircraft, this, &m_interpolationLogger)); + emit this->aircraftRenderingChanged(addedRemoteAircraft); + } + + void CSimulatorXPlane::onRemoteAircraftAddingFailed(const QString &callsign) + { + + const CCallsign cs(callsign); + CSimulatedAircraft failedRemoteAircraft = this->getAircraftInRangeForCallsign(cs); + + if (failedRemoteAircraft.hasCallsign()) + { + CLogMessage(this).warning(u"Adding aircraft failed: '%1'") << callsign; + failedRemoteAircraft.setRendered(false); + } + else + { + CLogMessage(this).warning(u"Adding '%1' failed, but aircraft no longer in range, will be removed") << callsign; + } + + const bool wasPending = (m_addingInProgressAircraft.remove(cs) > 0); + Q_UNUSED(wasPending); + + if (failedRemoteAircraft.hasCallsign() && !m_aircraftAddedFailed.containsCallsign(cs)) + { + m_aircraftAddedFailed.push_back(failedRemoteAircraft); + m_pendingToBeAddedAircraft.replaceOrAdd(failedRemoteAircraft); // try a second time + } + this->triggerAddNextPendingAircraft(); + } + + void CSimulatorXPlane::addNextPendingAircraft() + { + if (m_pendingToBeAddedAircraft.isEmpty()) { return; } // no more pending + + // housekeeping + this->detectTimeoutAdding(); + + // check if can add + if (!this->canAddAircraft()) { return; } + + // next add cycle + const CSimulatedAircraft newRemoteAircraft = m_pendingToBeAddedAircraft.front(); + m_pendingToBeAddedAircraft.pop_front(); + CLogMessage(this).info(u"Adding next pending aircraft '%1', pending %2, in progress %3") << newRemoteAircraft.getCallsignAsString() << m_pendingToBeAddedAircraft.size() << m_addingInProgressAircraft.size(); + this->physicallyAddRemoteAircraft(newRemoteAircraft); + } + + void CSimulatorXPlane::triggerAddNextPendingAircraft() + { + QPointer myself(this); + QTimer::singleShot(100, this, [ = ] + { + if (!myself) { return; } + this->addNextPendingAircraft(); + }); + } + + int CSimulatorXPlane::detectTimeoutAdding() + { + if (m_addingInProgressAircraft.isEmpty()) { return 0; } + const qint64 timeout = QDateTime::currentMSecsSinceEpoch() + TimeoutAdding; + CCallsignSet timeoutCallsigns; + const QList addingCallsigns = m_addingInProgressAircraft.keys(); + for (const CCallsign &cs : addingCallsigns) + { + if (m_addingInProgressAircraft.value(cs) < timeout) { continue; } + timeoutCallsigns.push_back(cs); + } + + for (const CCallsign &cs : as_const(timeoutCallsigns)) + { + m_addingInProgressAircraft.remove(cs); + CLogMessage(this).warning(u"Adding for '%1' timed out") << cs.asString(); + } + + return timeoutCallsigns.size(); + } + + void CSimulatorXPlane::triggerRemoveAircraft(const CCallsign &callsign, qint64 deferMs) + { + QPointer myself(this); + QTimer::singleShot(deferMs, this, [ = ] + { + if (!myself) { return; } + this->physicallyRemoveRemoteAircraft(callsign); + }); + } + + QPair CSimulatorXPlane::minMaxTimestampsAddInProgress() const + { + static const QPair empty(-1, -1); + if (m_addingInProgressAircraft.isEmpty()) { return empty; } + const QList ts = m_addingInProgressAircraft.values(); + const auto mm = std::minmax_element(ts.constBegin(), ts.constEnd()); + return QPair(*mm.first, *mm.second); + } + + bool CSimulatorXPlane::canAddAircraft() const + { + if (m_addingInProgressAircraft.isEmpty()) { return true; } + + // check + const qint64 now = QDateTime::currentMSecsSinceEpoch(); + const QPair tsMM = this->minMaxTimestampsAddInProgress(); + const qint64 deltaLatest = now - tsMM.second; + const bool canAdd = (deltaLatest > TimeoutAdding); + return canAdd; + } + + ISimulator *CSimulatorXPlaneFactory::create(const CSimulatorPluginInfo &info, + IOwnAircraftProvider *ownAircraftProvider, + IRemoteAircraftProvider *remoteAircraftProvider, + IWeatherGridProvider *weatherGridProvider, + IClientProvider *clientProvider) + { + return new CSimulatorXPlane(info, ownAircraftProvider, remoteAircraftProvider, weatherGridProvider, clientProvider, this); + } + + CSimulatorXPlaneListener::CSimulatorXPlaneListener(const CSimulatorPluginInfo &info): ISimulatorListener(info) + { + constexpr int QueryInterval = 5 * 1000; // 5 seconds + m_timer.setInterval(QueryInterval); + m_timer.setObjectName(this->objectName().append(":m_timer")); + connect(&m_timer, &QTimer::timeout, this, &CSimulatorXPlaneListener::checkConnection); + } + + void CSimulatorXPlaneListener::startImpl() + { + m_timer.start(); + } + + void CSimulatorXPlaneListener::stopImpl() + { + m_timer.stop(); + } + + void CSimulatorXPlaneListener::checkImpl() + { + if (!m_timer.isActive()) { return; } + if (this->isShuttingDown()) { return; } + + m_timer.start(); // restart because we will check just now + QPointer myself(this); + QTimer::singleShot(0, this, [ = ] + { + if (!myself) { return; } + checkConnection(); + }); + } + + void CSimulatorXPlaneListener::checkConnection() + { + if (this->isShuttingDown()) { return; } + Q_ASSERT_X(!CThreadUtils::isCurrentThreadApplicationThread(), Q_FUNC_INFO, "Expect to run in background"); + + QString dbusAddress = m_xswiftbusServerSetting.getThreadLocal(); + if (CDBusServer::isSessionOrSystemAddress(dbusAddress)) + { + checkConnectionViaBus(dbusAddress); + } + else if (CDBusServer::isQtDBusAddress(dbusAddress)) + { + checkConnectionViaPeer(dbusAddress); + } + } + + void CSimulatorXPlaneListener::checkConnectionViaBus(const QString &address) + { + m_conn = CSimulatorXPlane::connectionFromString(address); + if (!m_conn.isConnected()) + { + m_conn.disconnectFromBus(m_conn.name()); + return; + } + checkConnectionCommon(); + m_conn.disconnectFromBus(m_conn.name()); + } + + void CSimulatorXPlaneListener::checkConnectionViaPeer(const QString &address) + { + m_conn = QDBusConnection::connectToPeer(address, "xswiftbus"); + if (!m_conn.isConnected()) + { + // This is required to cleanup the connection in QtDBus + m_conn.disconnectFromPeer(m_conn.name()); + return; + } + checkConnectionCommon(); + m_conn.disconnectFromPeer(m_conn.name()); + } + + void CSimulatorXPlaneListener::checkConnectionCommon() + { + CXSwiftBusServiceProxy service(m_conn); + CXSwiftBusTrafficProxy traffic(m_conn); + CXSwiftBusWeatherProxy weather(m_conn); + + bool result = service.isValid() && traffic.isValid() && weather.isValid(); + if (! result) { return; } + + QString swiftVersion = BlackConfig::CBuildConfig::getVersionString(); + QString xswiftbusVersion = service.getVersionNumber(); + if (! swiftVersion.contains(xswiftbusVersion)) + { + CLogMessage(this).error(u"You are using an incorrect version of XSwiftBus. Make sure to install %1 into X-Plane plugins!") << xswiftbusVersion; + return; + } + + if (!traffic.initialize()) + { + CLogMessage(this).error(u"Connection to XSwiftBus successful, but could not initialize XSwiftBus. Check X-Plane Log.txt."); + return; + } + + MultiplayerAcquireInfo info = traffic.acquireMultiplayerPlanes(); + if (! info.hasAcquired) + { + CLogMessage(this).error(u"Connection to XSwiftBus successful, but could not acquire multiplayer planes. %1 has acquired them already. Disable %2 or remove it if not required and reload XSwiftBus.") << info.owner << info.owner; + return; + } + + emit simulatorStarted(getPluginInfo()); + } + + void CSimulatorXPlaneListener::serviceRegistered(const QString &serviceName) + { + if (serviceName == xswiftbusServiceName()) + { + emit simulatorStarted(getPluginInfo()); + } + m_conn.disconnectFromBus(m_conn.name()); + } + + void CSimulatorXPlaneListener::xSwiftBusServerSettingChanged() + { + this->stop(); + this->start(); + } + } // namespace +} // namespace diff --git a/src/plugins/simulator/flightgear/simulatorxplane.h b/src/plugins/simulator/flightgear/simulatorxplane.h new file mode 100644 index 000000000..a9beb4078 --- /dev/null +++ b/src/plugins/simulator/flightgear/simulatorxplane.h @@ -0,0 +1,314 @@ +/* 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. + */ + +//! \file + +#ifndef BLACKSIMPLUGIN_SIMULATOR_XPLANE_H +#define BLACKSIMPLUGIN_SIMULATOR_XPLANE_H + +#include "xplanempaircraft.h" +#include "plugins/simulator/xplaneconfig/simulatorxplaneconfig.h" +#include "plugins/simulator/plugincommon/simulatorplugincommon.h" +#include "blackmisc/simulation/aircraftmodellist.h" +#include "blackmisc/simulation/data/modelcaches.h" +#include "blackmisc/simulation/settings/simulatorsettings.h" +#include "blackmisc/simulation/settings/mysettings.h" +#include "blackmisc/simulation/simulatedaircraftlist.h" +#include "blackmisc/weather/weathergrid.h" +#include "blackmisc/aviation/airportlist.h" +#include "blackmisc/aviation/callsignset.h" +#include "blackmisc/geo/coordinategeodetic.h" +#include "blackmisc/pq/time.h" +#include "blackmisc/pq/units.h" +#include "blackmisc/settingscache.h" +#include "blackmisc/statusmessage.h" +#include "blackmisc/identifier.h" +#include "blackmisc/pixmap.h" +#include "blackmisc/sequence.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class QDBusServiceWatcher; + +namespace BlackMisc +{ + namespace Aviation + { + class CAircraftParts; + class CAircraftSituation; + class CCallsign; + } + namespace Network { class CTextMessage; } + namespace Simulation + { + class CSimulatedAircraft; + class CSimulatorPluginInfo; + class IOwnAircraftProvider; + class IRemoteAircraftProvider; + } + namespace Weather { class IWeatherGridProvider; } +} + +namespace BlackSimPlugin +{ + namespace XPlane + { + class CXSwiftBusServiceProxy; + class CXSwiftBusTrafficProxy; + class CXSwiftBusWeatherProxy; + + //! X-Plane data + struct XPlaneData + { + QString aircraftModelPath; //!< Aircraft model path + QString aircraftIcaoCode; //!< Aircraft ICAO code + double latitudeDeg; //!< Longitude [deg] + double longitudeDeg; //!< Latitude [deg] + double altitudeM; //!< Altitude [m] + double groundspeedMs; //!< Ground speed [m/s] + double pitchDeg; //!< Pitch [deg] + double rollDeg; //!< Roll [deg] + double trueHeadingDeg; //!< True heading [deg] + bool onGroundAll; //!< All wheels on ground? + int com1ActiveKhz; //!< COM1 active [kHz] + int com1StandbyKhz; //!< COM1 standby [kHz] + int com2ActiveKhz; //!< COM2 active [kHz] + int com2StandbyKhz; //!< COM2 standby [kHz] + int xpdrCode; //!< Transpondder code + int xpdrMode; //!< Transponder mode (off=0,stdby=1,on=2,test=3) + bool xpdrIdent; //!< Is transponder in ident? + bool beaconLightsOn; //!< Beacon lights on? + bool landingLightsOn; //!< Landing lights on? + bool navLightsOn; //!< NAV lights on? + bool strobeLightsOn; //!< Strobe lights on? + bool taxiLightsOn; //!< Taxi lights on? + double flapsReployRatio; //!< Flaps deployment ratio [%] + double gearReployRatio; //!< Gear deployment ratio [%] + QList enginesN1Percentage; //!< N1 per engine [%] + double speedBrakeRatio; //!< Speed break ratio [%] + double seaLevelPressureInHg; //!< Sea level pressure [inhg] + }; + + //! X-Plane ISimulator implementation + class CSimulatorXPlane : public Common::CSimulatorPluginCommon + { + Q_OBJECT + + public: + //! Constructor + CSimulatorXPlane(const BlackMisc::Simulation::CSimulatorPluginInfo &info, + BlackMisc::Simulation::IOwnAircraftProvider *ownAircraftProvider, + BlackMisc::Simulation::IRemoteAircraftProvider *remoteAircraftProvider, + BlackMisc::Weather::IWeatherGridProvider *weatherGridProvider, + BlackMisc::Network::IClientProvider *clientProvider, + QObject *parent = nullptr); + + //! Dtor + virtual ~CSimulatorXPlane() override; + + //! \name ISimulator implementations + //! @{ + virtual bool isTimeSynchronized() const override { return false; } // TODO: Can we query the XP intrinisc feature? + virtual bool connectTo() override; + virtual bool disconnectFrom() override; + virtual bool updateOwnSimulatorCockpit(const BlackMisc::Simulation::CSimulatedAircraft &aircraft, const BlackMisc::CIdentifier &originator) override; + virtual bool updateOwnSimulatorSelcal(const BlackMisc::Aviation::CSelcal &selcal, const BlackMisc::CIdentifier &originator) override; + virtual void displayStatusMessage(const BlackMisc::CStatusMessage &message) const override; + virtual void displayTextMessage(const BlackMisc::Network::CTextMessage &message) const override; + virtual BlackMisc::Aviation::CAirportList getAirportsInRange(bool recalculateDistance) const override; + virtual bool setTimeSynchronization(bool enable, const BlackMisc::PhysicalQuantities::CTime &offset) override; + virtual BlackMisc::PhysicalQuantities::CTime getTimeSynchronizationOffset() const override { return BlackMisc::PhysicalQuantities::CTime(0, BlackMisc::PhysicalQuantities::CTimeUnit::hrmin()); } + virtual bool isPhysicallyRenderedAircraft(const BlackMisc::Aviation::CCallsign &callsign) const override; + virtual BlackMisc::Aviation::CCallsignSet physicallyRenderedAircraft() const override; + virtual bool followAircraft(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void unload() override; + virtual QString getStatisticsSimulatorSpecific() const override; + virtual void resetAircraftStatistics() override; + virtual BlackMisc::CStatusMessageList getInterpolationMessages(const BlackMisc::Aviation::CCallsign &callsign) const override; + //! @} + + //! \copydoc BlackMisc::Simulation::ISimulationEnvironmentProvider::requestElevation + virtual bool requestElevation(const BlackMisc::Geo::ICoordinateGeodetic &reference, const BlackMisc::Aviation::CCallsign &callsign) override; + + //! Creates an appropriate dbus connection from the string describing it + static QDBusConnection connectionFromString(const QString &str); + + protected: + //! \name ISimulator implementations + //! @{ + virtual bool isConnected() const override; + virtual bool physicallyAddRemoteAircraft(const BlackMisc::Simulation::CSimulatedAircraft &newRemoteAircraft) override; + virtual bool physicallyRemoveRemoteAircraft(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual int physicallyRemoveAllRemoteAircraft() override; + virtual void clearAllRemoteAircraftData() override; + virtual void injectWeatherGrid(const BlackMisc::Weather::CWeatherGrid &weatherGrid) override; + virtual bool isPaused() const override + { + //! \todo XP: provide correct pause state + return false; + } + //! @} + + private slots: + void serviceUnregistered(); + + private: + enum DBusMode + { + Session, + P2P + }; + + using QDoubleList = QList; + + void setAirportsInRange(const QStringList &icaoCodes, const QStringList &names, const BlackMisc::CSequence &lats, const BlackMisc::CSequence &lons, const BlackMisc::CSequence &alts); + void emitOwnAircraftModelChanged(const QString &path, const QString &filename, const QString &livery, const QString &icao, + const QString &modelString, const QString &name, const QString &description); + void fastTimerTimeout(); + void slowTimerTimeout(); + + void loadCslPackages(); + QString findCslPackage(const QString &modelFileName); + + //! Update remote aircraft + //! \remark this is where the interpolated data are set + void updateRemoteAircraft(); + + //! Update airports + void updateAirportsInRange(); + + //! Request elevation and CG from XPlane @{ + void requestRemoteAircraftDataFromXPlane(); + void requestRemoteAircraftDataFromXPlane(const BlackMisc::Aviation::CCallsignSet &callsigns); + void triggerRequestRemoteAircraftDataFromXPlane(const BlackMisc::Aviation::CCallsignSet &callsigns); + //! @} + + //! Adding new aircraft @{ + void addNextPendingAircraft(); + void triggerAddNextPendingAircraft(); + //! @} + + //! Detect timeouts on adding + int detectTimeoutAdding(); + + //! Trigger a removal of an aircraft + void triggerRemoveAircraft(const BlackMisc::Aviation::CCallsign &callsign, qint64 deferMs); + + //! Timestamps of aircraft currently adding + QPair minMaxTimestampsAddInProgress() const; + + //! Can the next aircraft be added? + bool canAddAircraft() const; + + //! Callbacks from simulator @{ + void onRemoteAircraftAdded(const QString &callsign); + void onRemoteAircraftAddingFailed(const QString &callsign); + void updateRemoteAircraftFromSimulator(const QStringList &callsigns, const QDoubleList &latitudesDeg, const QDoubleList &longitudesDeg, + const QDoubleList &elevationsMeters, const QDoubleList &verticalOffsetsMeters); + //! @} + + //! Dsiconnect from DBus + void disconnectFromDBus(); + + DBusMode m_dbusMode; + BlackMisc::CSettingReadOnly m_xswiftbusServerSetting { this }; + static constexpr qint64 TimeoutAdding = 10000; + QDBusConnection m_dBusConnection { "default" }; + QDBusServiceWatcher *m_watcher { nullptr }; + CXSwiftBusServiceProxy *m_serviceProxy { nullptr }; + CXSwiftBusTrafficProxy *m_trafficProxy { nullptr }; + CXSwiftBusWeatherProxy *m_weatherProxy { nullptr }; + QTimer m_fastTimer; + QTimer m_slowTimer; + QTimer m_airportUpdater; + QTimer m_pendingAddedTimer; + BlackMisc::Aviation::CAirportList m_airportsInRange; //!< aiports in range of own aircraft + BlackMisc::CData m_modelSet { this }; //!< XPlane model set + BlackMisc::Simulation::CSimulatedAircraftList m_pendingToBeAddedAircraft; //!< aircraft to be added + QHash m_addingInProgressAircraft; //!< aircraft just adding + BlackMisc::Simulation::CSimulatedAircraftList m_aircraftAddedFailed; //! aircraft for which adding failed + CXPlaneMPAircraftObjects m_xplaneAircraftObjects; //!< XPlane multiplayer aircraft + XPlaneData m_xplaneData; //!< XPlane data + + // statistics + qint64 m_statsAddMaxTimeMs = -1; + qint64 m_statsAddCurrentTimeMs = -1; + + //! Reset the XPlane data + void resetXPlaneData() + { + m_xplaneData = { "", "", 0, 0, 0, 0, 0, 0, 0, false, 122800, 122800, 122800, 122800, 2000, 0, false, false, false, false, + false, false, 0, 0, {}, 0.0, 0.0 + }; + + } + }; + + //! Listener waits for xswiftbus service to show up + class CSimulatorXPlaneListener : public BlackCore::ISimulatorListener + { + Q_OBJECT + + public: + //! Constructor + CSimulatorXPlaneListener(const BlackMisc::Simulation::CSimulatorPluginInfo &info); + + protected: + //! \copydoc BlackCore::ISimulatorListener::startImpl + virtual void startImpl() override; + + //! \copydoc BlackCore::ISimulatorListener::stopImpl + virtual void stopImpl() override; + + //! \copydoc BlackCore::ISimulatorListener::checkImpl + virtual void checkImpl() override; + + private: + void checkConnection(); + void checkConnectionViaBus(const QString &address); + void checkConnectionViaPeer(const QString &address); + void checkConnectionCommon(); + + void serviceRegistered(const QString &serviceName); + void xSwiftBusServerSettingChanged(); + + QTimer m_timer { this }; + QDBusConnection m_conn { "default" }; + BlackMisc::CSettingReadOnly m_xswiftbusServerSetting { this, &CSimulatorXPlaneListener::xSwiftBusServerSettingChanged }; + }; + + //! Factory for creating CSimulatorXPlane instance + class CSimulatorXPlaneFactory : public QObject, public BlackCore::ISimulatorFactory + { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.swift-project.blackcore.simulatorinterface" FILE "simulatorxplane.json") + Q_INTERFACES(BlackCore::ISimulatorFactory) + + public: + //! \copydoc BlackCore::ISimulatorFactory::create + virtual BlackCore::ISimulator *create(const BlackMisc::Simulation::CSimulatorPluginInfo &info, + BlackMisc::Simulation::IOwnAircraftProvider *ownAircraftProvider, + BlackMisc::Simulation::IRemoteAircraftProvider *remoteAircraftProvider, + BlackMisc::Weather::IWeatherGridProvider *weatherGridProvider, + BlackMisc::Network::IClientProvider *clientProvider) override; + + //! \copydoc BlackCore::ISimulatorFactory::createListener + virtual BlackCore::ISimulatorListener *createListener(const BlackMisc::Simulation::CSimulatorPluginInfo &info) override { return new CSimulatorXPlaneListener(info); } + }; + } // ns +} // ns + +#endif // guard diff --git a/src/plugins/simulator/flightgear/simulatorxplane.json b/src/plugins/simulator/flightgear/simulatorxplane.json new file mode 100644 index 000000000..75fb3fa08 --- /dev/null +++ b/src/plugins/simulator/flightgear/simulatorxplane.json @@ -0,0 +1,7 @@ +{ + "identifier" : "org.swift-project.plugins.simulator.flightgear", + "name" : "Flightgear", + "simulator" : "flightgear", + "description" : "Support for the Flightgear simulator via the xswiftbus plugin.", + "config" : "org.swift-project.plugins.simulator.flightgear.config" +} diff --git a/src/plugins/simulator/flightgear/xplanempaircraft.cpp b/src/plugins/simulator/flightgear/xplanempaircraft.cpp new file mode 100644 index 000000000..92f89a728 --- /dev/null +++ b/src/plugins/simulator/flightgear/xplanempaircraft.cpp @@ -0,0 +1,73 @@ +/* Copyright (C) 2018 + * 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. + */ + +#include "xplanempaircraft.h" +#include "blackcore/simulator.h" +#include "blackmisc/simulation/interpolatormulti.h" + +using namespace BlackCore; +using namespace BlackMisc; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Simulation; + +namespace BlackSimPlugin +{ + namespace XPlane + { + CXPlaneMPAircraft::CXPlaneMPAircraft() + { } + + CXPlaneMPAircraft::CXPlaneMPAircraft( + const CSimulatedAircraft &aircraft, ISimulator *simulator, CInterpolationLogger *logger) : + m_aircraft(aircraft), + m_interpolator(QSharedPointer::create(aircraft.getCallsign(), simulator, simulator, simulator->getRemoteAircraftProvider(), logger)) + { + m_interpolator->attachLogger(logger); + m_interpolator->initCorrespondingModel(aircraft.getModel()); + } + + void CXPlaneMPAircraft::setSimulatedAircraft(const CSimulatedAircraft &simulatedAircraft) + { + m_aircraft = simulatedAircraft; + } + + QString CXPlaneMPAircraft::getInterpolatorInfo(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const + { + Q_ASSERT(m_interpolator); + return m_interpolator->getInterpolatorInfo(mode); + } + + void CXPlaneMPAircraft::attachInterpolatorLogger(CInterpolationLogger *logger) const + { + Q_ASSERT(m_interpolator); + m_interpolator->attachLogger(logger); + } + + CInterpolationResult CXPlaneMPAircraft::getInterpolation(qint64 currentTimeSinceEpoc, const CInterpolationAndRenderingSetupPerCallsign &setup, int aircraftNumber) const + { + Q_ASSERT(m_interpolator); + return m_interpolator->getInterpolation(currentTimeSinceEpoc, setup, aircraftNumber); + } + + CStatusMessageList CXPlaneMPAircraft::getInterpolationMessages(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const + { + return this->getInterpolator() ? this->getInterpolator()->getInterpolationMessages(mode) : CStatusMessageList(); + } + + CCallsignSet CXPlaneMPAircraftObjects::getAllCallsigns() const + { + return CCallsignSet(this->keys()); + } + + QStringList CXPlaneMPAircraftObjects::getAllCallsignStrings(bool sorted) const + { + return this->getAllCallsigns().getCallsignStrings(sorted); + } + } // namespace +} // namespace diff --git a/src/plugins/simulator/flightgear/xplanempaircraft.h b/src/plugins/simulator/flightgear/xplanempaircraft.h new file mode 100644 index 000000000..5d4328a96 --- /dev/null +++ b/src/plugins/simulator/flightgear/xplanempaircraft.h @@ -0,0 +1,94 @@ +/* Copyright (C) 2018 + * 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. + */ + +//! \file + +#ifndef BLACKSIMPLUGIN_XPLANE_XPLANEMPAIRCRAFT_H +#define BLACKSIMPLUGIN_XPLANE_XPLANEMPAIRCRAFT_H + +#include "blackmisc/simulation/simulatedaircraft.h" +#include "blackmisc/simulation/interpolatormulti.h" +#include +#include + +namespace BlackCore { class ISimulator; } +namespace BlackSimPlugin +{ + namespace XPlane + { + //! Class representing a X-Plane multiplayer aircraft + class CXPlaneMPAircraft + { + public: + //! Constructor + CXPlaneMPAircraft(); + + //! Constructor providing initial situation/parts + CXPlaneMPAircraft(const BlackMisc::Simulation::CSimulatedAircraft &aircraft, + BlackCore::ISimulator *simulator, + BlackMisc::Simulation::CInterpolationLogger *logger); + + //! Destructor + ~CXPlaneMPAircraft() {} + + //! Set simulated aircraft + void setSimulatedAircraft(const BlackMisc::Simulation::CSimulatedAircraft &simulatedAircraft); + + //! Get callsign + const BlackMisc::Aviation::CCallsign &getCallsign() const { return m_aircraft.getCallsign(); } + + //! Simulated aircraft (as added) + const BlackMisc::Simulation::CSimulatedAircraft &getAircraft() const { return m_aircraft; } + + //! Simulated aircraft model string + const QString &getAircraftModelString() const { return m_aircraft.getModelString(); } + + //! \copydoc BlackMisc::Simulation::CInterpolator::getInterpolatorInfo + QString getInterpolatorInfo(BlackMisc::Simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const; + + //! \copydoc BlackMisc::Simulation::CInterpolator::attachLogger + void attachInterpolatorLogger(BlackMisc::Simulation::CInterpolationLogger *logger) const; + + //! \copydoc BlackMisc::Simulation::CInterpolator::getInterpolation + BlackMisc::Simulation::CInterpolationResult getInterpolation(qint64 currentTimeSinceEpoc, const BlackMisc::Simulation::CInterpolationAndRenderingSetupPerCallsign &setup, int aircraftNumber) const; + + //! \copydoc BlackMisc::Simulation::CInterpolator::getInterpolationMessages + BlackMisc::CStatusMessageList getInterpolationMessages(BlackMisc::Simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const; + + //! Interpolator + BlackMisc::Simulation::CInterpolatorMulti *getInterpolator() const { return m_interpolator.data(); } + + private: + BlackMisc::Simulation::CSimulatedAircraft m_aircraft; //!< corresponding aircraft + QSharedPointer m_interpolator; //!< shared pointer because CSimConnectObject can be copied + }; + + //! Simulator objects (aka AI aircraft) + class CXPlaneMPAircraftObjects : public QHash + { + public: + //! Get all callsigns + BlackMisc::Aviation::CCallsignSet getAllCallsigns() const; + + //! Get all callsign strings + QStringList getAllCallsignStrings(bool sorted = false) const; + + //! Get all callsign strings as string + QString getAllCallsignStringsAsString(bool sorted = false, const QString &separator = ", ") const; + + //! Toggle interpolator modes + void toggleInterpolatorModes(); + + //! Toggle interpolator modes + void toggleInterpolatorMode(const BlackMisc::Aviation::CCallsign &callsign); + }; + } // namespace +} // namespace + +#endif // guard diff --git a/src/plugins/simulator/flightgear/xswiftbusserviceproxy.cpp b/src/plugins/simulator/flightgear/xswiftbusserviceproxy.cpp new file mode 100644 index 000000000..599912589 --- /dev/null +++ b/src/plugins/simulator/flightgear/xswiftbusserviceproxy.cpp @@ -0,0 +1,510 @@ +/* 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. + */ + +#include "xswiftbusserviceproxy.h" +#include "simulatorxplane.h" + +#include +#include + +class QDBusConnection; + +#define XSWIFTBUS_SERVICE_SERVICENAME "org.swift-project.xswiftbus" + +namespace BlackSimPlugin +{ + namespace XPlane + { + CXSwiftBusServiceProxy::CXSwiftBusServiceProxy(QDBusConnection &connection, QObject *parent, bool dummy) : QObject(parent) + { + m_dbusInterface = new BlackMisc::CGenericDBusInterface(XSWIFTBUS_SERVICE_SERVICENAME, ObjectPath(), InterfaceName(), connection, this); + if (!dummy) + { + bool s; + s = connection.connect(QString(), "/xswiftbus/service", "org.swift_project.xswiftbus.service", + "aircraftModelChanged", this, + SIGNAL(aircraftModelChanged(QString, QString, QString, QString, QString, QString, QString))); + Q_ASSERT(s); + + s = connection.connect(QString(), "/xswiftbus/service", "org.swift_project.xswiftbus.service", + "airportsInRangeUpdated", this, + SIGNAL(airportsInRangeUpdated(QStringList, QStringList, QList, QList, QList))); + Q_ASSERT(s); + } + } + + QString CXSwiftBusServiceProxy::getVersionNumber() + { + return m_dbusInterface->callDBusRet(QLatin1String("getVersionNumber")); + } + + void CXSwiftBusServiceProxy::getOwnAircraftSituationData(XPlaneData *o_xplaneData) + { + QPointer myself(this); + std::function callback = [ = ](QDBusPendingCallWatcher * watcher) + { + if (!myself) { return; } + QDBusPendingReply reply = *watcher; + if (!reply.isError()) + { + o_xplaneData->latitudeDeg = reply.argumentAt<0>(); + o_xplaneData->longitudeDeg = reply.argumentAt<1>(); + o_xplaneData->altitudeM = reply.argumentAt<2>(); + o_xplaneData->groundspeedMs = reply.argumentAt<3>(); + o_xplaneData->pitchDeg = reply.argumentAt<4>(); + o_xplaneData->rollDeg = reply.argumentAt<5>(); + o_xplaneData->trueHeadingDeg = reply.argumentAt<6>(); + o_xplaneData->seaLevelPressureInHg = reply.argumentAt<7>(); + } + watcher->deleteLater(); + }; + m_dbusInterface->callDBusAsync(QLatin1String("getOwnAircraftSituationData"), callback); + } + + void CXSwiftBusServiceProxy::addTextMessage(const QString &text, double red, double green, double blue) + { + m_dbusInterface->callDBus(QLatin1String("addTextMessage"), text, red, green, blue); + } + + void CXSwiftBusServiceProxy::updateAirportsInRange() + { + m_dbusInterface->callDBus(QLatin1String("updateAirportsInRange")); + } + + QString CXSwiftBusServiceProxy::getAircraftModelPath() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAircraftModelPath")); + } + void CXSwiftBusServiceProxy::getAircraftModelPathAsync(QString *o_modelPath) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAircraftModelPath"), setterCallback(o_modelPath)); + } + + QString CXSwiftBusServiceProxy::getAircraftModelFilename() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAircraftModelFilename")); + } + void CXSwiftBusServiceProxy::getAircraftModelFilenameAsync(QString *o_modelFilename) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAircraftModelFilename"), setterCallback(o_modelFilename)); + } + + QString CXSwiftBusServiceProxy::getAircraftLivery() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAircraftLivery")); + } + void CXSwiftBusServiceProxy::getAircraftLiveryAsync(QString *o_modelLivery) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAircraftLivery"), setterCallback(o_modelLivery)); + } + + QString CXSwiftBusServiceProxy::getAircraftIcaoCode() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAircraftIcaoCode")); + } + void CXSwiftBusServiceProxy::getAircraftIcaoCodeAsync(QString *o_icaoCode) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAircraftIcaoCode"), setterCallback(o_icaoCode)); + } + + QString CXSwiftBusServiceProxy::getAircraftDescription() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAircraftDescription")); + } + void CXSwiftBusServiceProxy::getAircraftDescriptionAsync(QString *o_description) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAircraftDescription"), setterCallback(o_description)); + } + + QString CXSwiftBusServiceProxy::getAircraftModelString() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAircraftModelString")); + } + void CXSwiftBusServiceProxy::getAircraftModelStringAsync(QString *o_modelString) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAircraftModelString"), setterCallback(o_modelString)); + } + + QString CXSwiftBusServiceProxy::getAircraftName() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAircraftName")); + } + void CXSwiftBusServiceProxy::getAircraftNameAsync(QString *o_name) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAircraftName"), setterCallback(o_name)); + } + + int CXSwiftBusServiceProxy::getXPlaneVersionMajor() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getXPlaneVersionMajor")); + } + void CXSwiftBusServiceProxy::getXPlaneVersionMajorAsync(int *o_versionMajor) + { + m_dbusInterface->callDBusAsync(QLatin1String("getXPlaneVersionMajor"), setterCallback(o_versionMajor)); + } + + int CXSwiftBusServiceProxy::getXPlaneVersionMinor() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getXPlaneVersionMinor")); + } + void CXSwiftBusServiceProxy::getXPlaneVersionMinorAsync(int *o_versionMinor) + { + m_dbusInterface->callDBusAsync(QLatin1String("getXPlaneVersionMinor"), setterCallback(o_versionMinor)); + } + + QString CXSwiftBusServiceProxy::getXPlaneInstallationPath() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getXPlaneInstallationPath")); + } + void CXSwiftBusServiceProxy::getXPlaneInstallationPathAsync(QString *o_installPath) + { + m_dbusInterface->callDBusAsync(QLatin1String("getXPlaneInstallationPath"), setterCallback(o_installPath)); + } + + QString CXSwiftBusServiceProxy::getXPlanePreferencesPath() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getXPlanePreferencesPath")); + } + void CXSwiftBusServiceProxy::getXPlanePreferencesPathAsync(QString *o_prefsPath) + { + m_dbusInterface->callDBusAsync(QLatin1String("getXPlanePreferencesPath"), setterCallback(o_prefsPath)); + } + + bool CXSwiftBusServiceProxy::isPaused() const + { + return m_dbusInterface->callDBusRet(QLatin1String("isPaused")); + } + void CXSwiftBusServiceProxy::isPausedAsync(bool *o_paused) + { + m_dbusInterface->callDBusAsync(QLatin1String("isPaused"), setterCallback(o_paused)); + } + + bool CXSwiftBusServiceProxy::isUsingRealTime() const + { + return m_dbusInterface->callDBusRet(QLatin1String("isUsingRealTime")); + } + void CXSwiftBusServiceProxy::isUsingRealTimeAsync(bool *o_isRealTime) + { + m_dbusInterface->callDBusAsync(QLatin1String("isUsingRealTime"), setterCallback(o_isRealTime)); + } + + double CXSwiftBusServiceProxy::getLatitudeDeg() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getLatitudeDeg")); + } + void CXSwiftBusServiceProxy::getLatitudeDegAsync(double *o_latitude) + { + m_dbusInterface->callDBusAsync(QLatin1String("getLatitudeDeg"), setterCallback(o_latitude)); + } + + double CXSwiftBusServiceProxy::getLongitudeDeg() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getLongitudeDeg")); + } + void CXSwiftBusServiceProxy::getLongitudeDegAsync(double *o_longitude) + { + m_dbusInterface->callDBusAsync(QLatin1String("getLongitudeDeg"), setterCallback(o_longitude)); + } + + double CXSwiftBusServiceProxy::getAltitudeMslM() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAltitudeMslM")); + } + void CXSwiftBusServiceProxy::getAltitudeMslMAsync(double *o_altitude) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAltitudeMslM"), setterCallback(o_altitude)); + } + + double CXSwiftBusServiceProxy::getHeightAglM() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getHeightAglM")); + } + void CXSwiftBusServiceProxy::getHeightAglMAsync(double *o_height) + { + m_dbusInterface->callDBusAsync(QLatin1String("getHeightAglM"), setterCallback(o_height)); + } + + double CXSwiftBusServiceProxy::getGroundSpeedMps() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getGroundSpeedMps")); + } + void CXSwiftBusServiceProxy::getGroundSpeedMpsAsync(double *o_groundspeed) + { + m_dbusInterface->callDBusAsync(QLatin1String("getGroundSpeedMps"), setterCallback(o_groundspeed)); + } + + double CXSwiftBusServiceProxy::getIndicatedAirspeedKias() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getIndicatedAirspeedKias")); + } + void CXSwiftBusServiceProxy::getIndicatedAirspeedKiasAsync(double *o_ias) + { + m_dbusInterface->callDBusAsync(QLatin1String("getIndicatedAirspeedKias"), setterCallback(o_ias)); + } + + double CXSwiftBusServiceProxy::getTrueAirspeedKias() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getTrueAirspeedKias")); + } + void CXSwiftBusServiceProxy::getTrueAirspeedKiasAsync(double *o_tas) + { + m_dbusInterface->callDBusAsync(QLatin1String("getTrueAirspeedKias"), setterCallback(o_tas)); + } + + double CXSwiftBusServiceProxy::getPitchDeg() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getPitchDeg")); + } + void CXSwiftBusServiceProxy::getPitchDegAsync(double *o_pitch) + { + m_dbusInterface->callDBusAsync(QLatin1String("getPitchDeg"), setterCallback(o_pitch)); + } + + double CXSwiftBusServiceProxy::getRollDeg() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getRollDeg")); + } + void CXSwiftBusServiceProxy::getRollDegAsync(double *o_roll) + { + m_dbusInterface->callDBusAsync(QLatin1String("getRollDeg"), setterCallback(o_roll)); + } + + double CXSwiftBusServiceProxy::getTrueHeadingDeg() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getTrueHeadingDeg")); + } + void CXSwiftBusServiceProxy::getTrueHeadingDegAsync(double *o_heading) + { + m_dbusInterface->callDBusAsync(QLatin1String("getTrueHeadingDeg"), setterCallback(o_heading)); + } + + bool CXSwiftBusServiceProxy::getAnyWheelOnGround() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAnyWheelOnGround")); + } + void CXSwiftBusServiceProxy::getAnyWheelOnGroundAsync(bool *o_anyWheel) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAnyWheelOnGround"), setterCallback(o_anyWheel)); + } + + bool CXSwiftBusServiceProxy::getAllWheelsOnGround() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getAllWheelsOnGround")); + } + void CXSwiftBusServiceProxy::getAllWheelsOnGroundAsync(bool *o_allWheels) + { + m_dbusInterface->callDBusAsync(QLatin1String("getAllWheelsOnGround"), setterCallback(o_allWheels)); + } + + int CXSwiftBusServiceProxy::getCom1ActiveKhz() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getCom1ActiveKhz")); + } + void CXSwiftBusServiceProxy::getCom1ActiveKhzAsync(int *o_com1Active) + { + m_dbusInterface->callDBusAsync(QLatin1String("getCom1ActiveKhz"), setterCallback(o_com1Active)); + } + + int CXSwiftBusServiceProxy::getCom1StandbyKhz() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getCom1StandbyKhz")); + } + void CXSwiftBusServiceProxy::getCom1StandbyKhzAsync(int *o_com1Standby) + { + m_dbusInterface->callDBusAsync(QLatin1String("getCom1StandbyKhz"), setterCallback(o_com1Standby)); + } + + int CXSwiftBusServiceProxy::getCom2ActiveKhz() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getCom2ActiveKhz")); + } + void CXSwiftBusServiceProxy::getCom2ActiveKhzAsync(int *o_com2Active) + { + m_dbusInterface->callDBusAsync(QLatin1String("getCom2ActiveKhz"), setterCallback(o_com2Active)); + } + + int CXSwiftBusServiceProxy::getCom2StandbyKhz() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getCom2StandbyKhz")); + } + void CXSwiftBusServiceProxy::getCom2StandbyKhzAsync(int *o_com2Standby) + { + m_dbusInterface->callDBusAsync(QLatin1String("getCom2StandbyKhz"), setterCallback(o_com2Standby)); + } + + int CXSwiftBusServiceProxy::getTransponderCode() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getTransponderCode")); + } + void CXSwiftBusServiceProxy::getTransponderCodeAsync(int *o_xpdrCode) + { + m_dbusInterface->callDBusAsync(QLatin1String("getTransponderCode"), setterCallback(o_xpdrCode)); + } + + int CXSwiftBusServiceProxy::getTransponderMode() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getTransponderMode")); + } + void CXSwiftBusServiceProxy::getTransponderModeAsync(int *o_xpdrMode) + { + m_dbusInterface->callDBusAsync(QLatin1String("getTransponderMode"), setterCallback(o_xpdrMode)); + } + + bool CXSwiftBusServiceProxy::getTransponderIdent() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getTransponderIdent")); + } + void CXSwiftBusServiceProxy::getTransponderIdentAsync(bool *o_ident) + { + m_dbusInterface->callDBusAsync(QLatin1String("getTransponderIdent"), setterCallback(o_ident)); + } + + bool CXSwiftBusServiceProxy::getBeaconLightsOn() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getBeaconLightsOn")); + } + + void CXSwiftBusServiceProxy::getBeaconLightsOnAsync(bool *o_beaconLightsOn) + { + m_dbusInterface->callDBusAsync(QLatin1String("getBeaconLightsOn"), setterCallback(o_beaconLightsOn)); + } + + bool CXSwiftBusServiceProxy::getLandingLightsOn() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getLandingLightsOn")); + } + + void CXSwiftBusServiceProxy::getLandingLightsOnAsync(bool *o_landingLightsOn) + { + m_dbusInterface->callDBusAsync(QLatin1String("getLandingLightsOn"), setterCallback(o_landingLightsOn)); + } + + bool CXSwiftBusServiceProxy::getNavLightsOn() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getNavLightsOn")); + } + + void CXSwiftBusServiceProxy::getNavLightsOnAsync(bool *o_navLightsOn) + { + m_dbusInterface->callDBusAsync(QLatin1String("getNavLightsOn"), setterCallback(o_navLightsOn)); + } + + bool CXSwiftBusServiceProxy::getStrobeLightsOn() const + + { + return m_dbusInterface->callDBusRet(QLatin1String("getStrobeLightsOn")); + } + + void CXSwiftBusServiceProxy::getStrobeLightsOnAsync(bool *o_strobeLightsOn) + { + m_dbusInterface->callDBusAsync(QLatin1String("getStrobeLightsOn"), setterCallback(o_strobeLightsOn)); + } + + bool CXSwiftBusServiceProxy::getTaxiLightsOn() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getTaxiLightsOn")); + } + + void CXSwiftBusServiceProxy::getTaxiLightsOnAsync(bool *o_taxiLightsOn) + { + m_dbusInterface->callDBusAsync(QLatin1String("getTaxiLightsOn"), setterCallback(o_taxiLightsOn)); + } + + + double CXSwiftBusServiceProxy::getQNHInHg() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getQNHInHg")); + } + + void CXSwiftBusServiceProxy::getQNHInHgAsync(double *o_qnh) + { + m_dbusInterface->callDBusAsync(QLatin1String("getQNHInHg"), setterCallback(o_qnh)); + } + + + void CXSwiftBusServiceProxy::setCom1ActiveKhz(int freq) + { + m_dbusInterface->callDBus(QLatin1String("setCom1ActiveKhz"), freq); + } + + void CXSwiftBusServiceProxy::setCom1StandbyKhz(int freq) + { + m_dbusInterface->callDBus(QLatin1String("setCom1StandbyKhz"), freq); + } + + void CXSwiftBusServiceProxy::setCom2ActiveKhz(int freq) + { + m_dbusInterface->callDBus(QLatin1String("setCom2ActiveKhz"), freq); + } + + void CXSwiftBusServiceProxy::setCom2StandbyKhz(int freq) + { + m_dbusInterface->callDBus(QLatin1String("setCom2StandbyKhz"), freq); + } + + void CXSwiftBusServiceProxy::setTransponderCode(int code) + { + m_dbusInterface->callDBus(QLatin1String("setTransponderCode"), code); + } + + void CXSwiftBusServiceProxy::setTransponderMode(int mode) + { + m_dbusInterface->callDBus(QLatin1String("setTransponderMode"), mode); + } + double CXSwiftBusServiceProxy::getFlapsDeployRatio() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getFlapsDeployRatio")); + } + + void CXSwiftBusServiceProxy::getFlapsDeployRatioAsync(double *o_flapsDeployRatio) + { + m_dbusInterface->callDBusAsync(QLatin1String("getFlapsDeployRatio"), setterCallback(o_flapsDeployRatio)); + } + + double CXSwiftBusServiceProxy::getGearDeployRatio() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getGearDeployRatio")); + } + + void CXSwiftBusServiceProxy::getGearDeployRatioAsync(double *o_gearDeployRatio) + { + m_dbusInterface->callDBusAsync(QLatin1String("getGearDeployRatio"), setterCallback(o_gearDeployRatio)); + } + + int CXSwiftBusServiceProxy::getNumberOfEngines() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getNumberOfEngines")); + } + + void CXSwiftBusServiceProxy::getNumberOfEnginesAsync(double *o_numberOfEngines) + { + m_dbusInterface->callDBusAsync(QLatin1String("getNumberOfEngines"), setterCallback(o_numberOfEngines)); + } + + QList CXSwiftBusServiceProxy::getEngineN1Percentage() const + { + return m_dbusInterface->callDBusRet>(QLatin1String("getEngineN1Percentage")); + } + + void CXSwiftBusServiceProxy::getEngineN1PercentageAsync(QList *o_engineN1Percentage) + { + m_dbusInterface->callDBusAsync(QLatin1String("getEngineN1Percentage"), setterCallback(o_engineN1Percentage)); + } + + double CXSwiftBusServiceProxy::getSpeedBrakeRatio() const + { + return m_dbusInterface->callDBusRet(QLatin1String("getSpeedBrakeRatio")); + } + + void CXSwiftBusServiceProxy::getSpeedBrakeRatioAsync(double *o_speedBrakeRatio) + { + m_dbusInterface->callDBusAsync(QLatin1String("getSpeedBrakeRatio"), setterCallback(o_speedBrakeRatio)); + } + } // ns +} // ns diff --git a/src/plugins/simulator/flightgear/xswiftbusserviceproxy.h b/src/plugins/simulator/flightgear/xswiftbusserviceproxy.h new file mode 100644 index 000000000..4c12ca3f4 --- /dev/null +++ b/src/plugins/simulator/flightgear/xswiftbusserviceproxy.h @@ -0,0 +1,394 @@ +/* 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. + */ + +//! \file + +#ifndef BLACKSIMPLUGIN_XSWIFTBUS_SERVICE_PROXY_H +#define BLACKSIMPLUGIN_XSWIFTBUS_SERVICE_PROXY_H + +#include "blackmisc/genericdbusinterface.h" + +#include +#include +#include +#include +#include +#include +#include + +// clazy:excludeall=const-signal-or-slot + +class QDBusConnection; +class QDBusPendingCallWatcher; + +//! \cond PRIVATE +#define XSWIFTBUS_SERVICE_INTERFACENAME "org.swift_project.xswiftbus.service" +#define XSWIFTBUS_SERVICE_OBJECTPATH "/xswiftbus/service" +//! \endcond + +namespace BlackSimPlugin +{ + namespace XPlane + { + struct XPlaneData; + + /*! + * Proxy object connected to a real XSwiftBus::CService object via DBus + */ + class CXSwiftBusServiceProxy : public QObject + { + Q_OBJECT + + public: + //! Service name + static const QString &InterfaceName() + { + static const QString s(XSWIFTBUS_SERVICE_INTERFACENAME); + return s; + } + + //! Service path + static const QString &ObjectPath() + { + static const QString s(XSWIFTBUS_SERVICE_OBJECTPATH); + return s; + } + + //! Constructor + CXSwiftBusServiceProxy(QDBusConnection &connection, QObject *parent = nullptr, bool dummy = false); + + //! Does the remote object exist? + bool isValid() const { return m_dbusInterface->isValid(); } + + //! Cancel all current async slot calls + void cancelAllPendingAsyncCalls() { m_dbusInterface->cancelAllPendingAsyncCalls(); } + + private: + BlackMisc::CGenericDBusInterface *m_dbusInterface = nullptr; + + // Returns a function object which can be passed to CGenericDBusInterface::callDBusAsync. + template + std::function setterCallback(T *obj) + { + return [this, obj](QDBusPendingCallWatcher * watcher) + { + QDBusPendingReply reply = *watcher; + if (reply.isError()) { emit this->asyncMethodError(reply.error()); } + else { *obj = reply; } + watcher->deleteLater(); + }; + } + + signals: + //! Emitted if an asynchronous method call caused a DBus error + BLACK_NO_RELAY void asyncMethodError(QDBusError error); + + //! Own aircraft model changed + void aircraftModelChanged( + const QString &path, const QString &filename, const QString &livery, + const QString &icao, const QString &modelString, const QString &name, const QString &description); + + //! Airports in range are updated + void airportsInRangeUpdated(const QStringList &icaoCodes, const QStringList &names, const QList &lats, const QList &lons, const QList &alts); + + public slots: + //! Get XSwiftBus version number + QString getVersionNumber(); + + //! Get own aircraft situation data + void getOwnAircraftSituationData(BlackSimPlugin::XPlane::XPlaneData *o_xplaneData); + + //! \copydoc XSwiftBus::CService::addTextMessage + void addTextMessage(const QString &text, double red, double green, double blue); + + //! \copydoc XSwiftBus::CService::updateAirportsInRange + void updateAirportsInRange(); + + //! \copydoc XSwiftBus::CService::getAircraftModelPath + //! @{ + QString getAircraftModelPath() const; + void getAircraftModelPathAsync(QString *o_modelPath); + //! @} + + //! \copydoc XSwiftBus::CService::getAircraftModelFilename + //! @{ + QString getAircraftModelFilename() const; + void getAircraftModelFilenameAsync(QString *o_modelFilename); + //! @} + + //! \copydoc XSwiftBus::CService::getAircraftLivery + //! @{ + QString getAircraftLivery() const; + void getAircraftLiveryAsync(QString *o_modelLivery); + //! @} + + //! \copydoc XSwiftBus::CService::getAircraftIcaoCode + //! @{ + QString getAircraftIcaoCode() const; + void getAircraftIcaoCodeAsync(QString *o_icaoCode); + //! @} + + //! \copydoc XSwiftBus::CService::getAircraftDescription + //! @{ + QString getAircraftDescription() const; + void getAircraftDescriptionAsync(QString *o_description); + //! @} + + //! \copydoc XSwiftBus::CService::getAircraftModelString + //! @{ + QString getAircraftModelString() const; + void getAircraftModelStringAsync(QString *o_modelString); + //! @} + + //! \copydoc XSwiftBus::CService::getAircraftName + //! @{ + QString getAircraftName() const; + void getAircraftNameAsync(QString *o_name); + //! @} + + //! \copydoc XSwiftBus::CService::getXPlaneVersionMajor + //! @{ + int getXPlaneVersionMajor() const; + void getXPlaneVersionMajorAsync(int *o_versionMajor); + //! @} + + //! \copydoc XSwiftBus::CService::getXPlaneVersionMinor + //! @{ + int getXPlaneVersionMinor() const; + void getXPlaneVersionMinorAsync(int *o_versionMinor); + //! @} + + //! \copydoc XSwiftBus::CService::getXPlaneInstallationPath + //! @{ + QString getXPlaneInstallationPath() const; + void getXPlaneInstallationPathAsync(QString *o_installPath); + //! @} + + //! \copydoc XSwiftBus::CService::getXPlanePreferencesPath + //! @{ + QString getXPlanePreferencesPath() const; + void getXPlanePreferencesPathAsync(QString *o_prefsPath); + //! @} + + //! \copydoc XSwiftBus::CService::isPaused + //! @{ + bool isPaused() const; + void isPausedAsync(bool *o_paused); + //! @} + + //! \copydoc XSwiftBus::CService::isUsingRealTime + //! @{ + bool isUsingRealTime() const; + void isUsingRealTimeAsync(bool *o_isRealTime); + //! @} + + //! \copydoc XSwiftBus::CService::getLatitudeDeg + //! @{ + double getLatitudeDeg() const; + void getLatitudeDegAsync(double *o_latitude); + //! @} + + //! \copydoc XSwiftBus::CService::getLongitudeDeg + //! @{ + double getLongitudeDeg() const; + void getLongitudeDegAsync(double *o_longitude); + //! @} + + //! \copydoc XSwiftBus::CService::getAltitudeMslM + //! @{ + double getAltitudeMslM() const; + void getAltitudeMslMAsync(double *o_altitude); + //! @} + + //! \copydoc XSwiftBus::CService::getHeightAglM + //! @{ + double getHeightAglM() const; + void getHeightAglMAsync(double *o_height); + //! @} + + //! \copydoc XSwiftBus::CService::getGroundSpeedMps + //! @{ + double getGroundSpeedMps() const; + void getGroundSpeedMpsAsync(double *o_groundspeed); + //! @} + + //! \copydoc XSwiftBus::CService::getIndicatedAirspeedKias + //! @{ + double getIndicatedAirspeedKias() const; + void getIndicatedAirspeedKiasAsync(double *o_ias); + //! @} + + //! \copydoc XSwiftBus::CService::getTrueAirspeedKias + //! @{ + double getTrueAirspeedKias() const; + void getTrueAirspeedKiasAsync(double *o_tas); + //! @} + + //! \copydoc XSwiftBus::CService::getPitchDeg + //! @{ + double getPitchDeg() const; + void getPitchDegAsync(double *o_pitch); + //! @} + + //! \copydoc XSwiftBus::CService::getRollDeg + //! @{ + double getRollDeg() const; + void getRollDegAsync(double *o_roll); + //! @} + + //! \copydoc XSwiftBus::CService::getTrueHeadingDeg + //! @{ + double getTrueHeadingDeg() const; + void getTrueHeadingDegAsync(double *o_heading); + //! @} + + //! Get whether any wheel is on the ground + //! @{ + bool getAnyWheelOnGround() const; + void getAnyWheelOnGroundAsync(bool *o_anyWheel); + //! @} + + //! Get whether all wheels are on the ground + //! @{ + bool getAllWheelsOnGround() const; + void getAllWheelsOnGroundAsync(bool *o_allWheels); + //! @} + + //! \copydoc XSwiftBus::CService::getCom1ActiveKhz + //! @{ + int getCom1ActiveKhz() const; + void getCom1ActiveKhzAsync(int *o_com1Active); + //! @} + + //! \copydoc XSwiftBus::CService::getCom1StandbyKhz + //! @{ + int getCom1StandbyKhz() const; + void getCom1StandbyKhzAsync(int *o_com1Standby); + //! @} + + //! \copydoc XSwiftBus::CService::getCom2ActiveKhz + //! @{ + int getCom2ActiveKhz() const; + void getCom2ActiveKhzAsync(int *o_com2Active); + //! @} + + //! \copydoc XSwiftBus::CService::getCom2StandbyKhz + //! @{ + int getCom2StandbyKhz() const; + void getCom2StandbyKhzAsync(int *o_com2Standby); + //! @} + + //! \copydoc XSwiftBus::CService::getTransponderCode + //! @{ + int getTransponderCode() const; + void getTransponderCodeAsync(int *o_xpdrCode); + //! @} + + //! \copydoc XSwiftBus::CService::getTransponderMode + //! @{ + int getTransponderMode() const; + void getTransponderModeAsync(int *o_xpdrMode); + //! @} + + //! \copydoc XSwiftBus::CService::getTransponderIdent + //! @{ + bool getTransponderIdent() const; + void getTransponderIdentAsync(bool *o_ident); + //! @} + + //! \copydoc XSwiftBus::CService::getLandingLightsOn + //! @{ + bool getBeaconLightsOn() const; + void getBeaconLightsOnAsync(bool *o_beaconLightsOn); + //! @} + + //! \copydoc XSwiftBus::CService::getLandingLightsOn + //! @{ + bool getLandingLightsOn() const; + void getLandingLightsOnAsync(bool *o_landingLightsOn); + //! @} + + //! \copydoc XSwiftBus::CService::getNavLightsOn + //! @{ + bool getNavLightsOn() const; + void getNavLightsOnAsync(bool *o_navLightsOn); + //! @} + + //! \copydoc XSwiftBus::CService::getStrobeLightsOn + //! @{ + bool getStrobeLightsOn() const; + void getStrobeLightsOnAsync(bool *o_strobeLightsOn); + //! @} + + //! \copydoc XSwiftBus::CService::getTaxiLightsOn + //! @{ + bool getTaxiLightsOn() const; + void getTaxiLightsOnAsync(bool *o_taxiLightsOn); + //! @} + + //! \copydoc XSwiftBus::CService::getQNHInHg + //! @{ + double getQNHInHg() const; + void getQNHInHgAsync(double *o_qnh); + //! @} + + //! \copydoc XSwiftBus::CService::setCom1ActiveKhz + void setCom1ActiveKhz(int freq); + + //! \copydoc XSwiftBus::CService::setCom1StandbyKhz + void setCom1StandbyKhz(int freq); + + //! \copydoc XSwiftBus::CService::setCom2ActiveKhz + void setCom2ActiveKhz(int freq); + + //! \copydoc XSwiftBus::CService::setCom2StandbyKhz + void setCom2StandbyKhz(int freq); + + //! \copydoc XSwiftBus::CService::setTransponderCode + void setTransponderCode(int code); + + //! \copydoc XSwiftBus::CService::setTransponderMode + void setTransponderMode(int mode); + + //! \copydoc XSwiftBus::CService::getFlapsDeployRatio + + //! @{ + double getFlapsDeployRatio() const; + void getFlapsDeployRatioAsync(double *o_flapsDeployRatio); + //! @} + + //! \copydoc XSwiftBus::CService::getGearDeployRatio + //! @{ + double getGearDeployRatio() const; + void getGearDeployRatioAsync(double *o_gearDeployRatio); + //! @} + + //! \copydoc XSwiftBus::CService::getNumberOfEngines + + //! @{ + int getNumberOfEngines() const; + void getNumberOfEnginesAsync(double *o_numberOfEngines); + //! @} + + //! \copydoc XSwiftBus::CService::getEngineN1Percentage + //! @{ + QList getEngineN1Percentage() const; + void getEngineN1PercentageAsync(QList *o_engineN1Percentage); + //! @} + + //! \copydoc XSwiftBus::CService::getSpeedBrakeRatio + //! @{ + double getSpeedBrakeRatio() const; + void getSpeedBrakeRatioAsync(double *o_speedBrakeRatio); + //! @} + }; + } +} + +#endif // guard diff --git a/src/plugins/simulator/flightgear/xswiftbustrafficproxy.cpp b/src/plugins/simulator/flightgear/xswiftbustrafficproxy.cpp new file mode 100644 index 000000000..fdbf58223 --- /dev/null +++ b/src/plugins/simulator/flightgear/xswiftbustrafficproxy.cpp @@ -0,0 +1,189 @@ +/* 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. + */ + +#include "xswiftbustrafficproxy.h" +#include +#include + +#define XSWIFTBUS_SERVICENAME "org.swift-project.xswiftbus" + +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Geo; +using namespace BlackMisc::PhysicalQuantities; + +namespace BlackSimPlugin +{ + namespace XPlane + { + CXSwiftBusTrafficProxy::CXSwiftBusTrafficProxy(QDBusConnection &connection, QObject *parent, bool dummy) : QObject(parent) + { + m_dbusInterface = new BlackMisc::CGenericDBusInterface(XSWIFTBUS_SERVICENAME, ObjectPath(), InterfaceName(), connection, this); + if (!dummy) + { + bool s; + s = connection.connect(QString(), "/xswiftbus/traffic", "org.swift_project.xswiftbus.traffic", + "simFrame", this, SIGNAL(simFrame())); + Q_ASSERT(s); + + s = connection.connect(QString(), "/xswiftbus/traffic", "org.swift_project.xswiftbus.traffic", + "remoteAircraftAdded", this, SIGNAL(remoteAircraftAdded(QString))); + Q_ASSERT(s); + + s = connection.connect(QString(), "/xswiftbus/traffic", "org.swift_project.xswiftbus.traffic", + "remoteAircraftAddingFailed", this, SIGNAL(remoteAircraftAddingFailed(QString))); + Q_ASSERT(s); + } + } + + MultiplayerAcquireInfo CXSwiftBusTrafficProxy::acquireMultiplayerPlanes() + { + QDBusPendingReply reply = m_dbusInterface->asyncCall(QLatin1String("acquireMultiplayerPlanes")); + reply.waitForFinished(); + if (reply.isError()) + { + BlackMisc::CLogMessage(this).debug(u"CXSwiftBusTrafficProxy::acquireMultiplayerPlanes returned: %1") << reply.error().message(); + } + MultiplayerAcquireInfo info; + info.hasAcquired = reply.argumentAt<0>(); + info.owner = reply.argumentAt<1>(); + return info; + } + + bool CXSwiftBusTrafficProxy::initialize() + { + return m_dbusInterface->callDBusRet(QLatin1String("initialize")); + } + + void CXSwiftBusTrafficProxy::cleanup() + { + m_dbusInterface->callDBus(QLatin1String("cleanup")); + } + + bool CXSwiftBusTrafficProxy::loadPlanesPackage(const QString &path) + { + return m_dbusInterface->callDBusRet(QLatin1String("loadPlanesPackage"), path); + } + + void CXSwiftBusTrafficProxy::setDefaultIcao(const QString &defaultIcao) + { + m_dbusInterface->callDBus(QLatin1String("setDefaultIcao"), defaultIcao); + } + + void CXSwiftBusTrafficProxy::setDrawingLabels(bool drawing) + { + m_dbusInterface->callDBus(QLatin1String("setDrawingLabels"), drawing); + } + + bool CXSwiftBusTrafficProxy::isDrawingLabels() const + { + return m_dbusInterface->callDBusRet(QLatin1String("isDrawingLabels")); + } + + void CXSwiftBusTrafficProxy::setMaxPlanes(int planes) + { + m_dbusInterface->callDBus(QLatin1String("setMaxPlanes"), planes); + } + + void CXSwiftBusTrafficProxy::setMaxDrawDistance(double nauticalMiles) + { + m_dbusInterface->callDBus(QLatin1String("setMaxDrawDistance"), nauticalMiles); + } + + void CXSwiftBusTrafficProxy::addPlane(const QString &callsign, const QString &modelName, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery) + { + m_dbusInterface->callDBus(QLatin1String("addPlane"), callsign, modelName, aircraftIcao, airlineIcao, livery); + } + + void CXSwiftBusTrafficProxy::removePlane(const QString &callsign) + { + m_dbusInterface->callDBus(QLatin1String("removePlane"), callsign); + } + + void CXSwiftBusTrafficProxy::removeAllPlanes() + { + m_dbusInterface->callDBus(QLatin1String("removeAllPlanes")); + } + + void CXSwiftBusTrafficProxy::setPlanesPositions(const PlanesPositions &planesPositions) + { + m_dbusInterface->callDBus(QLatin1String("setPlanesPositions"), + planesPositions.callsigns, planesPositions.latitudesDeg, planesPositions.longitudesDeg, + planesPositions.altitudesFt, planesPositions.pitchesDeg, planesPositions.rollsDeg, + planesPositions.headingsDeg, planesPositions.onGrounds); + } + + void CXSwiftBusTrafficProxy::setPlanesSurfaces(const PlanesSurfaces &planesSurfaces) + { + m_dbusInterface->callDBus(QLatin1String("setPlanesSurfaces"), + planesSurfaces.callsigns, planesSurfaces.gears, planesSurfaces.flaps, + planesSurfaces.spoilers, planesSurfaces.speedBrakes, planesSurfaces.slats, + planesSurfaces.wingSweeps, planesSurfaces.thrusts, planesSurfaces.elevators, + planesSurfaces.rudders, planesSurfaces.ailerons, + planesSurfaces.landLights, planesSurfaces.beaconLights, planesSurfaces.strobeLights, + planesSurfaces.navLights, planesSurfaces.lightPatterns); + } + + void CXSwiftBusTrafficProxy::setPlanesTransponders(const PlanesTransponders &planesTransponders) + { + m_dbusInterface->callDBus(QLatin1String("setPlanesTransponders"), + planesTransponders.callsigns, planesTransponders.codes, + planesTransponders.modeCs, planesTransponders.idents); + } + + void CXSwiftBusTrafficProxy::setInterpolatorMode(const QString &callsign, bool spline) + { + m_dbusInterface->callDBus(QLatin1String("setInterpolatorMode"), callsign, spline); + } + + void CXSwiftBusTrafficProxy::getRemoteAircraftData(const QStringList &callsigns, const RemoteAircraftDataCallback &setter) const + { + std::function callback = [ = ](QDBusPendingCallWatcher * watcher) + { + QDBusPendingReply, QList, QList, QList> reply = *watcher; + if (!reply.isError()) + { + const QStringList callsigns = reply.argumentAt<0>(); + const QList latitudesDeg = reply.argumentAt<1>(); + const QList longitudesDeg = reply.argumentAt<2>(); + const QList elevationsM = reply.argumentAt<3>(); + const QList verticalOffsets = reply.argumentAt<4>(); + setter(callsigns, latitudesDeg, longitudesDeg, elevationsM, verticalOffsets); + } + watcher->deleteLater(); + }; + m_dbusInterface->callDBusAsync(QLatin1String("getRemoteAircraftData"), callback, callsigns); + } + + void CXSwiftBusTrafficProxy::getElevationAtPosition(const CCallsign &callsign, double latitudeDeg, double longitudeDeg, double altitudeMeters, + const ElevationCallback &setter) const + { + std::function callback = [ = ](QDBusPendingCallWatcher * watcher) + { + QDBusPendingReply reply = *watcher; + if (!reply.isError()) + { + const CCallsign cs(reply.argumentAt<0>()); + const double elevationMeters = reply.argumentAt<1>(); + const CAltitude elevationAlt(elevationMeters, CLengthUnit::m(), CLengthUnit::ft()); + const CElevationPlane elevation(CLatitude(latitudeDeg, CAngleUnit::deg()), + CLongitude(longitudeDeg, CAngleUnit::deg()), + elevationAlt, CElevationPlane::singlePointRadius()); + setter(elevation, cs); + } + watcher->deleteLater(); + }; + m_dbusInterface->callDBusAsync(QLatin1String("getElevationAtPosition"), callback, callsign.asString(), latitudeDeg, longitudeDeg, altitudeMeters); + } + + void CXSwiftBusTrafficProxy::setFollowedAircraft(const QString &callsign) + { + m_dbusInterface->callDBus(QLatin1String("setFollowedAircraft"), callsign); + } + } +} diff --git a/src/plugins/simulator/flightgear/xswiftbustrafficproxy.h b/src/plugins/simulator/flightgear/xswiftbustrafficproxy.h new file mode 100644 index 000000000..79d91c86e --- /dev/null +++ b/src/plugins/simulator/flightgear/xswiftbustrafficproxy.h @@ -0,0 +1,221 @@ +/* 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. + */ + +//! \file + +#ifndef BLACKSIMPLUGIN_XSWIFTBUS_TRAFFIC_PROXY_H +#define BLACKSIMPLUGIN_XSWIFTBUS_TRAFFIC_PROXY_H + +#include "blackmisc/genericdbusinterface.h" +#include "blackmisc/aviation/callsign.h" +#include "blackmisc/geo/elevationplane.h" + +#include +#include +#include + +// clazy:excludeall=const-signal-or-slot + +class QDBusConnection; + +//! \cond PRIVATE +#define XSWIFTBUS_TRAFFIC_INTERFACENAME "org.swift_project.xswiftbus.traffic" +#define XSWIFTBUS_TRAFFIC_OBJECTPATH "/xswiftbus/traffic" +//! \endcond + +namespace BlackSimPlugin +{ + namespace XPlane + { + //! List of doubles + using QDoubleList = QList; + + //! Planes positions + struct PlanesPositions + { + //! Is empty? + bool isEmpty() const { return callsigns.isEmpty(); } + + //! Check function + bool hasSameSizes() const + { + const int s = callsigns.size(); + if (s != latitudesDeg.size()) { return false; } + if (s != longitudesDeg.size()) { return false; } + if (s != altitudesFt.size()) { return false; } + if (s != pitchesDeg.size()) { return false; } + if (s != rollsDeg.size()) { return false; } + if (s != headingsDeg.size()) { return false; } + return true; + } + + QStringList callsigns; //!< List of callsigns + QList latitudesDeg; //!< List of latitudes + QList longitudesDeg; //!< List of longitudes + QList altitudesFt; //!< List of altitudes + QList pitchesDeg; //!< List of pitches + QList rollsDeg; //!< List of rolls + QList headingsDeg; //!< List of headings + QList onGrounds; //!< List of onGrounds + }; + + //! Planes surfaces + struct PlanesSurfaces + { + //! Is empty? + bool isEmpty() const { return callsigns.isEmpty(); } + + QStringList callsigns; //!< List of callsigns + QList gears; //!< List of gears + QList flaps; //!< List of flaps + QList spoilers; //!< List of spoilers + QList speedBrakes; //!< List of speedBrakes + QList slats; //!< List of slats + QList wingSweeps; //!< List of wingSweeps + QList thrusts; //!< List of thrusts + QList elevators; //!< List of elevators + QList rudders; //!< List of rudders + QList ailerons; //!< List of ailerons + QList landLights; //!< List of landLights + QList beaconLights; //!< List of beaconLights + QList strobeLights; //!< List of strobeLights + QList navLights; //!< List of navLights + QList lightPatterns; //!< List of lightPatterns + }; + + //! Plane Transponders + struct PlanesTransponders + { + //! Is empty? + bool isEmpty() const { return callsigns.isEmpty(); } + + QStringList callsigns; //!< List of callsigns + QList codes; //!< List of transponder codes + QList modeCs; //!< List of active mode C's + QList idents; //!< List of active idents + }; + + //! Multiplayer Acquire Info + struct MultiplayerAcquireInfo + { + bool hasAcquired; //!< Has XSwiftBus acquired multiplayer planes? + QString owner; //!< Name of the plugin having multiplayer planes acquired + }; + + /*! + * Proxy object connected to a real XSwiftBus::CTraffic object via DBus + */ + class CXSwiftBusTrafficProxy : public QObject + { + Q_OBJECT + + public: + //! Elevation callback + using ElevationCallback = std::function; + + //! Remote aircrafts data callback + using RemoteAircraftDataCallback = std::function; + + //! Service name + static const QString &InterfaceName() + { + static QString s(XSWIFTBUS_TRAFFIC_INTERFACENAME); + return s; + } + + //! Service path + static const QString &ObjectPath() + { + static QString s(XSWIFTBUS_TRAFFIC_OBJECTPATH); + return s; + } + + //! Constructor + CXSwiftBusTrafficProxy(QDBusConnection &connection, QObject *parent = nullptr, bool dummy = false); + + //! Does the remote object exist? + bool isValid() const { return m_dbusInterface->isValid(); } + + signals: + //! Simulator frame + //! \remark from simulator to driver + void simFrame(); + + //! Remote aircraft successfully added + void remoteAircraftAdded(const QString &callsign); + + //! Remote aircraft adding failed + void remoteAircraftAddingFailed(const QString &callsign); + + public slots: + //! \copydoc XSwiftBus::CTraffic::acquireMultiplayerPlanes + MultiplayerAcquireInfo acquireMultiplayerPlanes(); + + //! \copydoc XSwiftBus::CTraffic::initialize + bool initialize(); + + //! \copydoc XSwiftBus::CTraffic::cleanup + void cleanup(); + + //! \copydoc XSwiftBus::CTraffic::loadPlanesPackage + bool loadPlanesPackage(const QString &path); + + //! \copydoc XSwiftBus::CTraffic::setDefaultIcao + void setDefaultIcao(const QString &defaultIcao); + + //! \copydoc XSwiftBus::CTraffic::setDrawingLabels + void setDrawingLabels(bool drawing); + + //! \copydoc XSwiftBus::CTraffic::isDrawingLabels + bool isDrawingLabels() const; + + //! \copydoc XSwiftBus::CTraffic::setMaxPlanes + void setMaxPlanes(int planes); + + //! \copydoc XSwiftBus::CTraffic::setMaxDrawDistance + void setMaxDrawDistance(double nauticalMiles); + + //! \copydoc XSwiftBus::CTraffic::addPlane + void addPlane(const QString &callsign, const QString &modelName, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery); + + //! \copydoc XSwiftBus::CTraffic::removePlane + void removePlane(const QString &callsign); + + //! \copydoc XSwiftBus::CTraffic::removeAllPlanes + void removeAllPlanes(); + + //! \copydoc XSwiftBus::CTraffic::setPlanesPositions + void setPlanesPositions(const BlackSimPlugin::XPlane::PlanesPositions &planesPositions); + + //! \copydoc XSwiftBus::CTraffic::setPlanesSurfaces + void setPlanesSurfaces(const BlackSimPlugin::XPlane::PlanesSurfaces &planesSurfaces); + + //! \copydoc XSwiftBus::CTraffic::setPlanesTransponders + void setPlanesTransponders(const BlackSimPlugin::XPlane::PlanesTransponders &planesTransponders); + + //! \deprecated XSwiftBus::CTraffic::setInterpolatorMode + void setInterpolatorMode(const QString &callsign, bool spline); + + //! \copydoc XSwiftBus::CTraffic::getRemoteAircraftData + void getRemoteAircraftData(const QStringList &callsigns, const RemoteAircraftDataCallback &setter) const; + + //! \copydoc XSwiftBus::CTraffic::getElevationAtPosition + void getElevationAtPosition(const BlackMisc::Aviation::CCallsign &callsign, double latitudeDeg, double longitudeDeg, double altitudeMeters, + const ElevationCallback &setter) const; + + //! \copydoc XSwiftBus::CTraffic::setFollowedAircraft + void setFollowedAircraft(const QString &callsign); + + private: + BlackMisc::CGenericDBusInterface *m_dbusInterface = nullptr; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/plugins/simulator/flightgear/xswiftbusweatherproxy.cpp b/src/plugins/simulator/flightgear/xswiftbusweatherproxy.cpp new file mode 100644 index 000000000..223c4b5cf --- /dev/null +++ b/src/plugins/simulator/flightgear/xswiftbusweatherproxy.cpp @@ -0,0 +1,89 @@ +/* Copyright (C) 2015 + * 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. + */ + +#include "xswiftbusweatherproxy.h" + +#include + +class QDBusConnection; + +#define XSWIFTBUS_SERVICENAME "org.swift-project.xswiftbus" + +namespace BlackSimPlugin +{ + namespace XPlane + { + + CXSwiftBusWeatherProxy::CXSwiftBusWeatherProxy(QDBusConnection &connection, QObject *parent) : QObject(parent) + { + m_dbusInterface = new BlackMisc::CGenericDBusInterface(XSWIFTBUS_SERVICENAME, ObjectPath(), InterfaceName(), connection, this); + } + + bool CXSwiftBusWeatherProxy::isUsingRealWeather() const + { + return m_dbusInterface->callDBusRet(QLatin1String("isUsingRealWeather")); + } + + void CXSwiftBusWeatherProxy::setUseRealWeather(bool enable) + { + m_dbusInterface->callDBus(QLatin1String("setUseRealWeather"), enable); + } + + void CXSwiftBusWeatherProxy::setVisibility(double visibilityM) + { + m_dbusInterface->callDBus(QLatin1String("setVisibility"), visibilityM); + } + + void CXSwiftBusWeatherProxy::setTemperature(int degreesC) + { + m_dbusInterface->callDBus(QLatin1String("setTemperature"), degreesC); + } + + void CXSwiftBusWeatherProxy::setDewPoint(int degreesC) + { + m_dbusInterface->callDBus(QLatin1String("setDewPoint"), degreesC); + } + + void CXSwiftBusWeatherProxy::setQNH(double inHg) + { + m_dbusInterface->callDBus(QLatin1String("setQNH"), inHg); + } + + void CXSwiftBusWeatherProxy::setPrecipitationRatio(double precipRatio) + { + m_dbusInterface->callDBus(QLatin1String("setPrecipitationRatio"), precipRatio); + } + + void CXSwiftBusWeatherProxy::setThunderstormRatio(double cbRatio) + { + m_dbusInterface->callDBus(QLatin1String("setThunderstormRatio"), cbRatio); + } + + void CXSwiftBusWeatherProxy::setTurbulenceRatio(double turbulenceRatio) + { + m_dbusInterface->callDBus(QLatin1String("setTurbulenceRatio"), turbulenceRatio); + } + + void CXSwiftBusWeatherProxy::setRunwayFriction(int friction) + { + m_dbusInterface->callDBus(QLatin1String("setRunwayFriction"), friction); + } + + void CXSwiftBusWeatherProxy::setCloudLayer(int layer, int baseM, int topsM, int type, int coverage) + { + m_dbusInterface->callDBus(QLatin1String("setCloudLayer"), layer, baseM, topsM, type, coverage); + } + + void CXSwiftBusWeatherProxy::setWindLayer(int layer, int altitudeM, double directionDeg, int speedKt, int shearDirectionDeg, int shearSpeedKt, int turbulence) + { + m_dbusInterface->callDBus(QLatin1String("setWindLayer"), layer, altitudeM, directionDeg, speedKt, shearDirectionDeg, shearSpeedKt, turbulence); + } + + } +} diff --git a/src/plugins/simulator/flightgear/xswiftbusweatherproxy.h b/src/plugins/simulator/flightgear/xswiftbusweatherproxy.h new file mode 100644 index 000000000..a281aa4ec --- /dev/null +++ b/src/plugins/simulator/flightgear/xswiftbusweatherproxy.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2015 + * 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. + */ + +//! \file + +#ifndef BLACKSIMPLUGIN_XSWIFTBUS_WEATHER_PROXY_H +#define BLACKSIMPLUGIN_XSWIFTBUS_WEATHER_PROXY_H + +#include "blackmisc/genericdbusinterface.h" + +#include +#include + +// clazy:excludeall=const-signal-or-slot + +class QDBusConnection; + +//! \cond PRIVATE +#define XSWIFTBUS_WEATHER_INTERFACENAME "org.swift_project.xswiftbus.weather" +#define XSWIFTBUS_WEATHER_OBJECTPATH "/xswiftbus/weather" +//! \endcond + +namespace BlackSimPlugin +{ + namespace XPlane + { + + /*! + * Proxy object connected to a real XSwiftBus::CWeather object via DBus + */ + class CXSwiftBusWeatherProxy : public QObject + { + Q_OBJECT + + public: + //! Service name + static const QString &InterfaceName() + { + static QString s(XSWIFTBUS_WEATHER_INTERFACENAME); + return s; + } + + //! Service path + static const QString &ObjectPath() + { + static QString s(XSWIFTBUS_WEATHER_OBJECTPATH); + return s; + } + + //! Constructor + CXSwiftBusWeatherProxy(QDBusConnection &connection, QObject *parent = nullptr); + + //! Does the remote object exist? + bool isValid() const { return m_dbusInterface->isValid(); } + + private: + BlackMisc::CGenericDBusInterface *m_dbusInterface = nullptr; + + public slots: + //! \copydoc XSwiftBus::CWeather::isUsingRealWeather + bool isUsingRealWeather() const; + + //! \copydoc XSwiftBus::CWeather::setUseRealWeather + void setUseRealWeather(bool enable); + + //! \copydoc XSwiftBus::CWeather::setVisibility + void setVisibility(double visibilityM); + + //! \copydoc XSwiftBus::CWeather::setTemperature + void setTemperature(int degreesC); + + //! \copydoc XSwiftBus::CWeather::setDewPoint + void setDewPoint(int degreesC); + + //! \copydoc XSwiftBus::CWeather::setQNH + void setQNH(double inHg); + + //! \copydoc XSwiftBus::CWeather::setPrecipitationRatio + void setPrecipitationRatio(double precipRatio); + + //! \copydoc XSwiftBus::CWeather::setThunderstormRatio + void setThunderstormRatio(double cbRatio); + + //! \copydoc XSwiftBus::CWeather::setTurbulenceRatio + void setTurbulenceRatio(double turbulenceRatio); + + //! \copydoc XSwiftBus::CWeather::setRunwayFriction + void setRunwayFriction(int friction); + + //! \copydoc XSwiftBus::CWeather::setCloudLayer + void setCloudLayer(int layer, int baseM, int topsM, int type, int coverage); + + //! \copydoc XSwiftBus::CWeather::setWindLayer + void setWindLayer(int layer, int altitudeM, double directionDeg, int speedKt, int shearDirectionDeg, int shearSpeedKt, int turbulence); + }; + + } +} + +#endif // guard diff --git a/src/plugins/simulator/flightgearconfig/flightgearconfig.pro b/src/plugins/simulator/flightgearconfig/flightgearconfig.pro new file mode 100644 index 000000000..41a787ff4 --- /dev/null +++ b/src/plugins/simulator/flightgearconfig/flightgearconfig.pro @@ -0,0 +1,29 @@ +load(common_pre) + +QT += core dbus network widgets + +TARGET = simulatorflightgearconfig +TEMPLATE = lib + +CONFIG += plugin shared +CONFIG += blackmisc blackcore blackgui + +DEPENDPATH += . $$SourceRoot/src +INCLUDEPATH += . $$SourceRoot/src + +SOURCES += *.cpp +HEADERS += *.h +FORMS += *.ui +DISTFILES += simulatorxplaneconfig.json + +DESTDIR = $$DestRoot/bin/plugins/simulator + +win32 { + dlltarget.path = $$PREFIX/bin/plugins/simulator + INSTALLS += dlltarget +} else { + target.path = $$PREFIX/bin/plugins/simulator + INSTALLS += target +} + +load(common_post) diff --git a/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.cpp b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.cpp new file mode 100644 index 000000000..35dd47dc1 --- /dev/null +++ b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.cpp @@ -0,0 +1,30 @@ +/* Copyright (C) 2015 + * 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. + */ + +#include "simulatorxplaneconfig.h" +#include "simulatorxplaneconfigwindow.h" + +class QWidget; + +namespace BlackSimPlugin +{ + namespace XPlane + { + CSimulatorXPlaneConfig::CSimulatorXPlaneConfig(QObject *parent) : QObject(parent) + { + + } + + BlackGui::CPluginConfigWindow *CSimulatorXPlaneConfig::createConfigWindow(QWidget *parent) + { + CSimulatorXPlaneConfigWindow* w = new CSimulatorXPlaneConfigWindow(parent); + return w; + } + } +} diff --git a/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.h b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.h new file mode 100644 index 000000000..3f156bf38 --- /dev/null +++ b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2015 + * 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. + */ + +//! \file + +#ifndef BLACKSIMPLUGIN_SIMULATOR_XPLANE_CONFIG_H +#define BLACKSIMPLUGIN_SIMULATOR_XPLANE_CONFIG_H + +#include "blackgui/pluginconfig.h" +#include "blackmisc/settingscache.h" + +#include +#include + +class QWidget; + +namespace BlackGui { class CPluginConfigWindow; } +namespace BlackSimPlugin +{ + namespace XPlane + { + /*! + * Config plugin for the X-Plane plugin. + */ + class CSimulatorXPlaneConfig : public QObject, public BlackGui::IPluginConfig + { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.swift-project.blackgui.pluginconfiginterface" FILE "simulatorxplaneconfig.json") + Q_INTERFACES(BlackGui::IPluginConfig) + + public: + //! Ctor + CSimulatorXPlaneConfig(QObject *parent = nullptr); + + //! Dtor + virtual ~CSimulatorXPlaneConfig() {} + + //! \copydoc BlackGui::IPluginConfig::createConfigWindow() + BlackGui::CPluginConfigWindow *createConfigWindow(QWidget *parent) override; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.json b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.json new file mode 100644 index 000000000..f298b3d70 --- /dev/null +++ b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfig.json @@ -0,0 +1,3 @@ +{ + "identifier" : "org.swift-project.plugins.simulator.flightgear.config" +} diff --git a/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.cpp b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.cpp new file mode 100644 index 000000000..52202115e --- /dev/null +++ b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.cpp @@ -0,0 +1,55 @@ +/* Copyright (C) 2015 + * 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. + */ + +#include "simulatorxplaneconfigwindow.h" +#include "blackcore/application.h" +#include "ui_simulatorxplaneconfigwindow.h" +#include "blackmisc/simulation/xplane/xswiftbusconfigwriter.h" + +#include +#include + +using namespace BlackGui; +using namespace BlackMisc; +using namespace BlackMisc::Simulation::XPlane; + +namespace BlackSimPlugin +{ + namespace XPlane + { + CSimulatorXPlaneConfigWindow::CSimulatorXPlaneConfigWindow(QWidget *parent) : + CPluginConfigWindow(parent), + ui(new Ui::CSimulatorXPlaneConfigWindow) + { + ui->setupUi(this); + ui->comp_SettingsXSwiftBus->setDefaultP2PAddress(m_xswiftbusServerSetting.getDefault()); + ui->comp_SettingsXSwiftBus->set(m_xswiftbusServerSetting.getThreadLocal()); + + connect(ui->bb_OkCancel, &QDialogButtonBox::accepted, this, &CSimulatorXPlaneConfigWindow::onSettingsAccepted); + connect(ui->bb_OkCancel, &QDialogButtonBox::rejected, this, &CSimulatorXPlaneConfigWindow::close); + } + + CSimulatorXPlaneConfigWindow::~CSimulatorXPlaneConfigWindow() + { } + + void CSimulatorXPlaneConfigWindow::onSettingsAccepted() + { + const QString currentAddress = m_xswiftbusServerSetting.getThreadLocal(); + const QString updatedAddress = ui->comp_SettingsXSwiftBus->getDBusAddress(); + if (currentAddress != ui->comp_SettingsXSwiftBus->getDBusAddress()) + { + m_xswiftbusServerSetting.set(updatedAddress); + CXSwiftBusConfigWriter xswiftbusConfigWriter; + xswiftbusConfigWriter.setDBusAddress(updatedAddress); + xswiftbusConfigWriter.updateInAllXPlaneVersions(); + } + close(); + } + } // ns +} // ns diff --git a/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.h b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.h new file mode 100644 index 000000000..f24ddca4e --- /dev/null +++ b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2015 + * 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. + */ + +//! \file + +#ifndef BLACKSIMPLUGIN_SIMULATOR_XPLANE_CONFIG_WINDOW_H +#define BLACKSIMPLUGIN_SIMULATOR_XPLANE_CONFIG_WINDOW_H + +#include "blackgui/pluginconfigwindow.h" +#include "blackmisc/simulation/settings/mysettings.h" +#include "simulatorxplaneconfig.h" +#include +#include + +namespace Ui { class CSimulatorXPlaneConfigWindow; } +namespace BlackSimPlugin +{ + namespace XPlane + { + /** + * A window that shows all the X-Plane plugin options. + */ + class CSimulatorXPlaneConfigWindow : public BlackGui::CPluginConfigWindow + { + Q_OBJECT + + public: + //! Ctor. + CSimulatorXPlaneConfigWindow(QWidget *parent); + + //! Dtor. + virtual ~CSimulatorXPlaneConfigWindow(); + + private: + //! Settings have been accepted + void onSettingsAccepted(); + + QScopedPointer ui; + BlackMisc::CSetting m_xswiftbusServerSetting { this }; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.ui b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.ui new file mode 100644 index 000000000..97b35171f --- /dev/null +++ b/src/plugins/simulator/flightgearconfig/simulatorxplaneconfigwindow.ui @@ -0,0 +1,70 @@ + + + CSimulatorXPlaneConfigWindow + + + + 0 + 0 + 275 + 175 + + + + + 250 + 150 + + + + X-Plane plugin settings + + + + 5 + + + 5 + + + 5 + + + 5 + + + 2 + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + 0 + 100 + + + + + + + + + BlackGui::Components::CDBusServerAddressSelector + QFrame +
blackgui/components/dbusserveraddressselector.h
+ 1 +
+
+ + +
diff --git a/src/plugins/simulator/simulator.pro b/src/plugins/simulator/simulator.pro index 8a6ee8b31..e4b3c12a7 100644 --- a/src/plugins/simulator/simulator.pro +++ b/src/plugins/simulator/simulator.pro @@ -30,4 +30,8 @@ swiftConfig(sims.xplane) { SUBDIRS += xplane SUBDIRS += xplaneconfig } +swiftConfig(sims.fg) { + SUBDIRS += flightgear + SUBDIRS += flightgearconfig +} load(common_post)