/**************************************************************************** * * (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(); } } }