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