[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.
This commit is contained in:
Roland Winklmeier
2018-03-19 17:08:25 +01:00
parent 849124fe7c
commit d77931e5ec
24 changed files with 2437 additions and 139 deletions

View File

@@ -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<double> &lats, const QList<double> &lons, const QList<double> &alts);
public slots:

View File

@@ -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);

View File

@@ -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 <dbus/dbus.h>
#include <functional>
namespace XSwiftBus
{
//! \cond PRIVATE
template <typename T>
class DBusAsyncCallbacks
{
public:
DBusAsyncCallbacks() = default;
DBusAsyncCallbacks(const std::function<dbus_bool_t(T *)> &add,
const std::function<void(T *)> &remove,
const std::function<void(T *)> &toggled)
: m_addHandler(add), m_removeHandler(remove), m_toggledHandler(toggled)
{ }
static dbus_bool_t add(T *watch, void *refcon)
{
return static_cast<DBusAsyncCallbacks *>(refcon)->m_addHandler(watch);
}
static void remove(T *watch, void *refcon)
{
return static_cast<DBusAsyncCallbacks *>(refcon)->m_removeHandler(watch);
}
static void toggled(T *watch, void *refcon)
{
return static_cast<DBusAsyncCallbacks *>(refcon)->m_toggledHandler(watch);
}
private:
std::function<dbus_bool_t(T *)> m_addHandler;
std::function<void(T *)> m_removeHandler;
std::function<void(T *)> m_toggledHandler;
};
//! \endcond
}
#endif // guard

View File

