diff --git a/src/blackcore/blackcore.contextnetwork.xml b/src/blackcore/blackcore.contextnetwork.xml new file mode 100644 index 000000000..9da320a3f --- /dev/null +++ b/src/blackcore/blackcore.contextnetwork.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/blackcore/blackcore.contextsettings.xml b/src/blackcore/blackcore.contextsettings.xml new file mode 100644 index 000000000..38717488e --- /dev/null +++ b/src/blackcore/blackcore.contextsettings.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/blackcore/blackcore.pro b/src/blackcore/blackcore.pro index 6839867b6..ed1f9d7ec 100644 --- a/src/blackcore/blackcore.pro +++ b/src/blackcore/blackcore.pro @@ -1,7 +1,7 @@ include (../../externals.pri) # GUI is required for the matrix classes -QT += network dbus +QT += network dbus xml TARGET = blackcore TEMPLATE = lib @@ -17,9 +17,24 @@ precompile_header:!isEmpty(PRECOMPILED_HEADER) { DEFINES += USING_PCH } +# Causes nmake to run qdbusxml2cpp to automatically generate the dbus adaptor and interface classes, +# then automatically adds them to the sources to compile +# !! Make sure the plugin is available as release build and known QT_PLUGIN_PATH +# QDBUSXML2CPP_INTERFACE_HEADER_FLAGS = -i blackmisc/blackmiscfreefunctions.h -i blackmisc/blackmiscallvalueclasses.h +QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS = -i blackmisc/blackmiscfreefunctions.h -i blackmisc/blackmiscallvalueclasses.h +DBUS_ADAPTORS += blackcore.contextnetwork.xml +DBUS_ADAPTORS += blackcore.contextsettings.xml + +# DBUS_INTERFACES += blackcore.contextnetwork.xml + DEFINES += LOG_IN_FILE HEADERS += *.h SOURCES += *.cpp +win32:!win32-g++*: PRE_TARGETDEPS += ../../lib/blackmisc.lib +else: PRE_TARGETDEPS += ../../lib/libblackmisc.a + DESTDIR = ../../lib + +OTHER_FILES += readme.txt blackcore.contextnetwork.xml blackcore.contextsettings.xml diff --git a/src/blackcore/context_network.cpp b/src/blackcore/context_network.cpp new file mode 100644 index 000000000..e00efd185 --- /dev/null +++ b/src/blackcore/context_network.cpp @@ -0,0 +1,398 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "context_network.h" +#include "coreruntime.h" +#include "blackmisc/avatcstationlist.h" +#include + +using namespace BlackMisc; +using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Network; +using namespace BlackMisc::Geo; + +namespace BlackCore +{ + + /* + * Init this context + */ + CContextNetwork::CContextNetwork(CCoreRuntime *parent) : + IContextNetwork(parent), + m_atcStationsOnline(), m_atcStationsBooked(), m_aircraftsInRange(), + m_network(nullptr), m_ownAircraft(), + m_metarCache() + { + + // 1. Init by "network driver" + this->m_network = new NetworkVatlib(this); + + // 2. Init own aircraft + this->initOwnAircraft(); + + // 3. Init network access driver for XML data (bookings) + this->m_networkManager = new QNetworkAccessManager(this); + this->connect(this->m_networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(psAtcBookingsRead(QNetworkReply *))); + this->m_atcBookingTimer = new QTimer(this); + this->connect(this->m_atcBookingTimer, SIGNAL(timeout()), this, SLOT(readAtcBookingsFromSource())); + this->m_atcBookingTimer->start(15 * 1000); + + // 4. connect signals and slots + bool connect = this->connect(this->m_network, SIGNAL(connectionStatusChanged(Cvatlib_Network::connStatus, Cvatlib_Network::connStatus)), + this, SLOT(psFsdConnectionStatusChanged(Cvatlib_Network::connStatus, Cvatlib_Network::connStatus))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect connectionStatusChanged"); + + connect = this->connect(this->m_network, SIGNAL(terminate()), + this, SLOT(psFsdConnectionTerminated())); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect terminate"); + + connect = this->connect(this->m_network, SIGNAL(atcPositionUpdate(BlackMisc::Aviation::CCallsign, BlackMisc::PhysicalQuantities::CFrequency, BlackMisc::Geo::CCoordinateGeodetic, BlackMisc::PhysicalQuantities::CLength)), + this, SLOT(psFsdAtcPositionUpdate(BlackMisc::Aviation::CCallsign, BlackMisc::PhysicalQuantities::CFrequency, BlackMisc::Geo::CCoordinateGeodetic, BlackMisc::PhysicalQuantities::CLength))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect atcPositionUpdate"); + + connect = this->connect(this->m_network, SIGNAL(atisQueryReplyReceived(BlackMisc::Aviation::CCallsign, QString)), + this, SLOT(psFsdAtisQueryReceived(BlackMisc::Aviation::CCallsign, QString))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect atis"); + + connect = this->connect(this->m_network, SIGNAL(metarReceived(QString)), + this, SLOT(psFsdMetarReceived(QString))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect metar"); + + connect = this->connect(this->m_network, SIGNAL(nameQueryReplyReceived(BlackMisc::Aviation::CCallsign, QString)), + this, SLOT(psFsdNameQueryReplyReceived(BlackMisc::Aviation::CCallsign, QString))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect name reply"); + + connect = this->connect(this->m_network, SIGNAL(exception(QString, bool)), + this, SLOT(psVatlibExceptionMessage(QString, bool))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect exception"); + + connect = this->connect(this->m_network, SIGNAL(aircraftInfoReceived(BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftIcao)), + this, SLOT(psFsdAircraftInfoReceived(BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftIcao))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect aircraft info"); + + connect = this->connect(this->m_network, SIGNAL(pilotDisconnected(BlackMisc::Aviation::CCallsign)), + this, SLOT(psFsdPilotDisconnected(BlackMisc::Aviation::CCallsign))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect pilot disconnected"); + + connect = this->connect(this->m_network, SIGNAL(aircraftPositionUpdate(BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituation, BlackMisc::Aviation::CTransponder)), + this, SLOT(psFsdAircraftPositionUpdate(BlackMisc::Aviation::CCallsign, BlackMisc::Aviation::CAircraftSituation, BlackMisc::Aviation::CTransponder))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect aircraft position update"); + + connect = this->connect(this->m_network, SIGNAL(frequencyQueryReplyReceived(BlackMisc::Aviation::CCallsign, BlackMisc::PhysicalQuantities::CFrequency)), + this, SLOT(psFsdFrequencyReceived(BlackMisc::Aviation::CCallsign, BlackMisc::PhysicalQuantities::CFrequency))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect frequency update"); + + connect = this->connect(this->m_network, SIGNAL(textMessagesReceived(BlackMisc::Network::CTextMessageList)), + this, SLOT(psFsdTextMessageReceived(BlackMisc::Network::CTextMessageList))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect text message"); + + + // relay status message + connect = this->connect(this->m_network, SIGNAL(statusMessage(BlackMisc::CStatusMessage)), + this, SIGNAL(statusMessage(BlackMisc::CStatusMessage))); + Q_ASSERT_X(connect, "CContextNetwork", "Cannot connect status message"); + } + + /* + * Cleanup + */ + CContextNetwork::~CContextNetwork() + { + if (this->isConnected()) this->disconnectFromNetwork(); + this->disconnect(this); + this->disconnect(this->m_network); + } + + /* + * Init own aircraft + */ + void CContextNetwork::initOwnAircraft() + { + Q_ASSERT(this->getRuntime()); + Q_ASSERT(this->getRuntime()->getIContextSettings()); + this->m_ownAircraft.initComSystems(); + this->m_ownAircraft.initTransponder(); + CAircraftSituation situation( + CCoordinateGeodetic( + CLatitude::fromWgs84("N 049° 18' 17"), + CLongitude::fromWgs84("E 008° 27' 05"), + CLength(0, CLengthUnit::m())), + CAltitude(312, CAltitude::MeanSeaLevel, CLengthUnit::ft()) + ); + this->m_ownAircraft.setSituation(situation); + this->m_ownAircraft.setPilot(this->getRuntime()->getIContextSettings()->getNetworkSettings().getCurrentNetworkServer().getUser()); + + // TODO: This would need to come from somewhere (mappings) + // Own callsign, plane ICAO status, model used + this->m_ownAircraft.setCallsign(CCallsign("BLACK")); + this->m_ownAircraft.setIcaoInfo(CAircraftIcao("C172", "L1P", "GA", "GA", "0000ff")); + } + + /* + * Connect to network + */ + CStatusMessages CContextNetwork::connectToNetwork() + { + qDebug() << Q_FUNC_INFO; + + CStatusMessages msgs; + CServer currentServer = this->getRuntime()->getIContextSettings()->getNetworkSettings().getCurrentNetworkServer(); + + if (!currentServer.getUser().isValid()) + { + msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityWarning, "Invalid user credentials")); + } + //else if (!this->m_network->isDisconnected()) + //{ + // msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityWarning, "Already connected")); + //} + else + { + this->m_ownAircraft.setPilot(currentServer.getUser()); + this->m_network->setServer(currentServer); + //this->m_network->setOwnAircraft(this->m_ownAircraft); + this->m_network->initiateConnection(); + QString msg = "Connection pending "; + msg.append(" ").append(currentServer.getAddress()).append(" ").append(QString::number(currentServer.getPort())); + msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityInfo, msg)); + } + return msgs; + } + + /* + * Disconnect from network + */ + CStatusMessages CContextNetwork::disconnectFromNetwork() + { + qDebug() << Q_FUNC_INFO; + + CStatusMessages msgs; + //if (this->m_network->isDisconnected()) + //{ + // msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityWarning, "Already disconnected")); + //} + //else + { + this->m_network->terminateConnection(); + this->m_aircraftsInRange.clear(); + this->m_atcStationsBooked.clear(); + this->m_atcStationsOnline.clear(); + this->m_metarCache.clear(); + msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityInfo, "Connection terminating")); + } + return msgs; + } + + /* + * Connected + */ + bool CContextNetwork::isConnected() const + { + return false; + } + + /* + * Own's plane position update + */ + void CContextNetwork::sendOwnAircraftCheckedPositionUpdateToNetwork() const + { + // TODO: Would this logic go into network_vatlib? + // 1. Check not sending to many updates + // 2. Send as pull update to intermediate update + // 3. Same position, no update? + // ... + + // this->log(Q_FUNC_INFO, this->m_ownAircraft.toQString()); + // this->m_network->sendAircraftUpdate(this->m_ownAircraft); + } + + /* + * Own Aircraft + */ + CStatusMessages CContextNetwork::setOwnAircraft(const BlackMisc::Aviation::CAircraft &aircraft) + { + this->log(Q_FUNC_INFO, aircraft.toQString()); + CStatusMessages msgs; + //if (this->m_network->isDisconnected()) + //{ + // this->m_ownAircraft = aircraft; + //} + //else + { + msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityError, "Cannot set plane info, network already connected")); + } + return msgs; + } + + /* + * Own Aircraft details + */ + CStatusMessages CContextNetwork::sendOwnAircraftDetails() + { +#if 0 + CStatusMessages msgs; + if (this->m_network->isDisconnected() && false) + { + msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityError, "Cannot send plane info, network not connected")); + + } + else if (!this->m_ownAircraft.isValidForLogin()) + { + msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityError, "Cannot send plane info, own aircraft is invalid")); + } + else + { + // TODO: really sending plane info ?? + // NOPE: this->m_network->sendAircraftInfo(this->m_ownAircraft); + this->sendOwnAircraftCheckedPositionUpdateToNetwork(); + QString m("Aircraft send "); + m.append(this->m_ownAircraft.toQString(true)); + msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityInfo, m)); + } + return msgs; +#endif // 0 + return CStatusMessages(); + } + + /* + * Own position + */ + void CContextNetwork::updateOwnPosition(const BlackMisc::Geo::CCoordinateGeodetic &position, const BlackMisc::Aviation::CAltitude &altitude) + { + if (position == this->m_ownAircraft.getPosition() && altitude == this->m_ownAircraft.getAltitude()) return; + this->m_ownAircraft.setPosition(position); + this->m_ownAircraft.setAltitude(altitude); + this->sendOwnAircraftCheckedPositionUpdateToNetwork(); + } + + /* + * Update own situation + */ + void CContextNetwork::updateOwnSituation(const BlackMisc::Aviation::CAircraftSituation &situation) + { + if (situation == this->m_ownAircraft.getSituation()) return; + this->m_ownAircraft.setSituation(situation); + this->sendOwnAircraftCheckedPositionUpdateToNetwork(); + } + + /* + * Own cockpit data + */ + void CContextNetwork::updateOwnCockpit(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2, const BlackMisc::Aviation::CTransponder &transponder) + { + bool changed = false; + if (com1 != this->m_ownAircraft.getCom1System()) + { + this->m_ownAircraft.setCom1System(com1); + changed = true; + } + if (com2 != this->m_ownAircraft.getCom2System()) + { + this->m_ownAircraft.setCom2System(com2); + changed = true; + } + if (transponder != this->m_ownAircraft.getTransponder()) + { + this->m_ownAircraft.setTransponder(transponder); + changed = true; + } + + if (!changed) return; + //this->m_network->updateOwnCockpit(com1, com2, transponder); + } + + /* + * Own aircraft + */ + CAircraft CContextNetwork::getOwnAircraft() const + { + this->log(Q_FUNC_INFO, this->m_ownAircraft.toQString()); + return this->m_ownAircraft; + } + + /* + * Send text messages + */ + void CContextNetwork::sendTextMessages(const CTextMessageList &textMessages) + { + this->log(Q_FUNC_INFO, textMessages.toQString()); + this->m_network->sendTextMessages(textMessages); + } + + /* + * Connection terminated + */ + void CContextNetwork::psFsdConnectionTerminated() + { + emit this->statusMessage(CStatusMessage(CStatusMessage::TypeTrafficNetwork, CStatusMessage::SeverityInfo, "connection terminated")); + emit this->connectionTerminated(); + } + + /* + * Connection status changed + */ + void CContextNetwork::psFsdConnectionStatusChanged(Cvatlib_Network::connStatus from, Cvatlib_Network::connStatus to) + { + ConnectionStatus fromCs = static_cast(from); + ConnectionStatus toCs = static_cast(to); + CStatusMessages msgs; + + + // send 1st position + if (toCs == CContextNetwork::ConnectionStatusConnected) + { + msgs = this->sendOwnAircraftDetails(); + } + + // send as message + QString m("connection status changed "); + m.append(this->connectionStatusToString(fromCs)). + append(" "). + append(this->connectionStatusToString(toCs)); + qDebug() << m; + msgs.append(CStatusMessage(CStatusMessage::TypeTrafficNetwork, + toCs == ConnectionStatusError ? CStatusMessage::SeverityError : CStatusMessage::SeverityInfo, m)); + emit this->statusMessage(msgs.at(0)); + + // send as own signal + emit this->connectionStatusChanged(fromCs, toCs); + } + + /* + * Name query + */ + void CContextNetwork::psFsdNameQueryReplyReceived(const CCallsign &callsign, const QString &realname) + { + this->log(Q_FUNC_INFO, callsign.toQString(), realname); + if (realname.isEmpty()) return; + CValueMap vm(CAtcStation::IndexControllerRealname, realname); + this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm); + this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm); + + vm = CValueMap(CAircraft::IndexPilotRealname, realname); + this->m_aircraftsInRange.applyIf(&CAircraft::getCallsign, callsign, vm); + } + + + /* + * Ping, is DBus alive? + */ + qint64 CContextNetwork::ping(qint64 token) const + { + return token; + } + + /* + * Exception to status message + */ + void CContextNetwork::psVatlibExceptionMessage(const QString &message, bool fatal) + { + CStatusMessage msg(CStatusMessage::TypeTrafficNetwork, + fatal ? CStatusMessage::SeverityError : CStatusMessage::SeverityWarning, message); + emit this->statusMessage(msg); + } + +} // namespace diff --git a/src/blackcore/context_network.h b/src/blackcore/context_network.h new file mode 100644 index 000000000..2c9d3e82e --- /dev/null +++ b/src/blackcore/context_network.h @@ -0,0 +1,354 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BLACKCORE_CONTEXTNETWORK_H +#define BLACKCORE_CONTEXTNETWORK_H + +#include "blackcore/dbus_server.h" +#include "blackcore/network_vatlib.h" +#include "blackcore/context_network_interface.h" +#include "blackmisc/avallclasses.h" +#include "blackmisc/statusmessage.h" +#include "blackmisc/statusmessages.h" +#include +#include +#include +#include +#include +#include + +#define BLACKCORE_CONTEXTNETWORK_INTERFACENAME "blackcore.contextnetwork" + +namespace BlackCore +{ + + class CCoreRuntime; + + + /*! + * \brief Network context + */ + class CContextNetwork : public IContextNetwork + { + // Register by same name, make signals sender independent + // http://dbus.freedesktop.org/doc/dbus-faq.html#idp48032144 + Q_CLASSINFO("D-Bus Interface", BLACKCORE_CONTEXTNETWORK_INTERFACENAME) + Q_OBJECT + + public: + + /*! + * \brief With link to server + * \param server + */ + CContextNetwork(CCoreRuntime *parent); + + /*! + * \brief Destructor + */ + virtual ~CContextNetwork(); + + /*! + * \brief Register myself in DBus + * \param server + */ + void registerWithDBus(CDBusServer *server) + { + server->addObject(IContextNetwork::ServicePath(), this); + } + + /*! + * \brief Runtime + * \return + */ + const CCoreRuntime *getRuntime() const + { + return reinterpret_cast(this->parent()); + } + + /*! + * \brief Using local objects? + * \return + */ + virtual bool usingLocalObjects() const { return true; } + + public slots: + + /*! + * \brief Read ATC bookings + * \return + */ + virtual void readAtcBookingsFromSource() const; + + /*! + * \brief The "central" ATC list with online ATC controllers + * \return + */ + // If I make this &getAtcStations XML is not generated correctly + // needs to be crosschecked with the latest version of Qt + virtual const BlackMisc::Aviation::CAtcStationList getAtcStationsOnline() const + { + this->log(Q_FUNC_INFO); + return m_atcStationsOnline; + } + + /*! + * \brief ATC list, with booked controllers + * \return + */ + // If I make this &getAtcStations XML is not generated correctly + virtual const BlackMisc::Aviation::CAtcStationList getAtcStationsBooked() const + { + this->log(Q_FUNC_INFO); + return m_atcStationsBooked; + } + + /*! + * \brief Aircraft list + * \return + */ + // If I make this &getAtcStations XML is not generated correctly + virtual const BlackMisc::Aviation::CAircraftList getAircraftsInRange() const + { + this->log(Q_FUNC_INFO); + return m_aircraftsInRange; + } + + /*! + * \brief Connect to Network + * \return + */ + virtual BlackMisc::CStatusMessages connectToNetwork(); + + /*! + * \brief Disconnect from network + * \return + */ + virtual BlackMisc::CStatusMessages disconnectFromNetwork(); + + /*! + * \brief Network connected? + * \return + */ + virtual bool isConnected() const; + + /*! + * \brief Set own aircraft + * \param aircraft + * \return + */ + virtual BlackMisc::CStatusMessages setOwnAircraft(const BlackMisc::Aviation::CAircraft &aircraft); + + /*! + * \brief Update own position + * \param position + * \param altitude + */ + virtual void updateOwnPosition(const BlackMisc::Geo::CCoordinateGeodetic &position, const BlackMisc::Aviation::CAltitude &altitude); + + /*! + * \brief Update own situation + * \param situation + */ + virtual void updateOwnSituation(const BlackMisc::Aviation::CAircraftSituation &situation); + + /*! + * \brief Update own cockpit + * \param com1 + * \param com2 + * \param transponder + */ + virtual void updateOwnCockpit(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2, const BlackMisc::Aviation::CTransponder &transponder); + + /*! + * \brief Get own aircraft + * \return + */ + virtual BlackMisc::Aviation::CAircraft getOwnAircraft() const; + + /*! + * \brief Text messages (also private chat messages) + * \param textMessage + */ + virtual void sendTextMessages(const BlackMisc::Network::CTextMessageList &textMessages); + + /*! + * \brief Request METAR + * \param airportIcaoCode + */ + virtual BlackMisc::Aviation::CInformationMessage getMetar(const QString &airportIcaoCode); + + /*! + * \brief Used to check if network is alive + * \param token + * \return + */ + virtual qint64 ping(qint64 token) const; + + private: + BlackMisc::Aviation::CAtcStationList m_atcStationsOnline; + BlackMisc::Aviation::CAtcStationList m_atcStationsBooked; + BlackMisc::Aviation::CAircraftList m_aircraftsInRange; + BlackCore::INetwork *m_network; + BlackMisc::Aviation::CAircraft m_ownAircraft; + QMap m_metarCache; + + // for reading XML + QNetworkAccessManager *m_networkManager; + QTimer *m_atcBookingTimer; + QDateTime m_atcBookingsUpdateTimestamp; + + /*! + * \brief Replace value by new values, but keep object itself intact + * \param newStations + */ + void setAtcStationsBooked(const BlackMisc::Aviation::CAtcStationList &newStations); + + /*! + * \brief Replace value by new values, but keep object itself intact + * \param newStations + */ + void setAtcStationsOnline(const BlackMisc::Aviation::CAtcStationList &newStations); + + /*! + * \brief The "central" ATC list with online ATC controllers + * \return + */ + BlackMisc::Aviation::CAtcStationList &atcStationsOnline() + { + return m_atcStationsOnline; + } + + /*! + * \brief ATC list, with booked controllers + * \return + */ + BlackMisc::Aviation::CAtcStationList &atcStationsBooked() + { + return m_atcStationsBooked; + } + + /*! + * \brief Check position update before sending to network + */ + void sendOwnAircraftCheckedPositionUpdateToNetwork() const; + + /*! + * \brief Own aircraft details + * \param aircraft + * \return + */ + BlackMisc::CStatusMessages sendOwnAircraftDetails(); + + /*! + * \brief Init my very onw aircraft + */ + void initOwnAircraft(); + + private slots: + /*! + * \brief Terminated connection + */ + void psFsdConnectionTerminated(); + + /*! + * \brief Connection status changed + * \param from + * \param to + */ + void psFsdConnectionStatusChanged(Cvatlib_Network::connStatus from, Cvatlib_Network::connStatus to); + + /*! + * \brief ATC position update + * \param callsign + * \param frequency + * \param position + * \param range + */ + void psFsdAtcPositionUpdate(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency, const BlackMisc::Geo::CCoordinateGeodetic &position, const BlackMisc::PhysicalQuantities::CLength &range); + + /*! + * \brief Controller disconnected + * \param callsign + */ + void psFsdAtcControllerDisconnected(const BlackMisc::Aviation::CCallsign &callsign); + + /*! + * \brief ATIS received + * \param callsign + * \param atisMessage + */ + void psFsdAtisQueryReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &atisMessage); + + /*! + * \brief METAR received + * \param metarMessage + */ + void psFsdMetarReceived(const QString &metarMessage); + + /*! + * \brief Realnname recevied + * \param callsign + * \param realname + */ + void psFsdNameQueryReplyReceived(const BlackMisc::Aviation::CCallsign &callsign, const QString &realname); + + /*! + * \brief Plane info received + * \param callsign + * \param icaoData + */ + void psFsdAircraftInfoReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftIcao &icaoData); + + /*! + * \brief Aircraft position update + * \param callsign + * \param situation + * \param transponder + */ + void psFsdAircraftPositionUpdate(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::Aviation::CAircraftSituation &situation, const BlackMisc::Aviation::CTransponder &transponder); + + /*! + * \brief Pilot disconnected + * \param callsign + */ + void psFsdPilotDisconnected(const BlackMisc::Aviation::CCallsign &callsign); + + /*! + * \brief Frequency received + * \param callsign + * \param frequency + */ + void psFsdFrequencyReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency); + + /*! + * \brief Radio text message received + * \param callsign + * \param message + * \param frequencies + */ + void psFsdTextMessageReceived(const BlackMisc::Network::CTextMessageList &messages); + + /*! + * \brief Bookings via XML read + * \param nwReply + * TODO: encapsulate reading from WWW in some class + */ + void psAtcBookingsRead(QNetworkReply *nwReply); + + /*! + * \brief Exception message + * \param message + * \param fatal + */ + void psVatlibExceptionMessage(const QString &message, bool fatal); + + }; +} + +// Declaring BlackCore::CContextNetwork * crashed when reading data +Q_DECLARE_METATYPE(BlackCore::CContextNetwork::ConnectionStatus) + +#endif // guard diff --git a/src/blackcore/context_network_aircraft.cpp b/src/blackcore/context_network_aircraft.cpp new file mode 100644 index 000000000..02981f3fc --- /dev/null +++ b/src/blackcore/context_network_aircraft.cpp @@ -0,0 +1,101 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "context_network.h" +#include "coreruntime.h" +#include "blackmisc/predicates.h" + +using namespace BlackMisc; +using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Network; +using namespace BlackMisc::Geo; + +namespace BlackCore +{ + + /* + * Aircraft info received + */ + void CContextNetwork::psFsdAircraftInfoReceived(const CCallsign &callsign, const CAircraftIcao &icaoData) + { + this->log(Q_FUNC_INFO, callsign.toQString(), icaoData.toQString()); + CAircraftList aircraftsWithCallsign = this->m_aircraftsInRange.findByCallsign(callsign); + if (aircraftsWithCallsign.isEmpty()) + { + // new aircraft + CAircraft aircraft; + aircraft.setCallsign(callsign); + aircraft.setIcaoInfo(icaoData); + aircraft.calculcateDistanceToPlane(this->m_ownAircraft.getPosition()); + this->m_aircraftsInRange.insert(aircraft); + emit this->m_network->sendFrequencyQuery(callsign); + emit this->m_network->sendNameQuery(callsign); + emit this->changedAircraftsInRange(); + } + else + { + // update + CValueMap vm(CAircraft::IndexIcao, icaoData.toQVariant()); + this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual(&CAircraft::getCallsign, callsign), vm); + emit this->changedAircraftsInRange(); + } + } + + /* + * Aircraft update received + */ + void CContextNetwork::psFsdAircraftPositionUpdate(const CCallsign &callsign, const CAircraftSituation &situation, const CTransponder &transponder) + { + this->log(Q_FUNC_INFO, callsign.toQString(), situation.toQString(), transponder.toQString()); + CAircraftList list = this->m_aircraftsInRange.findByCallsign(callsign); + if (list.isEmpty()) + { + // new aircraft + CAircraft aircraft; + aircraft.setCallsign(callsign); + aircraft.setSituation(situation); + aircraft.setTransponder(transponder); + aircraft.calculcateDistanceToPlane(this->m_ownAircraft.getPosition()); + this->m_aircraftsInRange.insert(aircraft); + emit this->m_network->sendFrequencyQuery(callsign); + emit this->m_network->sendNameQuery(callsign); + emit this->m_network->requestAircraftInfo(callsign); + emit this->changedAircraftsInRange(); + } + else + { + // update + CValueMap vm; + vm.addValue(CAircraft::IndexTransponder, transponder); + vm.addValue(CAircraft::IndexSituation, situation); + this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual(&CAircraft::getCallsign, callsign), vm); + emit this->changedAircraftsInRange(); + } + } + + /* + * Pilot disconnected + */ + void CContextNetwork::psFsdPilotDisconnected(const CCallsign &callsign) + { + this->log(Q_FUNC_INFO, callsign.toQString()); + this->m_aircraftsInRange.removeIf(&CAircraft::getCallsign, callsign); + } + + /* + * Frequency received + */ + void CContextNetwork::psFsdFrequencyReceived(const CCallsign &callsign, const CFrequency &frequency) + { + this->log(Q_FUNC_INFO, callsign.toQString(), frequency.toQString()); + + // update + CValueMap vm(CAircraft::IndexFrequencyCom1, frequency.toQVariant()); + this->m_aircraftsInRange.applyIf(BlackMisc::Predicates::MemberEqual(&CAircraft::getCallsign, callsign), vm); + emit this->changedAircraftsInRange(); + } + +} // namespace diff --git a/src/blackcore/context_network_atc.cpp b/src/blackcore/context_network_atc.cpp new file mode 100644 index 000000000..2e4b0e210 --- /dev/null +++ b/src/blackcore/context_network_atc.cpp @@ -0,0 +1,238 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "context_network.h" +#include "coreruntime.h" +#include "blackmisc/avatcstationlist.h" +#include "blackmisc/predicates.h" +#include +#include +#include +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Network; +using namespace BlackMisc::Geo; + +namespace BlackCore +{ + + /* + * Read bookings + */ + void CContextNetwork::readAtcBookingsFromSource() const + { + QUrl url("http://vatbook.euroutepro.com/xml2.php"); + QNetworkRequest request(url); + this->m_networkManager->get(request); + } + + /* + * Booked stations + */ + void CContextNetwork::setAtcStationsBooked(const BlackMisc::Aviation::CAtcStationList &newStations) + { + this->m_atcStationsBooked = newStations; + } + + /* + * Online stations + */ + void CContextNetwork::setAtcStationsOnline(const BlackMisc::Aviation::CAtcStationList &newStations) + { + this->m_atcStationsOnline = newStations; + } + + /* + * Request METAR + */ + BlackMisc::Aviation::CInformationMessage CContextNetwork::getMetar(const QString &airportIcaoCode) + { + CInformationMessage metar; + QString icao = airportIcaoCode.trimmed().toUpper(); + if (icao.length() != 4) return metar; + if (this->m_metarCache.contains(icao)) metar = this->m_metarCache[icao]; + if (metar.isEmpty() || metar.timeDiffReceivedMs() > 10 * 1000) + { + // outdated, or not in cache at all + this->m_network->requestMetar(airportIcaoCode.trimmed().toUpper()); + + // with this little trick we try to make an asynchronous signal / slot + // based approach a synchronous return value + QTime waitForMetar = QTime::currentTime().addMSecs(1000); + while (QTime::currentTime() < waitForMetar) + { + // process some other events and hope network answer is received already + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + if (m_metarCache.contains(icao)) + { + metar = this->m_metarCache[icao]; + break; + } + } + } + return metar; + } + + /* + * ATC Position update + */ + void CContextNetwork::psFsdAtcPositionUpdate(const CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &frequency, const CCoordinateGeodetic &position, const BlackMisc::PhysicalQuantities::CLength &range) + { + this->log(Q_FUNC_INFO, callsign.toQString(), frequency.toQString(), position.toQString(), range.toQString()); + CAtcStationList stationsWithCallsign = this->m_atcStationsOnline.findByCallsign(callsign); + if (stationsWithCallsign.isEmpty()) + { + // new station + CAtcStation station; + station.setCallsign(callsign); + station.setRange(range); + station.setFrequency(frequency); + station.setPosition(position); + station.setOnline(true); + station.calculcateDistanceToPlane(this->m_ownAircraft.getPosition()); + this->m_atcStationsOnline.insert(station); + emit this->changedAtcStationsOnline(); + emit this->m_network->sendAtisQuery(callsign); // request ATIS + emit this->m_network->sendNameQuery(callsign); + } + else + { + // update + CValueMap values; + values.addValue(CAtcStation::IndexFrequency, frequency); + values.addValue(CAtcStation::IndexPosition, position); + values.addValue(CAtcStation::IndexRange, range); + this->m_atcStationsOnline.applyIf(BlackMisc::Predicates::MemberEqual(&CAtcStation::getCallsign, callsign), values); + emit this->changedAtcStationsOnline(); + } + } + + /* + * ATC Controller disconnected + */ + void CContextNetwork::psFsdAtcControllerDisconnected(const CCallsign &callsign) + { + this->log(Q_FUNC_INFO, callsign.toQString()); + this->m_atcStationsOnline.removeIf(&CAtcStation::getCallsign, callsign); + emit this->changedAtcStationsOnline(); + this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, CValueMap(CAtcStation::IndexIsOnline, QVariant(false))); + } + + /* + * ATIS received + */ + void CContextNetwork::psFsdAtisQueryReceived(const CCallsign &callsign, const QString &atisMessage) + { + this->log(Q_FUNC_INFO, callsign.toQString(), atisMessage); + CValueMap vm(CAtcStation::IndexAtisMessage, atisMessage); + this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsign, vm); + this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsign, vm); + } + + /* + * Metar received + */ + void CContextNetwork::psFsdMetarReceived(const QString &metarMessage) + { + this->log(Q_FUNC_INFO, metarMessage); + if (metarMessage.length() < 10) return; // invalid + const QString icaoCode = metarMessage.left(4).toUpper(); + const QString icaoCodeTower = icaoCode + "_TWR"; + CCallsign callsignTower(icaoCodeTower); + CInformationMessage metar(CInformationMessage::METAR, metarMessage); + + // add METAR to existing stations + CValueMap vm(CAtcStation::IndexMetar, metar.toQVariant()); + this->m_atcStationsOnline.applyIf(&CAtcStation::getCallsign, callsignTower, vm); + this->m_atcStationsBooked.applyIf(&CAtcStation::getCallsign, callsignTower, vm); + this->m_metarCache.insert(icaoCode, metar); + } + + /* + * Bookings read from XML + * TODO: encapsulate reading from WWW in some class + */ + void CContextNetwork::psAtcBookingsRead(QNetworkReply *nwReply) + { + if (nwReply->error() == QNetworkReply::NoError) + { + QString xmlData = nwReply->readAll(); + QDomDocument doc; + + if (doc.setContent(xmlData)) + { + QDomNode atc = doc.elementsByTagName("atcs").at(0); + QDomNodeList bookingNodes = atc.toElement().elementsByTagName("booking"); + int size = bookingNodes.size(); + CSequence stations; + for (int i = 0; i < size; i++) + { + QDomNode bookingNode = bookingNodes.at(i); + QDomNodeList bookingNodeValues = bookingNode.childNodes(); + CAtcStation bookedStation; + CUser user; + for (int v = 0; v < bookingNodeValues.size(); v++) + { + QDomNode bookingNodeValue = bookingNodeValues.at(v); + QString name = bookingNodeValue.nodeName().toLower(); + QString value = bookingNodeValue.toElement().text(); + if (name == "id") + { + // could be used as unique key + } + else if (name == "callsign") + { + bookedStation.setCallsign(CCallsign(value)); + } + else if (name == "name") + { + user.setRealname(value); + } + else if (name == "cid") + { + user.setId(value); + } + else if (name == "time_end") + { + QDateTime t = QDateTime::fromString(value, "yyyy-MM-dd HH:mm:ss"); + bookedStation.setBookedUntilUtc(t); + } + else if (name == "time_start") + { + QDateTime t = QDateTime::fromString(value, "yyyy-MM-dd HH:mm:ss"); + bookedStation.setBookedFromUtc(t); + } + } + // time checks + QDateTime now = QDateTime::currentDateTimeUtc(); + if (now.msecsTo(bookedStation.getBookedUntilUtc()) < (1000 * 60 * 15)) continue; // until n mins in past + if (now.msecsTo(bookedStation.getBookedFromUtc()) > (1000 * 60 * 60 * 24)) continue; // to far in the future, n hours + + // booking does not have position, so distance cannot be calculated + // bookedStation.calculcateDistanceToPlane(this->m_ownAircraft.getPosition()); + + bookedStation.setController(user); + + // consolidate and append + this->m_atcStationsOnline.mergeWithBooking(bookedStation); + stations.insert(bookedStation); + } + nwReply->close(); + nwReply->deleteLater(); + + // set the new values + if (this->getAtcStationsBooked() != stations) + { + this->atcStationsBooked() = stations; + emit this->changedAtcStationsBooked(); + } + } // node + } // content + } // method +} // namespace diff --git a/src/blackcore/context_network_interface.cpp b/src/blackcore/context_network_interface.cpp new file mode 100644 index 000000000..511ec222a --- /dev/null +++ b/src/blackcore/context_network_interface.cpp @@ -0,0 +1,194 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "blackcore/context_network_interface.h" +#include +#include +#include + + +namespace BlackCore +{ + + /* + * Constructor for DBus + */ + IContextNetwork::IContextNetwork(const QString &serviceName, QDBusConnection &connection, QObject *parent) : QObject(parent), m_dBusInterface(0) + { + this->m_dBusInterface = new BlackMisc::CGenericDBusInterface( + serviceName , IContextNetwork::ServicePath(), IContextNetwork::InterfaceName(), + connection, this); + this->relaySignals(serviceName, connection); + } + + /* + * Workaround for signals, not working without, but why? + */ + void IContextNetwork::relaySignals(const QString &serviceName, QDBusConnection &connection) + { + connection.connect(serviceName, IContextNetwork::ServicePath(), IContextNetwork::InterfaceName(), + "connectionStatusChanged", this, SIGNAL(connectionStatusChanged(uint, uint))); + connection.connect(serviceName, IContextNetwork::ServicePath(), IContextNetwork::InterfaceName(), + "changedAtcStationsBooked", this, SIGNAL(changedAtcStationsBooked())); + connection.connect(serviceName, IContextNetwork::ServicePath(), IContextNetwork::InterfaceName(), + "changedAtcStationsOnline", this, SIGNAL(changedAtcStationsOnline())); + connection.connect(serviceName, IContextNetwork::ServicePath(), IContextNetwork::InterfaceName(), + "connectionTerminated", this, SIGNAL(connectionTerminated())); + connection.connect(serviceName, IContextNetwork::ServicePath(), IContextNetwork::InterfaceName(), + "statusMessage", this, SIGNAL(statusMessage(BlackMisc::CStatusMessage))); + connection.connect(serviceName, IContextNetwork::ServicePath(), IContextNetwork::InterfaceName(), + "textMessagesReceived", this, SIGNAL(textMessagesReceived(BlackMisc::Network::CTextMessageList))); + } + + /* + * Clear text of status + */ + QString IContextNetwork::connectionStatusToString(ConnectionStatus status) const + { + int index = metaObject()->indexOfEnumerator("ConnectionStatus"); + QMetaEnum metaEnum = metaObject()->enumerator(index); + return metaEnum.valueToKey(status); + } + + /* + * Relay to DBus + */ + void IContextNetwork::readAtcBookingsFromSource() const + { + this->m_dBusInterface->callDBus(QLatin1Literal("readAtcBookingsFromSource")); + } + + /* + * Relay to DBus + */ + const BlackMisc::Aviation::CAtcStationList IContextNetwork::getAtcStationsOnline() const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getAtcStationsOnline")); + } + + /* + * Relay to DBus + */ + const BlackMisc::Aviation::CAtcStationList IContextNetwork::getAtcStationsBooked() const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getAtcStationsBooked")); + } + + /* + * Relay to DBus + */ + const BlackMisc::Aviation::CAircraftList IContextNetwork::getAircraftsInRange() const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getAircraftsInRange")); + } + + /* + * Relay to DBus + */ + BlackMisc::Aviation::CAircraft IContextNetwork::getOwnAircraft() const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getOwnAircraft")); + } + + /* + * Relay to DBus + */ + BlackMisc::CStatusMessages IContextNetwork::connectToNetwork() + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("connectToNetwork")); + } + + /* + * Relay to DBus + */ + BlackMisc::CStatusMessages IContextNetwork::disconnectFromNetwork() + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("disconnectFromNetwork")); + } + + /* + * Relay to DBus + */ + bool IContextNetwork::isConnected() const + { + qint64 token = QDateTime::currentMSecsSinceEpoch(); + if (this->ping(token) != token) return false; // Core cannot be reached + return this->m_dBusInterface->callDBusRet(QLatin1Literal("isConnected")); + } + + /* + * Relay to DBus + */ + BlackMisc::CStatusMessages IContextNetwork::setOwnAircraft(const BlackMisc::Aviation::CAircraft &aircraft) + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("setOwnAircraft"), aircraft); + } + + /* + * Relay to DBus + */ + void IContextNetwork::updateOwnPosition(const BlackMisc::Geo::CCoordinateGeodetic &position, const BlackMisc::Aviation::CAltitude &altitude) + { + this->m_dBusInterface->callDBus(QLatin1Literal("updateOwnPosition"), position, altitude); + } + + /* + * Relay to DBus + */ + void IContextNetwork::updateOwnSituation(const BlackMisc::Aviation::CAircraftSituation &situation) + { + this->m_dBusInterface->callDBus(QLatin1Literal("updateOwnSituation"), situation); + } + + /* + * Relay to DBus + */ + void IContextNetwork::updateOwnCockpit(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2, const BlackMisc::Aviation::CTransponder &transponder) + { + this->m_dBusInterface->callDBus(QLatin1Literal("updateOwnCockpit"), com1, com2, transponder); + } + + /* + * Relay to DBus + */ + void IContextNetwork::sendTextMessages(const BlackMisc::Network::CTextMessageList &textMessages) + { + this->m_dBusInterface->callDBus(QLatin1Literal("sendTextMessages"), textMessages); + } + + /* + * Relay to DBus + */ + BlackMisc::Aviation::CInformationMessage IContextNetwork::getMetar(const QString &airportIcaoCode) + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("requestMetar"), airportIcaoCode); + } + + /* + * Ping, is DBus alive? + */ + qint64 IContextNetwork::ping(qint64 token) const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("ping"), token); + } + + /* + * Logging + */ + void IContextNetwork::log(const QString &method, const QString &m1, const QString &m2, const QString &m3, const QString &m4) const + { + if (m1.isEmpty()) + qDebug() << " LOG: " << method; + else if (m2.isEmpty()) + qDebug() << " LOG: " << method << m1; + else if (m3.isEmpty()) + qDebug() << " LOG: " << method << m1 << m2; + else if (m4.isEmpty()) + qDebug() << " LOG: " << method << m1 << m2 << m3; + else + qDebug() << " LOG: " << method << m1 << m2 << m3 << m4; + } + +} // namespace diff --git a/src/blackcore/context_network_interface.h b/src/blackcore/context_network_interface.h new file mode 100644 index 000000000..893a3d6d8 --- /dev/null +++ b/src/blackcore/context_network_interface.h @@ -0,0 +1,267 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BLACKCORE_CONTEXTNETWORK_INTERFACE_H +#define BLACKCORE_CONTEXTNETWORK_INTERFACE_H + +#include "blackmisc/avallclasses.h" +#include "blackmisc/statusmessage.h" +#include "blackmisc/statusmessages.h" +#include "blackmisc/nwtextmessagelist.h" +#include "blackmisc/genericdbusinterface.h" +#include "blackcore/network_vatlib.h" +#include +#include + +#define BLACKCORE_CONTEXTNETWORK_INTERFACENAME "blackcore.contextnetwork" +#define BLACKCORE_CONTEXTNETWORK_SERVICEPATH "/network" + +// SERVICENAME must contain at least one ".", otherwise generation fails +// as this is interpreted in the way comain.somename + +namespace BlackCore +{ + + /*! + * \brief The IContextNetwork class + */ + class IContextNetwork : public QObject + { + Q_OBJECT + Q_ENUMS(ConnectionStatus) + Q_CLASSINFO("D-Bus Interface", BLACKCORE_CONTEXTNETWORK_INTERFACENAME) + + public: + /*! + * \brief Service name + * \return + */ + static const QString &InterfaceName() + { + static QString s(BLACKCORE_CONTEXTNETWORK_INTERFACENAME); + return s; + } + + /*! + * \brief Service path + * \return + */ + static const QString &ServicePath() + { + static QString s(BLACKCORE_CONTEXTNETWORK_SERVICEPATH); + return s; + } + + /*! + * \brief Qt compliant status + */ + enum ConnectionStatus + { + ConnectionStatusIdle = static_cast(Cvatlib_Network::connStatus_Idle), + ConnectionStatusConnected = static_cast(Cvatlib_Network::connStatus_Connected), + ConnectionStatusConnecting = static_cast(Cvatlib_Network::connStatus_Connecting), + ConnectionStatusDisconnected = static_cast(Cvatlib_Network::connStatus_Disconnected), + ConnectionStatusError = static_cast(Cvatlib_Network::connStatus_Error) + }; + + /*! + * \brief DBus version constructor + * \param serviceName + * \param connection + * \param parent + */ + IContextNetwork(const QString &serviceName, QDBusConnection &connection, QObject *parent = 0); + + /*! + * Destructor + */ + ~IContextNetwork() {} + + + /*! + * \brief Using local objects? + * \return + */ + virtual bool usingLocalObjects() const { return false; } + + private: + BlackMisc::CGenericDBusInterface *m_dBusInterface; + + /*! + * Relay connection signals to local signals + * No idea why this has to be wired and is not done automatically + * \param connection + */ + void relaySignals(const QString &serviceName, QDBusConnection &connection); + + protected: + /*! + * \brief IContextNetwork + * \param parent + */ + IContextNetwork(QObject *parent = 0) : QObject(parent), m_dBusInterface(0) {} + + /*! + * Connection status as cleartext + */ + QString connectionStatusToString(ConnectionStatus status) const; + + /*! + * \brief Helper for logging, likely to be removed / changed + * \param method + * \param m1 + * \param m2 + * \param m3 + * \param m4 + */ + void log(const QString &method, const QString &m1 = "", const QString &m2 = "", const QString &m3 = "", const QString &m4 = "") const; + + signals: + + /*! + * \brief ATC station list has been changed + * \param message + */ + void statusMessage(const BlackMisc::CStatusMessage &message); + + /*! + * \brief List has been changed + */ + void changedAtcStationsOnline(); + + /*! + * \brief ATC station list has been changed + */ + void changedAtcStationsBooked(); + + /*! + * \brief Aircraft list has been changed + */ + void changedAircraftsInRange(); + + /*! + * \brief Terminated connection + */ + void connectionTerminated(); + + /*! + * \brief Connection status changed + * \param from + * \param to + */ + // If I use the enum, adaptor / interface are not created correctly + void connectionStatusChanged(uint from, uint to); + + /*! + * \brief Text messages (also private chat messages) + * \param textMessage + */ + void textMessagesReceived(const BlackMisc::Network::CTextMessageList &textMessages); + + public slots: + + /*! + * \brief Read ATC bookings + * \return + */ + virtual void readAtcBookingsFromSource() const; + + /*! + * \brief The "central" ATC list with online ATC controllers + * \return + */ + // If I make this &getAtcStations XML is not generated correctly + virtual const BlackMisc::Aviation::CAtcStationList getAtcStationsOnline() const; + + /*! + * \brief ATC list, with booked controllers + * \return + */ + virtual const BlackMisc::Aviation::CAtcStationList getAtcStationsBooked() const; + + /*! + * \brief Aircraft list + * \return + */ + virtual const BlackMisc::Aviation::CAircraftList getAircraftsInRange() const; + + /*! + * \brief Get own aircraft + * \return + */ + virtual BlackMisc::Aviation::CAircraft getOwnAircraft() const; + + /*! + * \brief Connect to Network + * \return + */ + virtual BlackMisc::CStatusMessages connectToNetwork(); + + /*! + * \brief Disconnect from network + * \return + */ + virtual BlackMisc::CStatusMessages disconnectFromNetwork(); + + /*! + * \brief Network connected? + * \return + */ + virtual bool isConnected() const; + + /*! + * Set own aircraft + * \param aircraft + * \return + */ + virtual BlackMisc::CStatusMessages setOwnAircraft(const BlackMisc::Aviation::CAircraft &aircraft); + + /*! + * \brief Own position, be aware height is terrain height + * \param Position + * \param altitude + * \return + */ + virtual void updateOwnPosition(const BlackMisc::Geo::CCoordinateGeodetic &position, const BlackMisc::Aviation::CAltitude &altitude); + + /*! + * \brief Complete situation update + * \param Situation + * \return + */ + virtual void updateOwnSituation(const BlackMisc::Aviation::CAircraftSituation &situation); + + /*! + * \brief Update own cockpit + * \param com1 + * \param com2 + * \param transponder + */ + virtual void updateOwnCockpit(const BlackMisc::Aviation::CComSystem &com1, const BlackMisc::Aviation::CComSystem &com2, const BlackMisc::Aviation::CTransponder &transponder); + + /*! + * \brief Text messages (radio and private chat messages) + * \param textMessage + */ + virtual void sendTextMessages(const BlackMisc::Network::CTextMessageList &textMessages); + + /*! + * \brief Get METAR, if not available request it + * \param airportIcaoCode + * \return + */ + virtual BlackMisc::Aviation::CInformationMessage getMetar(const QString &airportIcaoCode); + + /*! + * \brief Ping + * \param token + * \return + */ + virtual qint64 ping(qint64 token) const; + + }; +} + +#endif // guard diff --git a/src/blackcore/context_network_textmessages.cpp b/src/blackcore/context_network_textmessages.cpp new file mode 100644 index 000000000..7b735565e --- /dev/null +++ b/src/blackcore/context_network_textmessages.cpp @@ -0,0 +1,27 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "context_network.h" +#include "coreruntime.h" + +using namespace BlackMisc; +using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Network; +using namespace BlackMisc::Geo; + +namespace BlackCore +{ + /* + * Radio text message received + */ + void CContextNetwork::psFsdTextMessageReceived(const CTextMessageList &messages) + { + if (messages.isEmpty()) return; + this->log(Q_FUNC_INFO, messages.toQString()); + this->textMessagesReceived(messages); // relay + } + +} // namespace diff --git a/src/blackcore/context_settings.cpp b/src/blackcore/context_settings.cpp new file mode 100644 index 000000000..78624390f --- /dev/null +++ b/src/blackcore/context_settings.cpp @@ -0,0 +1,61 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "context_settings.h" +#include "coreruntime.h" +#include "blackmisc/settingutilities.h" + +using namespace BlackMisc::Settings; +using namespace BlackMisc::Network; +using namespace BlackMisc; + +namespace BlackCore +{ + + /* + * Init this context + */ + CContextSettings::CContextSettings(CCoreRuntime *parent) : IContextSettings(parent), m_settingsNetwork() + { + // create some dummy settings + // this would actually be reading the settings from disk .. + + this->m_settingsNetwork.setCurrentNetworkServer( + CServer("Testserver", "Client project testserver", "vatsim-germany.org", 6809, + CUser("guest", "Guest Client project", "", "guest"))); + this->m_settingsNetwork.addTrafficNetworkServer(this->m_settingsNetwork.getCurrentNetworkServer()); + this->m_settingsNetwork.addTrafficNetworkServer( + CServer("Europe C2", "Real VATSIM Server", "88.198.19.202", 6809, + CUser("vatsimid", "Black Client", "", "vatsimpw")) + ); + } + + /* + * Network settings + */ + BlackMisc::Settings::CSettingsNetwork CContextSettings::getNetworkSettings() const + { + return this->m_settingsNetwork; + } + + /* + * Pass value + */ + BlackMisc::CStatusMessages CContextSettings::value(const QString &path, const QString &command, const QVariant &value) + { + Q_ASSERT(path.length() > 3); + Q_ASSERT(path.indexOf('/') >= 0); + QString nextLevelPath = CSettingUtilities::removeLeadingPath(path); + BlackMisc::CStatusMessages msgs = CSettingUtilities::wrongPathMessages(path); + bool changed = false; + if (path.startsWith(IContextSettings::PathNetworkSettings())) + { + msgs = this->m_settingsNetwork.value(nextLevelPath, command, value, changed); + if (changed) emit this->changedNetworkSettings(); + } + return msgs; + } + +} // namespace diff --git a/src/blackcore/context_settings.h b/src/blackcore/context_settings.h new file mode 100644 index 000000000..9e74ef933 --- /dev/null +++ b/src/blackcore/context_settings.h @@ -0,0 +1,83 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BLACKCORE_CONTEXTSETTINGS_H +#define BLACKCORE_CONTEXTSETTINGS_H + +#include "blackcore/dbus_server.h" +#include "blackcore/context_settings_interface.h" +#include "blackmisc/statusmessage.h" +#include "blackmisc/statusmessages.h" +#include + +#define BLACKCORE_CONTEXTSETTINGS_INTERFACENAME "blackcore.contextsettings" + +namespace BlackCore +{ + class CCoreRuntime; + + /*! + * \brief Network context + */ + class CContextSettings : public IContextSettings + { + // Register by same name, make signals sender independent + // http://dbus.freedesktop.org/doc/dbus-faq.html#idp48032144 + Q_CLASSINFO("D-Bus Interface", BLACKCORE_CONTEXTSETTINGS_INTERFACENAME) + Q_OBJECT + + public: + + /*! + * Context + * \param parent + */ + CContextSettings(CCoreRuntime *parent); + + /*! + * Destructor + */ + virtual ~CContextSettings() {} + + /*! + * \brief Register myself in DBus + * \param server + */ + void registerWithDBus(CDBusServer *server) + { + server->addObject(IContextSettings::ServicePath(), this); + } + + /*! + * \brief Runtime + * \return + */ + const CCoreRuntime *getRuntime() const + { + return reinterpret_cast(this->parent()); + } + + public slots: + /*! + * \brief Network settings + * \return + */ + virtual BlackMisc::Settings::CSettingsNetwork getNetworkSettings() const; + + /*! + * \brief Validate + * \param path + * \param value + * \return + */ + virtual BlackMisc::CStatusMessages value(const QString &path, const QString &command, const QVariant &value); + + private: + BlackMisc::Settings::CSettingsNetwork m_settingsNetwork; + + }; +} + +#endif // guard diff --git a/src/blackcore/context_settings_interface.cpp b/src/blackcore/context_settings_interface.cpp new file mode 100644 index 000000000..48f1d148b --- /dev/null +++ b/src/blackcore/context_settings_interface.cpp @@ -0,0 +1,70 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "blackcore/context_settings_interface.h" +#include "blackmisc/blackmiscfreefunctions.h" +#include +#include +#include + +namespace BlackCore +{ + + /* + * Constructor for DBus + */ + IContextSettings::IContextSettings(const QString &serviceName, QDBusConnection &connection, QObject *parent) : QObject(parent), m_dBusInterface(0) + { + this->m_dBusInterface = new BlackMisc::CGenericDBusInterface( + serviceName , IContextSettings::ServicePath(), IContextSettings::InterfaceName(), + connection, this); + this->relaySignals(serviceName, connection); + } + + /* + * Workaround for unreliable signals + */ + void IContextSettings::relaySignals(const QString &serviceName, QDBusConnection &connection) + { + connection.connect(serviceName, IContextSettings::ServicePath(), IContextSettings::InterfaceName(), + "changedNetworkSettings", this, SIGNAL(changedNetworkSettings())); + } + + /* + * Relay to DBus + */ + BlackMisc::Settings::CSettingsNetwork IContextSettings::getNetworkSettings() const + { + return this->m_dBusInterface->callDBusRet(QLatin1Literal("getNetworkSettings")); + } + + /* + * Relay to DBus + */ + BlackMisc::CStatusMessages IContextSettings::value(const QString &path, const QString &command, const QVariant &value) + { + int type = value.userType() - BlackMisc::firstBlackMetaType(); + return this->m_dBusInterface->callDBusRet(QLatin1Literal("value"), path, command, QDBusVariant(value), type); + } + + /* + * DBus version of value + */ + BlackMisc::CStatusMessages IContextSettings::value(const QString &path, const QString &command, QDBusVariant value, int unifiedBlackMetaType) + { + QVariant qv = value.variant(); + if (qv.canConvert()) + { + // convert from QDBusArgument + int type = BlackMisc::firstBlackMetaType() + unifiedBlackMetaType; // unify + qv = BlackMisc::fixQVariantFromDbusArgument(qv, type); + } + // when called locally, this will call the virtual method + // of the concrete implementation in context_settings + return this->value(path, command, qv); + } + + +} // namespace diff --git a/src/blackcore/context_settings_interface.h b/src/blackcore/context_settings_interface.h new file mode 100644 index 000000000..30bab6af9 --- /dev/null +++ b/src/blackcore/context_settings_interface.h @@ -0,0 +1,136 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BLACKCORE_CONTEXTSETTINGS_INTERFACE_H +#define BLACKCORE_CONTEXTSETTINGS_INTERFACE_H + +#include "blackmisc/statusmessages.h" +#include "blackmisc/genericdbusinterface.h" +#include "blackmisc/settingutilities.h" +#include "blackmisc/setnetwork.h" +#include +#include +#include + +#define BLACKCORE_CONTEXTSETTINGS_INTERFACENAME "blackcore.contextsettings" +#define BLACKCORE_CONTEXTSETTINGS_SERVICEPATH "/settings" + +// SERVICENAME must contain at least one ".", otherwise generation fails +// as this is interpreted in the way comain.somename + +namespace BlackCore +{ + + /*! + * \brief The IContextSettings class + */ + class IContextSettings : public QObject + { + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", BLACKCORE_CONTEXTSETTINGS_INTERFACENAME) + + public: + + /*! + * \brief Service name + * \return + */ + static const QString &InterfaceName() + { + static QString s(BLACKCORE_CONTEXTSETTINGS_INTERFACENAME); + return s; + } + + /*! + * \brief Service path + * \return + */ + static const QString &ServicePath() + { + static QString s(BLACKCORE_CONTEXTSETTINGS_SERVICEPATH); + return s; + } + + /*! + * \brief Path + * \return + */ + static const QString &PathNetworkSettings() + { + static QString s("network"); + return s; + } + + /*! + * \brief DBus version constructor + * \param serviceName + * \param connection + * \param parent + */ + IContextSettings(const QString &serviceName, QDBusConnection &connection, QObject *parent = 0); + + /*! + * Destructor + */ + ~IContextSettings() {} + + private: + BlackMisc::CGenericDBusInterface *m_dBusInterface; + + /*! + * \brief Relay connection signals to local signals + * No idea why this has to be wired and is not done automatically + * \param connection + */ + void relaySignals(const QString &serviceName, QDBusConnection &connection); + + protected: + /*! + * \brief IContextSettings + * \param parent + */ + IContextSettings(QObject *parent = 0) : QObject(parent), m_dBusInterface(0) {} + + signals: + + /*! + * \brief Settings have been changed + */ + void changedNetworkSettings(); + + public slots: + + /*! + * \brief Network settings + * \return + */ + virtual BlackMisc::Settings::CSettingsNetwork getNetworkSettings() const; + + /*! + * \brief Handle value + * \param path + * \param command + * \param value + * \return + */ + virtual BlackMisc::CStatusMessages value(const QString &path, const QString &command, const QVariant &value); + + /*! + * Basically an unwanted signature as this is different from the "local" signature and + * contains explicit DBus types (a: QDbusArgument, b: type for conversion). + * If this can be removed, fine. + * + * \brief value + * \param path + * \param command + * \param value + * \param unifiedBlackMetaType + * \return + */ + virtual BlackMisc::CStatusMessages value(const QString &path, const QString &command, QDBusVariant value, int unifiedBlackMetaType); + }; +} + +#endif // guard diff --git a/src/blackcore/coreruntime.cpp b/src/blackcore/coreruntime.cpp new file mode 100644 index 000000000..fd6c67f74 --- /dev/null +++ b/src/blackcore/coreruntime.cpp @@ -0,0 +1,42 @@ +#include "blackcore/coreruntime.h" +#include "blackmisc/blackmiscfreefunctions.h" +#include "blackmisc/nwserver.h" + +namespace BlackCore +{ + /* + * Constructor + */ + CCoreRuntime::CCoreRuntime(bool withDbus, QObject *parent) : + QObject(parent), m_init(false), m_dbusServer(nullptr), m_contextNetwork(nullptr), m_settings(nullptr) + { + this->init(withDbus); + } + + /* + * Init runtime + */ + void CCoreRuntime::init(bool withDbus) + { + if (m_init) return; + BlackMisc::registerMetadata(); + BlackMisc::initResources(); + + // TODO: read settings + if (withDbus) + { + QString dBusAddress = "session"; + this->m_dbusServer = new CDBusServer(dBusAddress, this); + } + + // contexts + this->m_settings = new CContextSettings(this); + if (withDbus) this->m_settings->registerWithDBus(this->m_dbusServer); + + this->m_contextNetwork = new CContextNetwork(this); + if (withDbus) this->m_contextNetwork->registerWithDBus(this->m_dbusServer); // complete object after init + + // flag + m_init = true; + } +} diff --git a/src/blackcore/coreruntime.h b/src/blackcore/coreruntime.h new file mode 100644 index 000000000..7c5fc48e9 --- /dev/null +++ b/src/blackcore/coreruntime.h @@ -0,0 +1,90 @@ +#ifndef BLACKCORE_CORERUNTIME_H +#define BLACKCORE_CORERUNTIME_H + +#include +#include "dbus_server.h" +#include "context_network.h" +#include "context_settings.h" + +namespace BlackCore +{ + /*! + * \brief The CCoreRuntime class + */ + class CCoreRuntime : public QObject + { + Q_OBJECT + + private: + bool m_init; /*!< flag */ + CDBusServer *m_dbusServer; + CContextNetwork *m_contextNetwork; + CContextSettings *m_settings; + + /*! + * \brief Init + * \param withDbus + */ + void init(bool withDbus); + + public: + /*! + * \brief Constructor + * \param withDbus + * \param parent + */ + CCoreRuntime(bool withDbus = true, QObject *parent = nullptr); + + /*! + * \brief Destructor + */ + virtual ~CCoreRuntime() {} + + /*! + * \brief DBus server + * \return + */ + const CDBusServer *getDBusServer() const + { + return this->m_dbusServer; + } + + /*! + * \brief Context for network + * \return + */ + IContextNetwork *getIContextNetwork() + { + return this->m_contextNetwork; + } + + /*! + * \brief Context for network + * \return + */ + const IContextNetwork *getIContextNetwork() const + { + return this->m_contextNetwork; + } + + /*! + * \brief Settings + * \return + */ + IContextSettings *getIContextSettings() + { + return this->m_settings; + } + + /*! + * \brief Settings + * \return + */ + const IContextSettings *getIContextSettings() const + { + return this->m_settings; + } + + }; +} +#endif // guard diff --git a/src/blackcore/dbus_server.cpp b/src/blackcore/dbus_server.cpp index 9af3b339f..5e1071419 100644 --- a/src/blackcore/dbus_server.cpp +++ b/src/blackcore/dbus_server.cpp @@ -4,53 +4,155 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include "dbus_server.h" namespace BlackCore { + const QString CDBusServer::ServiceName = QString(BLACKCORE_DBUSERVER_SERVICENAME); + + /* + * Constructor + * Remark, without the default "unix:tmpdir=/tmp" any refereal to address crashes + * see http://download.froglogic.com/public/qt5-squishcoco-report/QtBase/source_241_preprocessed.html + */ CDBusServer::CDBusServer(const QString &address, QObject *parent) : - QObject(parent), m_busServer(address, parent) + QObject(parent), m_busServer(CDBusServer::isP2P(address) ? address : "unix:tmpdir=/tmp" , parent), m_serverMode(CDBusServer::SERVERMODE_P2P) { - if (!m_busServer.isConnected()) + if (address.isEmpty() || address.toLower() == "session") { - qWarning() << m_busServer.lastError().message(); + // we use a session bus connection instead of a real P2P connection + this->m_serverMode = CDBusServer::SERVERMODE_SESSIONBUS; + QDBusConnection con = QDBusConnection::sessionBus(); + + if (!con.registerService(CDBusServer::ServiceName)) + { + qFatal("Cannot register DBus service"); + } + this->newConnection(con); + } + else if (address.toLower() == "system") + { + // we use a system bus connection instead of a real P2P connection + this->m_serverMode = CDBusServer::SERVERMODE_SYSTEMBUS; + QDBusConnection con = QDBusConnection::systemBus(); + if (!con.registerService(CDBusServer::ServiceName)) + { + qFatal("Cannot register DBus service"); + } + this->newConnection(con); } else { - qDebug() << "Server listening on address: " << m_busServer.address(); + this->m_serverMode = CDBusServer::SERVERMODE_P2P; + // Note: P2P has no service name + if (!m_busServer.isConnected()) + { + qWarning() << m_busServer.lastError().message(); + } + else + { + qDebug() << "Server listening on address: " << m_busServer.address(); + } + connect(&m_busServer, &QDBusServer::newConnection, this, &CDBusServer::newConnection); } - - connect(&m_busServer, &QDBusServer::newConnection, this, &CDBusServer::newConnection); } - void CDBusServer::newConnection(const QDBusConnection & connection) + /* + * Check for P2P address + */ + bool CDBusServer::isP2P(const QString &address) { - QMap::ConstIterator i = m_objects.begin(); + return !(address.isEmpty() || address.toLower() == "session" || address.toLower() == "system"); + } + + /* + * Class info + */ + const QString CDBusServer::getClassInfo(QObject *object) + { + if (!object) return ""; + const QMetaObject *mo = object->metaObject(); + if (mo->classInfoCount() < 1) return ""; + for (int i = 0; i < mo->classInfoCount(); i++) + { + QMetaClassInfo ci = mo->classInfo(i); + QString name(ci.name()); + if (name == "D-Bus Interface") return QString(ci.value()); + } + return ""; + } + + /* + * Connection established + */ + bool CDBusServer::newConnection(const QDBusConnection &connection) + { + QMap::ConstIterator i = m_objects.begin(); QDBusConnection newConnection(connection); - m_DBusConnections.insert(newConnection.name(), newConnection); - + bool success = true; qDebug() << "New Connection from: " << newConnection.name(); while (i != m_objects.end()) { - qDebug() << "Adding " << i.key() << "to the new connection."; - newConnection.registerObject(i.key(), i.value()); + qDebug() << "Adding " << i.key() << CDBusServer::getClassInfo(i.value()) << "to the new connection."; + bool ok = newConnection.registerObject(i.key(), i.value(), CDBusServer::RegisterOptions()); + Q_ASSERT_X(ok, "CDBusServer::newConnection", "Registration failed"); + if (!ok) success = false; ++i; } + + return success; } - void CDBusServer::addObject(const QString &name, QObject *object) + /* + * Add the objects + */ + void CDBusServer::addObject(const QString &path, QObject *object) { - m_objects.insert(name, object); + if (!object) return; + m_objects.insert(path, object); // this will be added when connection is established + + if (this->m_serverMode == CDBusServer::SERVERMODE_P2P) return; + + bool success = false; + if (this->m_serverMode == CDBusServer::SERVERMODE_SESSIONBUS) + { + success = QDBusConnection::sessionBus().registerObject(path, object, CDBusServer::RegisterOptions()); + qDebug() << "Adding " << path << CDBusServer::getClassInfo(object) << "to the session bus."; + if (!success) qDebug() << "Error, no success with registration" << this->lastError().message(); + } + else if (this->m_serverMode == CDBusServer::SERVERMODE_SYSTEMBUS) + { + success = QDBusConnection::systemBus().registerObject(path, object, CDBusServer::RegisterOptions()); + qDebug() << "Adding " << path << CDBusServer::getClassInfo(object) << "to the system bus."; + if (!success) qDebug() << "Error, no success with registration" << this->lastError().message(); + } + else + { + Q_ASSERT_X(false, "CDBusServer::addObject", "Wrong server mode"); + } + // Q_ASSERT_X(success, "CDBusServer::addObject", "Registration failed"); } - void CDBusServer::printError() + /* + * Print last error message + */ + void CDBusServer::printError() const { qWarning() << m_busServer.lastError().name(); } + /* + * Last error + */ + QDBusError CDBusServer::lastError() const + { + return this->m_busServer.lastError(); + } + } // namespace BlackCore diff --git a/src/blackcore/dbus_server.h b/src/blackcore/dbus_server.h index 3c9068cd8..9bd0c2429 100644 --- a/src/blackcore/dbus_server.h +++ b/src/blackcore/dbus_server.h @@ -6,6 +6,7 @@ #ifndef BLACKCORE_DBUSSERVER_H #define BLACKCORE_DBUSSERVER_H +#include "blackmisc/valueobject.h" // for qHash overload, include before Qt stuff due GCC issue #include #include #include @@ -13,52 +14,147 @@ #include #include -namespace BlackCore { +#define BLACKCORE_DBUSERVER_SERVICENAME "org.vatsim.pilotClient" - /*! - * \brief Custom DBusServer - * \details This class implements a custom DBusServer for DBus peer connections - */ +namespace BlackCore +{ + + /*! + * \brief Custom DBusServer + * \details This class implements a custom DBusServer for DBus peer connections + */ class CDBusServer : public QObject - { - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "org.vatsim.pilotClient") + { + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", BLACKCORE_DBUSERVER_SERVICENAME) - private: - QDBusServer m_busServer; //!< QDBusServer implementation + public: + static const QString ServiceName; - QMap m_objects; //!< Mapping of all exposed objects + /*! + * \brief Server mode, normally P2P, but can be changed for debugging / testing + */ + enum ServerMode + { + SERVERMODE_P2P, + SERVERMODE_SESSIONBUS, + SERVERMODE_SYSTEMBUS + }; - QMap m_DBusConnections; //!< Mapping of all DBusConnection objects + private: + QDBusServer m_busServer; //!< QDBusServer implementation + ServerMode m_serverMode; + QMap m_objects; //!< Mapping of all exposed objects + QMap m_DBusConnections; //!< Mapping of all DBusConnection objects - public: + /*! + * \brief Check if address means a real server with P2P connection + * \param address + * \return + */ + static bool isP2P(const QString &address); - /*! - * \brief Constructor - * \param parent - */ + /*! + * \brief Get the class info + * \param object + * \return + */ + static const QString getClassInfo(QObject *object); + + /*! + * \brief Register options with connection + * \return + */ + static const QDBusConnection::RegisterOptions &RegisterOptions() + { + static QDBusConnection::RegisterOptions opt = QDBusConnection::ExportAdaptors | QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllSlots; + return opt; + } + + public: + /*! + * \brief Constructor + * \param parent + */ CDBusServer(const QString &address, QObject *parent = 0); - /*! - * \brief Adds a QObject to be exposed to DBus - * \param name - * \param object - */ - void addObject(const QString &name, QObject *object); + /*! + * \brief Adds a QObject to be exposed to DBus + * \param name + * \param object + */ + void addObject(const QString &name, QObject *object); - void printError(); + /*! + * \brief printError + */ + void printError() const; - public slots: + /*! + * \brief Last error + * \return + */ + QDBusError lastError() const; - /*! - * \brief Called when a new DBus client has connected - * \param connection - */ - void newConnection(const QDBusConnection & connection); - signals: - }; + /*! + * \brief Connected? + * \return + */ + bool isConnected() const + { + return this->m_busServer.isConnected(); + } + /*! + * \brief address + * \return + */ + QString address() const + { + + return this->m_busServer.address(); + } + + /*! + * \brief Connection by name + * \param connectionName + * \return + */ + const QDBusConnection getDbusConnection(const QString &connectionName) const + { + return this->m_DBusConnections.value(connectionName, CDBusServer::defaultConnection()); + } + + /*! + * \brief Get DBbus connections + * \return + */ + const QList getDbusConnections() const + { + // http://stackoverflow.com/questions/1124340/any-ideas-why-qhash-and-qmap-return-const-t-instead-of-const-t + return this->m_DBusConnections.values(); + } + + /*! + * \brief Default connection + * \return + */ + static const QDBusConnection &defaultConnection() + { + static QDBusConnection defaultConnection("default"); + return defaultConnection; + } + + private slots: + + /*! + * \brief Called when a new DBus client has connected + * \param connection + * \return + */ + bool newConnection(const QDBusConnection &connection); + }; } -#endif // DBUSSERVER_H +#endif // guard diff --git a/src/blackcore/readme.txt b/src/blackcore/readme.txt new file mode 100644 index 000000000..5b9adacb9 --- /dev/null +++ b/src/blackcore/readme.txt @@ -0,0 +1,27 @@ +Starting: dbus-daemon.exe --session +- blocks CMD (sometimes daemon continues to run when pressing CTRL/C) +- does not start without directory session.d, i.e. ..\Qt\Qt5.1.0DBus\qtbase\etc\dbus-1\session.d + +!!! The includes are set in the qmake file, there are the header files +!!! which are used in the interface +/// qdbuscpp2xml context_network.h -x blackmisc_cpp2xml.dll -o blackcore.contextnetwork.xml +qdbuscpp2xml context_network_interface.h -x blackmisc_cpp2xml.dll -o blackcore.contextnetwork.xml +qdbuscpp2xml context_settings_interface.h -x blackmisc_cpp2xml.dll -o blackcore.contextsettings.xml + +Set search path for plugins: env.var. QT_PLUGIN_PATH + +Done automatically (qmake), but if required manually +Interface: +qdbusxml2cpp -i blackmisc/blackmiscfreefunctions.h -i blackmisc/blackmiscallvalueclasses.h -p contextnetwork_interface.h: H:\Projects\Qt\VatsimClient\client\src\blackcore\blackcore.contextnetwork.xml +qdbusxml2cpp -i contextnetwork_interface.h -p :contextnetwork_interface.cpp H:\Projects\Qt\VatsimClient\client\src\blackcore\blackcore.contextnetwork.xml + +The : indicates generation of cpp file + +Adaptor: +qdbusxml2cpp -i blackmisc/blackmiscfreefunctions.h -i blackmisc/blackmiscallvalueclasses.h -a contextnetwork_adaptor.h blackcore.contextnetwork.xml +qdbusxml2cpp -i context_adaptor.h -a :contextnetwork_adaptor.cpp blackcore.contextnetwork.xml + + +Manually: + + diff --git a/src/blackmisc/genericdbusinterface.h b/src/blackmisc/genericdbusinterface.h new file mode 100644 index 000000000..5309cef6c --- /dev/null +++ b/src/blackmisc/genericdbusinterface.h @@ -0,0 +1,181 @@ +#ifndef BLACKMISC_GENERICDBUSINTERFACE_H +#define BLACKMISC_GENERICDBUSINTERFACE_H + +#include +#include +#include + + +namespace BlackMisc +{ + +#include + + /*! + * Used for hand written interface based on virtual methods. + * Allows to relay a message to DBus in a single code line + */ + class CGenericDBusInterface : public QDBusAbstractInterface + { + + public: + + /*! + * \brief Generic DBus interface + * \param serverName + * \param path + * \param interfaceName + * \param connection + * \param parent + */ + CGenericDBusInterface(const QString &serverName, const QString &path, const QString &interfaceName, const QDBusConnection &connection, QObject *parent = 0) : + QDBusAbstractInterface(serverName, path, interfaceName.toUtf8().constData(), connection, parent) + { + // void + } + + /*! + * \brief Call DBus + * \param method + */ + void callDBus(const QLatin1String &method) + { + QList argumentList; + this->asyncCallWithArgumentList(method, argumentList); + } + + /*! + * \brief Call DBus, no return value + * \param method + * \param p1 + * \return + */ + template void callDBus(const QLatin1String &method, P1 p1) + { + QList argumentList; + argumentList << QVariant::fromValue(p1); + this->asyncCallWithArgumentList(method, argumentList); + } + + /*! + * \brief Call DBus, no return value + * \param method + * \param p1 + * \param p2 + * \return + */ + template void callDBus(const QLatin1String &method, P1 p1, P2 p2) + { + QList argumentList; + argumentList << QVariant::fromValue(p1) << QVariant::fromValue(p2); + this->asyncCallWithArgumentList(method, argumentList); + } + + /*! + * \brief Call DBus, no return value + * \param method + * \param p1 + * \param p2 + * \param p3 + * \return + */ + template void callDBus(const QLatin1String &method, P1 p1, P2 p2, P3 p3) + { + QList argumentList; + argumentList << QVariant::fromValue(p1) << QVariant::fromValue(p2) << QVariant::fromValue(p3); + this->asyncCallWithArgumentList(method, argumentList); + } + + /*! + * \brief Call DBus, no return value + * \param method + * \param p1 + * \param p2 + * \param p3 + * \param p4 + * \return + */ + template void callDBus(const QLatin1String &method, P1 p1, P2 p2, P3 p3, P4 p4) + { + QList argumentList; + argumentList << QVariant::fromValue(p1) << QVariant::fromValue(p2) << QVariant::fromValue(p3) << QVariant::fromValue(p4); + this->asyncCallWithArgumentList(method, argumentList); + } + + /*! + * \brief Call DBus with return value + * \param method + * \return + */ + template Ret callDBusRet(const QLatin1String &method) + { + QList argumentList; + QDBusPendingReply pr = this->asyncCallWithArgumentList(method, argumentList); + return pr; + } + + /*! + * \brief Call DBus with return value + * \param method + * \param p1 + * \return + */ + template Ret callDBusRet(const QLatin1String &method, P1 p1) + { + QList argumentList; + argumentList << QVariant::fromValue(p1); + QDBusPendingReply pr = this->asyncCallWithArgumentList(method, argumentList); + return pr; + } + + /*! + * \brief Call DBus with return value + * \param method + * \param p1 + * \param p2 + * \return + */ + template Ret callDBusRet(const QLatin1String &method, P1 p1, P2 p2) + { + QList argumentList; + argumentList << QVariant::fromValue(p1) << QVariant::fromValue(p2); + QDBusPendingReply pr = this->asyncCallWithArgumentList(method, argumentList); + return pr; + } + + /*! + * \brief Call DBus with return value + * \param method + * \param p1 + * \param p2 + * \param p3 + * \return + */ + template Ret callDBusRet(const QLatin1String &method, P1 p1, P2 p2, P3 p3) + { + QList argumentList; + argumentList << QVariant::fromValue(p1) << QVariant::fromValue(p2) << QVariant::fromValue(p3); + QDBusPendingReply pr = this->asyncCallWithArgumentList(method, argumentList); + return pr; + } + + /*! + * \brief Call DBus with return value + * \param method + * \param p1 + * \param p2 + * \param p3 + * \param p4 + * \return + */ + template Ret callDBusRet(const QLatin1String &method, P1 p1, P2 p2, P3 p3, P4 p4) + { + QList argumentList; + argumentList << QVariant::fromValue(p1) << QVariant::fromValue(p2) << QVariant::fromValue(p3) << QVariant::fromValue(p4); + QDBusPendingReply pr = this->asyncCallWithArgumentList(method, argumentList); + return pr; + } + }; +} + +#endif // guard