diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bf5201b90d..2477eca044b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -269,7 +269,6 @@ list(APPEND QGC_RESOURCES ) if(QGC_UTM_ADAPTER) - add_definitions(-DQGC_UTM_ADAPTER) list(APPEND QGC_RESOURCES ${CMAKE_SOURCE_DIR}/src/UTMSP/utmsp.qrc) else() list(APPEND QGC_RESOURCES ${CMAKE_SOURCE_DIR}/src/UTMSP/dummy/utmsp_dummy.qrc) diff --git a/custom-example/qgroundcontrol.qrc b/custom-example/qgroundcontrol.qrc index 0f249c9d16d..f93644c9a20 100644 --- a/custom-example/qgroundcontrol.qrc +++ b/custom-example/qgroundcontrol.qrc @@ -31,7 +31,7 @@ ../src/FlightMap/MapItems/MapLineArrow.qml ../src/FlightMap/MapItems/SplitIndicator.qml ../src/UI/preferences/ADSBServerSettings.qml - ../src/AirLink/AirLinkSettings.qml + ../src/Comms/AirLink/AirLinkSettings.qml ../src/AnalyzeView/AnalyzeView.qml ../src/UI/AppSettings.qml ../src/UI/preferences/BluetoothSettings.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 61516930996..a8a9488e16d 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -31,7 +31,7 @@ src/FlightMap/MapItems/MapLineArrow.qml src/FlightMap/MapItems/SplitIndicator.qml src/UI/preferences/ADSBServerSettings.qml - src/AirLink/AirLinkSettings.qml + src/Comms/AirLink/AirLinkSettings.qml src/AnalyzeView/AnalyzeView.qml src/UI/AppSettings.qml src/UI/preferences/BluetoothSettings.qml diff --git a/src/AirLink/AirLinkManager.cc b/src/AirLink/AirLinkManager.cc deleted file mode 100644 index 76c38895810..00000000000 --- a/src/AirLink/AirLinkManager.cc +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2024 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#include "AirLinkManager.h" -#include "SettingsManager.h" - -#include -#include -#include -#include -#include -#include - -const QString AirLinkManager::airlinkHost = "air-link.space"; - -AirLinkManager::AirLinkManager(QGCApplication* app, QGCToolbox* toolbox) - : QGCTool(app, toolbox) -{ -} - -AirLinkManager::~AirLinkManager() -{ -} - -void AirLinkManager::setToolbox(QGCToolbox* toolbox) -{ - QGCTool::setToolbox(toolbox); -} - -QStringList AirLinkManager::droneList() const -{ - return _vehiclesFromServer.keys(); -} - -void AirLinkManager::updateDroneList(const QString &login, const QString &pass) -{ - connectToAirLinkServer(login, pass); -} - -bool AirLinkManager::isOnline(const QString &drone) -{ - if (!_vehiclesFromServer.contains(drone)) { - return false; - } else { - return _vehiclesFromServer[drone]; - } -} - -void AirLinkManager::connectToAirLinkServer(const QString &login, const QString &pass) -{ - QNetworkAccessManager *mngr = new QNetworkAccessManager(this); - - const QUrl url("https://air-link.space/api/gs/getModems"); - QNetworkRequest request(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - - QJsonObject obj; - obj["login"] = login; - obj["password"] = pass; - QJsonDocument doc(obj); - QByteArray data = doc.toJson(); - - _reply = mngr->post(request, data); - - QObject::connect(_reply, &QNetworkReply::finished, [this](){ - _processReplyAirlinkServer(*_reply); - _reply->deleteLater(); - }); - - mngr = nullptr; - delete mngr; -} - -void AirLinkManager::updateCredentials(const QString &login, const QString &pass) -{ - _toolbox->settingsManager()->appSettings()->loginAirLink()->setRawValue(login); - _toolbox->settingsManager()->appSettings()->passAirLink()->setRawValue(pass); -} - -void AirLinkManager::_parseAnswer(const QByteArray &ba) -{ - _vehiclesFromServer.clear(); - for (const auto &arr : QJsonDocument::fromJson(ba)["modems"].toArray()) { - QString droneModem = arr.toObject()["name"].toString(); - bool isOnline = arr.toObject()["isOnline"].toBool(); - _vehiclesFromServer[droneModem] = isOnline; - } - emit droneListChanged(); -} - -void AirLinkManager::_processReplyAirlinkServer(QNetworkReply &reply) -{ - QByteArray ba = reply.readAll(); - - if (reply.error() == QNetworkReply::NoError) { - if (!QJsonDocument::fromJson(ba)["modems"].toArray().isEmpty()) { - _parseAnswer(ba); - } else { - qDebug() << "No airlink modems in answer"; - } - } else { - qDebug() << "Airlink auth - network error"; - } -} - diff --git a/src/AirLink/AirLinkManager.h b/src/AirLink/AirLinkManager.h deleted file mode 100644 index 0b18dcf1abf..00000000000 --- a/src/AirLink/AirLinkManager.h +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2024 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#pragma once - -#include "QGCToolbox.h" - -#include - -class AppSettings; -class QGCApplication; -class LinkInterface; -class QNetworkReply; - -//----------------------------------------------------------------------------- -class AirLinkManager : public QGCTool -{ - Q_OBJECT - -public: - Q_PROPERTY(QStringList droneList READ droneList NOTIFY droneListChanged) - Q_INVOKABLE void updateDroneList(const QString &login, const QString &pass); - Q_INVOKABLE bool isOnline(const QString &drone); - Q_INVOKABLE void connectToAirLinkServer(const QString &login, const QString &pass); - Q_INVOKABLE void updateCredentials(const QString &login, const QString &pass); - - explicit AirLinkManager(QGCApplication* app, QGCToolbox* toolbox); - ~AirLinkManager() override; - - void setToolbox (QGCToolbox* toolbox) override; - QStringList droneList() const; - - static const QString airlinkHost; - static constexpr int airlinkPort = 10000; - -signals: - void droneListChanged(); - -private: - void _parseAnswer (const QByteArray &ba); - void _processReplyAirlinkServer (QNetworkReply &reply); - -private: - QMap _vehiclesFromServer; - QNetworkReply* _reply; -}; diff --git a/src/AirLink/AirlinkLink.cc b/src/AirLink/AirlinkLink.cc deleted file mode 100644 index 6d6ffff151f..00000000000 --- a/src/AirLink/AirlinkLink.cc +++ /dev/null @@ -1,200 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2024 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#include "AirlinkLink.h" -#include "AirLinkManager.h" -#include "QGCApplication.h" -#include "AppSettings.h" -#include "SettingsManager.h" -#include "MAVLinkProtocol.h" - -#include -#include -#include - - -AirlinkConfiguration::AirlinkConfiguration(const QString &name) : UDPConfiguration(name) -{ - -} - -AirlinkConfiguration::AirlinkConfiguration(AirlinkConfiguration *source) : UDPConfiguration(source) -{ - _copyFrom(source); -} - -AirlinkConfiguration::~AirlinkConfiguration() -{ -} - -void AirlinkConfiguration::setUsername(QString username) -{ - _username = username; -} - -void AirlinkConfiguration::setPassword(QString password) -{ - _password = password; -} - -void AirlinkConfiguration::setModemName(QString modemName) -{ - _modemName = modemName; -} - -void AirlinkConfiguration::loadSettings(QSettings &settings, const QString &root) -{ - AppSettings *appSettings = qgcApp()->toolbox()->settingsManager()->appSettings(); - settings.beginGroup(root); - _username = settings.value(_usernameSettingsKey, appSettings->loginAirLink()->rawValueString()).toString(); - _password = settings.value(_passwordSettingsKey, appSettings->passAirLink()->rawValueString()).toString(); - _modemName = settings.value(_modemNameSettingsKey).toString(); - settings.endGroup(); -} - -void AirlinkConfiguration::saveSettings(QSettings &settings, const QString &root) -{ - settings.beginGroup(root); - settings.setValue(_usernameSettingsKey, _username); - settings.setValue(_passwordSettingsKey, _password); - settings.setValue(_modemNameSettingsKey, _modemName); - settings.endGroup(); -} - -void AirlinkConfiguration::copyFrom(LinkConfiguration *source) -{ - LinkConfiguration::copyFrom(source); - auto* udpSource = qobject_cast(source); - if (udpSource) { - UDPConfiguration::copyFrom(source); - } - _copyFrom(source); -} - -void AirlinkConfiguration::_copyFrom(LinkConfiguration *source) -{ - auto* airlinkSource = qobject_cast(source); - if (airlinkSource) { - _username = airlinkSource->username(); - _password = airlinkSource->password(); - _modemName = airlinkSource->modemName(); - } else { - qWarning() << "Internal error: cannot read AirlinkConfiguration from given source"; - } -} - - -AirlinkLink::AirlinkLink(SharedLinkConfigurationPtr &config) : UDPLink(config) -{ - _configureUdpSettings(); -} - -AirlinkLink::~AirlinkLink() -{ -} - -// bool AirlinkLink::isConnected() const -// { -// return UDPLink::isConnected(); -// } - -void AirlinkLink::disconnect() -{ - _setConnectFlag(false); - UDPLink::disconnect(); -} - -// void AirlinkLink::run() -// { -// UDPLink::run(); -// } - -bool AirlinkLink::_connect() -{ - start(NormalPriority); - QTimer *pendingTimer = new QTimer; - connect(pendingTimer, &QTimer::timeout, [this, pendingTimer] { - pendingTimer->setInterval(3000); - if (_stillConnecting()) { - qDebug() << "Connecting..."; - _sendLoginMsgToAirLink(); - } else { - qDebug() << "Stopping..."; - pendingTimer->stop(); - pendingTimer->deleteLater(); - } - }); - MAVLinkProtocol *mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - auto conn = std::make_shared(); - *conn = connect(mavlink, &MAVLinkProtocol::messageReceived, [this, conn] (LinkInterface* linkSrc, mavlink_message_t message) { - if (this != linkSrc || message.msgid != MAVLINK_MSG_ID_AIRLINK_AUTH_RESPONSE) { - return; - } - mavlink_airlink_auth_response_t responseMsg; - mavlink_msg_airlink_auth_response_decode(&message, &responseMsg); - int answer = responseMsg.resp_type; - if (answer != AIRLINK_AUTH_RESPONSE_TYPE::AIRLINK_AUTH_OK) { - qDebug() << "Airlink auth failed"; - return; - } - qDebug() << "Connected successfully"; - QObject::disconnect(*conn); - _setConnectFlag(false); - }); - _setConnectFlag(true); - pendingTimer->start(0); - return true; -} - -void AirlinkLink::_configureUdpSettings() -{ - quint16 availablePort = 14550; - QUdpSocket udpSocket; - while (!udpSocket.bind(QHostAddress::LocalHost, availablePort)) - availablePort++; - UDPConfiguration* udpConfig = dynamic_cast(UDPLink::m_config.get()); - udpConfig->addHost(AirLinkManager::airlinkHost, AirLinkManager::airlinkPort); - udpConfig->setLocalPort(availablePort); - udpConfig->setDynamic(false); -} - -void AirlinkLink::_sendLoginMsgToAirLink() -{ - __mavlink_airlink_auth_t auth; - uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; - mavlink_message_t mavmsg; - AirlinkConfiguration* config = dynamic_cast(m_config.get()); - QString login = config->modemName(); ///< Connect not to account but to specific modem - QString pass = config->password(); - - memset(&auth.login, 0, sizeof(auth.login)); - memset(&auth.password, 0, sizeof(auth.password)); - strcpy(auth.login, login.toUtf8().constData()); - strcpy(auth.password, pass.toUtf8().constData()); - - mavlink_msg_airlink_auth_pack(0, 0, &mavmsg, auth.login, auth.password); - uint16_t len = mavlink_msg_to_send_buffer(buffer, &mavmsg); - if (!_stillConnecting()) { - qDebug() << "Force exit from connection"; - return; - } - writeBytesThreadSafe((const char *)buffer, len); -} - -bool AirlinkLink::_stillConnecting() -{ - QMutexLocker locker(&_mutex); - return _needToConnect; -} - -void AirlinkLink::_setConnectFlag(bool connect) -{ - QMutexLocker locker(&_mutex); - _needToConnect = connect; -} diff --git a/src/AirLink/AirlinkLink.h b/src/AirLink/AirlinkLink.h deleted file mode 100644 index 2291fbcedaa..00000000000 --- a/src/AirLink/AirlinkLink.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2024 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#pragma once - -#include - -#include "UDPLink.h" - -class AirlinkConfiguration : public UDPConfiguration -{ - Q_OBJECT -public: - Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged) - Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) - Q_PROPERTY(QString modemName READ modemName WRITE setModemName NOTIFY modemNameChanged) - - AirlinkConfiguration(const QString& name); - AirlinkConfiguration(AirlinkConfiguration* source); - ~AirlinkConfiguration(); - - QString username () const { return _username; } - QString password () const { return _password; } - QString modemName() const { return _modemName; } - - void setUsername (QString username); - void setPassword (QString password); - void setModemName (QString modemName); - - /// LinkConfiguration overrides - LinkType type (void) override { return LinkConfiguration::Airlink; } - void loadSettings (QSettings& settings, const QString& root) override; - void saveSettings (QSettings& settings, const QString& root) override; - QString settingsURL (void) override { return "AirLinkSettings.qml"; } - QString settingsTitle (void) override { return tr("Airlink Link Settings"); } - void copyFrom (LinkConfiguration* source) override; - - -signals: - void usernameChanged (void); - void passwordChanged (void); - void modemNameChanged (void); - -private: - void _copyFrom (LinkConfiguration *source); - - - QString _username; - QString _password; - QString _modemName; - - const QString _usernameSettingsKey = "username"; - const QString _passwordSettingsKey = "password"; - const QString _modemNameSettingsKey = "modemName"; -}; - -class AirlinkLink : public UDPLink -{ - Q_OBJECT -public: - AirlinkLink(SharedLinkConfigurationPtr& config); - virtual ~AirlinkLink(); - - /// LinkInterface overrides - // bool isConnected(void) const override; - void disconnect (void) override; - - /// QThread overrides - // void run(void) override; - -private: - /// LinkInterface overrides - bool _connect(void) override; - - void _configureUdpSettings(); - void _sendLoginMsgToAirLink(); - bool _stillConnecting(); - void _setConnectFlag(bool connect); - - QMutex _mutex; - /// Access this varible only with _mutex locked - bool _needToConnect {false}; -}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93b769f7eea..746f32199f8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,12 +18,7 @@ qt_add_library(QGC STATIC QGCToolbox.h ) -if(QGC_UTM_ADAPTER) - add_definitions(-DQGC_UTM_ADAPTER) -endif() - add_subdirectory(ADSB) -add_subdirectory(AirLink) add_subdirectory(AnalyzeView) add_subdirectory(API) add_subdirectory(Audio) @@ -68,7 +63,6 @@ target_link_libraries(QGC Qt6::Bluetooth API ADSB - AirLink AnalyzeView Audio AutoPilotPlugins diff --git a/src/Comms/AirLink/AirLinkLink.cc b/src/Comms/AirLink/AirLinkLink.cc new file mode 100644 index 00000000000..c4a93891e8d --- /dev/null +++ b/src/Comms/AirLink/AirLinkLink.cc @@ -0,0 +1,216 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "AirLinkLink.h" +#include "QGCApplication.h" +#include "AppSettings.h" +#include "SettingsManager.h" +#include "MAVLinkProtocol.h" +#include "QGCLoggingCategory.h" + +#include +#include +#include + +QGC_LOGGING_CATEGORY(AirLinkLinkLog, "qgc.AirLink.AirLinklink"); + +AirLinkLink::AirLinkLink(SharedLinkConfigurationPtr &config) + : UDPLink(config) + , _AirLinkConfig(qobject_cast(config.get())) +{ + // qCDebug(AirLinkLinkLog) << Q_FUNC_INFO << this; + + _configureUdpSettings(); +} + +AirLinkLink::~AirLinkLink() +{ + // qCDebug(AirLinkLinkLog) << Q_FUNC_INFO << this; +} + +void AirLinkLink::disconnect() +{ + _setConnectFlag(false); + UDPLink::disconnect(); +} + +bool AirLinkLink::_connect() +{ + start(NormalPriority); + + QTimer *const pendingTimer = new QTimer(this); + (void) connect(pendingTimer, &QTimer::timeout, this, [this, pendingTimer] { + pendingTimer->setInterval(3000); + if (_stillConnecting()) { + qCDebug(AirLinkLinkLog) << "Connecting..."; + _sendLoginMsgToAirLink(); + } else { + qCDebug(AirLinkLinkLog) << "Stopping..."; + pendingTimer->stop(); + pendingTimer->deleteLater(); + } + }); + + MAVLinkProtocol *const mavlink = qgcApp()->toolbox()->mavlinkProtocol(); + auto conn = std::make_shared(); + *conn = connect(mavlink, &MAVLinkProtocol::messageReceived, this, [this, conn] (LinkInterface* linkSrc, mavlink_message_t message) { + if (this != linkSrc || message.msgid != MAVLINK_MSG_ID_AIRLINK_AUTH_RESPONSE) { + return; + } + + mavlink_airlink_auth_response_t responseMsg; + mavlink_msg_airlink_auth_response_decode(&message, &responseMsg); + const int answer = responseMsg.resp_type; + if (answer != AIRLINK_AUTH_RESPONSE_TYPE::AIRLINK_AUTH_OK) { + qCDebug(AirLinkLinkLog) << "AirLink auth failed"; + return; + } + + qCDebug(AirLinkLinkLog) << "Connected successfully"; + QObject::disconnect(*conn); + _setConnectFlag(false); + }); + + _setConnectFlag(true); + pendingTimer->start(0); + + return true; +} + +void AirLinkLink::_configureUdpSettings() +{ + quint16 availablePort = 14550; + QUdpSocket udpSocket; + while (!udpSocket.bind(QHostAddress::LocalHost, availablePort)) { + availablePort++; + } + + UDPConfiguration *const udpConfig = dynamic_cast(UDPLink::_config.get()); + udpConfig->addHost(_airLinkHost, _airLinkPort); + udpConfig->setLocalPort(availablePort); + udpConfig->setDynamic(false); +} + +void AirLinkLink::_sendLoginMsgToAirLink() +{ + mavlink_airlink_auth_t auth; + uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; + mavlink_message_t mavmsg; + const QString login = _AirLinkConfig->modemName(); ///< Connect not to account but to specific modem + const QString pass = _AirLinkConfig->password(); + + memset(&auth.login, 0, sizeof(auth.login)); + memset(&auth.password, 0, sizeof(auth.password)); + strcpy(auth.login, login.toUtf8().constData()); + strcpy(auth.password, pass.toUtf8().constData()); + + (void) mavlink_msg_airlink_auth_pack(0, 0, &mavmsg, auth.login, auth.password); + const uint16_t len = mavlink_msg_to_send_buffer(buffer, &mavmsg); + if (!_stillConnecting()) { + qCDebug(AirLinkLinkLog) << "Force exit from connection"; + return; + } + + writeBytesThreadSafe((const char *)buffer, len); +} + +bool AirLinkLink::_stillConnecting() +{ + QMutexLocker locker(&_mutex); + return _needToConnect; +} + +void AirLinkLink::_setConnectFlag(bool connect) +{ + QMutexLocker locker(&_mutex); + _needToConnect = connect; +} + +AirLinkConfiguration::AirLinkConfiguration(const QString &name) + : UDPConfiguration(name) +{ + // qCDebug(AirLinkLinkLog) << Q_FUNC_INFO << this; +} + +AirLinkConfiguration::AirLinkConfiguration(const AirLinkConfiguration *source) + : UDPConfiguration(source) +{ + // qCDebug(AirLinkLinkLog) << Q_FUNC_INFO << this; + + _copyFrom(source); +} + +AirLinkConfiguration::~AirLinkConfiguration() +{ + // qCDebug(AirLinkLinkLog) << Q_FUNC_INFO << this; +} + +void AirLinkConfiguration::setUsername(const QString &username) +{ + if (username != _username) { + _username = username; + emit usernameChanged(); + } +} + +void AirLinkConfiguration::setPassword(const QString &password) +{ + if (password != _password) { + _password = password; + emit passwordChanged(); + } +} + +void AirLinkConfiguration::setModemName(const QString &modemName) +{ + if (modemName != _modemName) { + _modemName = modemName; + modemNameChanged(); + } +} + +void AirLinkConfiguration::loadSettings(QSettings &settings, const QString &root) +{ + AppSettings *const appSettings = qgcApp()->toolbox()->settingsManager()->appSettings(); + settings.beginGroup(root); + _username = settings.value(_usernameSettingsKey, appSettings->loginAirLink()->rawValueString()).toString(); + _password = settings.value(_passwordSettingsKey, appSettings->passAirLink()->rawValueString()).toString(); + _modemName = settings.value(_modemNameSettingsKey).toString(); + settings.endGroup(); +} + +void AirLinkConfiguration::saveSettings(QSettings &settings, const QString &root) +{ + settings.beginGroup(root); + settings.setValue(_usernameSettingsKey, _username); + settings.setValue(_passwordSettingsKey, _password); + settings.setValue(_modemNameSettingsKey, _modemName); + settings.endGroup(); +} + +void AirLinkConfiguration::copyFrom(const LinkConfiguration *source) +{ + const UDPConfiguration *const udpSource = qobject_cast(source); + if (udpSource) { + UDPConfiguration::copyFrom(source); + } + _copyFrom(source); +} + +void AirLinkConfiguration::_copyFrom(const LinkConfiguration *source) +{ + const AirLinkConfiguration *const AirLinkSource = qobject_cast(source); + if (AirLinkSource) { + _username = AirLinkSource->username(); + _password = AirLinkSource->password(); + _modemName = AirLinkSource->modemName(); + } else { + qCWarning(AirLinkLinkLog) << "Internal error: cannot read AirLinkConfiguration from given source"; + } +} diff --git a/src/Comms/AirLink/AirLinkLink.h b/src/Comms/AirLink/AirLinkLink.h new file mode 100644 index 00000000000..f5380132599 --- /dev/null +++ b/src/Comms/AirLink/AirLinkLink.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include +#include + +#include "UDPLink.h" + +Q_DECLARE_LOGGING_CATEGORY(AirLinkLinkLog) + +class AirLinkConfiguration : public UDPConfiguration +{ + Q_OBJECT + + Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged) + Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) + Q_PROPERTY(QString modemName READ modemName WRITE setModemName NOTIFY modemNameChanged) +public: + AirLinkConfiguration(const QString &name); + AirLinkConfiguration(const AirLinkConfiguration *source); + ~AirLinkConfiguration(); + + QString username() const { return _username; } + QString password() const { return _password; } + QString modemName() const { return _modemName; } + + void setUsername(const QString &username); + void setPassword(const QString &password); + void setModemName(const QString &modemName); + + LinkType type() const override { return LinkConfiguration::AirLink; } + void loadSettings(QSettings& settings, const QString& root) override; + void saveSettings(QSettings& settings, const QString& root) override; + QString settingsURL() override { return "AirLinkSettings.qml"; } + QString settingsTitle() override { return tr("AirLink Link Settings"); } + void copyFrom(const LinkConfiguration *source) override; + +signals: + void usernameChanged(); + void passwordChanged(); + void modemNameChanged(); + +private: + void _copyFrom(const LinkConfiguration *source); + + QString _username; + QString _password; + QString _modemName; + + const QString _usernameSettingsKey = QStringLiteral("username"); + const QString _passwordSettingsKey = QStringLiteral("password"); + const QString _modemNameSettingsKey = QStringLiteral("modemName"); +}; + +class AirLinkLink : public UDPLink +{ + Q_OBJECT + +public: + AirLinkLink(SharedLinkConfigurationPtr &config); + ~AirLinkLink(); + + void disconnect() override; + +private: + bool _connect() override; + + void _configureUdpSettings(); + void _sendLoginMsgToAirLink(); + bool _stillConnecting(); + void _setConnectFlag(bool connect); + + const AirLinkConfiguration *_AirLinkConfig = nullptr; + QMutex _mutex; + bool _needToConnect = false; + + static constexpr const char *_airLinkHost = "air-link.space"; + static constexpr int _airLinkPort = 10000; +}; diff --git a/src/Comms/AirLink/AirLinkManager.cc b/src/Comms/AirLink/AirLinkManager.cc new file mode 100644 index 00000000000..2ca336791e8 --- /dev/null +++ b/src/Comms/AirLink/AirLinkManager.cc @@ -0,0 +1,107 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "AirLinkManager.h" +#include "QGCApplication.h" +#include "QGCToolbox.h" +#include "SettingsManager.h" +#include "QGCLoggingCategory.h" + +#include +#include +#include +#include +#include + +QGC_LOGGING_CATEGORY(AirLinkManagerLog, "qgc.airlink.airlinkmanager"); + +Q_APPLICATION_STATIC(AirLinkManager, _airLinkManager); + +AirLinkManager::AirLinkManager(QObject *parent) + : QObject(parent) + , _mngr(new QNetworkAccessManager(this)) +{ + // qCDebug(AirLinkManagerLog) << Q_FUNC_INFO << this; +} + +AirLinkManager::~AirLinkManager() +{ + // qCDebug(AirLinkManagerLog) << Q_FUNC_INFO << this; +} + +AirLinkManager *AirLinkManager::instance() +{ + return _airLinkManager(); +} + +bool AirLinkManager::isOnline(const QString &drone) +{ + if (!_vehiclesFromServer.contains(drone)) { + return false; + } else { + return _vehiclesFromServer[drone]; + } +} + +void AirLinkManager::updateCredentials(const QString &login, const QString &pass) +{ + qgcApp()->toolbox()->settingsManager()->appSettings()->loginAirLink()->setRawValue(login); + qgcApp()->toolbox()->settingsManager()->appSettings()->passAirLink()->setRawValue(pass); +} + +void AirLinkManager::_connectToAirLinkServer(const QString &login, const QString &pass) +{ + const QUrl url("https://air-link.space/api/gs/getModems"); + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QJsonObject obj; + obj["login"] = login; + obj["password"] = pass; + QJsonDocument doc(obj); + const QByteArray data = doc.toJson(); + + QNetworkReply *const reply = _mngr->post(request, data); + (void) QObject::connect(reply, &QNetworkReply::finished, this, &AirLinkManager::_processReplyAirlinkServer); +} + +void AirLinkManager::_processReplyAirlinkServer() +{ + QNetworkReply* const reply = qobject_cast(sender()); + if (!reply) { + return; + } + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + qCDebug(AirLinkManagerLog) << "Airlink auth - network error"; + return; + } + + const QByteArray ba = reply->readAll(); + + if (!QJsonDocument::fromJson(ba)["modems"].toArray().isEmpty()) { + _parseAnswer(ba); + } else { + qCDebug(AirLinkManagerLog) << "No airlink modems in answer"; + } +} + +void AirLinkManager::_parseAnswer(const QByteArray &ba) +{ + _vehiclesFromServer.clear(); + + for (const auto &arr : QJsonDocument::fromJson(ba)["modems"].toArray()) { + const QString droneModem = arr.toObject()["name"].toString(); + const bool isOnline = arr.toObject()["isOnline"].toBool(); + _vehiclesFromServer[droneModem] = isOnline; + } + + emit droneListChanged(); +} diff --git a/src/Comms/AirLink/AirLinkManager.h b/src/Comms/AirLink/AirLinkManager.h new file mode 100644 index 00000000000..b125de011d6 --- /dev/null +++ b/src/Comms/AirLink/AirLinkManager.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +class QNetworkAccessManager; + +Q_DECLARE_LOGGING_CATEGORY(AirLinkManagerLog) + +class AirLinkManager : public QObject +{ + Q_OBJECT + // QML_ELEMENT + // QML_SINGLETON + + Q_PROPERTY(QStringList droneList READ droneList NOTIFY droneListChanged) + +public: + explicit AirLinkManager(QObject *parent = nullptr); + ~AirLinkManager(); + + /// Gets the singleton instance of AirLinkManager. + /// @return The singleton instance. + static AirLinkManager *instance(); + + Q_INVOKABLE void updateDroneList(const QString &login, const QString &pass) { _connectToAirLinkServer(login, pass); } + Q_INVOKABLE bool isOnline(const QString &drone); + Q_INVOKABLE void updateCredentials(const QString &login, const QString &pass); + + QStringList droneList() const { return _vehiclesFromServer.keys(); } + +signals: + void droneListChanged(); + +private slots: + void _processReplyAirlinkServer(); + +private: + void _connectToAirLinkServer(const QString &login, const QString &pass); + void _parseAnswer(const QByteArray &ba); + + QNetworkAccessManager *_mngr = nullptr; + QMap _vehiclesFromServer; +}; diff --git a/src/AirLink/AirLinkSettings.qml b/src/Comms/AirLink/AirLinkSettings.qml similarity index 68% rename from src/AirLink/AirLinkSettings.qml rename to src/Comms/AirLink/AirLinkSettings.qml index 2b676f32e10..f660ef652c7 100644 --- a/src/AirLink/AirLinkSettings.qml +++ b/src/Comms/AirLink/AirLinkSettings.qml @@ -8,28 +8,23 @@ ****************************************************************************/ -import QtMultimedia import QtQuick import QtQuick.Controls -import QtQuick.Dialogs import QtQuick.Layouts -import QtLocation -import QtPositioning import QGroundControl import QGroundControl.Controllers import QGroundControl.Controls import QGroundControl.FactControls import QGroundControl.FactSystem -import QGroundControl.Palette import QGroundControl.ScreenTools import QGroundControl.SettingsManager ColumnLayout { spacing: _rowSpacing - property Fact _loginFact: QGroundControl.settingsManager.appSettings.loginAirLink - property Fact _passFact: QGroundControl.settingsManager.appSettings.passAirLink + property Fact _loginFact: QGroundControl.settingsManager.appSettings.loginAirLink + property Fact _passFact: QGroundControl.settingsManager.appSettings.passAirLink function saveSettings() { // No need @@ -44,10 +39,7 @@ ColumnLayout { columnSpacing: _colSpacing rowSpacing: _rowSpacing - QGCLabel { - text: qsTr("Login:") - } - + QGCLabel { text: qsTr("Login:") } QGCTextField { id: loginField text: _loginFact.rawValue @@ -56,11 +48,7 @@ ColumnLayout { onTextChanged: subEditConfig.username = loginField.text } - - QGCLabel { - text: qsTr("Password:") - } - + QGCLabel { text: qsTr("Password:") } QGCTextField { id: passwordField text: _passFact.rawValue @@ -70,40 +58,41 @@ ColumnLayout { onTextChanged: subEditConfig.password = passwordField.text } } + QGCLabel { text: "Forgot Your AirLink Password?" font.underline: true Layout.columnSpan: 2 MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: Qt.openUrlExternally("https://air-link.space/forgot-pass") + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: Qt.openUrlExternally("https://air-link.space/forgot-pass") } } RowLayout { spacing: _colSpacing + QGCLabel { - wrapMode: Text.WordWrap - text: qsTr("Don't have an account?") + wrapMode: Text.WordWrap + text: qsTr("Don't have an account?") } + QGCLabel { - font.underline: true - wrapMode: Text.WordWrap - text: qsTr("Register") + font.underline: true + wrapMode: Text.WordWrap + text: qsTr("Register") MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: Qt.openUrlExternally("https://air-link.space/registration") + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: Qt.openUrlExternally("https://air-link.space/registration") } } } - QGCLabel { - text: qsTr("List of available devices") - } + QGCLabel { text: qsTr("List of available devices") } RowLayout { QGCComboBox { @@ -113,6 +102,7 @@ ColumnLayout { subEditConfig.modemName = QGroundControl.airlinkManager.droneList[index] updateConnectionName(subEditConfig.modemName) } + Connections { target: QGroundControl.airlinkManager // model update does not trigger onActivated, so we catch first element manually @@ -124,8 +114,9 @@ ColumnLayout { } } } + QGCButton { - text: qsTr("Refresh") + text: qsTr("Refresh") onClicked: { QGroundControl.airlinkManager.updateDroneList(loginField.text, passwordField.text) refreshHint.visible = false @@ -135,10 +126,10 @@ ColumnLayout { } QGCLabel { - id: refreshHint - Layout.fillWidth: true - font.pointSize: ScreenTools.smallFontPointSize - wrapMode: Text.WordWrap - text: qsTr("Click \"Refresh\" to authorize") + id: refreshHint + Layout.fillWidth: true + font.pointSize: ScreenTools.smallFontPointSize + wrapMode: Text.WordWrap + text: qsTr("Click \"Refresh\" to authorize") } } diff --git a/src/AirLink/CMakeLists.txt b/src/Comms/AirLink/CMakeLists.txt similarity index 78% rename from src/AirLink/CMakeLists.txt rename to src/Comms/AirLink/CMakeLists.txt index d77a0e6a492..aee8e678825 100644 --- a/src/AirLink/CMakeLists.txt +++ b/src/Comms/AirLink/CMakeLists.txt @@ -4,29 +4,26 @@ qt_add_library(AirLink STATIC) option(QGC_AIRLINK_DISABLED "Enable airlink" ON) if(NOT QGC_AIRLINK_DISABLED) - find_package(Qt6 COMPONENTS Network REQUIRED) + find_package(Qt6 REQUIRED COMPONENTS Network QmlIntegration) target_sources(AirLink PRIVATE - AirlinkLink.cc - AirlinkLink.h + AirLinkLink.cc + AirLinkLink.h AirLinkManager.cc AirLinkManager.h ) - add_custom_target(AirLinkQml - SOURCES - AirLinkSettings.qml - ) - target_link_libraries(AirLink PRIVATE Qt6::Network + QGC Settings + Utilities PUBLIC Qt6::Core + Qt6::QmlIntegration Comms - QGC ) target_include_directories(AirLink PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) @@ -39,23 +36,21 @@ if(NOT QGC_AIRLINK_DISABLED) # OUTPUT_TARGETS AirLink_targets # IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} # IMPORTS - # QtMultimedia # QtQuick # QtQuick.Controls - # QtQuick.Dialogs # QtQuick.Layouts - # QtLocation - # QtPositioning # QGroundControl # QGroundControl.Controllers # QGroundControl.Controls # QGroundControl.FactControls # QGroundControl.FactSystem - # QGroundControl.Palette # QGroundControl.ScreenTools # QGroundControl.SettingsManager # ) + + # cmake_print_variables(AirLink_targets) + # target_link_libraries(AirLink PRIVATE AirLinkplugin) else() target_compile_definitions(AirLink PUBLIC QGC_AIRLINK_DISABLED) endif() diff --git a/src/Comms/CMakeLists.txt b/src/Comms/CMakeLists.txt index 05da3a7f48a..643d3973fa3 100644 --- a/src/Comms/CMakeLists.txt +++ b/src/Comms/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(AirLink) add_subdirectory(MockLink) find_package(Qt6 REQUIRED COMPONENTS Core Network Qml Test Widgets) @@ -23,13 +24,13 @@ target_link_libraries(Comms PRIVATE Qt6::Qml Qt6::Test - AirLink MockLink Settings Vehicle PUBLIC Qt6::Core Qt6::Network + AirLink MAVLink QGC QmlControls diff --git a/src/Comms/LinkConfiguration.cc b/src/Comms/LinkConfiguration.cc index cfb4d46c62b..d65137c5951 100644 --- a/src/Comms/LinkConfiguration.cc +++ b/src/Comms/LinkConfiguration.cc @@ -21,7 +21,7 @@ #include "MockLink.h" #endif #ifndef QGC_AIRLINK_DISABLED -#include "AirlinkLink.h" +#include "AirLinkLink.h" #endif LinkConfiguration::LinkConfiguration(const QString &name, QObject *parent) @@ -90,8 +90,8 @@ LinkConfiguration *LinkConfiguration::createSettings(int type, const QString &na break; #endif #ifndef QGC_AIRLINK_DISABLED - case Airlink: - config = new AirlinkConfiguration(name); + case AirLink: + config = new AirLinkConfiguration(name); break; #endif case TypeLast: @@ -132,8 +132,8 @@ LinkConfiguration *LinkConfiguration::duplicateSettings(const LinkConfiguration break; #endif #ifndef QGC_AIRLINK_DISABLED - case Airlink: - dupe = new AirlinkConfiguration(qobject_cast(source)); + case AirLink: + dupe = new AirLinkConfiguration(qobject_cast(source)); break; #endif case TypeLast: diff --git a/src/Comms/LinkConfiguration.h b/src/Comms/LinkConfiguration.h index 0d644b6b65e..ef90c37e8e7 100644 --- a/src/Comms/LinkConfiguration.h +++ b/src/Comms/LinkConfiguration.h @@ -79,7 +79,7 @@ class LinkConfiguration : public QObject TypeMock, ///< Mock Link for Unitesting #endif #ifndef QGC_AIRLINK_DISABLED - Airlink, + AirLink, #endif TypeLogReplay, TypeLast // Last type value (type >= TypeLast == invalid) diff --git a/src/Comms/LinkManager.cc b/src/Comms/LinkManager.cc index 43df54e8d38..d3cd1a1fd54 100644 --- a/src/Comms/LinkManager.cc +++ b/src/Comms/LinkManager.cc @@ -35,7 +35,7 @@ #endif #ifndef QGC_AIRLINK_DISABLED -#include "AirlinkLink.h" +#include "AirLinkLink.h" #endif #ifdef QGC_ZEROCONF_ENABLED @@ -139,8 +139,8 @@ bool LinkManager::createConnectedLink(SharedLinkConfigurationPtr &config) break; #endif #ifndef QGC_AIRLINK_DISABLED - case LinkConfiguration::Airlink: - link = std::make_shared(config); + case LinkConfiguration::AirLink: + link = std::make_shared(config); break; #endif case LinkConfiguration::TypeLast: @@ -343,8 +343,8 @@ void LinkManager::loadLinkConfigurationList() break; #endif #ifndef QGC_AIRLINK_DISABLED - case LinkConfiguration::Airlink: - link = new AirlinkConfiguration(name); + case LinkConfiguration::AirLink: + link = new AirLinkConfiguration(name); break; #endif case LinkConfiguration::TypeLast: diff --git a/src/MAVLink/LibEvents/CMakeLists.txt b/src/MAVLink/LibEvents/CMakeLists.txt index 199a8d99ec4..d046cad14d3 100644 --- a/src/MAVLink/LibEvents/CMakeLists.txt +++ b/src/MAVLink/LibEvents/CMakeLists.txt @@ -31,4 +31,5 @@ target_include_directories(LibEventsWrapper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${libevents_SOURCE_DIR}/libs/cpp + ${libevents_SOURCE_DIR}/libs/cpp/parse ) diff --git a/src/MissionManager/CMakeLists.txt b/src/MissionManager/CMakeLists.txt index 916c33171e5..943fa3f91c3 100644 --- a/src/MissionManager/CMakeLists.txt +++ b/src/MissionManager/CMakeLists.txt @@ -1,7 +1,4 @@ find_package(Qt6 REQUIRED COMPONENTS Core Gui Positioning Qml Xml) -if(QGC_UTM_ADAPTER) - add_definitions(-DQGC_UTM_ADAPTER) -endif() qt_add_library(MissionManager STATIC BlankPlanCreator.cc @@ -97,6 +94,7 @@ target_link_libraries(MissionManager Settings Terrain Utilities + UTMSP Vehicle ) diff --git a/src/MissionManager/GeoFenceController.h b/src/MissionManager/GeoFenceController.h index 7a1d4bdd885..ba7c7d307a4 100644 --- a/src/MissionManager/GeoFenceController.h +++ b/src/MissionManager/GeoFenceController.h @@ -98,7 +98,6 @@ class GeoFenceController : public PlanElementController void paramCircularFenceChanged (void); #ifdef QGC_UTM_ADAPTER - void uploadFlagSent (bool flag); void polygonBoundarySent (QList coords); #endif diff --git a/src/PlanView/CMakeLists.txt b/src/PlanView/CMakeLists.txt index f751c65096f..4867edbaa9d 100644 --- a/src/PlanView/CMakeLists.txt +++ b/src/PlanView/CMakeLists.txt @@ -2,10 +2,6 @@ find_package(Qt6 REQUIRED COMPONENTS Core Qml) qt_add_library(PlanView STATIC) -if(QGC_UTM_ADAPTER) - add_definitions(-DQGC_UTM_ADAPTER) -endif() - file(GLOB QML_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.qml) qt_add_qml_module(PlanView URI QGroundControl.PlanView diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 09e933c7212..b5fd94da5ca 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -23,18 +23,11 @@ #include "QGCCorePlugin.h" #include "SettingsManager.h" #include "QGCApplication.h" -#ifndef QGC_AIRLINK_DISABLED -#include "AirLinkManager.h" -#endif #if defined(QGC_CUSTOM_BUILD) #include CUSTOMHEADER #endif -#ifdef QGC_UTM_ADAPTER -#include "UTMSPManager.h" -#endif - QGCToolbox::QGCToolbox(QGCApplication* app) : QObject(app) { @@ -56,12 +49,6 @@ QGCToolbox::QGCToolbox(QGCApplication* app) _videoManager = new VideoManager (app, this); _mavlinkLogManager = new MAVLinkLogManager (app, this); -#ifndef QGC_AIRLINK_DISABLED - _airlinkManager = new AirLinkManager (app, this); -#endif -#ifdef QGC_UTM_ADAPTER - _utmspManager = new UTMSPManager (app, this); -#endif } void QGCToolbox::setChildToolboxes(void) @@ -82,12 +69,6 @@ void QGCToolbox::setChildToolboxes(void) _qgcPositionManager->setToolbox(this); _videoManager->setToolbox(this); _mavlinkLogManager->setToolbox(this); -#ifndef QGC_AIRLINK_DISABLED - _airlinkManager->setToolbox(this); -#endif -#ifdef QGC_UTM_ADAPTER - _utmspManager->setToolbox(this); -#endif } void QGCToolbox::_scanAndLoadPlugins(QGCApplication* app) diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index 634ee38d213..3cb4a58dedb 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -26,12 +26,6 @@ class VideoManager; class MAVLinkLogManager; class QGCCorePlugin; class SettingsManager; -#ifndef QGC_AIRLINK_DISABLED -class AirLinkManager; -#endif -#ifdef QGC_UTM_ADAPTER -class UTMSPManager; -#endif /// This is used to manage all of our top level services/tools class QGCToolbox : public QObject { @@ -54,12 +48,6 @@ class QGCToolbox : public QObject { #ifndef NO_SERIAL_LINK GPSManager* gpsManager () { return _gpsManager; } #endif -#ifndef QGC_AIRLINK_DISABLED - AirLinkManager* airlinkManager () { return _airlinkManager; } -#endif -#ifdef QGC_UTM_ADAPTER - UTMSPManager* utmspManager () { return _utmspManager; } -#endif private: void setChildToolboxes(void); @@ -79,13 +67,6 @@ class QGCToolbox : public QObject { MAVLinkLogManager* _mavlinkLogManager = nullptr; QGCCorePlugin* _corePlugin = nullptr; SettingsManager* _settingsManager = nullptr; -#ifndef QGC_AIRLINK_DISABLED - AirLinkManager* _airlinkManager = nullptr; -#endif - -#ifdef QGC_UTM_ADAPTER - UTMSPManager* _utmspManager = nullptr; -#endif friend class QGCApplication; }; diff --git a/src/QmlControls/CMakeLists.txt b/src/QmlControls/CMakeLists.txt index 8bbdbbfdd2e..74670f1456a 100644 --- a/src/QmlControls/CMakeLists.txt +++ b/src/QmlControls/CMakeLists.txt @@ -1,9 +1,5 @@ find_package(Qt6 REQUIRED COMPONENTS Concurrent Core Gui Location Positioning Qml QmlIntegration Quick Widgets) -if(QGC_UTM_ADAPTER) - add_definitions(-DQGC_UTM_ADAPTER) -endif() - qt_add_library(QmlControls STATIC AppMessages.cc AppMessages.h diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index ffd7923a57b..3b73596e62f 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -24,7 +24,12 @@ #ifdef QT_DEBUG #include "MockLink.h" #endif - +#ifndef QGC_AIRLINK_DISABLED +#include "AirLinkManager.h" +#endif +#ifdef QGC_UTM_ADAPTER +#include "UTMSPManager.h" +#endif #include #include @@ -35,6 +40,12 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app, QGCToolbox : QGCTool(app, toolbox) , _mapEngineManager(QGCMapEngineManager::instance()) , _adsbVehicleManager(ADSBVehicleManager::instance()) +#ifndef QGC_AIRLINK_DISABLED + , _airlinkManager(AirLinkManager::instance()) +#endif +#ifdef QGC_UTM_ADAPTER + , _utmspManager(UTMSPManager::instance()) +#endif { // We clear the parent on this object since we run into shutdown problems caused by hybrid qml app. Instead we let it leak on shutdown. // setParent(nullptr); @@ -88,12 +99,6 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) _gpsRtkFactGroup = toolbox->gpsManager()->gpsRtkFactGroup(); #endif _globalPalette = new QGCPalette(this); -#ifndef QGC_AIRLINK_DISABLED - _airlinkManager = toolbox->airlinkManager(); -#endif -#ifdef QGC_UTM_ADAPTER - _utmspManager = toolbox->utmspManager(); -#endif } void QGroundControlQmlGlobal::saveGlobalSetting (const QString& key, const QString& value) diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index e9e2b41d39c..09a272e38b7 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -185,15 +185,18 @@ class QGroundControlQmlGlobal : public QGCTool static QGeoCoordinate flightMapPosition () { return _coord; } static double flightMapZoom () { return _zoom; } - AirLinkManager* airlinkManager () { return _airlinkManager; } #ifndef QGC_AIRLINK_DISABLED + AirLinkManager* airlinkManager () { return _airlinkManager; } bool airlinkSupported () { return true; } #else - bool airlinkSupported () { return false; } + bool airlinkSupported () { return false; } #endif #ifdef QGC_UTM_ADAPTER - UTMSPManager* utmspManager () {return _utmspManager;} + UTMSPManager* utmspManager () { return _utmspManager; } + bool utmspSupported () { return true; } +#else + bool utmspSupported () { return false; } #endif qreal zOrderTopMost () { return 1000; } @@ -239,12 +242,6 @@ class QGroundControlQmlGlobal : public QGCTool QString qgcVersion (void) const; -#ifdef QGC_UTM_ADAPTER - bool utmspSupported() { return true; } -#else - bool utmspSupported() { return false; } -#endif - // Overrides from QGCTool virtual void setToolbox(QGCToolbox* toolbox); @@ -271,12 +268,14 @@ class QGroundControlQmlGlobal : public QGCTool #ifndef NO_SERIAL_LINK FactGroup* _gpsRtkFactGroup = nullptr; #endif - AirLinkManager* _airlinkManager = nullptr; ADSBVehicleManager* _adsbVehicleManager = nullptr; QGCPalette* _globalPalette = nullptr; QmlUnitsConversion _unitsConversion; +#ifndef QGC_AIRLINK_DISABLED + AirLinkManager* _airlinkManager = nullptr; +#endif #ifdef QGC_UTM_ADAPTER - UTMSPManager* _utmspManager; + UTMSPManager* _utmspManager = nullptr; #endif bool _skipSetupPage = false; diff --git a/src/UTMSP/CMakeLists.txt b/src/UTMSP/CMakeLists.txt index 006baffcffd..6794a2f83fa 100644 --- a/src/UTMSP/CMakeLists.txt +++ b/src/UTMSP/CMakeLists.txt @@ -6,9 +6,9 @@ qt_add_library(UTMSP STATIC) option(QGC_UTM_ADAPTER "Enable UTM Adapter" OFF) if(QGC_UTM_ADAPTER) message(STATUS "UTMSP is Initialized") - add_definitions(-DQGC_UTM_ADAPTER) + target_compile_definitions(UTMSP PUBLIC QGC_UTM_ADAPTER) - if(NOT nlohmann_json_FOUND) + if(NOT TARGET nlohmann_json) message(STATUS "UTMSP: Fetching nlohmann/json") include(FetchContent) FetchContent_Declare(json diff --git a/src/UTMSP/UTMSPManager.cpp b/src/UTMSP/UTMSPManager.cpp index c90b60ddb65..67acf70f84c 100644 --- a/src/UTMSP/UTMSPManager.cpp +++ b/src/UTMSP/UTMSPManager.cpp @@ -14,12 +14,15 @@ #include "services/dispatcher.h" #include "Vehicle.h" #include "qqml.h" +#include -UTMSPManager::UTMSPManager(QGCApplication* app, QGCToolbox* toolbox) : - QGCTool(app, toolbox), - _dispatcher(std::make_shared()) +Q_APPLICATION_STATIC(UTMSPManager, _UTMSPManager); + +UTMSPManager::UTMSPManager(QObject *parent) + : QObject(parent) + , _dispatcher(std::make_shared()) + , _utmspAuthorization(new UTMSPAuthorization(this)) { - _utmspAuthorization = new UTMSPAuthorization(); qmlRegisterUncreatableType ("QGroundControl.UTMSP", 1, 0, "UTMSPManager", "Reference only"); qmlRegisterUncreatableType ("QGroundControl.UTMSP", 1, 0, "UTMSPVehicle", "Reference only"); qmlRegisterUncreatableType ("QGroundControl.UTMSP", 1, 0, "UTMSPAuthorization", "Reference only"); @@ -28,25 +31,20 @@ UTMSPManager::UTMSPManager(QGCApplication* app, QGCToolbox* toolbox) : UTMSPManager::~UTMSPManager() { UTMSP_LOG_DEBUG() << "UTMSPManager: Destructor called"; - delete _utmspAuthorization; - _utmspAuthorization = nullptr; } -void UTMSPManager::setToolbox(QGCToolbox* toolbox) +UTMSPManager *UTMSPManager::instance() { - UTMSP_LOG_INFO() << "UTMSPManager: ToolBox Set" ; - QGCTool::setToolbox(toolbox); + return _UTMSPManager(); } -UTMSPVehicle* UTMSPManager::instantiateVehicle(const Vehicle& vehicle) +void UTMSPManager::instantiateVehicle(Vehicle *vehicle) { // TODO: Investigate safe deletion of pointer in this modification of having a member pointer - _vehicle = new UTMSPVehicle(_dispatcher,vehicle); - - return _vehicle; + _vehicle = new UTMSPVehicle(_dispatcher, vehicle, vehicle); } -UTMSPAuthorization* UTMSPManager::instantiateUTMSPAuthorization(){ - +UTMSPAuthorization* UTMSPManager::instantiateUTMSPAuthorization() +{ return _utmspAuthorization; } diff --git a/src/UTMSP/UTMSPManager.h b/src/UTMSP/UTMSPManager.h index adf172b3a8c..2fe810cf56b 100644 --- a/src/UTMSP/UTMSPManager.h +++ b/src/UTMSP/UTMSPManager.h @@ -9,8 +9,6 @@ #pragma once -#include "QGCToolbox.h" - #include "UTMSPVehicle.h" class UTMSPVehicle; @@ -18,27 +16,29 @@ class Vehicle; class Dispatcher; class UTMSPAuthorization; -class UTMSPManager : public QGCTool +class UTMSPManager : public QObject { Q_OBJECT Q_MOC_INCLUDE("UTMSPAuthorization.h") -public: - UTMSPManager(QGCApplication* app, QGCToolbox* toolbox); - virtual ~UTMSPManager(); + Q_PROPERTY(UTMSPVehicle *utmspVehicle READ utmspVehicle CONSTANT) + Q_PROPERTY(UTMSPAuthorization *utmspAuthorization READ utmspAuthorization CONSTANT) - Q_PROPERTY(UTMSPVehicle* utmspVehicle READ utmspVehicle CONSTANT) - Q_PROPERTY(UTMSPAuthorization* utmspAuthorization READ utmspAuthorization CONSTANT) +public: + explicit UTMSPManager(QObject *parent = nullptr); + ~UTMSPManager(); - void setToolbox (QGCToolbox* toolbox); + /// Gets the singleton instance of UTMSPManager. + /// @return The singleton instance. + static UTMSPManager *instance(); - UTMSPVehicle* instantiateVehicle (const Vehicle& vehicle); - UTMSPAuthorization* instantiateUTMSPAuthorization (void); - UTMSPAuthorization* utmspAuthorization (void) { return _utmspAuthorization;} - UTMSPVehicle* utmspVehicle (void ) {return _vehicle;}; + void instantiateVehicle(Vehicle *vehicle); + UTMSPAuthorization *instantiateUTMSPAuthorization(); + UTMSPAuthorization *utmspAuthorization() { return _utmspAuthorization; } + UTMSPVehicle *utmspVehicle() { return _vehicle; }; private: - UTMSPVehicle* _vehicle = nullptr; - UTMSPAuthorization* _utmspAuthorization = nullptr; - std::shared_ptr _dispatcher; + UTMSPAuthorization *_utmspAuthorization = nullptr; + std::shared_ptr _dispatcher; + UTMSPVehicle *_vehicle = nullptr; }; diff --git a/src/UTMSP/UTMSPVehicle.cpp b/src/UTMSP/UTMSPVehicle.cpp index 8a7b5cf2a3f..1317f26e086 100644 --- a/src/UTMSP/UTMSPVehicle.cpp +++ b/src/UTMSP/UTMSPVehicle.cpp @@ -12,7 +12,8 @@ #include "services/dispatcher.h" #include "Vehicle.h" -UTMSPVehicle::UTMSPVehicle(std::shared_ptr dispatcher, const Vehicle& vehicle): +UTMSPVehicle::UTMSPVehicle(std::shared_ptr dispatcher, Vehicle *vehicle, QObject *parent) + : UTMSPServiceController(parent), _dispatcher(dispatcher), _remoteIDFlag(false), _stopFlag(false), @@ -21,7 +22,7 @@ UTMSPVehicle::UTMSPVehicle(std::shared_ptr dispatcher, const Vehicle _vehicleActivation(false) { UTMSP_LOG_INFO() << "UTMSPManagerLog: UTMSPVehicle Contructor"; - connect(&vehicle, &Vehicle::mavlinkMessageReceived, this, &UTMSPVehicle::triggerNetworkRemoteID); + connect(vehicle, &Vehicle::mavlinkMessageReceived, this, &UTMSPVehicle::triggerNetworkRemoteID); UTMSP_LOG_INFO() << "UTMSPManagerLog: UTMSPVehicle MAvlink msg slot connected"; _aircraftModel = _utmspAircraft.aircraftModel(); _aircraftClass = _utmspAircraft.aircraftClass(); diff --git a/src/UTMSP/UTMSPVehicle.h b/src/UTMSP/UTMSPVehicle.h index 0cffeb9749e..857ee416dd0 100644 --- a/src/UTMSP/UTMSPVehicle.h +++ b/src/UTMSP/UTMSPVehicle.h @@ -24,7 +24,7 @@ class UTMSPVehicle : public UTMSPServiceController Q_PROPERTY(bool vehicleActivation READ vehicleActivation NOTIFY vehicleActivationChanged) public: - UTMSPVehicle (std::shared_ptr dispatcher,const Vehicle& vehicle); + UTMSPVehicle (std::shared_ptr dispatcher, Vehicle *vehicle, QObject *parent = nullptr); ~UTMSPVehicle () override = default; Q_INVOKABLE void loadTelemetryFlag(bool value); diff --git a/src/Vehicle/CMakeLists.txt b/src/Vehicle/CMakeLists.txt index fa39a324ce6..486e1c1392c 100644 --- a/src/Vehicle/CMakeLists.txt +++ b/src/Vehicle/CMakeLists.txt @@ -4,10 +4,6 @@ add_subdirectory(FactGroups) find_package(Qt6 REQUIRED COMPONENTS Core Gui Positioning Qml) -if(QGC_UTM_ADAPTER) - add_definitions(-DQGC_UTM_ADAPTER) -endif() - qt_add_library(Vehicle STATIC Autotune.cpp Autotune.h diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 27145ba2df8..b611b672fe7 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -54,7 +54,6 @@ #include "GimbalController.h" #ifdef QGC_UTM_ADAPTER -#include "UTMSPVehicle.h" #include "UTMSPManager.h" #endif #ifdef QT_DEBUG @@ -149,10 +148,7 @@ Vehicle::Vehicle(LinkInterface* link, } #ifdef QGC_UTM_ADAPTER - UTMSPManager* utmspManager = _toolbox->utmspManager(); - if (utmspManager) { - _utmspVehicle = utmspManager->instantiateVehicle(*this); - } + UTMSPManager::instance()->instantiateVehicle(this); #endif _autopilotPlugin = _firmwarePlugin->autopilotPlugin(this); @@ -384,10 +380,6 @@ Vehicle::~Vehicle() delete _autopilotPlugin; _autopilotPlugin = nullptr; -#ifdef QGC_UTM_ADAPTER - delete _utmspVehicle; -#endif - deleteGimbalController(); } diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 9f96661f9da..3fbbd648fa1 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -79,9 +79,6 @@ class VehicleBatteryFactGroup; class VehicleObjectAvoidance; class QGCToolbox; class GimbalController; -#ifdef QGC_UTM_ADAPTER -class UTMSPVehicle; -#endif namespace events { namespace parser { @@ -1066,10 +1063,6 @@ private slots: Autotune* _autotune = nullptr; GimbalController* _gimbalController = nullptr; -#ifdef QGC_UTM_ADAPTER - UTMSPVehicle* _utmspVehicle = nullptr; -#endif - bool _armed = false; ///< true: vehicle is armed uint8_t _base_mode = 0; ///< base_mode from HEARTBEAT uint32_t _custom_mode = 0; ///< custom_mode from HEARTBEAT