@@ -0,0 +1,366 @@
#include <utility>
/* 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 <algorithm>
#include <cassert>
#include <memory>
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<WatchHandler *>(data);
watchHandler->m_parent->handleSocketReady(fd, event);
}
CDBusConnection *m_parent = nullptr;
std::unique_ptr<event, EventDeleter> 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<TimeoutHandler *>(data);
dbus_timeout_handle(timeoutHandler->m_timeout);
}
std::unique_ptr<event, EventDeleter> m_event;
DBusTimeout *m_timeout = nullptr;
};
//! Generic Timer
class Timer
{
public:
Timer() = default;
//! Constructor
Timer(CDBusConnection *parent, const timeval &timeout, const std::function<void()> &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<Timer *>(data);
timer->m_func();
delete timer;
}
std::unique_ptr<event, EventDeleter> m_event;
std::function<void()> 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<bool>(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<WatchHandler>(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<TimeoutHandler>(this, timeout));
return true;
}
void CDBusConnection::dbusRemoveTimeout(DBusTimeout *timeout)
{
auto predicate = [timeout](const std::unique_ptr<TimeoutHandler> &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<CDBusConnection *>(data);
return obj->dbusUpdateDispatchStatus(connection, newStatus);
}
}

View File

@@ -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 <event2/event.h>
#include <dbus/dbus.h>
#include <string>
#include <unordered_map>
#include <vector>
#include <memory>
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<DBusWatch>;
using TimeoutCallbacks = DBusAsyncCallbacks<DBusTimeout>;
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<event_base, EventBaseDeleter> m_eventBase;
std::unique_ptr<DBusConnection, DBusConnectionDeleter> m_connection;
CDBusError m_lastError;
WatchCallbacks m_watchCallbacks;
TimeoutCallbacks m_timeoutCallbacks;
std::unordered_multimap<evutil_socket_t, std::unique_ptr<WatchHandler>> m_watchers;
std::vector<std::unique_ptr<TimeoutHandler>> m_timeouts;
};
}
#endif // guard

View File

@@ -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)
{ }
}

47
src/xswiftbus/dbuserror.h Normal file
View File

@@ -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 <dbus/dbus.h>
#include <string>
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

View File

@@ -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<double> &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<std::string> &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;
}
}

View File

@@ -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 <string>
#include <vector>
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<double> &array);
void appendArgument(const std::vector<std::string> &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

View File

@@ -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<void ()> &func)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_qeuedDBusCalls.push_back(func);
}
void CDBusObject::invokeQueuedDBusCalls()
{
std::lock_guard<std::mutex> 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<CDBusObject *>(data);
DBusError err;
dbus_error_init(&err);
CDBusMessage dbusMessage(message);
return obj->dbusMessageHandler(dbusMessage);
}
}

View File

@@ -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 <XPLM/XPLMDisplay.h>
#include <mutex>
#include <deque>
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 <typename T>
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 <typename T>
void sendDBusReply(const std::string &destination, dbus_uint32_t serial, const std::vector<T> &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<void()> &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<std::function<void()>> m_qeuedDBusCalls;
const DBusObjectPathVTable m_dbusObjectPathVTable = { dbusObjectPathUnregisterFunction, dbusObjectPathMessageFunction, nullptr, nullptr, nullptr, nullptr };
};
}
#endif // guard

View File

@@ -14,7 +14,6 @@
#include "utils.h"
#include "traffic.h"
#include "service.h"
#include "blackmisc/dbus.h"
#include <XPLM/XPLMPlanes.h>
#if ! defined(XPLM210)
@@ -55,18 +54,11 @@ PLUGIN_API void XPluginStop()
PLUGIN_API int XPluginEnable()
{
qRegisterMetaType<QDoubleList>();
qDBusRegisterMetaType<QDoubleList>();
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;
}

View File

@@ -0,0 +1,171 @@
R"(<node>
<interface name="org.swift_project.xswiftbus.service">
<method name="addTextMessage">
<arg name="text" type="s" direction="in"/>
<arg name="red" type="d" direction="in"/>
<arg name="green" type="d" direction="in"/>
<arg name="blue" type="d" direction="in"/>
</method>
<method name="getOwnAircraftSituationData">
<arg name="lat" type="d" direction="out"/>
<arg name="lon" type="d" direction="out"/>
<arg name="alt" type="d" direction="out"/>
<arg name="gs" type="d" direction="out"/>
<arg name="pitch" type="d" direction="out"/>
<arg name="roll" type="d" direction="out"/>
<arg name="trueHeading" type="d" direction="out"/>
<arg name="qnh" type="d" direction="out"/>
</method>
<method name="updateAirportsInRange">
</method>
<method name="getAircraftModelPath">
<arg type="s" direction="out"/>
</method>
<method name="getAircraftModelFilename">
<arg type="s" direction="out"/>
</method>
<method name="getAircraftModelString">
<arg type="s" direction="out"/>
</method>
<method name="getAircraftName">
<arg type="s" direction="out"/>
</method>
<method name="getAircraftLivery">
<arg type="s" direction="out"/>
</method>
<method name="getAircraftIcaoCode">
<arg type="s" direction="out"/>
</method>
<method name="getAircraftDescription">
<arg type="s" direction="out"/>
</method>
<method name="getXPlaneVersionMajor">
<arg type="i" direction="out"/>
</method>
<method name="getXPlaneVersionMinor">
<arg type="i" direction="out"/>
</method>
<method name="getXPlaneInstallationPath">
<arg type="s" direction="out"/>
</method>
<method name="getXPlanePreferencesPath">
<arg type="s" direction="out"/>
</method>
<method name="isPaused">
<arg type="b" direction="out"/>
</method>
<method name="isUsingRealTime">
<arg type="b" direction="out"/>
</method>
<method name="getLatitude">
<arg type="d" direction="out"/>
</method>
<method name="getLongitude">
<arg type="d" direction="out"/>
</method>
<method name="getAltitudeMSL">
<arg type="d" direction="out"/>
</method>
<method name="getHeightAGL">
<arg type="d" direction="out"/>
</method>
<method name="getGroundSpeed">
<arg type="d" direction="out"/>
</method>
<method name="getIndicatedAirspeed">
<arg type="d" direction="out"/>
</method>
<method name="getTrueAirspeed">
<arg type="d" direction="out"/>
</method>
<method name="getPitch">
<arg type="d" direction="out"/>
</method>
<method name="getRoll">
<arg type="d" direction="out"/>
</method>
<method name="getTrueHeading">
<arg type="d" direction="out"/>
</method>
<method name="getAnyWheelOnGround">
<arg type="b" direction="out"/>
</method>
<method name="getAllWheelsOnGround">
<arg type="b" direction="out"/>
</method>
<method name="getCom1Active">
<arg type="i" direction="out"/>
</method>
<method name="getCom1Standby">
<arg type="i" direction="out"/>
</method>
<method name="getCom2Active">
<arg type="i" direction="out"/>
</method>
<method name="getCom2Standby">
<arg type="i" direction="out"/>
</method>
<method name="getTransponderCode">
<arg type="i" direction="out"/>
</method>
<method name="getTransponderMode">
<arg type="i" direction="out"/>
</method>
<method name="getTransponderIdent">
<arg type="b" direction="out"/>
</method>
<method name="getBeaconLightsOn">
<arg type="b" direction="out"/>
</method>
<method name="getLandingLightsOn">
<arg type="b" direction="out"/>
</method>
<method name="getNavLightsOn">
<arg type="b" direction="out"/>
</method>
<method name="getStrobeLightsOn">
<arg type="b" direction="out"/>
</method>
<method name="getTaxiLightsOn">
<arg type="b" direction="out"/>
</method>
<method name="getQNH">
<arg type="d" direction="out"/>
</method>
<method name="setCom1Active">
<arg name="freq" type="i" direction="in"/>
</method>
<method name="setCom1Standby">
<arg name="freq" type="i" direction="in"/>
</method>
<method name="setCom2Active">
<arg name="freq" type="i" direction="in"/>
</method>
<method name="setCom2Standby">
<arg name="freq" type="i" direction="in"/>
</method>
<method name="setTransponderCode">
<arg name="code" type="i" direction="in"/>
</method>
<method name="setTransponderMode">
<arg name="mode" type="i" direction="in"/>
</method>
<method name="getFlapsDeployRatio">
<arg type="d" direction="out"/>
</method>
<method name="getGearDeployRatio">
<arg type="d" direction="out"/>
</method>
<method name="getNumberOfEngines">
<arg type="i" direction="out"/>
</method>
<method name="getEngineN1Percentage">
<arg type="ad" direction="out"/>
</method>
<method name="getSpeedBrakeRatio">
<arg type="d" direction="out"/>
</method>
<method name="toggleMessageBoxVisibility">
</method>
</interface>
</node>)"

View File

@@ -0,0 +1,76 @@
R"(<node>
<interface name="org.swift_project.xswiftbus.traffic">
<method name="initialize">
<arg type="b" direction="out"/>
</method>
<method name="cleanup">
</method>
<method name="loadPlanesPackage">
<arg name="path" type="s" direction="in"/>
<arg type="b" direction="out"/>
</method>
<method name="setDefaultIcao">
<arg name="defaultIcao" type="s" direction="in"/>
</method>
<method name="setDrawingLabels">
<arg name="drawing" type="b" direction="in"/>
</method>
<method name="isDrawingLabels">
<arg type="b" direction="out"/>
</method>
<method name="setMaxPlanes">
<arg name="planes" type="i" direction="in"/>
</method>
<method name="setMaxDrawDistance">
<arg name="nauticalMiles" type="d" direction="in"/>
</method>
<method name="addPlane">
<arg name="callsign" type="s" direction="in"/>
<arg name="modelName" type="s" direction="in"/>
<arg name="aircraftIcao" type="s" direction="in"/>
<arg name="airlineIcao" type="s" direction="in"/>
<arg name="livery" type="s" direction="in"/>
</method>
<method name="removePlane">
<arg name="callsign" type="s" direction="in"/>
</method>
<method name="removeAllPlanes">
</method>
<method name="setPlanePosition">
<arg name="callsign" type="s" direction="in"/>
<arg name="latitude" type="d" direction="in"/>
<arg name="longitude" type="d" direction="in"/>
<arg name="altitude" type="d" direction="in"/>
<arg name="pitch" type="d" direction="in"/>
<arg name="roll" type="d" direction="in"/>
<arg name="heading" type="d" direction="in"/>
</method>
<method name="setPlaneSurfaces">
<arg name="callsign" type="s" direction="in"/>
<arg name="gear" type="d" direction="in"/>
<arg name="flap" type="d" direction="in"/>
<arg name="spoiler" type="d" direction="in"/>
<arg name="speedBrake" type="d" direction="in"/>
<arg name="slat" type="d" direction="in"/>
<arg name="wingSweep" type="d" direction="in"/>
<arg name="thrust" type="d" direction="in"/>
<arg name="elevator" type="d" direction="in"/>
<arg name="rudder" type="d" direction="in"/>
<arg name="aileron" type="d" direction="in"/>
<arg name="landLight" type="b" direction="in"/>
<arg name="beaconLight" type="b" direction="in"/>
<arg name="strobeLight" type="b" direction="in"/>
<arg name="navLight" type="b" direction="in"/>
<arg name="lightPattern" type="i" direction="in"/>
<arg name="onGround" type="b" direction="in"/>
</method>
<method name="setPlaneTransponder">
<arg name="callsign" type="s" direction="in"/>
<arg name="code" type="i" direction="in"/>
<arg name="modeC" type="b" direction="in"/>
<arg name="ident" type="b" direction="in"/>
</method>
<method name="requestRemoteAircraftData">
</method>
</interface>
</node>)"

View File

@@ -0,0 +1,50 @@
R"(<node>
<interface name="org.swift_project.xswiftbus.weather">
<method name="isUsingRealWeather">
<arg type="b" direction="out"/>
</method>
<method name="setUseRealWeather">
<arg name="enable" type="b" direction="in"/>
</method>
<method name="setVisibility">
<arg name="visibilityM" type="d" direction="in"/>
</method>
<method name="setTemperature">
<arg name="degreesC" type="i" direction="in"/>
</method>
<method name="setDewPoint">
<arg name="degreesC" type="i" direction="in"/>
</method>
<method name="setQNH">
<arg name="inHg" type="d" direction="in"/>
</method>
<method name="setPrecipitationRatio">
<arg name="precipRatio" type="d" direction="in"/>
</method>
<method name="setThunderstormRatio">
<arg name="cbRatio" type="d" direction="in"/>
</method>
<method name="setTurbulenceRatio">
<arg name="turbulenceRatio" type="d" direction="in"/>
</method>
<method name="setRunwayFriction">
<arg name="friction" type="i" direction="in"/>
</method>
<method name="setCloudLayer">
<arg name="layer" type="i" direction="in"/>
<arg name="base" type="i" direction="in"/>
<arg name="tops" type="i" direction="in"/>
<arg name="type" type="i" direction="in"/>
<arg name="coverage" type="i" direction="in"/>
</method>
<method name="setWindLayer">
<arg name="layer" type="i" direction="in"/>
<arg name="altitude" type="i" direction="in"/>
<arg name="direction" type="d" direction="in"/>
<arg name="speed" type="i" direction="in"/>
<arg name="shearDirection" type="i" direction="in"/>
<arg name="shearSpeed" type="i" direction="in"/>
<arg name="turbulence" type="i" direction="in"/>
</method>
</interface>
</node>)"

View File

@@ -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 <QTimer>
#include <functional>
#include <thread>
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<CDBusConnection>()), 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<CPlugin *>(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;
}
}

View File

@@ -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 <QObject>
#include <QVector>
#include <memory>
#include <thread>
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<CDBusConnection> 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);
};
}

View File

@@ -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<std::string> icaos, names;
std::vector<double> 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<double> 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<std::string> &icaoCodes, const std::vector<std::string> &names,
const std::vector<double> &lats, const std::vector<double> &lons, const std::vector<double> &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);
}
}

View File

@@ -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<double> as a DBus argument
using QDoubleList = QList<double>;
//! Typedef needed to use QList<double> 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<std::string> &icaoCodes, const std::vector<std::string> &names,
const std::vector<double> &lats, const std::vector<double> &lons, const std::vector<double> &alts);
CMessageBoxControl m_messages { 128, 128, 16 };
BlackMisc::Simulation::XPlane::CNavDataReferenceList m_airports;
QTimer *m_airportUpdater = nullptr;

View File

@@ -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<quint16>(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<Plane *> 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 <typename T>
int memcmpPayload(T *dst, T *src)
@@ -304,6 +549,7 @@ namespace XSwiftBus
int CTraffic::getPlaneData(void *id, int dataType, void *io_data)
{
QHash<void *, Plane *> 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<CTraffic *>(refcon);
traffic->emitSimFrame();
return 1;
}
}
//! \endcond

View File

@@ -12,6 +12,7 @@
//! \file
#include "dbusobject.h"
#include "datarefs.h"
#include "terrainprobe.h"
#include <QDateTime>
@@ -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<QString, Plane *> m_planesByCallsign;
QHash<void *, Plane *> m_planesById;
qint64 m_timestampLastSimFrame = QDateTime::currentMSecsSinceEpoch();
@@ -154,8 +151,6 @@ namespace XSwiftBus
{
return static_cast<CTraffic *>(self)->getPlaneData(id, dataType, io_data);
}
static int drawCallback(XPLMDrawingPhase phase, int isBefore, void *refcon);
};
}

View File

@@ -14,6 +14,11 @@
namespace XSwiftBus
{
CWeather::CWeather(CDBusConnection *dbusConnection)
: CDBusObject(dbusConnection)
{
registerDBusObjectPath(InterfaceName(), ObjectPath());
}
//! Set cloud layer
template <class T>
@@ -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

View File

@@ -15,8 +15,8 @@
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "dbusobject.h"
#include "datarefs.h"
#include <QObject>
//! \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<xplane::data::sim::weather::use_real_weather_bool> m_useRealWeather;
DataRef<xplane::data::sim::weather::visibility_reported_m> m_visibilityM;

View File

@@ -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