From 4c989f9368c380bf31472f06ae5b6af2286b021c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Wed, 13 Mar 2019 15:51:00 +0100 Subject: [PATCH] Microhard: Added settings panel similar to Taisync --- QGCCommon.pri | 5 + qgroundcontrol.pro | 17 + qgroundcontrol.qrc | 1 + src/Microhard/MicrohardHandler.cc | 107 ++++ src/Microhard/MicrohardHandler.h | 49 ++ src/Microhard/MicrohardManager.cc | 531 ++++++++++++++++++++ src/Microhard/MicrohardManager.h | 155 ++++++ src/Microhard/MicrohardSettings.cc | 173 +++++++ src/Microhard/MicrohardSettings.h | 41 ++ src/Microhard/MicrohardSettings.qml | 543 +++++++++++++++++++++ src/QGCToolbox.h | 9 + src/QmlControls/QGroundControlQmlGlobal.cc | 3 + src/QmlControls/QGroundControlQmlGlobal.h | 15 + src/Settings/App.SettingsGroup.json | 16 +- src/Settings/AppSettings.cc | 2 + src/Settings/AppSettings.h | 2 + src/api/QGCCorePlugin.cc | 13 + 17 files changed, 1681 insertions(+), 1 deletion(-) create mode 100644 src/Microhard/MicrohardHandler.cc create mode 100644 src/Microhard/MicrohardHandler.h create mode 100644 src/Microhard/MicrohardManager.cc create mode 100644 src/Microhard/MicrohardManager.h create mode 100644 src/Microhard/MicrohardSettings.cc create mode 100644 src/Microhard/MicrohardSettings.h create mode 100644 src/Microhard/MicrohardSettings.qml diff --git a/QGCCommon.pri b/QGCCommon.pri index 9c725866f..5f162ab49 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -22,6 +22,7 @@ linux { CONFIG += LinuxBuild DEFINES += __STDC_LIMIT_MACROS DEFINES += QGC_GST_TAISYNC_ENABLED + DEFINES += QGC_GST_MICROHARD_ENABLED linux-clang { message("Linux clang") QMAKE_CXXFLAGS += -Qunused-arguments -fcolor-diagnostics @@ -31,12 +32,14 @@ linux { CONFIG += LinuxBuild DEFINES += __STDC_LIMIT_MACROS __rasp_pi2__ DEFINES += QGC_GST_TAISYNC_ENABLED + DEFINES += QGC_GST_MICROHARD_ENABLED } else : android-g++ | android-clang { CONFIG += AndroidBuild MobileBuild DEFINES += __android__ DEFINES += __STDC_LIMIT_MACROS DEFINES += QGC_ENABLE_BLUETOOTH DEFINES += QGC_GST_TAISYNC_ENABLED + DEFINES += QGC_GST_MICROHARD_ENABLED target.path = $$DESTDIR equals(ANDROID_TARGET_ARCH, x86) { CONFIG += Androidx86Build @@ -54,6 +57,7 @@ linux { CONFIG += WindowsBuild DEFINES += __STDC_LIMIT_MACROS DEFINES += QGC_GST_TAISYNC_ENABLED + DEFINES += QGC_GST_MICROHARD_ENABLED } else { error("Unsupported Windows toolchain, only Visual Studio 2015 is supported") } @@ -64,6 +68,7 @@ linux { CONFIG += x86_64 CONFIG -= x86 DEFINES += QGC_GST_TAISYNC_ENABLED + DEFINES += QGC_GST_MICROHARD_ENABLED equals(QT_MAJOR_VERSION, 5) | greaterThan(QT_MINOR_VERSION, 5) { QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 } else { diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index c0248732b..79b2919a4 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -1148,6 +1148,23 @@ contains (DEFINES, QGC_GST_TAISYNC_ENABLED) { } } +#------------------------------------------------------------------------------------- +# Microhard +contains (DEFINES, QGC_GST_MICROHARD_ENABLED) { + INCLUDEPATH += \ + src/Microhard + + HEADERS += \ + src/Microhard/MicrohardManager.h \ + src/Microhard/MicrohardHandler.h \ + src/Microhard/MicrohardSettings.h \ + + SOURCES += \ + src/Microhard/MicrohardManager.cc \ + src/Microhard/MicrohardHandler.cc \ + src/Microhard/MicrohardSettings.cc \ +} + #------------------------------------------------------------------------------------- # AirMap diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index b811f360d..c414f09b7 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -201,6 +201,7 @@ src/AutoPilotPlugins/Common/SyslinkComponent.qml src/ui/preferences/TcpSettings.qml src/Taisync/TaisyncSettings.qml + src/Microhard/MicrohardSettings.qml src/test.qml src/ui/preferences/UdpSettings.qml src/FlightMap/Widgets/ValuePageWidget.qml diff --git a/src/Microhard/MicrohardHandler.cc b/src/Microhard/MicrohardHandler.cc new file mode 100644 index 000000000..eed5b6db8 --- /dev/null +++ b/src/Microhard/MicrohardHandler.cc @@ -0,0 +1,107 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "MicrohardHandler.h" +#include "SettingsManager.h" +#include "QGCApplication.h" +#include "VideoManager.h" + + +QGC_LOGGING_CATEGORY(MicrohardLog, "MicrohardLog") +QGC_LOGGING_CATEGORY(MicrohardVerbose, "MicrohardVerbose") + +//----------------------------------------------------------------------------- +MicrohardHandler::MicrohardHandler(QObject* parent) + : QObject (parent) +{ +} + +//----------------------------------------------------------------------------- +MicrohardHandler::~MicrohardHandler() +{ + close(); +} + +//----------------------------------------------------------------------------- +bool MicrohardHandler::close() +{ + bool res = (_tcpSocket || _tcpServer); + if(_tcpSocket) { + qCDebug(MicrohardLog) << "Close Microhard TCP socket on port" << _tcpSocket->localPort(); + _tcpSocket->close(); + _tcpSocket->deleteLater(); + _tcpSocket = nullptr; + } + if(_tcpServer) { + qCDebug(MicrohardLog) << "Close Microhard TCP server on port" << _tcpServer->serverPort();; + _tcpServer->close(); + _tcpServer->deleteLater(); + _tcpServer = nullptr; + } + return res; +} + +//----------------------------------------------------------------------------- +bool +MicrohardHandler::_start(uint16_t port, QHostAddress addr) +{ + close(); + _serverMode = addr == QHostAddress::AnyIPv4; + if(_serverMode) { + if(!_tcpServer) { + qCDebug(MicrohardLog) << "Listen for Microhard TCP on port" << port; + _tcpServer = new QTcpServer(this); + QObject::connect(_tcpServer, &QTcpServer::newConnection, this, &MicrohardHandler::_newConnection); + _tcpServer->listen(QHostAddress::AnyIPv4, port); + } + } else { + _tcpSocket = new QTcpSocket(); + QObject::connect(_tcpSocket, &QIODevice::readyRead, this, &MicrohardHandler::_readBytes); + qCDebug(MicrohardLog) << "Connecting to" << addr; + _tcpSocket->connectToHost(addr, port); + if (!_tcpSocket->waitForConnected(1000)) { + close(); + return false; + } + emit connected(); + } + return true; +} + +//----------------------------------------------------------------------------- +void +MicrohardHandler::_newConnection() +{ + qCDebug(MicrohardLog) << "New Microhard TCP Connection on port" << _tcpServer->serverPort(); + if(_tcpSocket) { + _tcpSocket->close(); + _tcpSocket->deleteLater(); + } + _tcpSocket = _tcpServer->nextPendingConnection(); + if(_tcpSocket) { + QObject::connect(_tcpSocket, &QIODevice::readyRead, this, &MicrohardHandler::_readBytes); + QObject::connect(_tcpSocket, &QAbstractSocket::disconnected, this, &MicrohardHandler::_socketDisconnected); + emit connected(); + } else { + qCWarning(MicrohardLog) << "New Microhard TCP Connection provided no socket"; + } +} + +//----------------------------------------------------------------------------- +void +MicrohardHandler::_socketDisconnected() +{ + qCDebug(MicrohardLog) << "Microhard TCP Connection Closed on port" << _tcpSocket->localPort(); + if(_tcpSocket) { + _tcpSocket->close(); + _tcpSocket->deleteLater(); + _tcpSocket = nullptr; + } + emit disconnected(); +} diff --git a/src/Microhard/MicrohardHandler.h b/src/Microhard/MicrohardHandler.h new file mode 100644 index 000000000..4b2217eb8 --- /dev/null +++ b/src/Microhard/MicrohardHandler.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "QGCLoggingCategory.h" + +#include +#include + +#define MICROHARD_SETTINGS_PORT 23 + +Q_DECLARE_LOGGING_CATEGORY(MicrohardLog) +Q_DECLARE_LOGGING_CATEGORY(MicrohardVerbose) + +class MicrohardHandler : public QObject +{ + Q_OBJECT +public: + + explicit MicrohardHandler (QObject* parent = nullptr); + ~MicrohardHandler (); + virtual bool start () = 0; + virtual bool close (); + virtual bool isServerRunning () { return (_serverMode && _tcpServer); } + +protected: + virtual bool _start (uint16_t port, QHostAddress addr = QHostAddress::AnyIPv4); + +protected slots: + virtual void _newConnection (); + virtual void _socketDisconnected (); + virtual void _readBytes () = 0; + +signals: + void connected (); + void disconnected (); + +protected: + bool _serverMode = true; + QTcpServer* _tcpServer = nullptr; + QTcpSocket* _tcpSocket = nullptr; +}; diff --git a/src/Microhard/MicrohardManager.cc b/src/Microhard/MicrohardManager.cc new file mode 100644 index 000000000..28e6d8ab5 --- /dev/null +++ b/src/Microhard/MicrohardManager.cc @@ -0,0 +1,531 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "MicrohardManager.h" +#include "MicrohardHandler.h" +#include "SettingsManager.h" +#include "QGCApplication.h" +#include "QGCCorePlugin.h" +#include "VideoManager.h" + +#include + +static const char *kMICROHARD_GROUP = "Microhard"; +static const char *kRADIO_MODE = "RadioMode"; +static const char *kRADIO_CHANNEL = "RadioChannel"; +static const char *kVIDEO_OUTPUT = "VideoOutput"; +static const char *kVIDEO_MODE = "VideoMode"; +static const char *kVIDEO_RATE = "VideoRate"; +static const char *kLOCAL_IP = "LocalIP"; +static const char *kREMOTE_IP = "RemoteIP"; +static const char *kNET_MASK = "NetMask"; +static const char *kRTSP_URI = "RTSPURI"; +static const char *kRTSP_ACCOUNT = "RTSPAccount"; +static const char *kRTSP_PASSWORD = "RTSPPassword"; + +//----------------------------------------------------------------------------- +MicrohardManager::MicrohardManager(QGCApplication* app, QGCToolbox* toolbox) + : QGCTool(app, toolbox) +{ + connect(&_workTimer, &QTimer::timeout, this, &MicrohardManager::_checkMicrohard); + _workTimer.setSingleShot(true); + QSettings settings; + settings.beginGroup(kMICROHARD_GROUP); + _localIPAddr = settings.value(kLOCAL_IP, QString("192.168.199.33")).toString(); + _remoteIPAddr = settings.value(kREMOTE_IP, QString("192.168.199.16")).toString(); + _netMask = settings.value(kNET_MASK, QString("255.255.255.0")).toString(); + _rtspURI = settings.value(kRTSP_URI, QString("rtsp://192.168.0.2")).toString(); + _rtspAccount = settings.value(kRTSP_ACCOUNT, QString("admin")).toString(); + _rtspPassword = settings.value(kRTSP_PASSWORD, QString("12345678")).toString(); + settings.endGroup(); +} + +//----------------------------------------------------------------------------- +MicrohardManager::~MicrohardManager() +{ + _close(); +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_close() +{ + if(_mhSettings) { + _mhSettings->close(); + _mhSettings->deleteLater(); + _mhSettings = nullptr; + } +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_reset() +{ + _close(); + _isConnected = false; + emit connectedChanged(); + _linkConnected = false; + emit linkConnectedChanged(); + if(!_appSettings) { + _appSettings = _toolbox->settingsManager()->appSettings(); + connect(_appSettings->enableMicrohard(), &Fact::rawValueChanged, this, &MicrohardManager::_setEnabled); + connect(_appSettings->enableMicrohardVideo(), &Fact::rawValueChanged, this, &MicrohardManager::_setVideoEnabled); + } + _setEnabled(); +} + +//----------------------------------------------------------------------------- +FactMetaData* +MicrohardManager::_createMetadata(const char* name, QStringList enums) +{ + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, name, this); + QQmlEngine::setObjectOwnership(metaData, QQmlEngine::CppOwnership); + metaData->setShortDescription(name); + metaData->setLongDescription(name); + metaData->setRawDefaultValue(QVariant(0)); + metaData->setHasControl(true); + metaData->setReadOnly(false); + for(int i = 0; i < enums.size(); i++) { + metaData->addEnumInfo(enums[i], QVariant(i)); + } + metaData->setRawMin(0); + metaData->setRawMin(enums.size() - 1); + return metaData; +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::setToolbox(QGCToolbox* toolbox) +{ + QGCTool::setToolbox(toolbox); + { + //-- Radio Mode + QStringList enums; + enums.append(tr("Auto")); + enums.append(tr("Manual")); + FactMetaData* metaData = _createMetadata(kRADIO_MODE, enums); + _radioMode = new Fact(kMICROHARD_GROUP, metaData, this); + QQmlEngine::setObjectOwnership(_radioMode, QQmlEngine::CppOwnership); + _radioModeList.append("auto"); + _radioModeList.append("manual"); + connect(_radioMode, &Fact::_containerRawValueChanged, this, &MicrohardManager::_radioSettingsChanged); + } + { + //-- Radio Channel + QStringList enums; + for(int i = 0; i < 14; i++) { + enums.append(QString("ch%1").arg(i)); + } + FactMetaData* metaData = _createMetadata(kRADIO_CHANNEL, enums); + _radioChannel = new Fact(kMICROHARD_GROUP, metaData, this); + QQmlEngine::setObjectOwnership(_radioChannel, QQmlEngine::CppOwnership); + connect(_radioChannel, &Fact::_containerRawValueChanged, this, &MicrohardManager::_radioSettingsChanged); + } + { + //-- Video Output + QStringList enums; + enums.append(tr("Stream")); + enums.append(tr("HDMI Port")); + FactMetaData* metaData = _createMetadata(kVIDEO_OUTPUT, enums); + _videoOutput = new Fact(kMICROHARD_GROUP, metaData, this); + QQmlEngine::setObjectOwnership(_videoOutput, QQmlEngine::CppOwnership); + _videoOutputList.append("phone"); + _videoOutputList.append("hdmi"); + connect(_videoOutput, &Fact::_containerRawValueChanged, this, &MicrohardManager::_videoSettingsChanged); + } + { + //-- Video Mode + QStringList enums; + enums.append("H264"); + enums.append("H265"); + FactMetaData* metaData = _createMetadata(kVIDEO_MODE, enums); + _videoMode = new Fact(kMICROHARD_GROUP, metaData, this); + QQmlEngine::setObjectOwnership(_videoMode, QQmlEngine::CppOwnership); + connect(_videoMode, &Fact::_containerRawValueChanged, this, &MicrohardManager::_videoSettingsChanged); + } + { + //-- Video Rate + QStringList enums; + enums.append(tr("Low")); + enums.append(tr("Medium")); + enums.append(tr("High")); + FactMetaData* metaData = _createMetadata(kVIDEO_RATE, enums); + _videoRate = new Fact(kMICROHARD_GROUP, metaData, this); + QQmlEngine::setObjectOwnership(_videoRate, QQmlEngine::CppOwnership); + _videoRateList.append("low"); + _videoRateList.append("middle"); + _videoRateList.append("high"); + connect(_videoRate, &Fact::_containerRawValueChanged, this, &MicrohardManager::_videoSettingsChanged); + } + //-- Start it all + _reset(); +} + +//----------------------------------------------------------------------------- +bool +MicrohardManager::setRTSPSettings(QString uri, QString account, QString password) +{ + if(_mhSettings && _isConnected) { + if(_mhSettings->setRTSPSettings(uri, account, password)) { + _rtspURI = uri; + _rtspAccount = account; + _rtspPassword = password; + QSettings settings; + settings.beginGroup(kMICROHARD_GROUP); + settings.setValue(kRTSP_URI, _rtspURI); + settings.setValue(kRTSP_ACCOUNT, _rtspAccount); + settings.setValue(kRTSP_PASSWORD, _rtspPassword); + settings.endGroup(); + emit rtspURIChanged(); + emit rtspAccountChanged(); + emit rtspPasswordChanged(); + _needReboot = true; + emit needRebootChanged(); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +bool +MicrohardManager::setIPSettings(QString localIP_, QString remoteIP_, QString netMask_) +{ + bool res = false; + if(_localIPAddr != localIP_ || _remoteIPAddr != remoteIP_ || _netMask != netMask_) { + //-- If we are connected to the Microhard + if(_isConnected) { + if(_mhSettings) { + //-- Change IP settings + res = _mhSettings->setIPSettings(localIP_, remoteIP_, netMask_); + if(res) { + _needReboot = true; + emit needRebootChanged(); + } + } + } else { + //-- We're not connected. Record the change and restart. + _localIPAddr = localIP_; + _remoteIPAddr = remoteIP_; + _netMask = netMask_; + _reset(); + res = true; + } + if(res) { + QSettings settings; + settings.beginGroup(kMICROHARD_GROUP); + settings.setValue(kLOCAL_IP, localIP_); + settings.setValue(kREMOTE_IP, remoteIP_); + settings.setValue(kNET_MASK, netMask_); + settings.endGroup(); + } + } else { + //-- Nothing to change + res = true; + } + return res; +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_radioSettingsChanged(QVariant) +{ + if(_mhSettings && _isConnected) { + _workTimer.stop(); + _mhSettings->setRadioSettings( + _radioModeList[_radioMode->rawValue().toInt()], + _radioChannel->enumStringValue()); + _reqMask |= REQ_RADIO_SETTINGS; + _workTimer.start(3000); + } +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_videoSettingsChanged(QVariant) +{ + if(_mhSettings && _isConnected) { + _workTimer.stop(); + _mhSettings->setVideoSettings( + _videoOutputList[_videoOutput->rawValue().toInt()], + _videoMode->enumStringValue(), + _videoRateList[_videoRate->rawValue().toInt()]); + _reqMask |= REQ_VIDEO_SETTINGS; + _workTimer.start(500); + } +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_setEnabled() +{ + bool enable = _appSettings->enableMicrohard()->rawValue().toBool(); + if(enable) { + if(!_mhSettings) { + _mhSettings = new MicrohardSettings(this); + connect(_mhSettings, &MicrohardSettings::updateSettings, this, &MicrohardManager::_updateSettings); + connect(_mhSettings, &MicrohardSettings::connected, this, &MicrohardManager::_connected); + connect(_mhSettings, &MicrohardSettings::disconnected, this, &MicrohardManager::_disconnected); + } + _reqMask = static_cast(REQ_ALL); + _workTimer.start(1000); + } else { + //-- Stop everything + _workTimer.stop(); + _close(); + } + _enabled = enable; + //-- Now handle video support + _setVideoEnabled(); +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_restoreVideoSettings(Fact* setting) +{ + SettingsFact* pFact = dynamic_cast(setting); + if(pFact) { + pFact->setVisible(qgcApp()->toolbox()->corePlugin()->adjustSettingMetaData(VideoSettings::settingsGroup, *setting->metaData())); + } +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_setVideoEnabled() +{ + //-- Check both if video is enabled and Microhard support itself is enabled as well. + bool enable = _appSettings->enableMicrohardVideo()->rawValue().toBool() && _appSettings->enableMicrohard()->rawValue().toBool(); + if(enable) { + //-- Set it up the way we need it do be. + VideoSettings* pVSettings = qgcApp()->toolbox()->settingsManager()->videoSettings(); + pVSettings->setVisible(false); + pVSettings->udpPort()->setRawValue(5600); + //-- TODO: this AR must come from somewhere + pVSettings->aspectRatio()->setRawValue(1024.0 / 768.0); + pVSettings->videoSource()->setRawValue(QString(VideoSettings::videoSourceUDP)); + } else { + //-- Restore video settings. + VideoSettings* pVSettings = qgcApp()->toolbox()->settingsManager()->videoSettings(); + _restoreVideoSettings(pVSettings->videoSource()); + _restoreVideoSettings(pVSettings->aspectRatio()); + _restoreVideoSettings(pVSettings->udpPort()); + pVSettings->setVisible(true); + } + _enableVideo = enable; +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_connected() +{ + qCDebug(MicrohardLog) << "Microhard Settings Connected"; + _isConnected = true; + emit connectedChanged(); + _needReboot = false; + emit needRebootChanged(); +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_disconnected() +{ + qCDebug(MicrohardLog) << "Microhard Settings Disconnected"; + _isConnected = false; + emit connectedChanged(); + _needReboot = false; + emit needRebootChanged(); + _linkConnected = false; + emit linkConnectedChanged(); + _reset(); +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_checkMicrohard() +{ + if(_enabled) { + if(!_isConnected) { + if(_mhSettings) { + if(!_mhSettings->isServerRunning()) { + _mhSettings->start(); + } + } + } else { + //qCDebug(MicrohardVerbose) << bin << _reqMask; + while(true) { + if (_reqMask & REQ_LINK_STATUS) { + _mhSettings->requestLinkStatus(); + break; + } + if (_reqMask & REQ_DEV_INFO) { + _mhSettings->requestDevInfo(); + break; + } + if (_reqMask & REQ_FREQ_SCAN) { + _reqMask &= ~static_cast(REQ_FREQ_SCAN); + _mhSettings->requestFreqScan(); + break; + } + if (_reqMask & REQ_VIDEO_SETTINGS) { + _mhSettings->requestVideoSettings(); + break; + } + if (_reqMask & REQ_RADIO_SETTINGS) { + _mhSettings->requestRadioSettings(); + break; + } + if (_reqMask & REQ_RTSP_SETTINGS) { + _reqMask &= ~static_cast(REQ_RTSP_SETTINGS); + _mhSettings->requestRTSPURISettings(); + break; + } + if (_reqMask & REQ_IP_SETTINGS) { + _reqMask &= ~static_cast(REQ_IP_SETTINGS); + _mhSettings->requestIPSettings(); + break; + } + //-- Check link status + if(_timeoutTimer.elapsed() > 3000) { + //-- Give up and restart + _disconnected(); + break; + } + //-- If it's been too long since we last heard, ping it. + if(_timeoutTimer.elapsed() > 1000) { + _mhSettings->requestLinkStatus(); + break; + } + break; + } + } + _workTimer.start(_isConnected ? 500 : 5000); + } +} + +//----------------------------------------------------------------------------- +void +MicrohardManager::_updateSettings(QByteArray jSonData) +{ + _timeoutTimer.start(); + qCDebug(MicrohardVerbose) << jSonData; + QJsonParseError jsonParseError; + QJsonDocument doc = QJsonDocument::fromJson(jSonData, &jsonParseError); + if (jsonParseError.error != QJsonParseError::NoError) { + qWarning() << "Unable to parse Microhard response:" << jsonParseError.errorString() << jsonParseError.offset; + return; + } + QJsonObject jObj = doc.object(); + //-- Link Status? + if(jSonData.contains("\"flight\":")) { + _reqMask &= ~static_cast(REQ_LINK_STATUS); + bool tlinkConnected = jObj["flight"].toString("") == "online"; + if(tlinkConnected != _linkConnected) { + _linkConnected = tlinkConnected; + emit linkConnectedChanged(); + } + QString tlinkVidFormat = jObj["videoformat"].toString(_linkVidFormat); + int tdownlinkRSSI = jObj["radiorssi"].toInt(_downlinkRSSI); + int tuplinkRSSI = jObj["hdrssi"].toInt(_uplinkRSSI); + if(_linkVidFormat != tlinkVidFormat || _downlinkRSSI != tdownlinkRSSI || _uplinkRSSI != tuplinkRSSI) { + _linkVidFormat = tlinkVidFormat; + _downlinkRSSI = tdownlinkRSSI; + _uplinkRSSI = tuplinkRSSI; + emit linkChanged(); + } + //-- Device Info? + } else if(jSonData.contains("\"firmwareversion\":")) { + _reqMask &= ~static_cast(REQ_DEV_INFO); + QString tfwVersion = jObj["firmwareversion"].toString(_fwVersion); + QString tserialNumber = jObj["sn"].toString(_serialNumber); + if(tfwVersion != _fwVersion || tserialNumber != _serialNumber) { + _fwVersion = tfwVersion; + _serialNumber = tserialNumber; + emit infoChanged(); + } + //-- Radio Settings? + } else if(jSonData.contains("\"freq\":")) { + _reqMask &= ~static_cast(REQ_RADIO_SETTINGS); + int idx = _radioModeList.indexOf(jObj["mode"].toString(_radioMode->enumStringValue())); + if(idx >= 0) _radioMode->_containerSetRawValue(idx); + idx = _radioChannel->valueIndex(jObj["freq"].toString(_radioChannel->enumStringValue())); + if(idx < 0) idx = 0; + _radioChannel->_containerSetRawValue(idx); + //-- Video Settings? + } else if(jSonData.contains("\"maxbitrate\":")) { + _reqMask &= ~static_cast(REQ_VIDEO_SETTINGS); + int idx; + idx = _videoMode->valueIndex(jObj["mode"].toString(_videoMode->enumStringValue())); + if(idx < 0) idx = 0; + _videoMode->_containerSetRawValue(idx); + idx = _videoRateList.indexOf(jObj["maxbitrate"].toString(_videoMode->enumStringValue())); + if(idx >= 0) _videoRate->_containerSetRawValue(idx); + idx = _videoOutputList.indexOf(jObj["decode"].toString(_videoOutput->enumStringValue())); + if(idx >= 0) _videoOutput->_containerSetRawValue(idx); + //-- IP Address Settings? + } else if(jSonData.contains("\"usbEthIp\":")) { + QString value; + bool changed = false; + value = jObj["ipaddr"].toString(_localIPAddr); + if(value != _localIPAddr) { + _localIPAddr = value; + changed = true; + emit localIPAddrChanged(); + } + value = jObj["netmask"].toString(_netMask); + if(value != _netMask) { + _netMask = value; + changed = true; + emit netMaskChanged(); + } + value = jObj["usbEthIp"].toString(_remoteIPAddr); + if(value != _remoteIPAddr) { + _remoteIPAddr = value; + changed = true; + emit remoteIPAddrChanged(); + } + if(changed) { + QSettings settings; + settings.beginGroup(kMICROHARD_GROUP); + settings.setValue(kLOCAL_IP, _localIPAddr); + settings.setValue(kREMOTE_IP, _remoteIPAddr); + settings.setValue(kNET_MASK, _netMask); + settings.endGroup(); + } + //-- RTSP URI Settings? + } else if(jSonData.contains("\"rtspURI\":")) { + QString value; + bool changed = false; + value = jObj["rtspURI"].toString(_rtspURI); + if(value != _rtspURI) { + _rtspURI = value; + changed = true; + emit rtspURIChanged(); + } + value = jObj["account"].toString(_rtspAccount); + if(value != _rtspAccount) { + _rtspAccount = value; + changed = true; + emit rtspAccountChanged(); + } + value = jObj["passwd"].toString(_rtspPassword); + if(value != _rtspPassword) { + _rtspPassword = value; + changed = true; + emit rtspPasswordChanged(); + } + if(changed) { + QSettings settings; + settings.beginGroup(kMICROHARD_GROUP); + settings.setValue(kRTSP_URI, _rtspURI); + settings.setValue(kRTSP_ACCOUNT, _rtspAccount); + settings.setValue(kRTSP_PASSWORD, _rtspPassword); + settings.endGroup(); + } + } +} diff --git a/src/Microhard/MicrohardManager.h b/src/Microhard/MicrohardManager.h new file mode 100644 index 000000000..17e19f5df --- /dev/null +++ b/src/Microhard/MicrohardManager.h @@ -0,0 +1,155 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "QGCLoggingCategory.h" +#include "MicrohardSettings.h" +#include "Fact.h" + +#include +#include + +class AppSettings; +class QGCApplication; + +//----------------------------------------------------------------------------- +class MicrohardManager : public QGCTool +{ + Q_OBJECT +public: + + Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) + Q_PROPERTY(bool linkConnected READ linkConnected NOTIFY linkConnectedChanged) + Q_PROPERTY(bool needReboot READ needReboot NOTIFY needRebootChanged) + Q_PROPERTY(QString linkVidFormat READ linkVidFormat NOTIFY linkChanged) + Q_PROPERTY(int uplinkRSSI READ uplinkRSSI NOTIFY linkChanged) + Q_PROPERTY(int downlinkRSSI READ downlinkRSSI NOTIFY linkChanged) + Q_PROPERTY(QString serialNumber READ serialNumber NOTIFY infoChanged) + Q_PROPERTY(QString fwVersion READ fwVersion NOTIFY infoChanged) + Q_PROPERTY(Fact* radioMode READ radioMode CONSTANT) + Q_PROPERTY(Fact* radioChannel READ radioChannel CONSTANT) + Q_PROPERTY(Fact* videoOutput READ videoOutput CONSTANT) + Q_PROPERTY(Fact* videoMode READ videoMode CONSTANT) + Q_PROPERTY(Fact* videoRate READ videoRate CONSTANT) + Q_PROPERTY(QString rtspURI READ rtspURI NOTIFY rtspURIChanged) + Q_PROPERTY(QString rtspAccount READ rtspAccount NOTIFY rtspAccountChanged) + Q_PROPERTY(QString rtspPassword READ rtspPassword NOTIFY rtspPasswordChanged) + Q_PROPERTY(QString localIPAddr READ localIPAddr NOTIFY localIPAddrChanged) + Q_PROPERTY(QString remoteIPAddr READ remoteIPAddr NOTIFY remoteIPAddrChanged) + Q_PROPERTY(QString netMask READ netMask NOTIFY netMaskChanged) + + Q_INVOKABLE bool setRTSPSettings (QString uri, QString account, QString password); + Q_INVOKABLE bool setIPSettings (QString localIP, QString remoteIP, QString netMask); + + explicit MicrohardManager (QGCApplication* app, QGCToolbox* toolbox); + ~MicrohardManager () override; + + void setToolbox (QGCToolbox* toolbox) override; + + bool connected () { return _isConnected; } + bool linkConnected () { return _linkConnected; } + bool needReboot () { return _needReboot; } + QString linkVidFormat () { return _linkVidFormat; } + int uplinkRSSI () { return _downlinkRSSI; } + int downlinkRSSI () { return _uplinkRSSI; } + QString serialNumber () { return _serialNumber; } + QString fwVersion () { return _fwVersion; } + Fact* radioMode () { return _radioMode; } + Fact* radioChannel () { return _radioChannel; } + Fact* videoOutput () { return _videoOutput; } + Fact* videoMode () { return _videoMode; } + Fact* videoRate () { return _videoRate; } + QString rtspURI () { return _rtspURI; } + QString rtspAccount () { return _rtspAccount; } + QString rtspPassword () { return _rtspPassword; } + QString localIPAddr () { return _localIPAddr; } + QString remoteIPAddr () { return _remoteIPAddr; } + QString netMask () { return _netMask; } + +signals: + void linkChanged (); + void linkConnectedChanged (); + void infoChanged (); + void connectedChanged (); + void decodeIndexChanged (); + void rateIndexChanged (); + void rtspURIChanged (); + void rtspAccountChanged (); + void rtspPasswordChanged (); + void localIPAddrChanged (); + void remoteIPAddrChanged (); + void netMaskChanged (); + void needRebootChanged (); + +private slots: + void _connected (); + void _disconnected (); + void _checkMicrohard (); + void _updateSettings (QByteArray jSonData); + void _setEnabled (); + void _setVideoEnabled (); + void _radioSettingsChanged (QVariant); + void _videoSettingsChanged (QVariant); + +private: + void _close (); + void _reset (); + void _restoreVideoSettings (Fact* setting); + FactMetaData *_createMetadata (const char *name, QStringList enums); + +private: + + enum { + REQ_LINK_STATUS = 1, + REQ_DEV_INFO = 2, + REQ_FREQ_SCAN = 4, + REQ_VIDEO_SETTINGS = 8, + REQ_RADIO_SETTINGS = 16, + REQ_RTSP_SETTINGS = 32, + REQ_IP_SETTINGS = 64, + REQ_ALL = 0xFFFFFFF, + }; + + uint32_t _reqMask = static_cast(REQ_ALL); + bool _running = false; + bool _isConnected = false; + AppSettings* _appSettings = nullptr; + MicrohardSettings* _mhSettings = nullptr; + bool _enableVideo = true; + bool _enabled = true; + bool _linkConnected = false; + bool _needReboot = false; + QTimer _workTimer; + QString _linkVidFormat; + int _downlinkRSSI = 0; + int _uplinkRSSI = 0; + QStringList _decodeList; + int _decodeIndex = 0; + QStringList _rateList; + int _rateIndex = 0; + QString _serialNumber; + QString _fwVersion; + Fact* _radioMode = nullptr; + Fact* _radioChannel = nullptr; + Fact* _videoOutput = nullptr; + Fact* _videoMode = nullptr; + Fact* _videoRate = nullptr; + QStringList _radioModeList; + QStringList _videoOutputList; + QStringList _videoRateList; + QString _rtspURI; + QString _rtspAccount; + QString _rtspPassword; + QString _localIPAddr; + QString _remoteIPAddr; + QString _netMask; + QTime _timeoutTimer; +}; diff --git a/src/Microhard/MicrohardSettings.cc b/src/Microhard/MicrohardSettings.cc new file mode 100644 index 000000000..b020a9fbb --- /dev/null +++ b/src/Microhard/MicrohardSettings.cc @@ -0,0 +1,173 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "MicrohardSettings.h" +#include "SettingsManager.h" +#include "QGCApplication.h" +#include "VideoManager.h" + +//----------------------------------------------------------------------------- +MicrohardSettings::MicrohardSettings(QObject* parent) + : MicrohardHandler(parent) +{ +} + +//----------------------------------------------------------------------------- +bool MicrohardSettings::start() +{ + qCDebug(MicrohardLog) << "Start Microhard Settings"; + return false; +// return _start(MICROHARD_SETTINGS_PORT, QHostAddress(qgcApp()->toolbox()->microhardManager()->remoteIPAddr())); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::requestLinkStatus() +{ + return _request("/v1/baseband.json"); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::requestDevInfo() +{ + return _request("/v1/device.json"); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::requestFreqScan() +{ + return _request("/v1/freqscan.json"); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::requestVideoSettings() +{ + return false; +// return _request(kVideoURI); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::requestRadioSettings() +{ + return false; +// return _request(kRadioURI); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::requestIPSettings() +{ + return false; +// return _request(kIPAddrURI); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::requestRTSPURISettings() +{ + return false; +// return _request(kRTSPURI); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::_request(const QString& request) +{ + /* + if(_tcpSocket) { + QString req = QString(kGetReq).arg(request); + //qCDebug(MicrohardVerbose) << "Request" << req; + _tcpSocket->write(req.toUtf8()); + return true; + } + */ + return false; +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::_post(const QString& post, const QString &postPayload) +{ + /* + if(_tcpSocket) { + QString req = QString(kPostReq).arg(post).arg(postPayload.size()).arg(postPayload); + qCDebug(MicrohardVerbose) << "Post" << req; + _tcpSocket->write(req.toUtf8()); + return true; + } + */ + return false; +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::setRadioSettings(const QString& mode, const QString& channel) +{ +// static const char* kRadioPost = "{\"mode\":\"%1\",\"freq\":\"%2\"}"; +// QString post = QString(kRadioPost).arg(mode).arg(channel); + return false; +// return _post(kRadioURI, post); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::setVideoSettings(const QString& output, const QString& mode, const QString& rate) +{ + return false; +/* + static const char* kVideoPost = "{\"decode\":\"%1\",\"mode\":\"%2\",\"maxbitrate\":\"%3\"}"; + QString post = QString(kVideoPost).arg(output).arg(mode).arg(rate); + return _post(kVideoURI, post); + */ +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::setRTSPSettings(const QString& uri, const QString& account, const QString& password) +{ + return false; +// static const char* kRTSPPost = "{\"rtspURI\":\"%1\",\"account\":\"%2\",\"passwd\":\"%3\"}"; +// QString post = QString(kRTSPPost).arg(uri).arg(account).arg(password); +// return _post(kRTSPURI, post); +} + +//----------------------------------------------------------------------------- +bool +MicrohardSettings::setIPSettings(const QString& localIP, const QString& remoteIP, const QString& netMask) +{ + return false; +// static const char* kRTSPPost = "{\"ipaddr\":\"%1\",\"netmask\":\"%2\",\"usbEthIp\":\"%3\"}"; +// QString post = QString(kRTSPPost).arg(localIP).arg(netMask).arg(remoteIP); +// return _post(kIPAddrURI, post); +} + +//----------------------------------------------------------------------------- +void +MicrohardSettings::_readBytes() +{ + QByteArray bytesIn = _tcpSocket->read(_tcpSocket->bytesAvailable()); + //-- Go straight to Json payload + int idx = bytesIn.indexOf('{'); + //-- We may receive more than one response within one TCP packet. + while(idx >= 0) { + bytesIn = bytesIn.mid(idx); + idx = bytesIn.indexOf('}'); + if(idx > 0) { + QByteArray data = bytesIn.left(idx + 1); + emit updateSettings(data); + bytesIn = bytesIn.mid(idx+1); + idx = bytesIn.indexOf('{'); + } + } +} + diff --git a/src/Microhard/MicrohardSettings.h b/src/Microhard/MicrohardSettings.h new file mode 100644 index 000000000..f38669783 --- /dev/null +++ b/src/Microhard/MicrohardSettings.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "MicrohardHandler.h" + +class MicrohardSettings : public MicrohardHandler +{ + Q_OBJECT +public: + explicit MicrohardSettings (QObject* parent = nullptr); + bool start () override; + bool requestLinkStatus (); + bool requestDevInfo (); + bool requestFreqScan (); + bool requestVideoSettings (); + bool requestRadioSettings (); + bool requestIPSettings (); + bool requestRTSPURISettings (); + bool setRadioSettings (const QString& mode, const QString& channel); + bool setVideoSettings (const QString& output, const QString& mode, const QString& rate); + bool setRTSPSettings (const QString& uri, const QString& account, const QString& password); + bool setIPSettings (const QString& localIP, const QString& remoteIP, const QString& netMask); + +signals: + void updateSettings (QByteArray jSonData); + +protected slots: + void _readBytes () override; + +private: + bool _request (const QString& request); + bool _post (const QString& post, const QString& postPayload); +}; diff --git a/src/Microhard/MicrohardSettings.qml b/src/Microhard/MicrohardSettings.qml new file mode 100644 index 000000000..311db2e21 --- /dev/null +++ b/src/Microhard/MicrohardSettings.qml @@ -0,0 +1,543 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + + +import QtGraphicalEffects 1.0 +import QtMultimedia 5.5 +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.2 +import QtLocation 5.3 +import QtPositioning 5.3 + +import QGroundControl 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.SettingsManager 1.0 + +QGCView { + id: _qgcView + viewPanel: panel + color: qgcPal.window + anchors.fill: parent + anchors.margins: ScreenTools.defaultFontPixelWidth + + property real _labelWidth: ScreenTools.defaultFontPixelWidth * 26 + property real _valueWidth: ScreenTools.defaultFontPixelWidth * 20 + property real _panelWidth: _qgcView.width * _internalWidthRatio + property Fact _microhardEnabledFact: QGroundControl.settingsManager.appSettings.enableMicrohard + property Fact _microhardVideoEnabledFact: QGroundControl.settingsManager.appSettings.enableMicrohardVideo + property bool _microhardEnabled: _microhardEnabledFact.rawValue + + readonly property real _internalWidthRatio: 0.8 + + QGCPalette { id: qgcPal } + + QGCViewPanel { + id: panel + anchors.fill: parent + QGCFlickable { + clip: true + anchors.fill: parent + contentHeight: settingsColumn.height + contentWidth: settingsColumn.width + Column { + id: settingsColumn + width: _qgcView.width + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.margins: ScreenTools.defaultFontPixelWidth + QGCLabel { + text: qsTr("Reboot ground unit for changes to take effect.") + color: qgcPal.colorOrange + visible: QGroundControl.microhardManager.needReboot + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + //----------------------------------------------------------------- + //-- General + Item { + width: _panelWidth + height: generalLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + id: generalLabel + text: qsTr("General") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: generalRow.height + (ScreenTools.defaultFontPixelHeight * 2) + width: _panelWidth + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Row { + id: generalRow + spacing: ScreenTools.defaultFontPixelWidth * 4 + anchors.centerIn: parent + Column { + spacing: ScreenTools.defaultFontPixelWidth + FactCheckBox { + text: qsTr("Enable Microhard") + fact: _microhardEnabledFact + enabled: !QGroundControl.microhardManager.needReboot + visible: _microhardEnabledFact.visible + } + FactCheckBox { + text: qsTr("Enable Microhard Video") + fact: _microhardVideoEnabledFact + visible: _microhardVideoEnabledFact.visible + enabled: _microhardEnabled && !QGroundControl.microhardManager.needReboot + } + } + } + } + //----------------------------------------------------------------- + //-- Connection Status + Item { + width: _panelWidth + height: statusLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _microhardEnabled + QGCLabel { + id: statusLabel + text: qsTr("Connection Status") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: statusCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: _panelWidth + color: qgcPal.windowShade + visible: _microhardEnabled + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: statusCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: parent.width + anchors.centerIn: parent + GridLayout { + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth * 2 + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + QGCLabel { + text: qsTr("Ground Unit:") + Layout.minimumWidth: _labelWidth + } + QGCLabel { + text: QGroundControl.microhardManager.connected ? qsTr("Connected") : qsTr("Not Connected") + color: QGroundControl.microhardManager.connected ? qgcPal.colorGreen : qgcPal.colorRed + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Air Unit:") + } + QGCLabel { + text: QGroundControl.microhardManager.linkConnected ? qsTr("Connected") : qsTr("Not Connected") + color: QGroundControl.microhardManager.linkConnected ? qgcPal.colorGreen : qgcPal.colorRed + } + QGCLabel { + text: qsTr("Uplink RSSI:") + } + QGCLabel { + text: QGroundControl.microhardManager.linkConnected ? QGroundControl.microhardManager.uplinkRSSI : "" + } + QGCLabel { + text: qsTr("Downlink RSSI:") + } + QGCLabel { + text: QGroundControl.microhardManager.linkConnected ? QGroundControl.microhardManager.downlinkRSSI : "" + } + } + } + } + //----------------------------------------------------------------- + //-- Device Info + Item { + width: _panelWidth + height: devInfoLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _microhardEnabled && QGroundControl.microhardManager.connected + QGCLabel { + id: devInfoLabel + text: qsTr("Device Info") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: devInfoCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: _panelWidth + color: qgcPal.windowShade + visible: _microhardEnabled && QGroundControl.microhardManager.connected + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: devInfoCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: parent.width + anchors.centerIn: parent + GridLayout { + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth * 2 + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + QGCLabel { + text: qsTr("Serial Number:") + Layout.minimumWidth: _labelWidth + } + QGCLabel { + text: QGroundControl.microhardManager.connected ? QGroundControl.microhardManager.serialNumber : qsTr("") + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Firmware Version:") + } + QGCLabel { + text: QGroundControl.microhardManager.connected ? QGroundControl.microhardManager.fwVersion : qsTr("") + } + } + } + } + //----------------------------------------------------------------- + //-- Radio Settings + Item { + width: _panelWidth + height: radioSettingsLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _microhardEnabled && QGroundControl.microhardManager.linkConnected + QGCLabel { + id: radioSettingsLabel + text: qsTr("Radio Settings") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: radioSettingsCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: _panelWidth + color: qgcPal.windowShade + visible: _microhardEnabled && QGroundControl.microhardManager.linkConnected + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: radioSettingsCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: parent.width + anchors.centerIn: parent + GridLayout { + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth * 2 + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + QGCLabel { + text: qsTr("Radio Mode:") + Layout.minimumWidth: _labelWidth + } + FactComboBox { + fact: QGroundControl.microhardManager.radioMode + indexModel: true + enabled: QGroundControl.microhardManager.linkConnected && !QGroundControl.microhardManager.needReboot + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Radio Frequency:") + } + FactComboBox { + fact: QGroundControl.microhardManager.radioChannel + indexModel: true + enabled: QGroundControl.microhardManager.linkConnected && QGroundControl.microhardManager.radioMode.rawValue > 0 && !QGroundControl.microhardManager.needReboot + Layout.minimumWidth: _valueWidth + } + } + } + } + //----------------------------------------------------------------- + //-- Video Settings + Item { + width: _panelWidth + height: videoSettingsLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _microhardEnabled && QGroundControl.microhardManager.linkConnected + QGCLabel { + id: videoSettingsLabel + text: qsTr("Video Settings") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: videoSettingsCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: _panelWidth + color: qgcPal.windowShade + visible: _microhardEnabled && QGroundControl.microhardManager.linkConnected + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: videoSettingsCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: parent.width + anchors.centerIn: parent + GridLayout { + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth * 2 + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + QGCLabel { + text: qsTr("Video Output:") + Layout.minimumWidth: _labelWidth + } + FactComboBox { + fact: QGroundControl.microhardManager.videoOutput + indexModel: true + enabled: QGroundControl.microhardManager.linkConnected && !QGroundControl.microhardManager.needReboot + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Encoder:") + } + FactComboBox { + fact: QGroundControl.microhardManager.videoMode + indexModel: true + enabled: QGroundControl.microhardManager.linkConnected && !QGroundControl.microhardManager.needReboot + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Bit Rate:") + } + FactComboBox { + fact: QGroundControl.microhardManager.videoRate + indexModel: true + enabled: QGroundControl.microhardManager.linkConnected && !QGroundControl.microhardManager.needReboot + Layout.minimumWidth: _valueWidth + } + } + } + } + //----------------------------------------------------------------- + //-- RTSP Settings + Item { + width: _panelWidth + height: rtspSettingsLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _microhardEnabled && QGroundControl.microhardManager.connected + QGCLabel { + id: rtspSettingsLabel + text: qsTr("Streaming Settings") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: rtspSettingsCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: _panelWidth + color: qgcPal.windowShade + visible: _microhardEnabled && QGroundControl.microhardManager.connected + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: rtspSettingsCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: parent.width + anchors.centerIn: parent + GridLayout { + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth * 2 + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + QGCLabel { + text: qsTr("RTSP URI:") + Layout.minimumWidth: _labelWidth + } + QGCTextField { + id: rtspURI + text: QGroundControl.microhardManager.rtspURI + enabled: QGroundControl.microhardManager.connected && !QGroundControl.microhardManager.needReboot + inputMethodHints: Qt.ImhUrlCharactersOnly + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Account:") + } + QGCTextField { + id: rtspAccount + text: QGroundControl.microhardManager.rtspAccount + enabled: QGroundControl.microhardManager.connected && !QGroundControl.microhardManager.needReboot + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Password:") + } + QGCTextField { + id: rtspPassword + text: QGroundControl.microhardManager.rtspPassword + enabled: QGroundControl.microhardManager.connected && !QGroundControl.microhardManager.needReboot + inputMethodHints: Qt.ImhHiddenText + Layout.minimumWidth: _valueWidth + } + } + Item { + width: 1 + height: ScreenTools.defaultFontPixelHeight + } + QGCButton { + function testEnabled() { + if(!QGroundControl.microhardManager.connected) + return false + if(rtspPassword.text === QGroundControl.microhardManager.rtspPassword && + rtspAccount.text === QGroundControl.microhardManager.rtspAccount && + rtspURI.text === QGroundControl.microhardManager.rtspURI) + return false + if(rtspURI === "") + return false + return true + } + enabled: testEnabled() && !QGroundControl.microhardManager.needReboot + text: qsTr("Apply") + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + setRTSPDialog.open() + } + MessageDialog { + id: setRTSPDialog + icon: StandardIcon.Warning + standardButtons: StandardButton.Yes | StandardButton.No + title: qsTr("Set Streaming Settings") + text: qsTr("Once changed, you will need to reboot the ground unit for the changes to take effect.\n\nConfirm change?") + onYes: { + QGroundControl.microhardManager.setRTSPSettings(rtspURI.text, rtspAccount.text, rtspPassword.text) + setRTSPDialog.close() + } + onNo: { + setRTSPDialog.close() + } + } + } + } + } + //----------------------------------------------------------------- + //-- IP Settings + Item { + width: _panelWidth + height: ipSettingsLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _microhardEnabled && (!ScreenTools.isiOS && !ScreenTools.isAndroid) + QGCLabel { + id: ipSettingsLabel + text: qsTr("Network Settings") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: ipSettingsCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: _panelWidth + color: qgcPal.windowShade + visible: _microhardEnabled && (!ScreenTools.isiOS && !ScreenTools.isAndroid) + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: ipSettingsCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: parent.width + anchors.centerIn: parent + GridLayout { + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth * 2 + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + QGCLabel { + text: qsTr("Local IP Address:") + Layout.minimumWidth: _labelWidth + } + QGCTextField { + id: localIP + text: QGroundControl.microhardManager.localIPAddr + enabled: !QGroundControl.microhardManager.needReboot + inputMethodHints: Qt.ImhFormattedNumbersOnly + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Ground Unit IP Address:") + } + QGCTextField { + id: remoteIP + text: QGroundControl.microhardManager.remoteIPAddr + enabled: !QGroundControl.microhardManager.needReboot + inputMethodHints: Qt.ImhFormattedNumbersOnly + Layout.minimumWidth: _valueWidth + } + QGCLabel { + text: qsTr("Network Mask:") + } + QGCTextField { + id: netMask + text: QGroundControl.microhardManager.netMask + enabled: !QGroundControl.microhardManager.needReboot + inputMethodHints: Qt.ImhFormattedNumbersOnly + Layout.minimumWidth: _valueWidth + } + } + Item { + width: 1 + height: ScreenTools.defaultFontPixelHeight + } + QGCButton { + function validateIPaddress(ipaddress) { + if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress)) + return true + return false + } + function testEnabled() { + if(localIP.text === QGroundControl.microhardManager.localIPAddr && + remoteIP.text === QGroundControl.microhardManager.remoteIPAddr && + netMask.text === QGroundControl.microhardManager.netMask) + return false + if(!validateIPaddress(localIP.text)) return false + if(!validateIPaddress(remoteIP.text)) return false + if(!validateIPaddress(netMask.text)) return false + return true + } + enabled: testEnabled() && !QGroundControl.microhardManager.needReboot + text: qsTr("Apply") + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + setIPDialog.open() + } + MessageDialog { + id: setIPDialog + icon: StandardIcon.Warning + standardButtons: StandardButton.Yes | StandardButton.No + title: qsTr("Set Network Settings") + text: qsTr("Once changed, you will need to reboot the ground unit for the changes to take effect. The local IP address must match the one entered (%1).\n\nConfirm change?").arg(localIP.text) + onYes: { + QGroundControl.microhardManager.setIPSettings(localIP.text, remoteIP.text, netMask.text) + setIPDialog.close() + } + onNo: { + setIPDialog.close() + } + } + } + } + } + } + } + } +} diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index 8df56423e..eaba4616e 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -36,6 +36,9 @@ class AirspaceManager; #if defined(QGC_GST_TAISYNC_ENABLED) class TaisyncManager; #endif +#if defined(QGC_GST_MICROHARD_ENABLED) +class MicrohardManager; +#endif /// This is used to manage all of our top level services/tools class QGCToolbox : public QObject { @@ -67,6 +70,9 @@ public: #if defined(QGC_GST_TAISYNC_ENABLED) TaisyncManager* taisyncManager () { return _taisyncManager; } #endif +#if defined(QGC_GST_MICROHARD_ENABLED) + MicrohardManager* microhardManager () { return _microhardManager; } +#endif private: void setChildToolboxes(void); @@ -96,6 +102,9 @@ private: AirspaceManager* _airspaceManager = nullptr; #if defined(QGC_GST_TAISYNC_ENABLED) TaisyncManager* _taisyncManager = nullptr; +#endif +#if defined(QGC_GST_MICROHARD_ENABLED) + MicrohardManager* _microhardManager = nullptr; #endif friend class QGCApplication; }; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index b223793fd..fbf84f2f7 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -69,6 +69,9 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) #if defined(QGC_GST_TAISYNC_ENABLED) _taisyncManager = toolbox->taisyncManager(); #endif +#if defined(QGC_GST_MICROHARD_ENABLED) + _microhardManager = toolbox->microhardManager(); +#endif } void QGroundControlQmlGlobal::saveGlobalSetting (const QString& key, const QString& value) diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 26c3631e5..0a4fb9205 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -28,6 +28,11 @@ #else class TaisyncManager; #endif +#if defined(QGC_GST_MICROHARD_ENABLED) +#include "MicrohardManager.h" +#else +class MicrohardManager; +#endif #ifdef QT_DEBUG #include "MockLink.h" @@ -68,6 +73,8 @@ public: Q_PROPERTY(bool airmapSupported READ airmapSupported CONSTANT) Q_PROPERTY(TaisyncManager* taisyncManager READ taisyncManager CONSTANT) Q_PROPERTY(bool taisyncSupported READ taisyncSupported CONSTANT) + Q_PROPERTY(MicrohardManager* microhardManager READ microhardManager CONSTANT) + Q_PROPERTY(bool microhardSupported READ microhardSupported CONSTANT) Q_PROPERTY(int supportedFirmwareCount READ supportedFirmwareCount CONSTANT) Q_PROPERTY(bool px4ProFirmwareSupported READ px4ProFirmwareSupported CONSTANT) @@ -170,6 +177,13 @@ public: bool taisyncSupported () { return false; } #endif + MicrohardManager* microhardManager () { return _microhardManager; } +#if defined(QGC_GST_TAISYNC_ENABLED) + bool microhardSupported () { return true; } +#else + bool microhardSupported () { return false; } +#endif + qreal zOrderTopMost () { return 1000; } qreal zOrderWidgets () { return 100; } qreal zOrderMapItems () { return 50; } @@ -230,6 +244,7 @@ private: FactGroup* _gpsRtkFactGroup = nullptr; AirspaceManager* _airspaceManager = nullptr; TaisyncManager* _taisyncManager = nullptr; + MicrohardManager* _microhardManager = nullptr; bool _skipSetupPage = false; diff --git a/src/Settings/App.SettingsGroup.json b/src/Settings/App.SettingsGroup.json index 67ef154dc..fd0c89667 100644 --- a/src/Settings/App.SettingsGroup.json +++ b/src/Settings/App.SettingsGroup.json @@ -221,4 +221,18 @@ "type": "bool", "defaultValue": true } -] +, +{ + "name": "enableMicrohard", + "shortDescription": "Enable Microhard Module Support", + "longDescription": "Enable Microhard Module Support", + "type": "bool", + "defaultValue": false +}, +{ + "name": "enableMicrohardVideo", + "shortDescription": "Enable Microhard Video Support", + "longDescription": "Enable Microhard Video Support", + "type": "bool", + "defaultValue": true +}] diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index abfdfb4a6..3d69c86e1 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -87,6 +87,8 @@ DECLARE_SETTINGSFACT(AppSettings, followTarget) DECLARE_SETTINGSFACT(AppSettings, apmStartMavlinkStreams) DECLARE_SETTINGSFACT(AppSettings, enableTaisync) DECLARE_SETTINGSFACT(AppSettings, enableTaisyncVideo) +DECLARE_SETTINGSFACT(AppSettings, enableMicrohard) +DECLARE_SETTINGSFACT(AppSettings, enableMicrohardVideo) DECLARE_SETTINGSFACT_NO_FUNC(AppSettings, indoorPalette) { diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index 37784b9be..c4e966fa3 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -45,6 +45,8 @@ public: DEFINE_SETTINGFACT(followTarget) DEFINE_SETTINGFACT(enableTaisync) DEFINE_SETTINGFACT(enableTaisyncVideo) + DEFINE_SETTINGFACT(enableMicrohard) + DEFINE_SETTINGFACT(enableMicrohardVideo) // Although this is a global setting it only affects ArduPilot vehicle since PX4 automatically starts the stream from the vehicle side DEFINE_SETTINGFACT(apmStartMavlinkStreams) diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index eeaa2678d..0a1a6dc02 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -48,6 +48,10 @@ public: if(pTaisync) delete pTaisync; #endif +#if defined(QGC_GST_MICROHARD_ENABLED) + if(pMicrohard) + delete pMicrohard; +#endif #if defined(QGC_AIRMAP_ENABLED) if(pAirmap) delete pAirmap; @@ -72,6 +76,9 @@ public: #if defined(QGC_GST_TAISYNC_ENABLED) QmlComponentInfo* pTaisync = nullptr; #endif +#if defined(QGC_GST_MICROHARD_ENABLED) + QmlComponentInfo* pMicrohard = nullptr; +#endif #if defined(QGC_AIRMAP_ENABLED) QmlComponentInfo* pAirmap = nullptr; #endif @@ -140,6 +147,12 @@ QVariantList &QGCCorePlugin::settingsPages() QUrl::fromUserInput("")); _p->settingsList.append(QVariant::fromValue(reinterpret_cast(_p->pTaisync))); #endif +#if defined(QGC_GST_MICROHARD_ENABLED) + _p->pMicrohard = new QmlComponentInfo(tr("Microhard"), + QUrl::fromUserInput("qrc:/qml/MicrohardSettings.qml"), + QUrl::fromUserInput("")); + _p->settingsList.append(QVariant::fromValue(reinterpret_cast(_p->pMicrohard))); +#endif #if defined(QGC_AIRMAP_ENABLED) _p->pAirmap = new QmlComponentInfo(tr("AirMap"), QUrl::fromUserInput("qrc:/qml/AirmapSettings.qml"), -- 2.22.0