From d77931e5ec17b1e25b9064703b5f6355a1d95253 Mon Sep 17 00:00:00 2001 From: Roland Winklmeier Date: Mon, 19 Mar 2018 17:08:25 +0100 Subject: [PATCH] [xswiftbus] Replace QtDBus with a libevent driven C++ wrapper on top of libdbus QtDBus was the main component of xswiftbus' Qt dependency. This is the first preparation step to get xswiftbus Qt free. The new implementation is based on the low level libdbus library, which was also in use by QtDBus itself. But instead of QtDBus, we use now a thin C++ wrapper. To keep DBus handling async, libevent is used to monitor timeouts and fds. --- .../simulator/xplane/xswiftbusserviceproxy.h | 4 +- .../simulator/xplane/xswiftbustrafficproxy.h | 4 +- src/xswiftbus/dbuscallbacks.h | 54 ++ src/xswiftbus/dbusconnection.cpp | 366 +++++++++++++ src/xswiftbus/dbusconnection.h | 124 +++++ src/xswiftbus/dbuserror.cpp | 19 + src/xswiftbus/dbuserror.h | 47 ++ src/xswiftbus/dbusmessage.cpp | 191 +++++++ src/xswiftbus/dbusmessage.h | 99 ++++ src/xswiftbus/dbusobject.cpp | 81 +++ src/xswiftbus/dbusobject.h | 91 ++++ src/xswiftbus/main.cpp | 8 - .../org.swift_project.xswiftbus.service.xml | 171 ++++++ .../org.swift_project.xswiftbus.traffic.xml | 76 +++ .../org.swift_project.xswiftbus.weather.xml | 50 ++ src/xswiftbus/plugin.cpp | 79 +-- src/xswiftbus/plugin.h | 23 +- src/xswiftbus/service.cpp | 508 +++++++++++++++++- src/xswiftbus/service.h | 40 +- src/xswiftbus/traffic.cpp | 287 +++++++++- src/xswiftbus/traffic.h | 25 +- src/xswiftbus/weather.cpp | 190 +++++++ src/xswiftbus/weather.h | 22 +- src/xswiftbus/xswiftbus.pro | 17 +- 24 files changed, 2437 insertions(+), 139 deletions(-) create mode 100644 src/xswiftbus/dbuscallbacks.h create mode 100644 src/xswiftbus/dbusconnection.cpp create mode 100644 src/xswiftbus/dbusconnection.h create mode 100644 src/xswiftbus/dbuserror.cpp create mode 100644 src/xswiftbus/dbuserror.h create mode 100644 src/xswiftbus/dbusmessage.cpp create mode 100644 src/xswiftbus/dbusmessage.h create mode 100644 src/xswiftbus/dbusobject.cpp create mode 100644 src/xswiftbus/dbusobject.h create mode 100644 src/xswiftbus/org.swift_project.xswiftbus.service.xml create mode 100644 src/xswiftbus/org.swift_project.xswiftbus.traffic.xml create mode 100644 src/xswiftbus/org.swift_project.xswiftbus.weather.xml diff --git a/src/plugins/simulator/xplane/xswiftbusserviceproxy.h b/src/plugins/simulator/xplane/xswiftbusserviceproxy.h index f72c0450a..bbef51a19 100644 --- a/src/plugins/simulator/xplane/xswiftbusserviceproxy.h +++ b/src/plugins/simulator/xplane/xswiftbusserviceproxy.h @@ -85,12 +85,12 @@ namespace BlackSimPlugin //! Emitted if an asynchronous method call caused a DBus error BLACK_NO_RELAY void asyncMethodError(QDBusError error); - //! \copydoc XSwiftBus::CService::aircraftModelChanged + //! 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); - //! \copydoc XSwiftBus::CService::airportsInRangeUpdated + //! Airports in range are updated void airportsInRangeUpdated(const QStringList &icaoCodes, const QStringList &names, const QList &lats, const QList &lons, const QList &alts); public slots: diff --git a/src/plugins/simulator/xplane/xswiftbustrafficproxy.h b/src/plugins/simulator/xplane/xswiftbustrafficproxy.h index 503c5762c..067316dd8 100644 --- a/src/plugins/simulator/xplane/xswiftbustrafficproxy.h +++ b/src/plugins/simulator/xplane/xswiftbustrafficproxy.h @@ -58,11 +58,11 @@ namespace BlackSimPlugin bool isValid() const { return m_dbusInterface->isValid(); } signals: - //! \copydoc XSwiftBus::CTraffic::simFrame + //! Simulator frame //! \remark from simulator to driver void simFrame(); - //! \copydoc XSwiftBus::CTraffic::remoteAircraftData + //! Remote aircraft data //! \remark from simulator to driver for elevation and CG void remoteAircraftData(const QString &callsign, double latitudeDeg, double longitudeDeg, double elevationMeters, double modelVerticalOffsetMeters); diff --git a/src/xswiftbus/dbuscallbacks.h b/src/xswiftbus/dbuscallbacks.h new file mode 100644 index 000000000..c683281e4 --- /dev/null +++ b/src/xswiftbus/dbuscallbacks.h @@ -0,0 +1,54 @@ +/* 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. + */ + +#ifndef BLACKSIM_XSWIFTBUS_DBUSASYNCCALLBACKS_H +#define BLACKSIM_XSWIFTBUS_DBUSASYNCCALLBACKS_H + +#include +#include + +namespace XSwiftBus +{ + //! \cond PRIVATE + template + class DBusAsyncCallbacks + { + public: + DBusAsyncCallbacks() = default; + DBusAsyncCallbacks(const std::function &add, + const std::function &remove, + const std::function &toggled) + : m_addHandler(add), m_removeHandler(remove), m_toggledHandler(toggled) + { } + + static dbus_bool_t add(T *watch, void *refcon) + { + return static_cast(refcon)->m_addHandler(watch); + } + + static void remove(T *watch, void *refcon) + { + return static_cast(refcon)->m_removeHandler(watch); + } + + static void toggled(T *watch, void *refcon) + { + return static_cast(refcon)->m_toggledHandler(watch); + } + + private: + std::function m_addHandler; + std::function m_removeHandler; + std::function m_toggledHandler; + }; + //! \endcond + +} + +#endif // guard diff --git a/src/xswiftbus/dbusconnection.cpp b/src/xswiftbus/dbusconnection.cpp new file mode 100644 index 000000000..083243520 --- /dev/null +++ b/src/xswiftbus/dbusconnection.cpp @@ -0,0 +1,366 @@ +#include + +/* 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 "dbusconnection.h" +#include "dbusobject.h" + +#include +#include +#include + +namespace XSwiftBus +{ + + //! Functor struct deleteing an event + struct EventDeleter + { + //! Delete functor + void operator()(event *obj) const + { + event_del(obj); + event_free(obj); + } + }; + + //! DBus watch handler + class WatchHandler + { + public: + //! Constructor + WatchHandler(CDBusConnection *parent, DBusWatch *watch) + : m_parent(parent), m_watch(watch) + { + const unsigned int flags = dbus_watch_get_flags(watch); + short monitoredEvents = EV_PERSIST; + + if (flags & DBUS_WATCH_READABLE) { monitoredEvents |= EV_READ; } + if (flags & DBUS_WATCH_WRITABLE) { monitoredEvents |= EV_WRITE; } + + const int fd = dbus_watch_get_unix_fd(watch); + m_event.reset(event_new(parent->m_eventBase.get(), fd, monitoredEvents, callback, this)); + event_add(m_event.get(), nullptr); + } + + //! Get DBus watch + DBusWatch *getWatch() { return m_watch; } + + //! Get DBus watch + const DBusWatch *getWatch() const { return m_watch; } + + private: + //! Event callback + static void callback(evutil_socket_t fd, short event, void *data) + { + auto *watchHandler = static_cast(data); + watchHandler->m_parent->handleSocketReady(fd, event); + } + + CDBusConnection *m_parent = nullptr; + std::unique_ptr m_event; + DBusWatch *m_watch = nullptr; + }; + + //! DBus timeout handler + class TimeoutHandler + { + public: + //! Constructor + TimeoutHandler(CDBusConnection *parent, DBusTimeout *timeout) + : m_timeout(timeout) + { + timeval timer; + const int interval = dbus_timeout_get_interval(timeout); + timer.tv_sec = interval / 1000; + timer.tv_usec = (interval % 1000) * 1000; + + m_event.reset(evtimer_new(parent->m_eventBase.get(), callback, this)); + evtimer_add(m_event.get(), &timer); + } + + //! Get DBus timeout + const DBusTimeout *getTimeout() const { return m_timeout; } + + private: + //! Event callback + static void callback(evutil_socket_t fd, short event, void *data) + { + (void) fd; // unused + (void) event; // unused + auto *timeoutHandler = static_cast(data); + dbus_timeout_handle(timeoutHandler->m_timeout); + } + + std::unique_ptr m_event; + DBusTimeout *m_timeout = nullptr; + }; + + //! Generic Timer + class Timer + { + public: + Timer() = default; + //! Constructor + Timer(CDBusConnection *parent, const timeval &timeout, const std::function &func) + : m_func(func) + { + m_event.reset(evtimer_new(parent->m_eventBase.get(), callback, this)); + evtimer_add(m_event.get(), &timeout); + } + + private: + //! Event callback + static void callback(evutil_socket_t fd, short event, void *data) + { + (void) fd; // unused + (void) event; // unused + auto *timer = static_cast(data); + timer->m_func(); + delete timer; + } + + std::unique_ptr m_event; + std::function m_func; + }; + + CDBusConnection::CDBusConnection() + : m_eventBase(event_base_new()) + { + dbus_threads_init_default(); + using namespace std::placeholders; + m_watchCallbacks = WatchCallbacks(std::bind(&CDBusConnection::dbusAddWatch, this, _1), + std::bind(&CDBusConnection::dbusRemoveWatch, this, _1), + std::bind(&CDBusConnection::dbusWatchToggled, this, _1)); + + m_timeoutCallbacks = TimeoutCallbacks(std::bind(&CDBusConnection::dbusAddTimeout, this, _1), + std::bind(&CDBusConnection::dbusRemoveTimeout, this, _1), + std::bind(&CDBusConnection::dbusTimeoutToggled, this, _1)); + } + + CDBusConnection::~CDBusConnection() + { + close(); + } + + bool CDBusConnection::connect(BusType type, const std::string &service) + { + assert(type == SessionBus); + DBusError error; + dbus_error_init(&error); + + DBusBusType dbusBusType; + switch (type) + { + case SessionBus: dbusBusType = DBUS_BUS_SESSION; break; + } + + m_connection.reset(dbus_bus_get_private(dbusBusType, &error)); + if (dbus_error_is_set(&error)) + { + m_lastError = CDBusError(&error); + return false; + } + + // Don't exit application, if the connection is disconnected + dbus_connection_set_exit_on_disconnect(m_connection.get(), false); + + if (!setupMainloop()) + { + m_connection.release(); + return false; + } + + dbus_bus_request_name(m_connection.get(), service.c_str(), 0, &error); + if (dbus_error_is_set(&error)) + { + m_lastError = CDBusError(&error); + return false; + } + + return true; + } + + bool CDBusConnection::isConnected() const + { + return static_cast(m_connection); + } + + void CDBusConnection::registerObjectPath(CDBusObject *object, const std::string &interfaceName, const std::string &objectPath, const DBusObjectPathVTable &dbusObjectPathVTable) + { + (void) interfaceName; + if (!m_connection) { return; } + + dbus_connection_try_register_object_path(m_connection.get(), objectPath.c_str(), &dbusObjectPathVTable, object, nullptr); + } + + void CDBusConnection::sendMessage(const CDBusMessage &message) + { + if (!isConnected()) { return; } + dbus_uint32_t serial = message.getSerial(); + dbus_connection_send(m_connection.get(), message.m_message, &serial); + } + + void CDBusConnection::close() + { + if (m_connection) { dbus_connection_close(m_connection.get()); } + } + + void CDBusConnection::runEventLoop() + { + if (!m_eventBase || !isConnected()) { return; } + event_base_loop(m_eventBase.get(), EVLOOP_NONBLOCK); + } + + void CDBusConnection::runBlockingEventLoop() + { + if (!m_eventBase || !isConnected()) { return; } + event_base_dispatch(m_eventBase.get()); + } + + bool CDBusConnection::setupMainloop() + { + DBusDispatchStatus status; + + if (dbus_connection_set_watch_functions( + m_connection.get(), + m_watchCallbacks.add, + m_watchCallbacks.remove, + m_watchCallbacks.toggled, + &m_watchCallbacks, nullptr) == FALSE) + { + return false; + } + + + if (dbus_connection_set_timeout_functions( + m_connection.get(), + m_timeoutCallbacks.add, + m_timeoutCallbacks.remove, + m_timeoutCallbacks.toggled, + &m_timeoutCallbacks, nullptr) == FALSE) + { + return false; + } + + dbus_connection_set_dispatch_status_function( + m_connection.get(), + dbusUpdateDispatchStatus, + this, nullptr); + + status = dbus_connection_get_dispatch_status(m_connection.get()); + if (status == DBUS_DISPATCH_DATA_REMAINS) { scheduleDBusDispatch(); } + + return true; + } + + dbus_bool_t CDBusConnection::dbusAddWatch(DBusWatch *watch) + { + if (dbus_watch_get_enabled(watch) == FALSE) { return true; } + + int fd = dbus_watch_get_unix_fd(watch); + m_watchers.emplace(fd, std::make_unique(this, watch)); + return true; + } + + void CDBusConnection::dbusRemoveWatch(DBusWatch *watch) + { + for (auto it = m_watchers.begin(); it != m_watchers.end();) + { + if (it->second->getWatch() == watch) { it = m_watchers.erase(it); } + else { ++it; } + } + } + + void CDBusConnection::dbusWatchToggled(DBusWatch *watch) + { + if (dbus_watch_get_enabled(watch) == TRUE) { dbusAddWatch(watch); } + else { dbusRemoveWatch(watch); } + } + + dbus_bool_t CDBusConnection::dbusAddTimeout(DBusTimeout *timeout) + { + if (dbus_timeout_get_enabled(timeout) == FALSE) { return TRUE; } + m_timeouts.emplace(m_timeouts.end(), std::make_unique(this, timeout)); + return true; + } + + void CDBusConnection::dbusRemoveTimeout(DBusTimeout *timeout) + { + auto predicate = [timeout](const std::unique_ptr &ptr) + { + return ptr->getTimeout() == timeout; + }; + + m_timeouts.erase(std::remove_if(m_timeouts.begin(), m_timeouts.end(), predicate), m_timeouts.end()); + } + + void CDBusConnection::dbusTimeoutToggled(DBusTimeout *timeout) + { + if (dbus_timeout_get_enabled(timeout) == TRUE) + dbusAddTimeout(timeout); + else + dbusRemoveTimeout(timeout); + } + + void CDBusConnection::scheduleDBusDispatch() + { + const timeval timeout = {0, 0}; + // This is no memory leak. The allocated timer will be deleted in its own callback + new Timer(this, timeout, [this]() { dbusDispatch(); }); + } + + void CDBusConnection::handleSocketReady(evutil_socket_t fd, short event) + { + DBusDispatchStatus status; + unsigned int flags = 0; + + auto watcher = m_watchers.find(fd); + if (watcher == m_watchers.end()) { return; } + + dbus_connection_ref(m_connection.get()); + + if (evutil_socket_geterror(fd) != 0) { flags |= DBUS_WATCH_ERROR; } + if (event & EV_READ) { flags |= DBUS_WATCH_READABLE; } + if (event & EV_WRITE) { flags |= DBUS_WATCH_WRITABLE; } + + dbus_watch_handle(watcher->second->getWatch(), flags); + + status = dbus_connection_get_dispatch_status(m_connection.get()); + if (status == DBUS_DISPATCH_DATA_REMAINS) { dbusDispatch(); } + + dbus_connection_unref(m_connection.get()); + } + + void CDBusConnection::dbusDispatch() + { + dbus_connection_ref(m_connection.get()); + while (dbus_connection_dispatch(m_connection.get()) == DBUS_DISPATCH_DATA_REMAINS); + dbus_connection_unref(m_connection.get()); + } + + void CDBusConnection::dbusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus newStatus) + { + (void)newStatus; // unused + + DBusDispatchStatus status; + + if (dbus_connection_get_is_connected(connection) == FALSE) { return; } + + status = dbus_connection_get_dispatch_status(connection); + if (status == DBUS_DISPATCH_DATA_REMAINS) { scheduleDBusDispatch(); } + } + + void CDBusConnection::dbusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus newStatus, void *data) + { + auto *obj = static_cast(data); + return obj->dbusUpdateDispatchStatus(connection, newStatus); + } + +} diff --git a/src/xswiftbus/dbusconnection.h b/src/xswiftbus/dbusconnection.h new file mode 100644 index 000000000..63cf14fe1 --- /dev/null +++ b/src/xswiftbus/dbusconnection.h @@ -0,0 +1,124 @@ +/* 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. + */ + +#ifndef BLACKSIM_XSWIFTBUS_DBUSCONNECTION_H +#define BLACKSIM_XSWIFTBUS_DBUSCONNECTION_H + +#include "dbusmessage.h" +#include "dbuserror.h" +#include "dbuscallbacks.h" + +#include +#include +#include +#include +#include +#include + +namespace XSwiftBus +{ + + class WatchHandler; + class TimeoutHandler; + class CDBusObject; + + //! DBus connection + class CDBusConnection + { + public: + //! Bus type + enum BusType { SessionBus }; + + //! Constructor + CDBusConnection(); + + //! Destructor + ~CDBusConnection(); + + // The ones below are not implemented yet. + // If you need them, make sure that connection reference count is correct + CDBusConnection(const CDBusConnection &) = delete; + CDBusConnection &operator=(const CDBusConnection &) = delete; + + //! Connect to bus + bool connect(BusType type, const std::string &service); + + //! Is connected? + bool isConnected() const; + + //! Register DBus object with interfaceName and objectPath. + //! \param object + //! \param interfaceName + //! \param objectPath + //! \param dbusObjectPathVTable Virtual table handling DBus messages + void registerObjectPath(CDBusObject *object, const std::string &interfaceName, const std::string &objectPath, const DBusObjectPathVTable &dbusObjectPathVTable); + + //! Send message to bus + void sendMessage(const CDBusMessage &message); + + //! Close connection + void close(); + + //! Run DBus event loop (non-blocking) + void runEventLoop(); + + //! Run DBus event loop (blocking) + void runBlockingEventLoop(); + + //! Get the last error + CDBusError lastError() const { return m_lastError; } + + private: + friend class WatchHandler; + friend class TimeoutHandler; + friend class Timer; + + using WatchCallbacks = DBusAsyncCallbacks; + using TimeoutCallbacks = DBusAsyncCallbacks; + + bool setupMainloop(); + + dbus_bool_t dbusAddWatch(DBusWatch *watch); + void dbusRemoveWatch(DBusWatch *watch); + void dbusWatchToggled(DBusWatch *watch); + + dbus_bool_t dbusAddTimeout(DBusTimeout *timeout); + void dbusRemoveTimeout(DBusTimeout *timeout); + void dbusTimeoutToggled(DBusTimeout *timeout); + + void scheduleDBusDispatch(); + void handleSocketReady(evutil_socket_t fd, short event); + void dbusDispatch(); + void dbusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus newStatus); + + static void dbusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus newStatus, void *data); + + struct EventBaseDeleter + { + void operator()(event_base *obj) const { event_base_free(obj); } + }; + + struct DBusConnectionDeleter + { + void operator()(DBusConnection *obj) const { dbus_connection_unref(obj); } + }; + + std::unique_ptr m_eventBase; + std::unique_ptr m_connection; + CDBusError m_lastError; + WatchCallbacks m_watchCallbacks; + TimeoutCallbacks m_timeoutCallbacks; + + std::unordered_multimap> m_watchers; + std::vector> m_timeouts; + }; + +} + +#endif // guard diff --git a/src/xswiftbus/dbuserror.cpp b/src/xswiftbus/dbuserror.cpp new file mode 100644 index 000000000..ad37d9329 --- /dev/null +++ b/src/xswiftbus/dbuserror.cpp @@ -0,0 +1,19 @@ +/* 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 "dbuserror.h" + +namespace XSwiftBus +{ + + CDBusError::CDBusError(const DBusError *error) + : m_name(error->name), m_message(error->message) + { } + +} diff --git a/src/xswiftbus/dbuserror.h b/src/xswiftbus/dbuserror.h new file mode 100644 index 000000000..136786240 --- /dev/null +++ b/src/xswiftbus/dbuserror.h @@ -0,0 +1,47 @@ +/* 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. + */ + +#ifndef BLACKSIM_XSWIFTBUS_DBUSERROR_H +#define BLACKSIM_XSWIFTBUS_DBUSERROR_H + +#include +#include + +namespace XSwiftBus +{ + + //! DBus error + class CDBusError + { + public: + //! Error type + enum ErrorType + { + NoError, + Other + }; + + //! Default constructur + CDBusError() = default; + + //! Constructor + explicit CDBusError(const DBusError *error); + + //! Get error type + ErrorType getType() const { return m_errorType; } + + private: + ErrorType m_errorType = NoError; + std::string m_name; + std::string m_message; + }; + +} + +#endif // guard diff --git a/src/xswiftbus/dbusmessage.cpp b/src/xswiftbus/dbusmessage.cpp new file mode 100644 index 000000000..7cc79b97e --- /dev/null +++ b/src/xswiftbus/dbusmessage.cpp @@ -0,0 +1,191 @@ +/* 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 "dbusmessage.h" + +namespace XSwiftBus +{ + + CDBusMessage::CDBusMessage(DBusMessage *message) + { + m_message = dbus_message_ref(message); + } + + CDBusMessage::CDBusMessage(const CDBusMessage &other) + { + m_message = dbus_message_ref(other.m_message); + m_serial = other.m_serial; + } + + CDBusMessage::CDBusMessage(DBusMessage *message, dbus_uint32_t serial) + { + m_message = dbus_message_ref(message); + m_serial = serial; + } + + CDBusMessage::~CDBusMessage() + { + dbus_message_unref(m_message); + } + + CDBusMessage &CDBusMessage::operator =(CDBusMessage other) + { + std::swap(m_serial, other.m_serial); + m_message = dbus_message_ref(other.m_message); + return *this; + } + + bool CDBusMessage::isMethodCall() const + { + return dbus_message_get_type(m_message) == DBUS_MESSAGE_TYPE_METHOD_CALL; + } + + bool CDBusMessage::wantsReply() const + { + return !dbus_message_get_no_reply(m_message); + } + + std::string CDBusMessage::getSender() const + { + const char *sender = nullptr; + sender = dbus_message_get_sender(m_message); + return std::string(sender); + } + + dbus_uint32_t CDBusMessage::getSerial() const + { + return dbus_message_get_serial(m_message); + } + + std::string CDBusMessage::getInterfaceName() const + { + return dbus_message_get_interface(m_message); + } + + std::string CDBusMessage::getObjectPath() const + { + return dbus_message_get_path(m_message); + } + + std::string CDBusMessage::getMethodName() const + { + return dbus_message_get_member(m_message); + } + + void CDBusMessage::beginArgumentWrite() + { + dbus_message_iter_init_append(m_message, &m_messageIterator); + } + + void CDBusMessage::appendArgument(bool value) + { + dbus_bool_t boolean = value ? 1 : 0; + dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_BOOLEAN, &boolean); + } + + void CDBusMessage::appendArgument(const char *value) + { + dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_STRING, &value); + } + + void CDBusMessage::appendArgument(const std::string &value) + { + const char *ptr = value.c_str(); + dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_STRING, &ptr); + } + + void CDBusMessage::appendArgument(int value) + { + dbus_int32_t i = value; + dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_INT32, &i); + } + + void CDBusMessage::appendArgument(double value) + { + dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_DOUBLE, &value); + } + + void CDBusMessage::appendArgument(const std::vector &array) + { + DBusMessageIter arrayIterator; + dbus_message_iter_open_container(&m_messageIterator, DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE_AS_STRING, &arrayIterator); + const double *ptr = array.data(); + dbus_message_iter_append_fixed_array(&arrayIterator, DBUS_TYPE_DOUBLE, &ptr, array.size()); + dbus_message_iter_close_container(&m_messageIterator, &arrayIterator); + } + + void CDBusMessage::appendArgument(const std::vector &array) + { + DBusMessageIter arrayIterator; + dbus_message_iter_open_container(&m_messageIterator, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &arrayIterator); + for (const auto & i : array) + { + const char *ptr = i.c_str(); + dbus_message_iter_append_basic(&arrayIterator, DBUS_TYPE_STRING, &ptr); + } + dbus_message_iter_close_container(&m_messageIterator, &arrayIterator); + } + + void CDBusMessage::beginArgumentRead() + { + dbus_message_iter_init(m_message, &m_messageIterator); + } + + void CDBusMessage::getArgument(int &value) + { + if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_INT32) { return; } + dbus_int32_t i; + dbus_message_iter_get_basic(&m_messageIterator, &i); + value = i; + dbus_message_iter_next(&m_messageIterator); + } + + void CDBusMessage::getArgument(bool &value) + { + if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_BOOLEAN) { return; } + dbus_bool_t v; + dbus_message_iter_get_basic(&m_messageIterator, &v); + value = v == TRUE ? true : false; + dbus_message_iter_next(&m_messageIterator); + } + + void CDBusMessage::getArgument(double &value) + { + if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_DOUBLE) { return; } + dbus_message_iter_get_basic(&m_messageIterator, &value); + dbus_message_iter_next(&m_messageIterator); + } + + void CDBusMessage::getArgument(std::string &value) + { + const char *str = nullptr; + if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_STRING) { return; } + dbus_message_iter_get_basic(&m_messageIterator, &str); + dbus_message_iter_next(&m_messageIterator); + value = std::string(str); + } + + CDBusMessage CDBusMessage::createSignal(const std::string &path, const std::string &interfaceName, const std::string &signalName) + { + DBusMessage *signal = dbus_message_new_signal(path.c_str(), interfaceName.c_str(), signalName.c_str()); + return CDBusMessage(signal); + } + + CDBusMessage CDBusMessage::createReply(const std::string &destination, dbus_uint32_t serial) + { + DBusMessage *reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + dbus_message_set_no_reply(reply, TRUE); + dbus_message_set_destination(reply, destination.c_str()); + dbus_message_set_reply_serial(reply, serial); + CDBusMessage msg(reply); + dbus_message_unref(reply); + return msg; + } + +} diff --git a/src/xswiftbus/dbusmessage.h b/src/xswiftbus/dbusmessage.h new file mode 100644 index 000000000..b6b5fd2d2 --- /dev/null +++ b/src/xswiftbus/dbusmessage.h @@ -0,0 +1,99 @@ +/* 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. + */ + +#ifndef BLACKSIM_XSWIFTBUS_DBUSMESSAGE_H +#define BLACKSIM_XSWIFTBUS_DBUSMESSAGE_H + +#include "dbus/dbus.h" +#include +#include + +namespace XSwiftBus +{ + + //! DBus Message + class CDBusMessage + { + public: + //! Constructor + //! @{ + CDBusMessage(DBusMessage *message); + CDBusMessage(const CDBusMessage &other); + //! @} + + //! Destructor + ~CDBusMessage(); + + //! Assignment operator + CDBusMessage &operator=(CDBusMessage other); + + //! Is this message a method call? + bool isMethodCall() const; + + //! Does this message want a reply? + bool wantsReply() const; + + //! Get the message sender + std::string getSender() const; + + //! Get the message serial. This is usally required for reply message. + dbus_uint32_t getSerial() const; + + //! Get the called interface name + std::string getInterfaceName() const; + + //! Get the called object path + std::string getObjectPath() const; + + //! Get the called method name + std::string getMethodName() const; + + //! Begin writing argument + void beginArgumentWrite(); + + //! Append argument. Make sure to call \sa beginArgumentWrite() before. + //! @{ + void appendArgument(bool value); + void appendArgument(const char *value); + void appendArgument(const std::string &value); + void appendArgument(int value); + void appendArgument(double value); + void appendArgument(const std::vector &array); + void appendArgument(const std::vector &array); + //! @} + + //! Begin reading arguments + void beginArgumentRead(); + + //! Read single argument. Make sure to call \sa beginArgumentRead() before. + //! @{ + void getArgument(int &value); + void getArgument(bool &value); + void getArgument(double &value); + void getArgument(std::string &value); + //! @} + + //! Creates a DBus message containing a DBus signal + static CDBusMessage createSignal(const std::string &path, const std::string &interfaceName, const std::string &signalName); + + //! Creates a DBus message containing a DBus reply + static CDBusMessage createReply(const std::string &destination, dbus_uint32_t serial); + + private: + friend class CDBusConnection; + + DBusMessage *m_message = nullptr; + DBusMessageIter m_messageIterator; + CDBusMessage(DBusMessage *message, dbus_uint32_t serial); + dbus_uint32_t m_serial = 0; + }; + +} + +#endif // guard diff --git a/src/xswiftbus/dbusobject.cpp b/src/xswiftbus/dbusobject.cpp new file mode 100644 index 000000000..4aaeb384b --- /dev/null +++ b/src/xswiftbus/dbusobject.cpp @@ -0,0 +1,81 @@ +/* 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 "dbusobject.h" + +namespace XSwiftBus +{ + CDBusObject::CDBusObject(CDBusConnection *dbusConnection) + : m_dbusConnection(dbusConnection) + { } + + CDBusObject::~CDBusObject() = default; + + void CDBusObject::registerDBusObjectPath(const std::string &interfaceName, const std::string &objectPath) + { + m_interfaceName = interfaceName; + m_objectPath = objectPath; + m_dbusConnection->registerObjectPath(this, interfaceName, objectPath, m_dbusObjectPathVTable); + } + + void CDBusObject::sendDBusSignal(const std::string &name) + { + CDBusMessage signal = CDBusMessage::createSignal(m_objectPath, m_interfaceName, name); + m_dbusConnection->sendMessage(signal); + } + + void CDBusObject::sendDBusMessage(const CDBusMessage &message) + { + m_dbusConnection->sendMessage(message); + } + + void CDBusObject::maybeSendEmptyDBusReply(bool wantsReply, const std::string &destination, dbus_uint32_t serial) + { + if (wantsReply) + { + CDBusMessage reply = CDBusMessage::createReply(destination, serial); + m_dbusConnection->sendMessage(reply); + } + } + + void CDBusObject::queueDBusCall(const std::function &func) + { + std::lock_guard lock(m_mutex); + m_qeuedDBusCalls.push_back(func); + } + + void CDBusObject::invokeQueuedDBusCalls() + { + std::lock_guard lock(m_mutex); + while (m_qeuedDBusCalls.size() > 0) + { + m_qeuedDBusCalls.front()(); + m_qeuedDBusCalls.pop_front(); + } + } + + void CDBusObject::dbusObjectPathUnregisterFunction(DBusConnection *connection, void *data) + { + (void)connection; // unused + (void)data; // unused + } + + DBusHandlerResult CDBusObject::dbusObjectPathMessageFunction(DBusConnection *connection, DBusMessage *message, void *data) + { + (void)connection; // unused + + auto *obj = static_cast(data); + DBusError err; + dbus_error_init(&err); + + CDBusMessage dbusMessage(message); + return obj->dbusMessageHandler(dbusMessage); + } + +} diff --git a/src/xswiftbus/dbusobject.h b/src/xswiftbus/dbusobject.h new file mode 100644 index 000000000..1659e1719 --- /dev/null +++ b/src/xswiftbus/dbusobject.h @@ -0,0 +1,91 @@ +/* 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. + */ + +#ifndef BLACKSIM_XSWIFTBUS_DBUSOBJECT_H +#define BLACKSIM_XSWIFTBUS_DBUSOBJECT_H + +#include "dbusconnection.h" +#include +#include +#include + +namespace XSwiftBus +{ + //! DBus base object + class CDBusObject + { + public: + //! Constructor + CDBusObject(CDBusConnection *dbusConnection); + + //! Destructor + virtual ~CDBusObject(); + + //! Process DBus messages. Needs to be implemented by deriving classes + virtual int processDBus() = 0; + + protected: + //! DBus message handler + virtual DBusHandlerResult dbusMessageHandler(const CDBusMessage &message) = 0; + + //! Register itself with interfaceName and objectPath + void registerDBusObjectPath(const std::string &interfaceName, const std::string &objectPath); + + //! Send DBus signal + void sendDBusSignal(const std::string &name); + + //! Send DBus message + void sendDBusMessage(const CDBusMessage &message); + + //! Maybe sends an empty DBus reply (acknowledgement) + void maybeSendEmptyDBusReply(bool wantsReply, const std::string &destination, dbus_uint32_t serial); + + //! Send DBus reply + template + void sendDBusReply(const std::string &destination, dbus_uint32_t serial, const T &argument) + { + CDBusMessage reply = CDBusMessage::createReply(destination, serial); + reply.beginArgumentWrite(); + reply.appendArgument(argument); + m_dbusConnection->sendMessage(reply); + } + + //! Send DBus reply + template + void sendDBusReply(const std::string &destination, dbus_uint32_t serial, const std::vector &array) + { + CDBusMessage reply = CDBusMessage::createReply(destination, serial); + reply.beginArgumentWrite(); + reply.appendArgument(array); + m_dbusConnection->sendMessage(reply); + } + + //! Queue a DBus call to be executed in a different thread + void queueDBusCall(const std::function &func); + + //! Invoke all pending DBus calls. They will be executed in the calling thread. + void invokeQueuedDBusCalls(); + + private: + static void dbusObjectPathUnregisterFunction(DBusConnection *connection, void *data); + static DBusHandlerResult dbusObjectPathMessageFunction(DBusConnection *connection, DBusMessage *message, void *data); + + CDBusConnection *m_dbusConnection; + std::string m_interfaceName; + std::string m_objectPath; + + std::mutex m_mutex; + std::deque> m_qeuedDBusCalls; + + const DBusObjectPathVTable m_dbusObjectPathVTable = { dbusObjectPathUnregisterFunction, dbusObjectPathMessageFunction, nullptr, nullptr, nullptr, nullptr }; + }; + +} + +#endif // guard diff --git a/src/xswiftbus/main.cpp b/src/xswiftbus/main.cpp index 89d79ad75..98823d866 100644 --- a/src/xswiftbus/main.cpp +++ b/src/xswiftbus/main.cpp @@ -14,7 +14,6 @@ #include "utils.h" #include "traffic.h" #include "service.h" -#include "blackmisc/dbus.h" #include #if ! defined(XPLM210) @@ -55,18 +54,11 @@ PLUGIN_API void XPluginStop() PLUGIN_API int XPluginEnable() { - qRegisterMetaType(); - qDBusRegisterMetaType(); - QXPlaneMessageHandler::install(); g_qApp = QSharedApplication::sharedInstance(); QXPlaneEventLoop::exec(); g_plugin = new XSwiftBus::CPlugin; - - // Here we can be safely assume that QtDBus was loaded by the process - preventQtDBusDllUnload(); - return 1; } diff --git a/src/xswiftbus/org.swift_project.xswiftbus.service.xml b/src/xswiftbus/org.swift_project.xswiftbus.service.xml new file mode 100644 index 000000000..16e5d9430 --- /dev/null +++ b/src/xswiftbus/org.swift_project.xswiftbus.service.xml @@ -0,0 +1,171 @@ +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)" diff --git a/src/xswiftbus/org.swift_project.xswiftbus.traffic.xml b/src/xswiftbus/org.swift_project.xswiftbus.traffic.xml new file mode 100644 index 000000000..4a3ff2008 --- /dev/null +++ b/src/xswiftbus/org.swift_project.xswiftbus.traffic.xml @@ -0,0 +1,76 @@ +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)" diff --git a/src/xswiftbus/org.swift_project.xswiftbus.weather.xml b/src/xswiftbus/org.swift_project.xswiftbus.weather.xml new file mode 100644 index 000000000..da6ad8b6f --- /dev/null +++ b/src/xswiftbus/org.swift_project.xswiftbus.weather.xml @@ -0,0 +1,50 @@ +R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)" diff --git a/src/xswiftbus/plugin.cpp b/src/xswiftbus/plugin.cpp index 3bc8fc457..a507b07ee 100644 --- a/src/xswiftbus/plugin.cpp +++ b/src/xswiftbus/plugin.cpp @@ -12,14 +12,14 @@ #include "traffic.h" #include "weather.h" #include "utils.h" -#include "blackmisc/librarypath.h" -#include "blackmisc/logmessage.h" +#include "XPLM/XPLMProcessing.h" #include #include +#include namespace { - inline QString xswiftbusServiceName() { - return QStringLiteral("org.swift-project.xswiftbus"); + inline std::string xswiftbusServiceName() { + return std::string("org.swift-project.xswiftbus"); } } @@ -27,54 +27,50 @@ namespace XSwiftBus { CPlugin::CPlugin() - : m_menu(CMenu::mainMenu().subMenu("XSwiftBus")) + : m_dbusConnection(std::make_unique()), m_menu(CMenu::mainMenu().subMenu("XSwiftBus")) { - m_startServerMenuItem = m_menu.item("Start XSwiftBus", [this]{ startServer(BlackMisc::CDBusServer::sessionBusAddress()); }); + m_startServerMenuItem = m_menu.item("Start XSwiftBus", [this]{ startServer(CDBusConnection::SessionBus); }); m_toggleMessageWindowMenuItem = m_menu.item("Toggle Message Window", [this] { if(m_service) { m_service->toggleMessageBoxVisibility(); } }); // m_startServerMenuItems.push_back(m_menu.item("Start server on system bus", [this]{ startServer(BlackMisc::CDBusServer::systemBusAddress()); })); // m_startServerMenuItems.push_back(m_menu.item("Start server on localhost P2P", [this]{ startServer(BlackMisc::CDBusServer::p2pAddress("localhost")); })); + + m_dbusThread = std::thread([this]() + { + while(!m_shouldStop) + { + m_dbusConnection->runBlockingEventLoop(); + } + }); + + XPLMRegisterFlightLoopCallback(flightLoopCallback, -1, this); } - void CPlugin::startServer(const QString &address) + CPlugin::~CPlugin() { + XPLMUnregisterFlightLoopCallback(flightLoopCallback, this); + m_dbusConnection->close(); + m_shouldStop = true; + if (m_dbusThread.joinable()) { m_dbusThread.join(); } + } + + void CPlugin::startServer(CDBusConnection::BusType bus) + { + (void) bus; // for (auto &item : m_startServerMenuItems) { item.setEnabled(false); } m_startServerMenuItem.setEnabled(false); - m_service = new CService(this); - m_traffic = new CTraffic(this); - m_weather = new CWeather(this); + // Todo: retry if it fails + bool success = m_dbusConnection->connect(CDBusConnection::SessionBus, xswiftbusServiceName()); - // XPLM API does not like to be called from a QTimer slot, so move the recurring part into a separate method. - tryStartServer(address); - } - - void CPlugin::tryStartServer(const QString &address) - { - // Make sure that there are no calls to XPLM in this method - Q_ASSERT(! m_server); - auto previousLibraryPath = BlackMisc::getCustomLibraryPath(); - auto libraryPath = g_xplanePath + "Resources" + g_sep + "plugins" + g_sep + "xswiftbus"; - #if !defined (Q_OS_MAC) && defined(WORD_SIZE_64) - libraryPath = libraryPath + g_sep + "64"; - #endif - BlackMisc::setCustomLibraryPath(libraryPath); - - QString message; - if (!BlackMisc::CDBusServer::isP2PAddress(address) && !BlackMisc::CDBusServer::isDBusAvailable(address, message)) + if (!success) { - constexpr int msec = 30000; - BlackMisc::CLogMessage(this).warning("DBus daemon not available. (%1) Trying again in %2 sec.") << message << (msec / 1000); - QTimer::singleShot(msec, this, [&] { tryStartServer(address); }); - BlackMisc::setCustomLibraryPath(previousLibraryPath); + // Print error return; } - m_server = new BlackMisc::CDBusServer(xswiftbusServiceName(), address, this); - BlackMisc::setCustomLibraryPath(previousLibraryPath); - - m_server->addObject(CService::ObjectPath(), m_service); - m_server->addObject(CTraffic::ObjectPath(), m_traffic); - m_server->addObject(CWeather::ObjectPath(), m_weather); + m_service = new CService(m_dbusConnection.get()); + m_traffic = new CTraffic(m_dbusConnection.get()); + m_weather = new CWeather(m_dbusConnection.get()); } void CPlugin::onAircraftModelChanged() @@ -92,4 +88,13 @@ namespace XSwiftBus m_service->updateAirportsInRange(); } } + + float CPlugin::flightLoopCallback(float, float, int, void *refcon) + { + auto *plugin = static_cast(refcon); + if (plugin->m_service) { plugin->m_service->processDBus(); } + if (plugin->m_weather) { plugin->m_weather->processDBus(); } + if (plugin->m_traffic) { plugin->m_traffic->processDBus(); } + return -1; + } } diff --git a/src/xswiftbus/plugin.h b/src/xswiftbus/plugin.h index 5ac8483b4..6af6ff66b 100644 --- a/src/xswiftbus/plugin.h +++ b/src/xswiftbus/plugin.h @@ -20,14 +20,12 @@ #ifndef NOMINMAX #define NOMINMAX #endif +#include "dbusconnection.h" #include "menus.h" - -#pragma push_macro("interface") -#undef interface -#include "blackmisc/dbusserver.h" -#pragma pop_macro("interface") - +#include #include +#include +#include namespace XSwiftBus { @@ -46,6 +44,9 @@ namespace XSwiftBus //! Constructor CPlugin(); + //! Destructor + ~CPlugin(); + //! Called by XPluginReceiveMessage when the model is changed void onAircraftModelChanged(); @@ -53,7 +54,7 @@ namespace XSwiftBus void onAircraftRepositioned(); private: - BlackMisc::CDBusServer *m_server = nullptr; + std::unique_ptr m_dbusConnection; CService *m_service = nullptr; CTraffic *m_traffic = nullptr; CWeather *m_weather = nullptr; @@ -61,8 +62,12 @@ namespace XSwiftBus CMenuItem m_startServerMenuItem; CMenuItem m_toggleMessageWindowMenuItem; - void startServer(const QString &address); - void tryStartServer(const QString &address); + std::thread m_dbusThread; + bool m_shouldStop = false; + + void startServer(CDBusConnection::BusType bus); + + static float flightLoopCallback(float, float, int, void *refcon); }; } diff --git a/src/xswiftbus/service.cpp b/src/xswiftbus/service.cpp index 98a21c936..cd3a84e9d 100644 --- a/src/xswiftbus/service.cpp +++ b/src/xswiftbus/service.cpp @@ -18,12 +18,13 @@ namespace XSwiftBus { - CService::CService(QObject *parent) : QObject(parent) + CService::CService(CDBusConnection *connection) : CDBusObject(connection) { + registerDBusObjectPath(XSWIFTBUS_SERVICE_INTERFACENAME, XSWIFTBUS_SERVICE_OBJECTPATH); m_messages.addMessage( { "xswiftbus started.", 0, 255, 255 } ); - m_airportUpdater = new QTimer(this); + m_airportUpdater = new QTimer(); m_airportUpdater->start(60000); - connect(m_airportUpdater, &QTimer::timeout, this, &CService::updateAirportsInRange); + QObject::connect(m_airportUpdater, &QTimer::timeout, [this] () { updateAirportsInRange(); }); updateAirportsInRange(); } @@ -33,8 +34,7 @@ namespace XSwiftBus char path[512]; XPLMGetNthAircraftModel(XPLM_USER_AIRCRAFT, filename, path); const auto model = BlackMisc::Simulation::XPlane::CAircraftModelLoaderXPlane::extractAcfProperties(path, QFileInfo(path)); - emit aircraftModelChanged(path, filename, getAircraftLivery(), getAircraftIcaoCode(), - model.getModelString(), model.getName(), getAircraftDescription()); + emitAircraftModelChanged(path, filename, getAircraftLivery(), getAircraftIcaoCode(), model.getModelString(), model.getName(), getAircraftDescription()); } void CService::addTextMessage(const QString &text, double red, double green, double blue) @@ -153,8 +153,8 @@ namespace XSwiftBus } using namespace BlackMisc::Math; using namespace BlackMisc::Geo; - QStringList icaos, names; - QDoubleList lats, lons, alts; + std::vector icaos, names; + std::vector lats, lons, alts; for (const auto &navref : m_airports.findClosest(20, CCoordinateGeodetic(getLatitude(), getLongitude(), 0))) { float lat, lon, alt; @@ -166,7 +166,499 @@ namespace XSwiftBus lons.push_back(lon); alts.push_back(alt); } - emit airportsInRangeUpdated(icaos, names, lats, lons, alts); + emitAirportsInRangeUpdated(icaos, names, lats, lons, alts); + } + + static const char *introspection_service = + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + #include "org.swift_project.xswiftbus.service.xml" + ; + + DBusHandlerResult CService::dbusMessageHandler(const CDBusMessage &message_) + { + CDBusMessage message(message_); + const std::string sender = message.getSender(); + const dbus_uint32_t serial = message.getSerial(); + const bool wantsReply = message.wantsReply(); + + if (message.getInterfaceName() == DBUS_INTERFACE_INTROSPECTABLE) + { + if (message.getMethodName() == "Introspect") + { + sendDBusReply(sender, serial, introspection_service); + } + } + else if (message.getInterfaceName() == XSWIFTBUS_SERVICE_INTERFACENAME) + { + if (message.getMethodName() == "addTextMessage") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::string text; + double red = 0; + double green = 0; + double blue = 0; + message.beginArgumentRead(); + message.getArgument(text); + message.getArgument(red); + message.getArgument(green); + message.getArgument(blue); + + queueDBusCall([=]() + { + addTextMessage(QString::fromStdString(text), red, green, blue); + }); + } + else if (message.getMethodName() == "getOwnAircraftSituationData") + { + queueDBusCall([=]() + { + double lat = m_latitude.get(); + double lon = m_longitude.get(); + double alt = m_elevation.get(); + double gs = m_groundSpeed.get(); + double pitch = m_pitch.get(); + double roll = m_roll.get(); + double trueHeading = m_heading.get(); + double qnh = m_qnhInhg.get(); + CDBusMessage reply = CDBusMessage::createReply(sender, serial); + reply.beginArgumentWrite(); + reply.appendArgument(lat); + reply.appendArgument(lon); + reply.appendArgument(alt); + reply.appendArgument(gs); + reply.appendArgument(pitch); + reply.appendArgument(roll); + reply.appendArgument(trueHeading); + reply.appendArgument(qnh); + sendDBusMessage(reply); + }); + } + else if (message.getMethodName() == "updateAirportsInRange") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + queueDBusCall([=]() + { + updateAirportsInRange(); + }); + } + else if (message.getMethodName() == "getAircraftModelPath") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAircraftModelPath().toStdString()); + }); + } + else if (message.getMethodName() == "getAircraftModelFilename") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAircraftModelFilename().toStdString()); + }); + } + else if (message.getMethodName() == "getAircraftModelString") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAircraftModelString().toStdString()); + }); + } + else if (message.getMethodName() == "getAircraftName") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAircraftName().toStdString()); + }); + } + else if (message.getMethodName() == "getAircraftLivery") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAircraftLivery().toStdString()); + }); + } + else if (message.getMethodName() == "getAircraftIcaoCode") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAircraftIcaoCode().toStdString()); + }); + } + else if (message.getMethodName() == "getAircraftDescription") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAircraftDescription().toStdString()); + }); + } + else if (message.getMethodName() == "getXPlaneVersionMajor") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getXPlaneVersionMajor()); + }); + } + else if (message.getMethodName() == "getXPlaneVersionMinor") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getXPlaneVersionMinor()); + }); + } + else if (message.getMethodName() == "getXPlaneInstallationPath") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getXPlaneInstallationPath().toStdString()); + }); + } + else if (message.getMethodName() == "getXPlanePreferencesPath") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getXPlanePreferencesPath().toStdString()); + }); + } + else if (message.getMethodName() == "isPaused") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, isPaused()); + }); + } + else if (message.getMethodName() == "isUsingRealTime") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, isUsingRealTime()); + }); + } + else if (message.getMethodName() == "getLatitude") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getLatitude()); + }); + } + else if (message.getMethodName() == "getLongitude") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getLongitude()); + }); + } + else if (message.getMethodName() == "getAltitudeMSL") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAltitudeMSL()); + }); + } + else if (message.getMethodName() == "getHeightAGL") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getHeightAGL()); + }); + } + else if (message.getMethodName() == "getGroundSpeed") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getGroundSpeed()); + }); + } + else if (message.getMethodName() == "getIndicatedAirspeed") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getIndicatedAirspeed()); + }); + } + else if (message.getMethodName() == "getTrueAirspeed") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getTrueAirspeed()); + }); + } + else if (message.getMethodName() == "getPitch") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getPitch()); + }); + } + else if (message.getMethodName() == "getRoll") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getRoll()); + }); + } + else if (message.getMethodName() == "getTrueHeading") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getTrueHeading()); + }); + } + else if (message.getMethodName() == "getAnyWheelOnGround") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAnyWheelOnGround()); + }); + } + else if (message.getMethodName() == "getAllWheelsOnGround") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getAllWheelsOnGround()); + }); + } + else if (message.getMethodName() == "getCom1Active") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getCom1Active()); + }); + } + else if (message.getMethodName() == "getCom1Standby") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getCom1Standby()); + }); + } + else if (message.getMethodName() == "getCom2Active") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getCom2Active()); + }); + } + else if (message.getMethodName() == "getCom2Standby") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getCom2Standby()); + }); + } + else if (message.getMethodName() == "getTransponderCode") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getTransponderCode()); + }); + } + else if (message.getMethodName() == "getTransponderMode") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getTransponderMode()); + }); + } + else if (message.getMethodName() == "getTransponderIdent") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getTransponderIdent()); + }); + } + else if (message.getMethodName() == "getBeaconLightsOn") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getBeaconLightsOn()); + }); + } + else if (message.getMethodName() == "getLandingLightsOn") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getLandingLightsOn()); + }); + } + else if (message.getMethodName() == "getNavLightsOn") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getNavLightsOn()); + }); + } + else if (message.getMethodName() == "getStrobeLightsOn") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getStrobeLightsOn()); + }); + } + else if (message.getMethodName() == "getTaxiLightsOn") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getTaxiLightsOn()); + }); + } + else if (message.getMethodName() == "getQNH") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getQNH()); + }); + } + else if (message.getMethodName() == "setCom1Active") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int frequency = 0; + message.beginArgumentRead(); + message.getArgument(frequency); + queueDBusCall([=]() + { + setCom1Active(frequency); + }); + } + else if (message.getMethodName() == "setCom1Standby") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int frequency = 0; + message.beginArgumentRead(); + message.getArgument(frequency); + queueDBusCall([=]() + { + setCom1Standby(frequency); + }); + } + else if (message.getMethodName() == "setCom2Active") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int frequency = 0; + message.beginArgumentRead(); + message.getArgument(frequency); + queueDBusCall([=]() + { + setCom2Active(frequency); + }); + } + else if (message.getMethodName() == "setCom2Standby") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int frequency = 0; + message.beginArgumentRead(); + message.getArgument(frequency); + queueDBusCall([=]() + { + setCom2Standby(frequency); + }); + } + else if (message.getMethodName() == "setTransponderCode") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int code = 0; + message.beginArgumentRead(); + message.getArgument(code); + queueDBusCall([=]() + { + setTransponderCode(code); + }); + } + else if (message.getMethodName() == "setTransponderMode") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int mode = 0; + message.beginArgumentRead(); + message.getArgument(mode); + queueDBusCall([=]() + { + setTransponderMode(mode); + }); + } + else if (message.getMethodName() == "getFlapsDeployRatio") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getFlapsDeployRatio()); + }); + } + else if (message.getMethodName() == "getGearDeployRatio") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getGearDeployRatio()); + }); + } + else if (message.getMethodName() == "getNumberOfEngines") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getNumberOfEngines()); + }); + } + else if (message.getMethodName() == "getEngineN1Percentage") + { + queueDBusCall([=]() + { + std::vector array = getEngineN1Percentage().toVector().toStdVector(); + sendDBusReply(sender, serial, array); + }); + } + else if (message.getMethodName() == "getSpeedBrakeRatio") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, getSpeedBrakeRatio()); + }); + } + else if (message.getMethodName() == "toggleMessageBoxVisibility") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + queueDBusCall([=]() + { + toggleMessageBoxVisibility(); + }); + } + else + { + // Unknown message. Tell DBus that we cannot handle it + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + return DBUS_HANDLER_RESULT_HANDLED; + } + + int CService::processDBus() + { + invokeQueuedDBusCalls(); + return 1; + } + + void CService::emitAircraftModelChanged(const QString &path, const QString &filename, const QString &livery, + const QString &icao, const QString &modelString, const QString &name, + const QString &description) + { + CDBusMessage signalAircraftModelChanged = CDBusMessage::createSignal(XSWIFTBUS_SERVICE_OBJECTPATH, XSWIFTBUS_SERVICE_INTERFACENAME, "aircraftModelChanged"); + signalAircraftModelChanged.beginArgumentWrite(); + signalAircraftModelChanged.appendArgument(path.toStdString()); + signalAircraftModelChanged.appendArgument(filename.toStdString()); + signalAircraftModelChanged.appendArgument(livery.toStdString()); + signalAircraftModelChanged.appendArgument(icao.toStdString()); + signalAircraftModelChanged.appendArgument(modelString.toStdString()); + signalAircraftModelChanged.appendArgument(name.toStdString()); + signalAircraftModelChanged.appendArgument(description.toStdString()); + sendDBusMessage(signalAircraftModelChanged); + } + + void CService::emitAirportsInRangeUpdated(const std::vector &icaoCodes, const std::vector &names, + const std::vector &lats, const std::vector &lons, const std::vector &alts) + { + CDBusMessage signalAirportsInRangeUpdated = CDBusMessage::createSignal(XSWIFTBUS_SERVICE_OBJECTPATH, XSWIFTBUS_SERVICE_INTERFACENAME, "airportsInRangeUpdated"); + signalAirportsInRangeUpdated.beginArgumentWrite(); + signalAirportsInRangeUpdated.appendArgument(icaoCodes); + signalAirportsInRangeUpdated.appendArgument(names); + signalAirportsInRangeUpdated.appendArgument(lats); + signalAirportsInRangeUpdated.appendArgument(lons); + signalAirportsInRangeUpdated.appendArgument(alts); + sendDBusMessage(signalAirportsInRangeUpdated); } } diff --git a/src/xswiftbus/service.h b/src/xswiftbus/service.h index 3a265cc44..f125a8c22 100644 --- a/src/xswiftbus/service.h +++ b/src/xswiftbus/service.h @@ -15,6 +15,8 @@ #ifndef NOMINMAX #define NOMINMAX #endif + +#include "dbusobject.h" #include "datarefs.h" #include "messages.h" #include "blackmisc/simulation/xplane/navdatareference.h" @@ -30,26 +32,20 @@ class QTimer; #define XSWIFTBUS_SERVICE_OBJECTPATH "/xswiftbus/service" //! \endcond -//! Typedef needed to use QList as a DBus argument -using QDoubleList = QList; - -//! Typedef needed to use QList as a DBus argument -Q_DECLARE_METATYPE(QDoubleList) - namespace XSwiftBus { /*! * XSwiftBus service object which is accessible through DBus */ - class CService : public QObject + class CService : public CDBusObject { - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", XSWIFTBUS_SERVICE_INTERFACENAME) - public: //! Constructor - CService(QObject *parent); + CService(CDBusConnection *connection); + + //! Destructor + ~CService() override = default; //! DBus interface name static const QString &InterfaceName() @@ -68,16 +64,6 @@ namespace XSwiftBus //! Called by XPluginReceiveMessage when the model changes. void onAircraftModelChanged(); - signals: - //! Emitted when the model or livery changes. - 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 updated. - void airportsInRangeUpdated(const QStringList &icaoCodes, const QStringList &names, const QDoubleList &lats, const QDoubleList &lons, const QDoubleList &alts); - - public slots: //! Add a text message to the on-screen display, with RGB components in the range [0,1] void addTextMessage(const QString &text, double red, double green, double blue); @@ -244,7 +230,19 @@ namespace XSwiftBus //! \copydoc XSwiftBus::CMessageBoxControl::toggle void toggleMessageBoxVisibility() { m_messages.toggle(); } + int processDBus() override; + + protected: + DBusHandlerResult dbusMessageHandler(const CDBusMessage &message) override; + private: + void emitAircraftModelChanged(const QString &path, const QString &filename, const QString &livery, + const QString &icao, const QString &modelString, const QString &name, + const QString &description); + + void emitAirportsInRangeUpdated(const std::vector &icaoCodes, const std::vector &names, + const std::vector &lats, const std::vector &lons, const std::vector &alts); + CMessageBoxControl m_messages { 128, 128, 16 }; BlackMisc::Simulation::XPlane::CNavDataReferenceList m_airports; QTimer *m_airportUpdater = nullptr; diff --git a/src/xswiftbus/traffic.cpp b/src/xswiftbus/traffic.cpp index e1605531d..e962c3532 100644 --- a/src/xswiftbus/traffic.cpp +++ b/src/xswiftbus/traffic.cpp @@ -14,7 +14,6 @@ #endif #include "traffic.h" #include "utils.h" -#include "blackmisc/aviation/callsign.h" #include "blackmisc/verify.h" #include "XPMPMultiplayer.h" #include "XPMPPlaneRenderer.h" @@ -43,10 +42,10 @@ namespace XSwiftBus surfaces.lights.timeOffset = static_cast(qrand() % 0xffff); } - CTraffic::CTraffic(QObject *parent) : - QObject(parent) + CTraffic::CTraffic(CDBusConnection *dbusConnection) : + CDBusObject(dbusConnection) { - XPLMRegisterDrawCallback(CTraffic::drawCallback, xplm_Phase_Airplanes, 0, this); + registerDBusObjectPath(XSWIFTBUS_TRAFFIC_INTERFACENAME, XSWIFTBUS_TRAFFIC_OBJECTPATH); } CTraffic::~CTraffic() @@ -98,21 +97,23 @@ namespace XSwiftBus m_initialized = false; XPMPMultiplayerCleanup(); } - - XPLMUnregisterDrawCallback(CTraffic::drawCallback, xplm_Phase_Airplanes, 0, this); } void CTraffic::emitSimFrame() { - qint64 currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); + sendDBusSignal("simFrame"); + } - // The draw callback is called twice for unknown reasons each frame. We can filter the second one by - // requiring a minimum offset of 5 ms (equal to 200 fps). - if (currentMSecsSinceEpoch > m_timestampLastSimFrame + 5) - { - emit simFrame(); - m_timestampLastSimFrame = currentMSecsSinceEpoch; - } + void CTraffic::emitRemoteAircraftData(const QString &callsign, double latitude, double longitude, double elevation, double modelVerticalOffset) + { + CDBusMessage signalRemoteAircraftData = CDBusMessage::createSignal(XSWIFTBUS_TRAFFIC_OBJECTPATH, XSWIFTBUS_TRAFFIC_INTERFACENAME, "remoteAircraftData"); + signalRemoteAircraftData.beginArgumentWrite(); + signalRemoteAircraftData.appendArgument(callsign.toStdString()); + signalRemoteAircraftData.appendArgument(latitude); + signalRemoteAircraftData.appendArgument(longitude); + signalRemoteAircraftData.appendArgument(elevation); + signalRemoteAircraftData.appendArgument(modelVerticalOffset); + sendDBusMessage(signalRemoteAircraftData); } int g_maxPlanes = 100; @@ -214,7 +215,8 @@ namespace XSwiftBus void CTraffic::removeAllPlanes() { - for (Plane *plane : BlackMisc::as_const(m_planesByCallsign)) + const QList planes = m_planesByCallsign.values(); + for (Plane *plane : planes) { BLACK_VERIFY_X(plane, Q_FUNC_INFO, "Missing Plane"); if (!plane) { continue; } @@ -289,10 +291,253 @@ namespace XSwiftBus if (std::isnan(groundElevation)) { groundElevation = 0.0; } double fudgeFactor = 3.0; actualVertOffsetInfo(qPrintable(plane->modelName), nullptr, &fudgeFactor); - emit remoteAircraftData(plane->callsign, lat, lon, groundElevation, fudgeFactor); + emitRemoteAircraftData(plane->callsign, lat, lon, groundElevation, fudgeFactor); } } + const char *introspection_traffic = + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE +#include "org.swift_project.xswiftbus.traffic.xml" + ; + + DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage &message_) + { + CDBusMessage message(message_); + const std::string sender = message.getSender(); + const dbus_uint32_t serial = message.getSerial(); + const bool wantsReply = message.wantsReply(); + + if (message.getInterfaceName() == DBUS_INTERFACE_INTROSPECTABLE) + { + if (message.getMethodName() == "Introspect") + { + sendDBusReply(sender, serial, introspection_traffic); + } + } + else if (message.getInterfaceName() == XSWIFTBUS_TRAFFIC_INTERFACENAME) + { + if (message.getMethodName() == "initialize") + { + sendDBusReply(sender, serial, initialize()); + } + else if (message.getMethodName() == "cleanup") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + queueDBusCall([ = ]() + { + cleanup(); + }); + } + else if (message.getMethodName() == "loadPlanesPackage") + { + std::string path; + message.beginArgumentRead(); + message.getArgument(path); + queueDBusCall([ = ]() + { + sendDBusReply(sender, serial, loadPlanesPackage(QString::fromStdString(path))); + }); + } + else if (message.getMethodName() == "setDefaultIcao") + { + std::string defaultIcao; + message.beginArgumentRead(); + message.getArgument(defaultIcao); + queueDBusCall([ = ]() + { + setDefaultIcao(QString::fromStdString(defaultIcao)); + }); + } + else if (message.getMethodName() == "setDrawingLabels") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + bool drawing = true; + message.beginArgumentRead(); + message.getArgument(drawing); + queueDBusCall([ = ]() + { + setDrawingLabels(drawing); + }); + + } + else if (message.getMethodName() == "isDrawingLabels") + { + queueDBusCall([ = ]() + { + sendDBusReply(sender, serial, isDrawingLabels()); + }); + } + else if (message.getMethodName() == "setMaxPlanes") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int planes = 100; + message.beginArgumentRead(); + message.getArgument(planes); + queueDBusCall([ = ]() + { + setMaxPlanes(planes); + }); + } + else if (message.getMethodName() == "setMaxDrawDistance") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + double nauticalMiles = 100; + message.beginArgumentRead(); + message.getArgument(nauticalMiles); + queueDBusCall([ = ]() + { + setMaxDrawDistance(nauticalMiles); + }); + + } + else if (message.getMethodName() == "addPlane") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::string callsign; + std::string modelName; + std::string aircraftIcao; + std::string airlineIcao; + std::string livery; + message.beginArgumentRead(); + message.getArgument(callsign); + message.getArgument(modelName); + message.getArgument(aircraftIcao); + message.getArgument(airlineIcao); + message.getArgument(livery); + + queueDBusCall([ = ]() + { + addPlane(QString::fromStdString(callsign), QString::fromStdString(modelName), QString::fromStdString(aircraftIcao), QString::fromStdString(airlineIcao), QString::fromStdString(livery)); + }); + } + else if (message.getMethodName() == "removePlane") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::string callsign; + message.beginArgumentRead(); + message.getArgument(callsign); + queueDBusCall([ = ]() + { + removePlane(QString::fromStdString(callsign)); + }); + } + else if (message.getMethodName() == "removeAllPlanes") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + queueDBusCall([ = ]() + { + removeAllPlanes(); + }); + } + else if (message.getMethodName() == "setPlanePosition") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::string callsign; + double latitude = 0.0; + double longitude = 0.0; + double altitude = 0.0; + double pitch = 0.0; + double roll = 0.0; + double heading = 0.0; + message.beginArgumentRead(); + message.getArgument(callsign); + message.getArgument(latitude); + message.getArgument(longitude); + message.getArgument(altitude); + message.getArgument(pitch); + message.getArgument(roll); + message.getArgument(heading); + queueDBusCall([ = ]() + { + setPlanePosition(QString::fromStdString(callsign), latitude, longitude, altitude, pitch, roll, heading); + }); + } + else if (message.getMethodName() == "setPlaneSurfaces") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::string callsign; + double gear = 0.0; + double flap = 0.0; + double spoiler = 0.0; + double speedBrake = 0.0; + double slat = 0.0; + double wingSweep = 0.0; + double thrust = 0.0; + double elevator = 0.0; + double rudder = 0.0; + double aileron = 0.0; + bool landLight = false; + bool beaconLight = false; + bool strobeLight = false; + bool navLight = false; + bool lightPattern = false; + bool onGround = false; + message.beginArgumentRead(); + message.getArgument(callsign); + message.getArgument(gear); + message.getArgument(flap); + message.getArgument(spoiler); + message.getArgument(speedBrake); + message.getArgument(slat); + message.getArgument(wingSweep); + message.getArgument(thrust); + message.getArgument(elevator); + message.getArgument(rudder); + message.getArgument(aileron); + message.getArgument(landLight); + message.getArgument(beaconLight); + message.getArgument(strobeLight); + message.getArgument(navLight); + message.getArgument(lightPattern); + message.getArgument(onGround); + queueDBusCall([ = ]() + { + setPlaneSurfaces(QString::fromStdString(callsign), gear, flap, spoiler, speedBrake, slat, wingSweep, thrust, elevator, + rudder, aileron, landLight, beaconLight, strobeLight, navLight, lightPattern, + onGround); + }); + } + else if (message.getMethodName() == "setPlaneTransponder") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::string callsign; + int code = 0; + bool modeC = false; + bool ident = false; + message.beginArgumentRead(); + message.getArgument(callsign); + message.getArgument(code); + message.getArgument(modeC); + message.getArgument(ident); + queueDBusCall([ = ]() + { + setPlaneTransponder(QString::fromStdString(callsign), code, modeC, ident); + }); + } + else if (message.getMethodName() == "requestRemoteAircraftData") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + queueDBusCall([ = ]() + { + requestRemoteAircraftData(); + }); + } + else + { + // Unknown message. Tell DBus that we cannot handle it + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + return DBUS_HANDLER_RESULT_HANDLED; + } + + int CTraffic::processDBus() + { + emitSimFrame(); + invokeQueuedDBusCalls(); + return 1; + } + //! memcmp function which ignores the header ("size" member) and compares only the payload (the rest of the struct) template int memcmpPayload(T *dst, T *src) @@ -304,6 +549,7 @@ namespace XSwiftBus int CTraffic::getPlaneData(void *id, int dataType, void *io_data) { + QHash planesById = m_planesById; Plane *plane = m_planesById.value(id, nullptr); if (!plane) { return xpmpData_Unavailable; } @@ -364,15 +610,6 @@ namespace XSwiftBus default: return xpmpData_Unavailable; } } - - int CTraffic::drawCallback(XPLMDrawingPhase phase, int isBefore, void *refcon) - { - Q_UNUSED(phase); - Q_UNUSED(isBefore); - CTraffic *traffic = static_cast(refcon); - traffic->emitSimFrame(); - return 1; - } } //! \endcond diff --git a/src/xswiftbus/traffic.h b/src/xswiftbus/traffic.h index fd094f554..540eb7dd2 100644 --- a/src/xswiftbus/traffic.h +++ b/src/xswiftbus/traffic.h @@ -12,6 +12,7 @@ //! \file +#include "dbusobject.h" #include "datarefs.h" #include "terrainprobe.h" #include @@ -34,14 +35,11 @@ namespace XSwiftBus /*! * XSwiftBus service object for traffic aircraft which is accessible through DBus */ - class CTraffic : public QObject + class CTraffic : public CDBusObject { - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", XSWIFTBUS_TRAFFIC_INTERFACENAME) - public: //! Constructor - CTraffic(QObject *parent); + CTraffic(CDBusConnection *dbusConnection); //! Destructor virtual ~CTraffic(); @@ -63,14 +61,6 @@ namespace XSwiftBus //! Called by XPluginStart static void initLegacyData(); - signals: - //! Signal emitted for each simulator rendering frame - void simFrame(); - - //! Remote aircraft data - void remoteAircraftData(const QString &callsign, double latitude, double longitude, double elevation, double modelVerticalOffset); - - public slots: //! Initialize the multiplayer planes rendering and return true if successful bool initialize(); @@ -117,11 +107,17 @@ namespace XSwiftBus //! Request traffic plane data. A signal remoteAircraftData will be emitted for each known plane void requestRemoteAircraftData(); + int processDBus() override; + + protected: + DBusHandlerResult dbusMessageHandler(const CDBusMessage &message) override; + private: bool m_initialized = false; bool m_enabled = false; void emitSimFrame(); + void emitRemoteAircraftData(const QString &callsign, double latitude, double longitude, double elevation, double modelVerticalOffset); static int preferences(const char *section, const char *name, int def); static float preferences(const char *section, const char *name, float def); @@ -145,6 +141,7 @@ namespace XSwiftBus XPMPPlanePosition_t position; Plane(void *id_, QString callsign_, QString aircraftIcao_, QString airlineIcao_, QString livery_, QString modelName_); }; + QHash m_planesByCallsign; QHash m_planesById; qint64 m_timestampLastSimFrame = QDateTime::currentMSecsSinceEpoch(); @@ -154,8 +151,6 @@ namespace XSwiftBus { return static_cast(self)->getPlaneData(id, dataType, io_data); } - - static int drawCallback(XPLMDrawingPhase phase, int isBefore, void *refcon); }; } diff --git a/src/xswiftbus/weather.cpp b/src/xswiftbus/weather.cpp index a884d1960..fc06473f8 100644 --- a/src/xswiftbus/weather.cpp +++ b/src/xswiftbus/weather.cpp @@ -14,6 +14,11 @@ namespace XSwiftBus { + CWeather::CWeather(CDBusConnection *dbusConnection) + : CDBusObject(dbusConnection) + { + registerDBusObjectPath(InterfaceName(), ObjectPath()); + } //! Set cloud layer template @@ -59,6 +64,191 @@ namespace XSwiftBus } } + const char *introspection_weather = + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + #include "org.swift_project.xswiftbus.weather.xml" + ; + + DBusHandlerResult CWeather::dbusMessageHandler(const CDBusMessage &message_) + { + CDBusMessage message(message_); + const std::string sender = message.getSender(); + const dbus_uint32_t serial = message.getSerial(); + const bool wantsReply = message.wantsReply(); + + if (message.getInterfaceName() == DBUS_INTERFACE_INTROSPECTABLE) + { + if (message.getMethodName() == "Introspect") + { + sendDBusReply(sender, serial, introspection_weather); + } + } + else if (message.getInterfaceName() == XSWIFTBUS_WEATHER_INTERFACENAME) + { + if (message.getMethodName() == "isUsingRealWeather") + { + queueDBusCall([=]() + { + sendDBusReply(sender, serial, isUsingRealWeather()); + }); + } + else if (message.getMethodName() == "setUseRealWeather") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + bool enable = false; + message.beginArgumentRead(); + message.getArgument(enable); + queueDBusCall([=]() + { + setUseRealWeather(enable); + }); + } + else if (message.getMethodName() == "setVisibility") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + double visibilityM = 10.0; + message.beginArgumentRead(); + message.getArgument(visibilityM); + queueDBusCall([=]() + { + setVisibility(visibilityM); + }); + } + else if (message.getMethodName() == "setTemperature") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int degreesC = 10; + message.beginArgumentRead(); + message.getArgument(degreesC); + queueDBusCall([=]() + { + setTemperature(degreesC); + }); + } + else if (message.getMethodName() == "setDewPoint") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int degreesC = 10; + message.beginArgumentRead(); + message.getArgument(degreesC); + queueDBusCall([=]() + { + setDewPoint(degreesC); + }); + } + else if (message.getMethodName() == "setQNH") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + double inHg = 29.92; + message.beginArgumentRead(); + message.getArgument(inHg); + queueDBusCall([=]() + { + setQNH(inHg); + }); + } + else if (message.getMethodName() == "setPrecipitationRatio") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + double precipRatio = 0.0; + message.beginArgumentRead(); + message.getArgument(precipRatio); + queueDBusCall([=]() + { + setPrecipitationRatio(precipRatio); + }); + } + else if (message.getMethodName() == "setThunderstormRatio") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + double cbRatio = 0.0; + message.beginArgumentRead(); + message.getArgument(cbRatio); + queueDBusCall([=]() + { + setThunderstormRatio(cbRatio); + }); + + } + else if (message.getMethodName() == "setTurbulenceRatio") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + double turbulenceRatio = 0.0; + message.beginArgumentRead(); + message.getArgument(turbulenceRatio); + queueDBusCall([=]() + { + setTurbulenceRatio(turbulenceRatio); + }); + } + else if (message.getMethodName() == "setRunwayFriction") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int friction = 10; + message.beginArgumentRead(); + message.getArgument(friction); + queueDBusCall([=]() + { + setRunwayFriction(friction); + }); + } + else if (message.getMethodName() == "setCloudLayer") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int layer = 0; + int base = 0; + int tops = 0; + int type = 0; + int coverage = 0; + message.beginArgumentRead(); + message.getArgument(layer); + message.getArgument(base); + message.getArgument(tops); + message.getArgument(type); + message.getArgument(coverage); + queueDBusCall([=]() + { + setCloudLayer(layer, base, tops, type, coverage); + }); + } + else if (message.getMethodName() == "setWindLayer") + { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int layer = 0; + int altitude = 0; + double direction = 0; + int speed = 0; + int shearDirection = 0; + int shearSpeed = 0; + int turbulence = 0; + message.beginArgumentRead(); + message.getArgument(layer); + message.getArgument(altitude); + message.getArgument(direction); + message.getArgument(speed); + message.getArgument(shearDirection); + message.getArgument(shearSpeed); + message.getArgument(turbulence); + queueDBusCall([=]() + { + setWindLayer(layer, altitude, direction, speed, shearDirection, shearSpeed, turbulence); + }); + } + else + { + // Unknown message. Tell DBus that we cannot handle it + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + return DBUS_HANDLER_RESULT_HANDLED; + } + + int CWeather::processDBus() + { + invokeQueuedDBusCalls(); + return 1; + } + } //! \endcond diff --git a/src/xswiftbus/weather.h b/src/xswiftbus/weather.h index 6a286fb24..5e8140ac7 100644 --- a/src/xswiftbus/weather.h +++ b/src/xswiftbus/weather.h @@ -15,8 +15,8 @@ #ifndef NOMINMAX #define NOMINMAX #endif +#include "dbusobject.h" #include "datarefs.h" -#include //! \cond PRIVATE #define XSWIFTBUS_WEATHER_INTERFACENAME "org.swift_project.xswiftbus.weather" @@ -29,30 +29,26 @@ namespace XSwiftBus /*! * XSwiftBus weather object which is accessible through DBus */ - class CWeather : public QObject + class CWeather : public CDBusObject { - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", XSWIFTBUS_WEATHER_INTERFACENAME) - public: //! Constructor - CWeather(QObject *parent) : QObject(parent) {} + CWeather(CDBusConnection *dbusConnection); //! DBus interface name - static const QString &InterfaceName() + static const std::string &InterfaceName() { - static QString s(XSWIFTBUS_WEATHER_INTERFACENAME); + static std::string s(XSWIFTBUS_WEATHER_INTERFACENAME); return s; } //! DBus object path - static const QString &ObjectPath() + static const std::string &ObjectPath() { - static QString s(XSWIFTBUS_WEATHER_OBJECTPATH); + static std::string s(XSWIFTBUS_WEATHER_OBJECTPATH); return s; } - public slots: //! True if the sim is using X-Plane built-in real weather source. bool isUsingRealWeather() const { return m_useRealWeather.get(); } @@ -101,6 +97,10 @@ namespace XSwiftBus //! \param turbulence Amount of turbulence [0,10]. void setWindLayer(int layer, int altitude, double direction, int speed, int shearDirection, int shearSpeed, int turbulence); + virtual int processDBus() override; + protected: + virtual DBusHandlerResult dbusMessageHandler(const CDBusMessage &message) override; + private: DataRef m_useRealWeather; DataRef m_visibilityM; diff --git a/src/xswiftbus/xswiftbus.pro b/src/xswiftbus/xswiftbus.pro index f3889c034..e2055b361 100644 --- a/src/xswiftbus/xswiftbus.pro +++ b/src/xswiftbus/xswiftbus.pro @@ -9,6 +9,11 @@ CONFIG += blackmisc INCLUDEPATH += $$EXTERNALSROOT/common/include/XPLM +LIBS += -levent_core -ldbus-1 + +OTHER_FILES += \ + org.swift_project.xswiftbus.*.xml + win32 { equals(WORD_SIZE,64): LIBS += -lXPLM_64 -lXPWidgets_64 equals(WORD_SIZE,32): LIBS += -lXPLM -lXPWidgets @@ -34,6 +39,11 @@ SOURCES += $$files(libxplanemp/src/*.cpp) HEADERS += $$files(libxplanemp/src/*.h) $$files(libxplanemp/include/*.h) INCLUDEPATH += ./libxplanemp ./libxplanemp/include ./libxplanemp/src +unix:!macx { + INCLUDEPATH *= /usr/include/dbus-1.0 + INCLUDEPATH *= /usr/lib/x86_64-linux-gnu/dbus-1.0/include +} + # PlatformUtils also not used SOURCES -= $$files(libxplanemp/src/PlatformUtils.*.cpp) HEADERS -= libxplanemp/src/PlatformUtils.h @@ -102,10 +112,12 @@ win32 { dep_target.files *= $$DestRoot/bin/dbus-daemon.exe win32-g++ { dep_target.files *= $$DestRoot/bin/libdbus-1-3.dll + dep_target.files *= $$DestRoot/bin/libevent_core.dll } else { dep_target.files *= $$DestRoot/bin/dbus-1-3.dll dep_target.files *= $$DestRoot/bin/expat.dll + dep_target.files *= $$DestRoot/bin/libevent_core.dll } dep_target.files *= $$[QT_INSTALL_BINS]/Qt5Core$${DLL_DEBUG_SUFFIX}.dll dep_target.files *= $$[QT_INSTALL_BINS]/Qt5Gui$${DLL_DEBUG_SUFFIX}.dll @@ -124,6 +136,7 @@ win32 { legacy_data_target.files *= LegacyData } else:macx: { dep_target.files *= $$DestRoot/lib/libdbus-1.3.dylib + dep_target.files *= $$DestRoot/lib/libevent_core.2.1.8.dylib dep_target.extra += rsync -avzl --exclude \'Headers*\' --exclude \'*debug*\' $$[QT_INSTALL_LIBS]/QtCore.framework/ $${PREFIX}/$$XSWIFTBUS_DIR/QtCore.framework/ && dep_target.extra += rsync -avzl --exclude \'Headers*\' --exclude \'*debug*\' $$[QT_INSTALL_LIBS]/QtGui.framework/ $${PREFIX}/$$XSWIFTBUS_DIR/QtGui.framework/ && dep_target.extra += rsync -avzl --exclude \'Headers*\' --exclude \'*debug*\' $$[QT_INSTALL_LIBS]/QtWidgets.framework/ $${PREFIX}/$$XSWIFTBUS_DIR/QtWidgets.framework/ && @@ -154,7 +167,9 @@ win32 { fix_plugin_rpath.commands += install_name_tool -change \"@rpath/QtDBus.framework/Versions/5/QtDBus\" \"@loader_path/QtDBus.framework/Versions/5/QtDBus\" $$shell_path($$PREFIX/$$XSWIFTBUS_DIR/mac.xpl) && fix_plugin_rpath.commands += install_name_tool -change \"@rpath/QtNetwork.framework/Versions/5/QtNetwork\" \"@loader_path/QtNetwork.framework/Versions/5/QtNetwork\" $$shell_path($$PREFIX/$$XSWIFTBUS_DIR/mac.xpl) && fix_plugin_rpath.commands += install_name_tool -change \"@rpath/QtCore.framework/Versions/5/QtCore\" \"@loader_path/QtCore.framework/Versions/5/QtCore\" $$shell_path($$PREFIX/$$XSWIFTBUS_DIR/mac.xpl) && - fix_plugin_rpath.commands += install_name_tool -change \"@rpath/QtXml.framework/Versions/5/QtXml\" \"@loader_path/QtXml.framework/Versions/5/QtXml\" $$shell_path($$PREFIX/$$XSWIFTBUS_DIR/libblackmisc.0.dylib) + fix_plugin_rpath.commands += install_name_tool -change \"@rpath/QtXml.framework/Versions/5/QtXml\" \"@loader_path/QtXml.framework/Versions/5/QtXml\" $$shell_path($$PREFIX/$$XSWIFTBUS_DIR/libblackmisc.0.dylib) && + fix_plugin_rpath.commands += install_name_tool -change \"@rpath/libevent_core.2.1.8.dylib\" \"@loader_path/libevent_core.2.1.8.dylib\" $$shell_path($$PREFIX/$$XSWIFTBUS_DIR/mac.xpl) && + fix_plugin_rpath.commands += install_name_tool -change \"@rpath/libdbus-1.3.dylib\" \"@loader_path/libdbus-1.3.dylib\" $$shell_path($$PREFIX/$$XSWIFTBUS_DIR/mac.xpl) QMAKE_EXTRA_TARGETS += fix_plugin_rpath fix_plugin_rpath.depends += fix_misc_rpath