diff --git a/CMakeLists.txt b/CMakeLists.txt index 91645cbf6..3bc05b110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,6 +202,7 @@ option(ENABLE_DEMCONVERT "Set to ON to build the dem conversion tool (default)" option(ENABLE_HID_INPUT "Set to ON to build HID-based input code" OFF) option(ENABLE_PLIB_JOYSTICK "Set to ON to enable legacy joystick code (default)" ON) option(ENABLE_COMPOSITOR "Set to ON to enable the Compositor-based Viewer" OFF) +option(ENABLE_SWIFT "Set to ON to build the swift module" OFF) # Test-suite options. @@ -263,6 +264,21 @@ if(EVENT_INPUT) endif() endif(EVENT_INPUT) +if (ENABLE_SWIFT) + message(STATUS "SWIFT support requested, checking for DBus/libEvent") + # DBUS + find_package(DBus REQUIRED) + list(APPEND PLATFORM_LIBS ${DBUS_LIBRARIES}) + + #libevent + find_package(LibEvent REQUIRED) + list(APPEND PLATFORM_LIBS ${LIBEVENT_LIB}) + + if (NOT DBUS_LIBRARIES OR NOT LIBEVENT_LIB) + message(FATAL_ERROR "SWIFT support requested, please ensure DBus/libEvent are available") + endif() +endif() + # check required dependencies find_package(Boost REQUIRED) find_package(OpenGL REQUIRED) @@ -497,7 +513,9 @@ include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIR} ${SIMGEAR_INCLUDE_DIRS} ${PLIB_INCLUDE_DIR} - ${SQLITE3_INCLUDED_DIR} ) + ${SQLITE3_INCLUDED_DIR} + ${DBUS_INCLUDE_DIRS} + ${LIBEVENT_INCLUDE_DIR} ) if (USE_AEONWAVE) find_package(AAX REQUIRED) diff --git a/CMakeModules/FindDBus.cmake b/CMakeModules/FindDBus.cmake new file mode 100644 index 000000000..1d0f29dd7 --- /dev/null +++ b/CMakeModules/FindDBus.cmake @@ -0,0 +1,59 @@ +# - Try to find DBus +# Once done, this will define +# +# DBUS_FOUND - system has DBus +# DBUS_INCLUDE_DIRS - the DBus include directories +# DBUS_LIBRARIES - link these to use DBus +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_DBUS QUIET dbus-1) + +FIND_LIBRARY(DBUS_LIBRARIES + NAMES dbus-1 + HINTS ${PC_DBUS_LIBDIR} + ${PC_DBUS_LIBRARY_DIRS} +) + +FIND_PATH(DBUS_INCLUDE_DIR + NAMES dbus/dbus.h + HINTS ${PC_DBUS_INCLUDEDIR} + ${PC_DBUS_INCLUDE_DIRS} +) + +GET_FILENAME_COMPONENT(_DBUS_LIBRARY_DIR ${DBUS_LIBRARIES} PATH) +FIND_PATH(DBUS_ARCH_INCLUDE_DIR + NAMES dbus/dbus-arch-deps.h + HINTS ${PC_DBUS_INCLUDEDIR} + ${PC_DBUS_INCLUDE_DIRS} + ${_DBUS_LIBRARY_DIR} + ${DBUS_INCLUDE_DIR} + PATH_SUFFIXES include +) + +SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES) \ No newline at end of file diff --git a/CMakeModules/FindLibEvent.cmake b/CMakeModules/FindLibEvent.cmake new file mode 100644 index 000000000..b776e13d6 --- /dev/null +++ b/CMakeModules/FindLibEvent.cmake @@ -0,0 +1,11 @@ +# Finding LibEvent (https://libevent.org/) +# Defining: +# LIBEVENT_LIB +# LIBEVENT_INCLUDE_DIR + +find_path(LIBEVENT_INCLUDE_DIR event.h PATHS ${LibEvent_INCLUDE_PATHS}) +find_library(LIBEVENT_LIB NAMES event PATHS ${LibEvent_LIB_PATHS}) + +if(LIBEVENT_INCLUDE_DIR AND LIBEVENT_LIB) +message(STATUS "LibEvent found.") +endif() \ No newline at end of file diff --git a/src/Include/config_cmake.h.in b/src/Include/config_cmake.h.in index d6b61941d..d3f8997a5 100644 --- a/src/Include/config_cmake.h.in +++ b/src/Include/config_cmake.h.in @@ -69,3 +69,5 @@ #cmakedefine HAVE_QRC_TRANSLATIONS #cmakedefine ENABLE_COMPOSITOR + +#cmakedefine ENABLE_SWIFT diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 9d1d65dda..6d4e32796 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -30,6 +30,7 @@ #include // strcmp() #if defined(SG_WINDOWS) +#define _WINSOCKAPI_ # include // isatty() # include // _getpid() # include @@ -117,6 +118,9 @@ #include #include +#if defined(ENABLE_SWIFT) +#include +#endif #include #include #include @@ -572,6 +576,12 @@ int fgInitConfig ( int argc, char **argv, bool reinit ) fgSetBool("/sim/developer-mode", developerMode); sglog().setDeveloperMode(developerMode); +#ifdef ENABLE_SWIFT + //Set standard settings for swift connection + fgSetString("/sim/swift/adress","127.0.0.1"); + fgSetString("/sim/swift/port","45003"); +#endif + // Read global defaults from $FG_ROOT/defaults SG_LOG(SG_GENERAL, SG_INFO, "Reading global defaults"); SGPath defaultsXML = globals->get_fg_root() / "defaults.xml"; @@ -937,6 +947,14 @@ void fgCreateSubsystems(bool duringReset) { globals->add_subsystem("mp", new FGMultiplayMgr, SGSubsystemMgr::POST_FDM); +#ifdef ENABLE_SWIFT + //////////////////////////////////////////////////////////////////// + // Initialize Swift subsystem + //////////////////////////////////////////////////////////////////// + + globals->add_subsystem("swift", new SwiftConnection, SGSubsystemMgr::POST_FDM); +#endif + //////////////////////////////////////////////////////////////////// // Initialise the AI Model Manager //////////////////////////////////////////////////////////////////// diff --git a/src/Main/subsystemFactory.cxx b/src/Main/subsystemFactory.cxx index 05fce574d..f0f8a20f8 100644 --- a/src/Main/subsystemFactory.cxx +++ b/src/Main/subsystemFactory.cxx @@ -54,6 +54,9 @@ #include #include #include +#if defined(ENABLE_SWIFT) +# include +#endif #include #include #include @@ -107,6 +110,9 @@ SGSubsystem* createSubsystemByName(const std::string& name) #endif #ifdef ENABLE_IAX MAKE_SUB(FGCom, "fgcom"); +#endif +#ifdef ENABLE_SWIFT + MAKE_SUB(SwiftConnection, "swift"); #endif MAKE_SUB(FGLight, "lighting"); MAKE_SUB(FGAircraftModel, "aircraft-model"); diff --git a/src/Network/CMakeLists.txt b/src/Network/CMakeLists.txt index 7a9288301..f094cb81f 100644 --- a/src/Network/CMakeLists.txt +++ b/src/Network/CMakeLists.txt @@ -73,3 +73,6 @@ endif() add_subdirectory(http) +if(ENABLE_SWIFT) + add_subdirectory(Swift) +endif() \ No newline at end of file diff --git a/src/Network/Swift/CMakeLists.txt b/src/Network/Swift/CMakeLists.txt new file mode 100644 index 000000000..6765bc784 --- /dev/null +++ b/src/Network/Swift/CMakeLists.txt @@ -0,0 +1,34 @@ +include(FlightGearComponent) + +set(SOURCES + swift_connection.cxx + dbusconnection.cpp + dbusobject.cpp + dbusmessage.cpp + dbusdispatcher.cpp + dbuserror.cpp + dbusserver.cpp + plugin.cpp + service.cpp + traffic.cpp + swiftaircraft.cpp + SwiftAircraftManager.cpp + ) + +set(HEADERS + swift_connection.hxx + dbusconnection.h + dbusobject.h + dbusmessage.h + dbuscallbacks.h + dbusdispatcher.h + dbuserror.h + dbusserver.h + plugin.h + service.h + traffic.h + swiftaircraft.h + SwiftAircraftManager.h + ) + +flightgear_component(Swift "${SOURCES}" "${HEADERS}") diff --git a/src/Network/Swift/SwiftAircraft.cpp b/src/Network/Swift/SwiftAircraft.cpp new file mode 100644 index 000000000..f4bf52140 --- /dev/null +++ b/src/Network/Swift/SwiftAircraft.cpp @@ -0,0 +1,102 @@ +// SwiftAircraft.cpp - Class representing an aircraft generated by swift +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "SwiftAircraft.h" +#include
+#include
+#include +#include +#include +#include + +FGSwiftAircraft::FGSwiftAircraft(std::string callsign, std::string modelpath) +{ + using namespace simgear; + _model = SGModelLib::loadModel(modelpath); + _model->setName(callsign); + if (_model.valid()) { + aip.init(_model); + aip.setVisible(true); + aip.update(); + globals->get_scenery()->get_models_branch()->addChild(aip.getSceneGraph()); + } +} + +bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation) +{ + + position = newPosition; + aip.setPosition(position); + aip.setPitchDeg(orientation.x()); + aip.setRollDeg(orientation.y()); + aip.setHeadingDeg(orientation.z()); + aip.update(); + return true; +} + + +FGSwiftAircraft::~FGSwiftAircraft() +{ + aip.setVisible(false); +} + +double FGSwiftAircraft::getLatDeg() +{ + return position.getLatitudeDeg(); +} + +double FGSwiftAircraft::getLongDeg() +{ + return position.getLongitudeDeg(); +} + +double FGSwiftAircraft::getFudgeFactor() +{ + return 0; +} + +inline bool FGSwiftAircraft::operator<(std::string extCallsign) +{ + return _model->getName().compare(extCallsign); +} + +double FGSwiftAircraft::getGroundElevation() +{ + double alt; + globals->get_scenery()->get_elevation_m(position,alt,0); + return alt; + +} \ No newline at end of file diff --git a/src/Network/Swift/SwiftAircraft.h b/src/Network/Swift/SwiftAircraft.h new file mode 100644 index 000000000..62949236e --- /dev/null +++ b/src/Network/Swift/SwiftAircraft.h @@ -0,0 +1,59 @@ +// SwiftAircraft.h - Class representing an aircraft generated by swift +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef FGSWIFTAIRCRAFT_H +#define FGSWIFTAIRCRAFT_H +#pragma once +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace osg { +class PagedLOD; +} + + +class FGSwiftAircraft +{ +public: + FGSwiftAircraft(std::string callsign, std::string modelpath); + bool updatePosition(SGGeod newPosition, SGVec3d orientation); + ~FGSwiftAircraft(); + std::string getName() { return _model->getName(); }; + double getLatDeg(); + double getLongDeg(); + double getGroundElevation(); + double getFudgeFactor(); + +private: + SGGeod position; + osg::ref_ptr _model; + SGModelPlacement aip; + inline bool operator<(std::string extCallsign); +}; +#endif \ No newline at end of file diff --git a/src/Network/Swift/SwiftAircraftManager.cpp b/src/Network/Swift/SwiftAircraftManager.cpp new file mode 100644 index 000000000..df8beac97 --- /dev/null +++ b/src/Network/Swift/SwiftAircraftManager.cpp @@ -0,0 +1,94 @@ +// SwiftAircraftManager.cpp - Manager class for aircrafts generated by swift +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "SwiftAircraftManager.h" +#include "SwiftAircraft.h" + +FGSwiftAircraftManager::FGSwiftAircraftManager() +{ +} + +FGSwiftAircraftManager::~FGSwiftAircraftManager() +{ +} + +bool FGSwiftAircraftManager::addPlane(std::string callsign, std::string modelString) +{ + if (aircraftByCallsign.find(callsign) != aircraftByCallsign.end()) + return false; + + FGSwiftAircraft* curAircraft = new FGSwiftAircraft(callsign, modelString); + aircraftByCallsign.insert(std::pair(callsign, curAircraft)); + return true; +} + +void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector onGrounds) +{ + for (int i = 0; i < callsigns.size(); i++) { + auto it = aircraftByCallsign.find(callsigns.at(i)); + if (it != aircraftByCallsign.end()) { + it->second->updatePosition(positions.at(i), orientations.at(i)); + } + } + +} + +void FGSwiftAircraftManager::getRemoteAircraftData(std::vector& callsigns, std::vector& latitudesDeg, std::vector& longitudesDeg, std::vector& elevationsM, std::vector& verticalOffsets) const +{ + const auto requestedCallsigns = callsigns; + callsigns.clear(); + latitudesDeg.clear(); + longitudesDeg.clear(); + elevationsM.clear(); + verticalOffsets.clear(); + + for (int i = 0; i < requestedCallsigns.size(); i++) { + auto it = aircraftByCallsign.find(requestedCallsigns.at(i)); + if (it != aircraftByCallsign.end()) { + double latDeg = it->second->getLatDeg(); + double lonDeg = it->second->getLongDeg(); + double groundElevation = it->second->getGroundElevation(); + double fudgeFactor = it->second->getFudgeFactor(); + + callsigns.push_back(requestedCallsigns.at(i)); + latitudesDeg.push_back(latDeg); + longitudesDeg.push_back(lonDeg); + elevationsM.push_back(groundElevation); + verticalOffsets.push_back(0); + } + } + +} + +void FGSwiftAircraftManager::removePlane(std::string callsign) +{ + auto it = aircraftByCallsign.find(callsign); + if (it != aircraftByCallsign.end()) { + delete it->second; + aircraftByCallsign.erase(it); + } +} + +void FGSwiftAircraftManager::removeAllPlanes() +{ + for (auto it = aircraftByCallsign.begin(); it != aircraftByCallsign.end(); ++it) { + delete it->second; + aircraftByCallsign.erase(it); + } +} diff --git a/src/Network/Swift/SwiftAircraftManager.h b/src/Network/Swift/SwiftAircraftManager.h new file mode 100644 index 000000000..fbdbb4239 --- /dev/null +++ b/src/Network/Swift/SwiftAircraftManager.h @@ -0,0 +1,39 @@ +// SwiftAircraftManager.h - Manger class for aircrafts generated for swift +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "SwiftAircraft.h" +#include + +#ifndef FGSWIFTAIRCRAFTMANAGER_H +#define FGSWIFTAIRCRAFTMANAGER_H + +class FGSwiftAircraftManager +{ +public: + FGSwiftAircraftManager(); + ~FGSwiftAircraftManager(); + std::map aircraftByCallsign; + bool addPlane(std::string callsign, std::string modelString); + void updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector onGrounds); + void getRemoteAircraftData(std::vector& callsigns, std::vector& latitudesDeg, std::vector& longitudesDeg, + std::vector& elevationsM, std::vector& verticalOffsets) const; + void removePlane(std::string callsign); + void removeAllPlanes(); +}; +#endif \ No newline at end of file diff --git a/src/Network/Swift/dbuscallbacks.h b/src/Network/Swift/dbuscallbacks.h new file mode 100644 index 000000000..5d3f1de6b --- /dev/null +++ b/src/Network/Swift/dbuscallbacks.h @@ -0,0 +1,64 @@ +// dbuscallbacks.h +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_DBUSASYNCCALLBACKS_H +#define BLACKSIM_FGSWIFTBUS_DBUSASYNCCALLBACKS_H + +#include +#include + +namespace FGSwiftBus +{ + //! \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/Network/Swift/dbusconnection.cpp b/src/Network/Swift/dbusconnection.cpp new file mode 100644 index 000000000..91fbd1062 --- /dev/null +++ b/src/Network/Swift/dbusconnection.cpp @@ -0,0 +1,195 @@ +#include + +// dbusconnection.cpp +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "dbusconnection.h" +#include "dbusobject.h" + +#include +#include +#include + +namespace FGSwiftBus +{ + + CDBusConnection::CDBusConnection() + { + dbus_threads_init_default(); + } + + CDBusConnection::CDBusConnection(DBusConnection *connection) + { + m_connection.reset(connection); + dbus_connection_ref(connection); + // Don't exit application, if the connection is disconnected + dbus_connection_set_exit_on_disconnect(connection, false); + dbus_connection_add_filter(connection, filterDisconnectedFunction, this, nullptr); + } + + CDBusConnection::~CDBusConnection() + { + close(); + if (m_connection) { dispatch(); } // dispatch is virtual, but safe to call in dtor, as it's declared final + if (m_dispatcher) { m_dispatcher->remove(this); } + } + + bool CDBusConnection::connect(BusType type) + { + 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); + return true; + } + + void CDBusConnection::setDispatcher(CDBusDispatcher *dispatcher) + { + assert(dispatcher); + + m_dispatcher = dispatcher; + + m_dispatcher->add(this); + + dbus_connection_set_watch_functions( + m_connection.get(), + dispatcher->m_watchCallbacks.add, + dispatcher->m_watchCallbacks.remove, + dispatcher->m_watchCallbacks.toggled, + &dispatcher->m_watchCallbacks, nullptr); + + dbus_connection_set_timeout_functions( + m_connection.get(), + dispatcher->m_timeoutCallbacks.add, + dispatcher->m_timeoutCallbacks.remove, + dispatcher->m_timeoutCallbacks.toggled, + &dispatcher->m_timeoutCallbacks, nullptr); + } + + void CDBusConnection::requestName(const std::string &name) + { + DBusError error; + dbus_error_init(&error); + dbus_bus_request_name(m_connection.get(), name.c_str(), 0, &error); + } + + bool CDBusConnection::isConnected() const + { + return m_connection && dbus_connection_get_is_connected(m_connection.get()); + } + + void CDBusConnection::registerDisconnectedCallback(CDBusObject *obj, DisconnectedCallback func) + { + m_disconnectedCallbacks[obj] = func; + } + + void CDBusConnection::unregisterDisconnectedCallback(CDBusObject *obj) + { + auto it = m_disconnectedCallbacks.find(obj); + if (it == m_disconnectedCallbacks.end()) { return; } + m_disconnectedCallbacks.erase(it); + } + + 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::dispatch() + { + dbus_connection_ref(m_connection.get()); + if (dbus_connection_get_dispatch_status(m_connection.get()) == DBUS_DISPATCH_DATA_REMAINS) + { + while (dbus_connection_dispatch(m_connection.get()) == DBUS_DISPATCH_DATA_REMAINS); + } + dbus_connection_unref(m_connection.get()); + } + + void CDBusConnection::setDispatchStatus(DBusConnection *connection, DBusDispatchStatus status) + { + if (dbus_connection_get_is_connected(connection) == FALSE) { return; } + + switch (status) + { + case DBUS_DISPATCH_DATA_REMAINS: + //m_dispatcher->add(this); + break; + case DBUS_DISPATCH_COMPLETE: + case DBUS_DISPATCH_NEED_MEMORY: + break; + } + } + + void CDBusConnection::setDispatchStatus(DBusConnection *connection, DBusDispatchStatus status, void *data) + { + auto *obj = static_cast(data); + obj->setDispatchStatus(connection, status); + } + + DBusHandlerResult CDBusConnection::filterDisconnectedFunction(DBusConnection *connection, DBusMessage *message, void *data) + { + (void)connection; // unused + + auto *obj = static_cast(data); + + DBusError err; + dbus_error_init(&err); + + if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")) + { + for (auto it = obj->m_disconnectedCallbacks.begin(); it != obj->m_disconnectedCallbacks.end(); ++it) + { + it->second(); + } + return DBUS_HANDLER_RESULT_HANDLED; + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + +} diff --git a/src/Network/Swift/dbusconnection.h b/src/Network/Swift/dbusconnection.h new file mode 100644 index 000000000..fdf737b23 --- /dev/null +++ b/src/Network/Swift/dbusconnection.h @@ -0,0 +1,119 @@ +// dbusconnection.h +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_DBUSCONNECTION_H +#define BLACKSIM_FGSWIFTBUS_DBUSCONNECTION_H + +#include "dbusmessage.h" +#include "dbuserror.h" +#include "dbuscallbacks.h" +#include "dbusdispatcher.h" + +#include +#include +#include +#include +#include + +namespace FGSwiftBus +{ + + class CDBusObject; + + //! DBus connection + class CDBusConnection : public IDispatchable + { + public: + //! Bus type + enum BusType { SessionBus }; + + //! Disconnect Callback + using DisconnectedCallback = std::function; + + //! Default constructor + CDBusConnection(); + + //! Constructor + CDBusConnection(DBusConnection *connection); + + //! Destructor + ~CDBusConnection() override; + + // 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); + + //! Set dispatcher + void setDispatcher(CDBusDispatcher *dispatcher); + + //! Request name to the bus + void requestName(const std::string &name); + + //! Is connected? + bool isConnected() const; + + //! Register a disconnected callback + void registerDisconnectedCallback(CDBusObject *obj, DisconnectedCallback func); + + //! Register a disconnected callback + void unregisterDisconnectedCallback(CDBusObject *obj); + + //! 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(); + + //! Get the last error + CDBusError lastError() const { return m_lastError; } + + protected: + // cppcheck-suppress virtualCallInConstructor + virtual void dispatch() override final; + + private: + void setDispatchStatus(DBusConnection *connection, DBusDispatchStatus status); + static void setDispatchStatus(DBusConnection *connection, DBusDispatchStatus status, void *data); + static DBusHandlerResult filterDisconnectedFunction(DBusConnection *connection, DBusMessage *message, void *data); + + struct DBusConnectionDeleter + { + void operator()(DBusConnection *obj) const { dbus_connection_unref(obj); } + }; + + CDBusDispatcher *m_dispatcher = nullptr; + std::unique_ptr m_connection; + CDBusError m_lastError; + std::unordered_map m_disconnectedCallbacks; + }; + +} + +#endif // guard diff --git a/src/Network/Swift/dbusdispatcher.cpp b/src/Network/Swift/dbusdispatcher.cpp new file mode 100644 index 000000000..b28df5497 --- /dev/null +++ b/src/Network/Swift/dbusdispatcher.cpp @@ -0,0 +1,245 @@ +// dbus-dispatcher.cpp +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "dbusdispatcher.h" +#include "dbusconnection.h" +#include + +namespace FGSwiftBus +{ + + //! 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(event_base *base, DBusWatch *watch) + : m_base(base), 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(m_base, 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) + { + (void) fd; // Not really unused, but GCC/Clang still complain about it. + auto *watchHandler = static_cast(data); + + unsigned int flags = 0; + if (event & EV_READ) { flags |= DBUS_WATCH_READABLE; } + if (event & EV_WRITE) { flags |= DBUS_WATCH_WRITABLE; } + dbus_watch_handle(watchHandler->m_watch, flags); + } + + event_base *m_base = nullptr; + std::unique_ptr m_event; + DBusWatch *m_watch = nullptr; + }; + + //! DBus timeout handler + class TimeoutHandler + { + public: + //! Constructor + TimeoutHandler(event_base *base, DBusTimeout *timeout) + : m_base(base), 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(m_base, 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); + } + + event_base *m_base = nullptr; + std::unique_ptr m_event; + DBusTimeout *m_timeout = nullptr; + }; + + //! Generic Timer + class Timer + { + public: + Timer() = default; + //! Constructor + Timer(event_base *base, const timeval &timeout, const std::function &func) + : m_base(base), m_func(func) + { + m_event.reset(evtimer_new(m_base, 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; + } + + event_base *m_base = nullptr; + std::unique_ptr m_event; + std::function m_func; + }; + + CDBusDispatcher::CDBusDispatcher() : + m_eventBase(event_base_new()) + { + using namespace std::placeholders; + m_watchCallbacks = WatchCallbacks(std::bind(&CDBusDispatcher::dbusAddWatch, this, _1), + std::bind(&CDBusDispatcher::dbusRemoveWatch, this, _1), + std::bind(&CDBusDispatcher::dbusWatchToggled, this, _1)); + + m_timeoutCallbacks = TimeoutCallbacks(std::bind(&CDBusDispatcher::dbusAddTimeout, this, _1), + std::bind(&CDBusDispatcher::dbusRemoveTimeout, this, _1), + std::bind(&CDBusDispatcher::dbusTimeoutToggled, this, _1)); + } + + CDBusDispatcher::~CDBusDispatcher() + { + } + + void CDBusDispatcher::add(IDispatchable *dispatchable) + { + m_dispatchList.push_back(dispatchable); + } + + void CDBusDispatcher::remove(IDispatchable *dispatchable) + { + auto it = std::find(m_dispatchList.begin(), m_dispatchList.end(), dispatchable); + if (it != m_dispatchList.end()) { m_dispatchList.erase(it); } + } + + void CDBusDispatcher::waitAndRun() + { + if (!m_eventBase) { return; } + event_base_dispatch(m_eventBase.get()); + } + + void CDBusDispatcher::runOnce() + { + if (!m_eventBase) { return; } + event_base_loop(m_eventBase.get(), EVLOOP_NONBLOCK); + dispatch(); + } + + void CDBusDispatcher::dispatch() + { + if (m_dispatchList.empty()) { return; } + + for (IDispatchable *dispatchable : m_dispatchList) + { + dispatchable->dispatch(); + } + } + + dbus_bool_t CDBusDispatcher::dbusAddWatch(DBusWatch *watch) + { + if (dbus_watch_get_enabled(watch) == FALSE) { return true; } + + int fd = dbus_watch_get_unix_fd(watch); + m_watchers.emplace(fd, new WatchHandler(m_eventBase.get(), watch)); + return true; + } + + void CDBusDispatcher::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 CDBusDispatcher::dbusWatchToggled(DBusWatch *watch) + { + if (dbus_watch_get_enabled(watch) == TRUE) { dbusAddWatch(watch); } + else { dbusRemoveWatch(watch); } + } + + dbus_bool_t CDBusDispatcher::dbusAddTimeout(DBusTimeout *timeout) + { + if (dbus_timeout_get_enabled(timeout) == FALSE) { return TRUE; } + m_timeouts.emplace_back(new TimeoutHandler(m_eventBase.get(), timeout)); + return true; + } + + void CDBusDispatcher::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 CDBusDispatcher::dbusTimeoutToggled(DBusTimeout *timeout) + { + if (dbus_timeout_get_enabled(timeout) == TRUE) + dbusAddTimeout(timeout); + else + dbusRemoveTimeout(timeout); + } + +} diff --git a/src/Network/Swift/dbusdispatcher.h b/src/Network/Swift/dbusdispatcher.h new file mode 100644 index 000000000..4412f0ad5 --- /dev/null +++ b/src/Network/Swift/dbusdispatcher.h @@ -0,0 +1,114 @@ +// dbusdispatcher.h +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_DBUSDISPATCHER_H +#define BLACKSIM_FGSWIFTBUS_DBUSDISPATCHER_H + +#include "dbuscallbacks.h" + +#include +#include + +#include +#include +#include + +namespace FGSwiftBus +{ + + class WatchHandler; + class TimeoutHandler; + class CDBusConnection; + class CDBusDispatcher; + + //! Dispatchable Interface + class IDispatchable + { + public: + //! Default constructor + IDispatchable() = default; + + //! Default destructor + virtual ~IDispatchable() = default; + + //! Dispatch execution method + virtual void dispatch() = 0; + + private: + friend CDBusDispatcher; + }; + + //! DBus Dispatcher + class CDBusDispatcher + { + public: + //! Constructor + CDBusDispatcher(); + + //! Destructor + virtual ~CDBusDispatcher(); + + //! Add dispatchable object + void add(IDispatchable *dispatchable); + + //! Remove dispatchable object + void remove(IDispatchable *dispatchable); + + //! Waits for events to be dispatched and handles them + void waitAndRun(); + + //! Dispatches ready handlers and returns without waiting + void runOnce(); + + private: + friend class WatchHandler; + friend class TimeoutHandler; + friend class Timer; + friend class CDBusConnection; + friend class CDBusServer; + + struct EventBaseDeleter + { + void operator()(event_base *obj) const { event_base_free(obj); } + }; + + using WatchCallbacks = DBusAsyncCallbacks; + using TimeoutCallbacks = DBusAsyncCallbacks; + + void dispatch(); + + 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); + + WatchCallbacks m_watchCallbacks; + TimeoutCallbacks m_timeoutCallbacks; + std::unordered_multimap> m_watchers; + std::vector> m_timeouts; + std::unique_ptr m_eventBase; + + std::vector m_dispatchList; + }; +} + +#endif diff --git a/src/Network/Swift/dbuserror.cpp b/src/Network/Swift/dbuserror.cpp new file mode 100644 index 000000000..3a1aabd92 --- /dev/null +++ b/src/Network/Swift/dbuserror.cpp @@ -0,0 +1,29 @@ +// dbuserror.cpp +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "dbuserror.h" + +namespace FGSwiftBus +{ + + CDBusError::CDBusError(const DBusError *error) + : m_name(error->name), m_message(error->message) + { } + +} diff --git a/src/Network/Swift/dbuserror.h b/src/Network/Swift/dbuserror.h new file mode 100644 index 000000000..044bf703e --- /dev/null +++ b/src/Network/Swift/dbuserror.h @@ -0,0 +1,57 @@ +// dbuserror.h +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_DBUSERROR_H +#define BLACKSIM_FGSWIFTBUS_DBUSERROR_H + +#include +#include + +namespace FGSwiftBus +{ + + //! 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/Network/Swift/dbusmessage.cpp b/src/Network/Swift/dbusmessage.cpp new file mode 100644 index 000000000..3a87c93c5 --- /dev/null +++ b/src/Network/Swift/dbusmessage.cpp @@ -0,0 +1,263 @@ +// dbusmessage.cpp +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "dbusmessage.h" + +namespace FGSwiftBus +{ + + 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 = dbus_message_get_sender(m_message); + return sender ? std::string(sender) : std::string(); + } + + 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, static_cast(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); + if (v == TRUE) { value = true; } + else { value = 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); + } + + void CDBusMessage::getArgument(std::vector &value) + { + DBusMessageIter arrayIterator; + dbus_message_iter_recurse(&m_messageIterator, &arrayIterator); + do + { + if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_INT32) { return; } + dbus_int32_t i; + dbus_message_iter_get_basic(&arrayIterator, &i); + value.push_back(i); + } + while (dbus_message_iter_next(&arrayIterator)); + dbus_message_iter_next(&m_messageIterator); + } + + void CDBusMessage::getArgument(std::vector &value) + { + if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_ARRAY) { return; } + DBusMessageIter arrayIterator; + dbus_message_iter_recurse(&m_messageIterator, &arrayIterator); + do + { + if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_BOOLEAN) { return; } + dbus_bool_t b; + dbus_message_iter_get_basic(&arrayIterator, &b); + if (b == TRUE) { value.push_back(true); } + else { value.push_back(false); } + } + while (dbus_message_iter_next(&arrayIterator)); + dbus_message_iter_next(&m_messageIterator); + } + + void CDBusMessage::getArgument(std::vector &value) + { + DBusMessageIter arrayIterator; + dbus_message_iter_recurse(&m_messageIterator, &arrayIterator); + do + { + if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DOUBLE) { return; } + double d; + dbus_message_iter_get_basic(&arrayIterator, &d); + value.push_back(d); + } + while (dbus_message_iter_next(&arrayIterator)); + dbus_message_iter_next(&m_messageIterator); + } + + void CDBusMessage::getArgument(std::vector &value) + { + DBusMessageIter arrayIterator; + dbus_message_iter_recurse(&m_messageIterator, &arrayIterator); + do + { + if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_STRING) { return; } + const char *str = nullptr; + dbus_message_iter_get_basic(&arrayIterator, &str); + value.push_back(std::string(str)); + } + while (dbus_message_iter_next(&arrayIterator)); + dbus_message_iter_next(&m_messageIterator); + } + + 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); + if (! destination.empty()) { 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/Network/Swift/dbusmessage.h b/src/Network/Swift/dbusmessage.h new file mode 100644 index 000000000..097aa07c4 --- /dev/null +++ b/src/Network/Swift/dbusmessage.h @@ -0,0 +1,113 @@ +// dbusmessage.h +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_DBUSMESSAGE_H +#define BLACKSIM_FGSWIFTBUS_DBUSMESSAGE_H + +#include "dbus/dbus.h" +#include +#include + +namespace FGSwiftBus +{ + + //! 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); + void getArgument(std::vector &value); + void getArgument(std::vector &value); + void getArgument(std::vector &value); + void getArgument(std::vector &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/Network/Swift/dbusobject.cpp b/src/Network/Swift/dbusobject.cpp new file mode 100644 index 000000000..880ed2b17 --- /dev/null +++ b/src/Network/Swift/dbusobject.cpp @@ -0,0 +1,106 @@ +// dbusobject.cpp +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "dbusobject.h" +#include + +namespace FGSwiftBus +{ + CDBusObject::CDBusObject() + { } + + CDBusObject::~CDBusObject() + { + if (m_dbusConnection) { m_dbusConnection->unregisterDisconnectedCallback(this); } + }; + + void CDBusObject::setDBusConnection(const std::shared_ptr &dbusConnection) + { + m_dbusConnection = dbusConnection; + dbusConnectedHandler(); + CDBusConnection::DisconnectedCallback disconnectedHandler = std::bind(&CDBusObject::dbusDisconnectedHandler, this); + m_dbusConnection->registerDisconnectedCallback(this, disconnectedHandler); + } + + void CDBusObject::registerDBusObjectPath(const std::string &interfaceName, const std::string &objectPath) + { + assert(m_dbusConnection); + m_interfaceName = interfaceName; + m_objectPath = objectPath; + m_dbusConnection->registerObjectPath(this, interfaceName, objectPath, m_dbusObjectPathVTable); + } + + void CDBusObject::sendDBusSignal(const std::string &name) + { + if (! m_dbusConnection) { return; } + CDBusMessage signal = CDBusMessage::createSignal(m_objectPath, m_interfaceName, name); + m_dbusConnection->sendMessage(signal); + } + + void CDBusObject::sendDBusMessage(const CDBusMessage &message) + { + if (! m_dbusConnection) { return; } + 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/Network/Swift/dbusobject.h b/src/Network/Swift/dbusobject.h new file mode 100644 index 000000000..4145533dd --- /dev/null +++ b/src/Network/Swift/dbusobject.h @@ -0,0 +1,108 @@ +// dbusobject.h +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_DBUSOBJECT_H +#define BLACKSIM_FGSWIFTBUS_DBUSOBJECT_H + +#include "dbusconnection.h" +#include +#include + +namespace FGSwiftBus +{ + //! DBus base object + class CDBusObject + { + public: + //! Constructor + CDBusObject(); + + //! Destructor + virtual ~CDBusObject(); + + //! Set the assigned DBus connection. + //! \remark Currently one object can only manage one connection at a time + void setDBusConnection(const std::shared_ptr &dbusConnection); + + //! Register itself with interfaceName and objectPath + //! \warning Before calling this method, make sure that a valid DBus connection was set. + void registerDBusObjectPath(const std::string &interfaceName, const std::string &objectPath); + + protected: + //! Handler which is called when DBusCconnection is established + virtual void dbusConnectedHandler() {} + + //! DBus message handler + virtual DBusHandlerResult dbusMessageHandler(const CDBusMessage &message) = 0; + + //! Handler which is called when DBusConnection disconnected + virtual void dbusDisconnectedHandler() {} + + //! 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); + + std::shared_ptr 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/Network/Swift/dbusserver.cpp b/src/Network/Swift/dbusserver.cpp new file mode 100644 index 000000000..217a7ef4c --- /dev/null +++ b/src/Network/Swift/dbusserver.cpp @@ -0,0 +1,101 @@ +#include + +// dbusserver.cpp +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "dbusserver.h" +#include "dbusobject.h" + +#include +#include +#include + +namespace FGSwiftBus +{ + + CDBusServer::CDBusServer() + { + dbus_threads_init_default(); + } + + CDBusServer::~CDBusServer() + { + close(); + } + + bool CDBusServer::listen(const std::string &address) + { + DBusError error; + dbus_error_init(&error); + m_server.reset(dbus_server_listen(address.c_str(), &error)); + + if (! m_server) + { + return false; + } + + dbus_server_set_new_connection_function(m_server.get(), onNewConnection, this, nullptr); + return true; + } + + bool CDBusServer::isConnected() const + { + return m_server ? dbus_server_get_is_connected(m_server.get()) : false; + } + + void CDBusServer::close() + { + if (m_server) { dbus_server_disconnect(m_server.get()); } + } + + void CDBusServer::setDispatcher(CDBusDispatcher *dispatcher) + { + assert(dispatcher); + assert(m_server); + + m_dispatcher = dispatcher; + + dbus_server_set_watch_functions( + m_server.get(), + dispatcher->m_watchCallbacks.add, + dispatcher->m_watchCallbacks.remove, + dispatcher->m_watchCallbacks.toggled, + &dispatcher->m_watchCallbacks, nullptr); + + dbus_server_set_timeout_functions( + m_server.get(), + dispatcher->m_timeoutCallbacks.add, + dispatcher->m_timeoutCallbacks.remove, + dispatcher->m_timeoutCallbacks.toggled, + &dispatcher->m_timeoutCallbacks, nullptr); + } + + void CDBusServer::onNewConnection(DBusServer *, DBusConnection *conn) + { + auto dbusConnection = std::make_shared(conn); + m_newConnectionFunc(dbusConnection); + } + + void CDBusServer::onNewConnection(DBusServer *server, DBusConnection *conn, void *data) + { + auto *obj = static_cast(data); + obj->onNewConnection(server, conn); + } + +} diff --git a/src/Network/Swift/dbusserver.h b/src/Network/Swift/dbusserver.h new file mode 100644 index 000000000..cfe9d9d3f --- /dev/null +++ b/src/Network/Swift/dbusserver.h @@ -0,0 +1,94 @@ +// dbusserver.h +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_DBUSSERVER_H +#define BLACKSIM_FGSWIFTBUS_DBUSSERVER_H + +#include "dbusmessage.h" +#include "dbuserror.h" +#include "dbuscallbacks.h" +#include "dbusdispatcher.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace FGSwiftBus +{ + + class CDBusObject; + + //! DBus connection + class CDBusServer : public IDispatchable + { + public: + //! New connection handler function + using NewConnectionFunc = std::function)>; + + //! Constructor + CDBusServer(); + + //! Destructor + ~CDBusServer(); + + //! Set the dispatcher + void setDispatcher(CDBusDispatcher *dispatcher); + + //! Connect to bus + bool listen(const std::string &address); + + //! Is connected? + bool isConnected() const; + + void dispatch() override {} + + //! Close connection + void close(); + + //! Get the last error + CDBusError lastError() const { return m_lastError; } + + //! Set the function to be used for handling new connections. + void setNewConnectionFunc(const NewConnectionFunc &func) + { + m_newConnectionFunc = func; + } + + private: + void onNewConnection(DBusServer *server, DBusConnection *conn); + static void onNewConnection(DBusServer *server, DBusConnection *conn, void *data); + + struct DBusServerDeleter + { + void operator()(DBusServer *obj) const { dbus_server_unref(obj); } + }; + + CDBusDispatcher *m_dispatcher = nullptr; + std::unique_ptr m_server; + CDBusError m_lastError; + NewConnectionFunc m_newConnectionFunc; + }; + +} + +#endif // guard diff --git a/src/Network/Swift/plugin.cpp b/src/Network/Swift/plugin.cpp new file mode 100644 index 000000000..e5eebd53e --- /dev/null +++ b/src/Network/Swift/plugin.cpp @@ -0,0 +1,97 @@ +// plugin.cpp +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "plugin.h" +#include "service.h" +#include "traffic.h" +#include +#include +#include
+#include
+#include +#include +#include +#include + +namespace { +inline std::string fgswiftbusServiceName() +{ + return std::string("org.swift-project.fgswiftbus"); +} +} // namespace + +namespace FGSwiftBus { +CPlugin::CPlugin() +{ + startServer(); +} + +CPlugin::~CPlugin() +{ + m_dbusConnection->close(); + m_shouldStop = true; + if (m_dbusThread.joinable()) { m_dbusThread.join(); } +} + +void CPlugin::startServer() +{ + + m_service.reset(new CService()); + m_traffic.reset(new CTraffic()); + m_dbusP2PServer.reset(new CDBusServer()); + + std::string ip = fgGetString("/sim/swift/adress"); + std::string port = fgGetString("/sim/swift/port"); + std::string listenAddress = "tcp:host=" + ip + ",port=" + port; + if (! m_dbusP2PServer->listen(listenAddress)) + { + m_service->addTextMessage("XSwiftBus startup failed!"); + return; + } + m_dbusP2PServer->setDispatcher(&m_dbusDispatcher); + m_dbusP2PServer->setNewConnectionFunc([this](const std::shared_ptr& conn) { + m_dbusConnection = conn; + m_dbusConnection->setDispatcher(&m_dbusDispatcher); + m_service->setDBusConnection(m_dbusConnection); + m_service->registerDBusObjectPath(m_service->InterfaceName(), m_service->ObjectPath()); + m_traffic->setDBusConnection(m_dbusConnection); + m_traffic->registerDBusObjectPath(m_traffic->InterfaceName(), m_traffic->ObjectPath()); + }); + + SG_LOG(SG_NETWORK,SG_INFO,"FGSwiftBus started"); +} + +float CPlugin::startServerDeferred(float, float, int, void* refcon) +{ + auto* plugin = static_cast(refcon); + if (!plugin->m_isRunning) { + plugin->startServer(); + plugin->m_isRunning = true; + } + return 0; +} + +void CPlugin::fastLoop() +{ + this->m_dbusDispatcher.runOnce(); + this->m_service->process(); + this->m_traffic->process(); + this->m_traffic->emitSimFrame(); +} +} // namespace FGSwiftBus diff --git a/src/Network/Swift/plugin.h b/src/Network/Swift/plugin.h new file mode 100644 index 000000000..5a8574c8a --- /dev/null +++ b/src/Network/Swift/plugin.h @@ -0,0 +1,74 @@ +// plugin.h +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_PLUGIN_H +#define BLACKSIM_FGSWIFTBUS_PLUGIN_H + +//! \file + +/*! + * \namespace FGSwiftBus + * Plugin loaded by Flightgear which publishes a DBus service + */ + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "dbusconnection.h" +#include "dbusdispatcher.h" +#include "dbusserver.h" +#include "config.h" +#include +#include + +namespace FGSwiftBus +{ + class CService; + class CTraffic; + class CWeather; + + /*! + * Main plugin class + */ + class CPlugin + { + public: + //! Constructor + CPlugin(); + void startServer(); + //! Destructor + ~CPlugin(); + static float startServerDeferred(float, float, int, void* refcon); + void fastLoop(); + + private: + CDBusDispatcher m_dbusDispatcher; + std::unique_ptr m_dbusP2PServer; + std::shared_ptr m_dbusConnection; + std::unique_ptr m_service; + std::unique_ptr m_traffic; + + std::thread m_dbusThread; + bool m_isRunning = false; + bool m_shouldStop = false; + }; +} + +#endif // guard diff --git a/src/Network/Swift/service.cpp b/src/Network/Swift/service.cpp new file mode 100644 index 000000000..377fe1376 --- /dev/null +++ b/src/Network/Swift/service.cpp @@ -0,0 +1,516 @@ +// service.cpp - Service module for swift<->FG connection +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "service.h" +#include
+#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace FGSwiftBus { + +CService::CService() +{ + SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Service initialized"); +} + +const std::string& CService::InterfaceName() { + static const std::string s(FGSWIFTBUS_SERVICE_INTERFACENAME); + return s; +} + +const std::string& CService::ObjectPath() +{ + static const std::string s(FGSWIFTBUS_SERVICE_OBJECTPATH); + return s; +} + +void CService::addTextMessage(const std::string& text) +{ + if (text.empty()) { return; } + fgSetString("/sim/messages/copilot", text); +} + +std::string CService::getAircraftModelPath() const +{ + return fgGetString("/sim/aircraft-dir"); +} + +std::string CService::getAircraftLivery() const +{ + return ""; +} + +std::string CService::getAircraftIcaoCode() const +{ + return ""; +} + +std::string CService::getAircraftDescription() const +{ + return fgGetString("/sim/description"); +} + +bool CService::isPaused() const +{ + return fgGetBool("/sim/freeze/master"); +} + +double CService::getLatitude() const +{ + return fgGetDouble("/position/latitude-deg"); +} + +double CService::getLongitude() const +{ + return fgGetDouble("/position/longitude-deg"); +} + +double CService::getAltitudeMSL() const +{ + return fgGetDouble("/position/altitude-ft"); +} + +double CService::getHeightAGL() const +{ + return fgGetDouble("/position/altitude-agl-ft"); +} + +double CService::getGroundSpeed() const +{ + return fgGetDouble("/velocities/groundspeed-kt"); +} + +double CService::getPitch() const +{ + return fgGetDouble("/orientation/pitch-deg"); +} + +double CService::getRoll() const +{ + return fgGetDouble("/orientation/roll-deg"); +} + +double CService::getTrueHeading() const +{ + return fgGetDouble("/orientation/heading-deg"); +} + +bool CService::getAllWheelsOnGround() const +{ + return fgGetBool("/gear/gear/wow"); +} + +int CService::getCom1Active() const +{ + return fgGetDouble("/instrumentation/comm/frequencies/selected-mhz") * 1000; +} + +int CService::getCom1Standby() const +{ + return fgGetDouble("/instrumentation/comm/frequencies/standby-mhz") * 1000; +} + +int CService::getCom2Active() const +{ + return fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz") * 1000; +} + +int CService::getCom2Standby() const +{ + return fgGetDouble("/instrumentation/comm[1]/frequencies/standby-mhz") * 1000; +} + +int CService::getTransponderCode() const +{ + return fgGetInt("/instrumentation/transponder/id-code"); +} + +int CService::getTransponderMode() const +{ + return fgGetInt("/instrumentation/transponder/inputs/knob-mode"); +} + +bool CService::getTransponderIdent() const +{ + return fgGetBool("/instrumentation/transponder/ident"); +} + +bool CService::getBeaconLightsOn() const +{ + return fgGetBool("/controls/lighting/beacon"); +} + +bool CService::getLandingLightsOn() const +{ + return fgGetBool("/controls/lighting/landing-lights"); +} + +bool CService::getNavLightsOn() const +{ + return fgGetBool("/controls/lighting/nav-lights"); +} + + +bool CService::getStrobeLightsOn() const +{ + return fgGetBool("/controls/lighting/strobe"); +} + +bool CService::getTaxiLightsOn() const +{ + return fgGetBool("/controls/lighting/taxi-light"); +} + +double CService::getQNH() const +{ + return fgGetDouble("/environment/pressure-sea-level-inhg"); +} + +void CService::setCom1Active(int freq) +{ + fgSetDouble("/instrumentation/comm/frequencies/selected-mhz", freq / (double)1000); +} + +void CService::setCom1Standby(int freq) +{ + fgSetDouble("/instrumentation/comm/frequencies/standby-mhz", freq / (double)1000); +} + +void CService::setCom2Active(int freq) +{ + fgSetDouble("/instrumentation/comm[1]/frequencies/selected-mhz", freq / (double)1000); +} + +void CService::setCom2Standby(int freq) +{ + fgSetDouble("/instrumentation/comm[1]/frequencies/standby-mhz", freq / (double)1000); +} + +void CService::setTransponderCode(int code) +{ + fgSetInt("/instrumentation/transponder/id-code", code); +} + +void CService::setTransponderMode(int mode) +{ + fgSetInt("/instrumentation/transponder/inputs/knob-mode", mode); +} + +double CService::getFlapsDeployRatio() const +{ + return fgGetFloat("/surface-positions/flap-pos-norm"); +} + +double CService::getGearDeployRatio() const +{ + return fgGetFloat("/gear/gear/position-norm"); +} + +int CService::getNumberOfEngines() const +{ + return 2; +} + +std::vector CService::getEngineN1Percentage() const +{ + std::vector list; + const auto number = static_cast(getNumberOfEngines()); + list.reserve(number); + for (unsigned int engineNumber = 0; engineNumber < number; ++engineNumber) { + list.push_back(fgGetDouble("/engine/engine/n1")); + } + return list; +} + +double CService::getSpeedBrakeRatio() const +{ + return fgGetFloat("/surface-positions/speedbrake-pos-norm"); +} + +std::string CService::getAircraftModelFilename() const +{ + std::string modelFileName = fgGetString("/sim/aircraft"); + modelFileName.append("-set.xml"); + return modelFileName; +} + +std::string CService::getAircraftModelString() const +{ + std::string modelName = fgGetString("/sim/aircraft"); + std::string modelString = "FG " + modelName; + return modelString; +} + +std::string CService::getAircraftName() const +{ + return fgGetString("/sim/aircraft"); +} + +static const char* introspection_service = DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE; + +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() == FGSWIFTBUS_SERVICE_INTERFACENAME) { + if (message.getMethodName() == "addTextMessage") { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::string text; + message.beginArgumentRead(); + message.getArgument(text); + + queueDBusCall([=]() { + addTextMessage(text); + }); + } else if (message.getMethodName() == "getOwnAircraftSituationData") { + queueDBusCall([=]() { + double lat = getLatitude(); + double lon = getLongitude(); + double alt = getAltitudeMSL(); + double gs = getGroundSpeed(); + double pitch = getPitch(); + double roll = getRoll(); + double trueHeading = getTrueHeading(); + double qnh = getQNH(); + 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() == "getAircraftModelPath") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAircraftModelPath()); + }); + } else if (message.getMethodName() == "getAircraftModelFilename") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAircraftModelFilename()); + }); + } else if (message.getMethodName() == "getAircraftModelString") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAircraftModelString()); + }); + } else if (message.getMethodName() == "getAircraftName") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAircraftName()); + }); + } else if (message.getMethodName() == "getAircraftLivery") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAircraftLivery()); + }); + } else if (message.getMethodName() == "getAircraftIcaoCode") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAircraftIcaoCode()); + }); + } else if (message.getMethodName() == "getAircraftDescription") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAircraftDescription()); + }); + } else if (message.getMethodName() == "isPaused") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, isPaused()); + }); + } else if (message.getMethodName() == "getLatitudeDeg") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getLatitude()); + }); + } else if (message.getMethodName() == "getLongitudeDeg") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getLongitude()); + }); + } else if (message.getMethodName() == "getAltitudeMslFt") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAltitudeMSL()); + }); + } else if (message.getMethodName() == "getHeightAglFt") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getHeightAGL()); + }); + } else if (message.getMethodName() == "getGroundSpeedKts") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getGroundSpeed()); + }); + } else if (message.getMethodName() == "getPitchDeg") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getPitch()); + }); + } else if (message.getMethodName() == "getRollDeg") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getRoll()); + }); + } else if (message.getMethodName() == "getAllWheelsOnGround") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getAllWheelsOnGround()); + }); + } else if (message.getMethodName() == "getCom1ActiveKhz") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getCom1Active()); + }); + } else if (message.getMethodName() == "getCom1StandbyKhz") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getCom1Standby()); + }); + } else if (message.getMethodName() == "getCom2ActiveKhz") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getCom2Active()); + }); + } else if (message.getMethodName() == "getCom2StandbyKhz") { + 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() == "getQNHInHg") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getQNH()); + }); + } else if (message.getMethodName() == "setCom1ActiveKhz") { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int frequency = 0; + message.beginArgumentRead(); + message.getArgument(frequency); + queueDBusCall([=]() { + setCom1Active(frequency); + }); + } else if (message.getMethodName() == "setCom1StandbyKhz") { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int frequency = 0; + message.beginArgumentRead(); + message.getArgument(frequency); + queueDBusCall([=]() { + setCom1Standby(frequency); + }); + } else if (message.getMethodName() == "setCom2ActiveKhz") { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + int frequency = 0; + message.beginArgumentRead(); + message.getArgument(frequency); + queueDBusCall([=]() { + setCom2Active(frequency); + }); + } else if (message.getMethodName() == "setCom2StandbyKhz") { + 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() == "getEngineN1Percentage") { + queueDBusCall([=]() { + std::vector array = getEngineN1Percentage(); + sendDBusReply(sender, serial, array); + }); + } else if (message.getMethodName() == "getSpeedBrakeRatio") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getSpeedBrakeRatio()); + }); + } else { + // Unknown message. Tell DBus that we cannot handle it + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + return DBUS_HANDLER_RESULT_HANDLED; +} + + +int CService::process() +{ + invokeQueuedDBusCalls(); + + return 1; +} + +} // namespace FGSwiftBus \ No newline at end of file diff --git a/src/Network/Swift/service.h b/src/Network/Swift/service.h new file mode 100644 index 000000000..631fc6021 --- /dev/null +++ b/src/Network/Swift/service.h @@ -0,0 +1,202 @@ +// service.h - Service module for swift<->FG connection +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_SERVICE_H +#define BLACKSIM_FGSWIFTBUS_SERVICE_H + +//! \file + +#ifndef NOMINMAX +#define NOMINMAX +#endif + + +#include "dbusobject.h" +#include +#include +#include
+#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//! \cond PRIVATE +#define FGSWIFTBUS_SERVICE_INTERFACENAME "org.swift_project.fgswiftbus.service" +#define FGSWIFTBUS_SERVICE_OBJECTPATH "/fgswiftbus/service" +//! \endcond + +namespace FGSwiftBus { + + /*! + * FGSwiftBus service object which is accessible through DBus + */ +class CService : public CDBusObject +{ +public: + //! Constructor + CService(); + + //! DBus interface name + static const std::string& InterfaceName(); + + //! DBus object path + static const std::string& ObjectPath(); + + ////! Add a text message to the on-screen display, with RGB components in the range [0,1] + void addTextMessage(const std::string& text); + + ////! Get full path to current aircraft model + std::string getAircraftModelPath() const; + + ////! Get base filename of current aircraft model + std::string getAircraftModelFilename() const; + + ////! Get canonical swift model string of current aircraft model + std::string getAircraftModelString() const; + + ////! Get name of current aircraft model + std::string getAircraftName() const; + + ////! Get path to current aircraft livery + std::string getAircraftLivery() const; + + //! Get the ICAO code of the current aircraft model + std::string getAircraftIcaoCode() const; + + ////! Get the description of the current aircraft model + std::string getAircraftDescription() const; + + //! True if sim is paused + bool isPaused() const; + + //! Get aircraft latitude in degrees + double getLatitude() const; + + //! Get aircraft longitude in degrees + double getLongitude() const; + + //! Get aircraft altitude in feet + double getAltitudeMSL() const; + + //! Get aircraft height in feet + double getHeightAGL() const; + + //! Get aircraft groundspeed in knots + double getGroundSpeed() const; + + //! Get aircraft pitch in degrees above horizon + double getPitch() const; + + //! Get aircraft roll in degrees + double getRoll() const; + + //! Get aircraft true heading in degrees + double getTrueHeading() const; + + //! Get whether all wheels are on the ground + bool getAllWheelsOnGround() const; + + //! Get the current COM1 active frequency in kHz + int getCom1Active() const; + + //! Get the current COM1 standby frequency in kHz + int getCom1Standby() const; + + //! Get the current COM2 active frequency in kHz + int getCom2Active() const; + + //! Get the current COM2 standby frequency in kHz + int getCom2Standby() const; + + //! Get the current transponder code in decimal + int getTransponderCode() const; + + //! Get the current transponder mode (depends on the aircraft, 0-2 usually mean standby, >2 active) + int getTransponderMode() const; + + //! Get whether we are currently squawking ident + bool getTransponderIdent() const; + + //! Get whether beacon lights are on + bool getBeaconLightsOn() const; + + //! Get whether landing lights are on + bool getLandingLightsOn() const; + + //! Get whether nav lights are on + bool getNavLightsOn() const; + + //! Get whether strobe lights are on + bool getStrobeLightsOn() const; + + //! Get whether taxi lights are on + bool getTaxiLightsOn() const; + + //! Get barometric pressure at sea level in inches of mercury. + double getQNH() const; + + //! Set the current COM1 active frequency in kHz + void setCom1Active(int freq); + + //! Set the current COM1 standby frequency in kHz + void setCom1Standby(int freq); + + //! Set the current COM2 active frequency in kHz + void setCom2Active(int freq); + + //! Set the current COM2 standby frequency in kHz + void setCom2Standby(int freq); + + ////! Set the current transponder code in decimal + void setTransponderCode(int code); + + ////! Set the current transponder mode (depends on the aircraft, 0 and 1 usually mean standby, >1 active) + void setTransponderMode(int mode); + + //! Get flaps deploy ratio, where 0.0 is flaps fully retracted, and 1.0 is flaps fully extended. + double getFlapsDeployRatio() const; + + //! Get gear deploy ratio, where 0 is up and 1 is down + double getGearDeployRatio() const; + + //! Get the number of engines of current aircraft + int getNumberOfEngines() const; + + //! Get the N1 speed as percent of max (per engine) + std::vector getEngineN1Percentage() const; + + //! Get the ratio how much the speedbrakes surfaces are extended (0.0 is fully retracted, and 1.0 is fully extended) + double getSpeedBrakeRatio() const; + + //! Perform generic processing + int process(); + +protected: + DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) override; + +}; +} // namespace FGSwiftBus + +#endif // guard \ No newline at end of file diff --git a/src/Network/Swift/swift_connection.cxx b/src/Network/Swift/swift_connection.cxx new file mode 100644 index 000000000..035a93fd2 --- /dev/null +++ b/src/Network/Swift/swift_connection.cxx @@ -0,0 +1,104 @@ +// swift_connection.cxx +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "plugin.h" +#include "swift_connection.hxx" +#include
+#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +inline std::string fgswiftbusServiceName() +{ + return std::string("org.swift-project.fgswiftbus"); +} +} // namespace + +bool SwiftConnection::startServer(const SGPropertyNode* arg, SGPropertyNode* root) +{ + SwiftConnection::plug = new FGSwiftBus::CPlugin(); + serverRunning = true; + fgSetBool("/sim/swift/serverRunning", true); + return true; +} + +bool SwiftConnection::stopServer(const SGPropertyNode* arg, SGPropertyNode* root) +{ + SwiftConnection::plug->~CPlugin(); + fgSetBool("/sim/swift/serverRunning", false); + serverRunning = false; + return true; +} + +SwiftConnection::SwiftConnection() +{ + init(); +} + + +SwiftConnection::~SwiftConnection() +{ + shutdown(); +} + +void SwiftConnection::init(void) +{ + if (initialized == false) { + globals->get_commands()->addCommand("swiftStart", this, &SwiftConnection::startServer); + globals->get_commands()->addCommand("swiftStop", this, &SwiftConnection::stopServer); + fgSetBool("/sim/swift/available", true); + initialized = true; + } + +} + +void SwiftConnection::update(double delta_time_sec) +{ + if (serverRunning == true) { + SwiftConnection::plug->fastLoop(); + } +} + +void SwiftConnection::shutdown(void) +{ + if (initialized == true) { + globals->get_commands()->removeCommand("swiftStart"); + globals->get_commands()->removeCommand("swiftStop"); + fgSetBool("/sim/swift/available", false); + initialized = false; + } + +} + +void SwiftConnection::reinit() +{ + shutdown(); + init(); +} diff --git a/src/Network/Swift/swift_connection.hxx b/src/Network/Swift/swift_connection.hxx new file mode 100644 index 000000000..0220276a6 --- /dev/null +++ b/src/Network/Swift/swift_connection.hxx @@ -0,0 +1,57 @@ +// swift_conection.hxx +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef SWIFT_CONNECTION_H +#define SWIFT_CONNECTION_H +#include "dbusconnection.h" +#include "dbusdispatcher.h" +#include
+#include +#include +#include +#include +#include "plugin.h" +#include "dbusserver.h" +#include +#include + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +class SwiftConnection : public SGSubsystem +{ +public: + bool startServer(const SGPropertyNode* arg, SGPropertyNode* root); + bool stopServer(const SGPropertyNode* arg, SGPropertyNode* root); + SwiftConnection(); + ~SwiftConnection(); + FGSwiftBus::CPlugin* plug; + + virtual void init(void); + virtual void update(double delta_time_sec) override; + virtual void shutdown(void); + virtual void reinit(); + +private: + bool serverRunning = false; + bool initialized = false; +}; + +#endif \ No newline at end of file diff --git a/src/Network/Swift/traffic.cpp b/src/Network/Swift/traffic.cpp new file mode 100644 index 000000000..e5d493ed2 --- /dev/null +++ b/src/Network/Swift/traffic.cpp @@ -0,0 +1,264 @@ +// traffic.cpp - Traffic module for swift<->FG connection +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +//! \cond PRIVATE + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include "traffic.h" +#include "SwiftAircraftManager.h" +#include +#include +#include +#include +#include +#include + + +// clazy:excludeall=reserve-candidates + +namespace FGSwiftBus { + +CTraffic::CTraffic() +{ + acm = new FGSwiftAircraftManager(); + SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Traffic started"); +} + +CTraffic::~CTraffic() +{ + cleanup(); + SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Traffic stopped"); +} + +const std::string& CTraffic::InterfaceName() +{ + static std::string s(FGSWIFTBUS_TRAFFIC_INTERFACENAME); + return s; +} + +const std::string& CTraffic::ObjectPath() +{ + static std::string s(FGSWIFTBUS_TRAFFIC_OBJECTPATH); + return s; +} + +void CTraffic::planeLoaded(void* id, bool succeeded, void* self) +{ + auto* traffic = static_cast(self); + auto planeIt = traffic->m_planesById.find(id); + if (planeIt == traffic->m_planesById.end()) { return; } + + if (succeeded) { + traffic->emitPlaneAdded(planeIt->second->callsign); + } else { + traffic->emitPlaneAddingFailed(planeIt->second->callsign); + } +} + +bool CTraffic::initialize() +{ + m_initialized = true; + + m_enabled = true; + + return true; +} + +void CTraffic::emitSimFrame() +{ + if (m_emitSimFrame) { sendDBusSignal("simFrame"); } + m_emitSimFrame = !m_emitSimFrame; +} + +void CTraffic::emitPlaneAdded(const std::string& callsign) +{ + CDBusMessage signalPlaneAdded = CDBusMessage::createSignal(FGSWIFTBUS_TRAFFIC_OBJECTPATH, FGSWIFTBUS_TRAFFIC_INTERFACENAME, "remoteAircraftAdded"); + signalPlaneAdded.beginArgumentWrite(); + signalPlaneAdded.appendArgument(callsign); + sendDBusMessage(signalPlaneAdded); +} + +void CTraffic::emitPlaneAddingFailed(const std::string& callsign) +{ + CDBusMessage signalPlaneAddingFailed = CDBusMessage::createSignal(FGSWIFTBUS_TRAFFIC_OBJECTPATH, FGSWIFTBUS_TRAFFIC_INTERFACENAME, "remoteAircraftAddingFailed"); + signalPlaneAddingFailed.beginArgumentWrite(); + signalPlaneAddingFailed.appendArgument(callsign); + sendDBusMessage(signalPlaneAddingFailed); +} + +void CTraffic::cleanup() +{ + acm->removeAllPlanes(); +} + +void CTraffic::removeAllPlanes() +{ + acm->removeAllPlanes(); +} + +void CTraffic::dbusDisconnectedHandler() +{ + acm->removeAllPlanes(); +} + +const char* introspection_traffic = DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE; + +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(); + std::string test = message.getMethodName(); + + if (message.getInterfaceName() == DBUS_INTERFACE_INTROSPECTABLE) { + if (message.getMethodName() == "Introspect") { + sendDBusReply(sender, serial, introspection_traffic); + } + } else if (message.getInterfaceName() == FGSWIFTBUS_TRAFFIC_INTERFACENAME) { + if (message.getMethodName() == "acquireMultiplayerPlanes") { + queueDBusCall([=]() { + std::string owner; + bool acquired = true; + CDBusMessage reply = CDBusMessage::createReply(sender, serial); + reply.beginArgumentWrite(); + reply.appendArgument(acquired); + reply.appendArgument(owner); + sendDBusMessage(reply); + }); + } else if (message.getMethodName() == "initialize") { + sendDBusReply(sender, serial, initialize()); + } else if (message.getMethodName() == "cleanup") { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + queueDBusCall([=]() { + cleanup(); + }); + } 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([=]() { + if (acm->addPlane(callsign, modelName)) { + emitPlaneAdded(callsign); + } + }); + } else if (message.getMethodName() == "removePlane") { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::string callsign; + message.beginArgumentRead(); + message.getArgument(callsign); + queueDBusCall([=]() { + acm->removePlane(callsign); + }); + } else if (message.getMethodName() == "removeAllPlanes") { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + queueDBusCall([=]() { + acm->removeAllPlanes(); + }); + } else if (message.getMethodName() == "setPlanesPositions") { + maybeSendEmptyDBusReply(wantsReply, sender, serial); + std::vector callsigns; + std::vector latitudes; + std::vector longitudes; + std::vector altitudes; + std::vector pitches; + std::vector rolls; + std::vector headings; + std::vector onGrounds; + message.beginArgumentRead(); + message.getArgument(callsigns); + message.getArgument(latitudes); + message.getArgument(longitudes); + message.getArgument(altitudes); + message.getArgument(pitches); + message.getArgument(rolls); + message.getArgument(headings); + message.getArgument(onGrounds); + queueDBusCall([=]() { + std::vector positions; + std::vector orientations; + for (int i = 0; i < latitudes.size(); i++) { + SGGeod newPos; + newPos.setLatitudeDeg(latitudes.at(i)); + newPos.setLongitudeDeg(longitudes.at(i)); + newPos.setElevationFt(altitudes.at(i)); + SGVec3d vec(pitches.at(i), rolls.at(i), headings.at(i)); + + positions.push_back(newPos); + orientations.push_back(vec); + } + acm->updatePlanes(callsigns, positions, orientations, onGrounds); + }); + } else if (message.getMethodName() == "getRemoteAircraftData") { + std::vector requestedcallsigns; + message.beginArgumentRead(); + message.getArgument(requestedcallsigns); + queueDBusCall([=]() { + std::vector callsigns = requestedcallsigns; + std::vector latitudesDeg; + std::vector longitudesDeg; + std::vector elevationsM; + std::vector verticalOffsets; + acm->getRemoteAircraftData(callsigns, latitudesDeg, longitudesDeg, elevationsM, verticalOffsets); + CDBusMessage reply = CDBusMessage::createReply(sender, serial); + reply.beginArgumentWrite(); + reply.appendArgument(callsigns); + reply.appendArgument(latitudesDeg); + reply.appendArgument(longitudesDeg); + reply.appendArgument(elevationsM); + reply.appendArgument(verticalOffsets); + sendDBusMessage(reply); + }); + } else { + // Unknown message. Tell DBus that we cannot handle it + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + return DBUS_HANDLER_RESULT_HANDLED; +} + +int CTraffic::process() +{ + 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) +{ + return std::memcmp(reinterpret_cast(dst) + sizeof(dst->size), + reinterpret_cast(src) + sizeof(src->size), + sizeof(*dst) - sizeof(dst->size)); +} +} // namespace FGSwiftBus + +//! \endcond diff --git a/src/Network/Swift/traffic.h b/src/Network/Swift/traffic.h new file mode 100644 index 000000000..711540a67 --- /dev/null +++ b/src/Network/Swift/traffic.h @@ -0,0 +1,108 @@ +// traffic.h - Traffic module for swift<->FG connection +// +// Copyright (C) 2019 - swift Project Community / Contributors (http://swift-project.org/) +// Adapted to Flightgear by Lars Toenning +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef BLACKSIM_FGSWIFTBUS_TRAFFIC_H +#define BLACKSIM_FGSWIFTBUS_TRAFFIC_H + +//! \file + +#include "SwiftAircraftManager.h" +#include "dbusobject.h" +#include +#include + +//! \cond PRIVATE +#define FGSWIFTBUS_TRAFFIC_INTERFACENAME "org.swift_project.fgswiftbus.traffic" +#define FGSWIFTBUS_TRAFFIC_OBJECTPATH "/fgswiftbus/traffic" +//! \endcond + +namespace FGSwiftBus { +/*! + * FGSwiftBus service object for traffic aircraft which is accessible through DBus + */ +class CTraffic : public CDBusObject +{ +public: + //! Constructor + CTraffic(); + + //! Destructor + ~CTraffic() override; + + //! DBus interface name + static const std::string& InterfaceName(); + + //! DBus object path + static const std::string& ObjectPath(); + + //! Initialize the multiplayer planes rendering and return true if successful + bool initialize(); + + //! Remove all traffic aircraft + void removeAllPlanes(); + + //! Perform generic processing + int process(); + + void emitSimFrame(); + +protected: + virtual void dbusDisconnectedHandler() override; + + DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) override; + +private: + bool m_initialized = false; + bool m_enabled = false; + + void emitPlaneAdded(const std::string& callsign); + void emitPlaneAddingFailed(const std::string& callsign); + void cleanup(); + + struct Plane { + void* id = nullptr; + std::string callsign; + std::string aircraftIcao; + std::string airlineIcao; + std::string livery; + std::string modelName; + bool hasSurfaces = false; + bool hasXpdr = false; + char label[32]{}; + float targetGearPosition = 0; + std::chrono::system_clock::time_point prevSurfacesLerpTime; + }; + + std::unordered_map m_planesByCallsign; + std::unordered_map m_planesById; + std::vector m_followPlaneViewSequence; + std::chrono::system_clock::time_point m_timestampLastSimFrame = std::chrono::system_clock::now(); + + std::string m_followPlaneViewCallsign; + + bool m_emitSimFrame = true; + + + FGSwiftAircraftManager* acm; + + static void planeLoaded(void* id, bool succeeded, void* self); +}; +} // namespace FGSwiftBus + +#endif // guard