Skip to content
LinkManager.cc 23.3 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/*=====================================================================
lm's avatar
lm committed

QGroundControl Open Source Ground Control Station

(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
lm's avatar
lm committed

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
pixhawk's avatar
pixhawk committed
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
lm's avatar
lm committed

    QGROUNDCONTROL is distributed in the hope that it will be useful,
pixhawk's avatar
pixhawk committed
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
pixhawk's avatar
pixhawk committed
    You should have received a copy of the GNU General Public License
lm's avatar
lm committed
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

pixhawk's avatar
pixhawk committed
======================================================================*/
pixhawk's avatar
pixhawk committed
/**
 * @file
 *   @brief Brief Description
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QList>
#include <QApplication>
dogmaphobic's avatar
dogmaphobic committed

#ifndef __ios__
Don Gagne's avatar
Don Gagne committed
#include "QGCSerialPortInfo.h"
dogmaphobic's avatar
dogmaphobic committed
#endif
#include "LinkManager.h"
#include "MainWindow.h"
Don Gagne's avatar
Don Gagne committed
#include "QGCMessageBox.h"
#include "QGCApplication.h"
#include "QGCApplication.h"
Don Gagne's avatar
Don Gagne committed
#include "UDPLink.h"
#include "TCPLink.h"
Don Gagne's avatar
Don Gagne committed
QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog")
Don Gagne's avatar
Don Gagne committed
QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "LinkManagerVerboseLog")

const char* LinkManager::_settingsGroup =           "LinkManager";
const char* LinkManager::_autoconnectUDPKey =       "AutoconnectUDP";
const char* LinkManager::_autoconnectPixhawkKey =   "AutoconnectPixhawk";
const char* LinkManager::_autoconnect3DRRadioKey =  "Autoconnect3DRRadio";
const char* LinkManager::_autoconnectPX4FlowKey =   "AutoconnectPX4Flow";
LinkManager::LinkManager(QGCApplication* app)
    : QGCTool(app)
    , _configUpdateSuspended(false)
    , _configurationsLoaded(false)
    , _connectionsSuspended(false)
    , _mavlinkChannelsUsedBitMask(0)
    , _mavlinkProtocol(NULL)
Don Gagne's avatar
Don Gagne committed
    , _autoconnectUDPConfig(NULL)
    , _autoconnectUDP(true)
    , _autoconnectPixhawk(true)
    , _autoconnect3DRRadio(true)
    , _autoconnectPX4Flow(true)

pixhawk's avatar
pixhawk committed
{
Don Gagne's avatar
Don Gagne committed
    qmlRegisterUncreatableType<LinkManager>         ("QGroundControl", 1, 0, "LinkManager",         "Reference only");
    qmlRegisterUncreatableType<LinkConfiguration>   ("QGroundControl", 1, 0, "LinkConfiguration",   "Reference only");
    qmlRegisterUncreatableType<LinkInterface>       ("QGroundControl", 1, 0, "LinkInterface",       "Reference only");

    QSettings settings;
Don Gagne's avatar
Don Gagne committed
    settings.beginGroup(_settingsGroup);
    _autoconnectUDP =       settings.value(_autoconnectUDPKey, true).toBool();
    _autoconnectPixhawk =   settings.value(_autoconnectPixhawkKey, true).toBool();
    _autoconnect3DRRadio =  settings.value(_autoconnect3DRRadioKey, true).toBool();
    _autoconnectPX4Flow =   settings.value(_autoconnectPX4FlowKey, true).toBool();
pixhawk's avatar
pixhawk committed
}

LinkManager::~LinkManager()
{
Don Gagne's avatar
Don Gagne committed
    if (anyActiveLinks()) {
        qWarning() << "Why are there still active links?";
pixhawk's avatar
pixhawk committed
}

void LinkManager::setToolbox(QGCToolbox *toolbox)
{
   QGCTool::setToolbox(toolbox);

   _mavlinkProtocol = _toolbox->mavlinkProtocol();
Don Gagne's avatar
Don Gagne committed
   connect(_mavlinkProtocol, &MAVLinkProtocol::vehicleHeartbeatInfo, this, &LinkManager::_vehicleHeartbeatInfo);

#ifndef __ios__
Don Gagne's avatar
Don Gagne committed
    connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateAutoConnectLinks);
    _portListTimer.start(1000);
#endif
}

Don Gagne's avatar
Don Gagne committed
LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config, bool autoconnectLink)
{
    Q_ASSERT(config);
    LinkInterface* pLink = NULL;
    switch(config->type()) {
dogmaphobic's avatar
dogmaphobic committed
#ifndef __ios__
        case LinkConfiguration::TypeSerial:
            pLink = new SerialLink(dynamic_cast<SerialConfiguration*>(config));
            break;
dogmaphobic's avatar
dogmaphobic committed
#endif
        case LinkConfiguration::TypeUdp:
            pLink = new UDPLink(dynamic_cast<UDPConfiguration*>(config));
            break;
        case LinkConfiguration::TypeTcp:
            pLink = new TCPLink(dynamic_cast<TCPConfiguration*>(config));
            break;
Don Gagne's avatar
Don Gagne committed
        case LinkConfiguration::TypeLogReplay:
            pLink = new LogReplayLink(dynamic_cast<LogReplayLinkConfiguration*>(config));
            break;
#ifdef QT_DEBUG
        case LinkConfiguration::TypeMock:
            pLink = new MockLink(dynamic_cast<MockConfiguration*>(config));
            break;
Don Gagne's avatar
Don Gagne committed
        pLink->setAutoconnect(autoconnectLink);
        _addLink(pLink);
        connectLink(pLink);
Don Gagne's avatar
Don Gagne committed
LinkInterface* LinkManager::createConnectedLink(const QString& name, bool autoconnectLink)
{
    Q_ASSERT(name.isEmpty() == false);
    for(int i = 0; i < _linkConfigurations.count(); i++) {
Don Gagne's avatar
Don Gagne committed
        LinkConfiguration* conf = _linkConfigurations.value<LinkConfiguration*>(i);
Don Gagne's avatar
Don Gagne committed
            return createConnectedLink(conf, autoconnectLink);
void LinkManager::_addLink(LinkInterface* link)
pixhawk's avatar
pixhawk committed
{
Don Gagne's avatar
Don Gagne committed
    if (thread() != QThread::currentThread()) {
        qWarning() << "_deleteLink called from incorrect thread";
        return;
    }
Don Gagne's avatar
Don Gagne committed
    if (!link) {
        return;
    }
Don Gagne's avatar
Don Gagne committed
    if (!_links.contains(link)) {
        // Find a mavlink channel to use for this link
        for (int i=0; i<32; i++) {
            if (!(_mavlinkChannelsUsedBitMask && 1 << i)) {
                mavlink_reset_channel_status(i);
                link->_setMavlinkChannel(i);
                _mavlinkChannelsUsedBitMask |= i << i;
                break;
            }
        }
        
Don Gagne's avatar
Don Gagne committed
        _links.append(link);
    // MainWindow may be around when doing things like running unit tests
    if (MainWindow::instance()) {
        connect(link, &LinkInterface::communicationError, _app, &QGCApplication::criticalMessageBoxOnMainThread);
    connect(link, &LinkInterface::bytesReceived,    _mavlinkProtocol, &MAVLinkProtocol::receiveBytes);
    connect(link, &LinkInterface::connected,        _mavlinkProtocol, &MAVLinkProtocol::linkConnected);
    connect(link, &LinkInterface::disconnected,     _mavlinkProtocol, &MAVLinkProtocol::linkDisconnected);
    _mavlinkProtocol->resetMetadataForLink(link);
    connect(link, &LinkInterface::connected,    this, &LinkManager::_linkConnected);
    connect(link, &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected);
pixhawk's avatar
pixhawk committed

Don Gagne's avatar
Don Gagne committed
void LinkManager::disconnectAll(bool disconnectAutoconnectLink)
pixhawk's avatar
pixhawk committed
{
Don Gagne's avatar
Don Gagne committed
    // Walk list in reverse order to preserve indices during delete
    for (int i=_links.count()-1; i>=0; i--) {
        disconnectLink(_links.value<LinkInterface*>(i), disconnectAutoconnectLink);
pixhawk's avatar
pixhawk committed
}

bool LinkManager::connectLink(LinkInterface* link)
{
    if (_connectionsSuspendedMsg()) {
        return false;
    }

Don Gagne's avatar
Don Gagne committed
    bool previousAnyConnectedLinks = anyConnectedLinks();
    bool previousAnyNonAutoconnectConnectedLinks = anyNonAutoconnectConnectedLinks();

    if (link->_connect()) {
Don Gagne's avatar
Don Gagne committed
        if (!previousAnyConnectedLinks) {
            emit anyConnectedLinksChanged(true);
        }
        if (!previousAnyNonAutoconnectConnectedLinks && anyNonAutoconnectConnectedLinks()) {
            emit anyNonAutoconnectConnectedLinksChanged(true);
        }
        return true;
    } else {
        return false;
    }
pixhawk's avatar
pixhawk committed
}

Don Gagne's avatar
Don Gagne committed
bool LinkManager::disconnectLink(LinkInterface* link, bool disconnectAutoconnectLink)
pixhawk's avatar
pixhawk committed
{
Don Gagne's avatar
Don Gagne committed

    if (disconnectAutoconnectLink || !link->autoconnect()) {
        link->_disconnect();
        LinkConfiguration* config = link->getLinkConfiguration();
        if(config) {
            config->setLink(NULL);
        }
        _deleteLink(link);
        return true;
    }
Don Gagne's avatar
Don Gagne committed

    return false;
pixhawk's avatar
pixhawk committed
}

void LinkManager::_deleteLink(LinkInterface* link)
Don Gagne's avatar
Don Gagne committed
    if (thread() != QThread::currentThread()) {
        qWarning() << "_deleteLink called from incorrect thread";
        return;
    }

    if (!link) {
        return;
    }
    // Free up the mavlink channel associated with this link
    _mavlinkChannelsUsedBitMask &= ~(1 << link->getMavlinkChannel());
Don Gagne's avatar
Don Gagne committed
    _links.removeOne(link);
    delete link;
Don Gagne's avatar
Don Gagne committed
    // Emit removal of link
    emit linkDeleted(link);
pixhawk's avatar
pixhawk committed
}

/// @brief If all new connections should be suspended a message is displayed to the user and true
///         is returned;
bool LinkManager::_connectionsSuspendedMsg(void)
{
    if (_connectionsSuspended) {
Don Gagne's avatar
Don Gagne committed
        QGCMessageBox::information(tr("Connect not allowed"),
                                   tr("Connect not allowed: %1").arg(_connectionsSuspendedReason));
        return true;
    } else {
        return false;
    }
}

void LinkManager::setConnectionsSuspended(QString reason)
{
    _connectionsSuspended = true;
    _connectionsSuspendedReason = reason;
    Q_ASSERT(!reason.isEmpty());
}
void LinkManager::_linkConnected(void)
{
    emit linkConnected((LinkInterface*)sender());
}

void LinkManager::_linkDisconnected(void)
{
    emit linkDisconnected((LinkInterface*)sender());
}

void LinkManager::suspendConfigurationUpdates(bool suspend)
{
    _configUpdateSuspended = suspend;
}

void LinkManager::saveLinkConfigurationList()
{
    QSettings settings;
    settings.remove(LinkConfiguration::settingsRoot());
Don Gagne's avatar
Don Gagne committed

    for (int i=0; i<_linkConfigurations.count(); i++) {
        LinkConfiguration* linkConfig = _linkConfigurations.value<LinkConfiguration*>(i);

        if (linkConfig) {
            if(!linkConfig->isDynamic())
            {
                QString root = LinkConfiguration::settingsRoot();
                root += QString("/Link%1").arg(i);
                settings.setValue(root + "/name", linkConfig->name());
                settings.setValue(root + "/type", linkConfig->type());
                // Have the instance save its own values
                linkConfig->saveSettings(settings, root);
            }
        } else {
            qWarning() << "Internal error";
dogmaphobic's avatar
dogmaphobic committed
        }
Don Gagne's avatar
Don Gagne committed

dogmaphobic's avatar
dogmaphobic committed
    QString root(LinkConfiguration::settingsRoot());
Don Gagne's avatar
Don Gagne committed
    settings.setValue(root + "/count", _linkConfigurations.count());
    emit linkConfigurationChanged();
}

void LinkManager::loadLinkConfigurationList()
{
    bool linksChanged = false;
Don Gagne's avatar
Don Gagne committed

    // Is the group even there?
    if(settings.contains(LinkConfiguration::settingsRoot() + "/count")) {
        // Find out how many configurations we have
        int count = settings.value(LinkConfiguration::settingsRoot() + "/count").toInt();
        for(int i = 0; i < count; i++) {
            QString root(LinkConfiguration::settingsRoot());
            root += QString("/Link%1").arg(i);
            if(settings.contains(root + "/type")) {
                int type = settings.value(root + "/type").toInt();
                if(type < LinkConfiguration::TypeLast) {
                    if(settings.contains(root + "/name")) {
                        QString name = settings.value(root + "/name").toString();
                        if(!name.isEmpty()) {
                            LinkConfiguration* pLink = NULL;
                            switch(type) {
dogmaphobic's avatar
dogmaphobic committed
#ifndef __ios__
                                case LinkConfiguration::TypeSerial:
                                    pLink = (LinkConfiguration*)new SerialConfiguration(name);
                                    break;
dogmaphobic's avatar
dogmaphobic committed
#endif
                                case LinkConfiguration::TypeUdp:
                                    pLink = (LinkConfiguration*)new UDPConfiguration(name);
                                    break;
                                case LinkConfiguration::TypeTcp:
                                    pLink = (LinkConfiguration*)new TCPConfiguration(name);
                                    break;
Don Gagne's avatar
Don Gagne committed
                                case LinkConfiguration::TypeLogReplay:
                                    pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name);
                                    break;
#ifdef QT_DEBUG
                                case LinkConfiguration::TypeMock:
                                    pLink = (LinkConfiguration*)new MockConfiguration(name);
                                    break;
                            }
                            if(pLink) {
                                // Have the instance load its own values
                                pLink->loadSettings(settings, root);
Don Gagne's avatar
Don Gagne committed
                                _linkConfigurations.append(pLink);
                                linksChanged = true;
                            }
                        } else {
                            qWarning() << "Link Configuration " << root << " has an empty name." ;
                        }
                    } else {
                        qWarning() << "Link Configuration " << root << " has no name." ;
                    }
                } else {
                    qWarning() << "Link Configuration " << root << " an invalid type: " << type;
                }
            } else {
                qWarning() << "Link Configuration " << root << " has no type." ;
            }
        }
    }
    
    // Debug buids always add MockLink automatically
#ifdef QT_DEBUG
Don Gagne's avatar
Don Gagne committed
    MockConfiguration* pMock = new MockConfiguration("Mock Link PX4");
    pMock->setDynamic(true);
Don Gagne's avatar
Don Gagne committed
    _linkConfigurations.append(pMock);
    linksChanged = true;

    if(linksChanged) {
        emit linkConfigurationChanged();
    }
Don Gagne's avatar
Don Gagne committed

    // Enable automatic Serial PX4/3DR Radio hunting
dogmaphobic's avatar
dogmaphobic committed
#ifndef __ios__
Don Gagne's avatar
Don Gagne committed
SerialConfiguration* LinkManager::_autoconnectConfigurationsContainsPort(const QString& portName)
Don Gagne's avatar
Don Gagne committed

    for (int i=0; i<_autoconnectConfigurations.count(); i++) {
        SerialConfiguration* linkConfig = _autoconnectConfigurations.value<SerialConfiguration*>(i);

        if (linkConfig) {
            if (linkConfig->portName() == searchPort) {
                return linkConfig;
Don Gagne's avatar
Don Gagne committed
        } else {
            qWarning() << "Internal error";
dogmaphobic's avatar
dogmaphobic committed
#endif
dogmaphobic's avatar
dogmaphobic committed
#ifndef __ios__
Don Gagne's avatar
Don Gagne committed
void LinkManager::_updateAutoConnectLinks(void)
Don Gagne's avatar
Don Gagne committed
    if (_connectionsSuspended || qgcApp()->runningUnitTests()) {
Don Gagne's avatar
Don Gagne committed

    if (_autoconnectUDP && !_autoconnectUDPConfig) {
        _autoconnectUDPConfig = new UDPConfiguration("Default UDP Link");
        _autoconnectUDPConfig->setLocalPort(QGC_UDP_LOCAL_PORT);
        _autoconnectUDPConfig->setDynamic(true);
        createConnectedLink(_autoconnectUDPConfig, true /* persistenLink */);
    }


dogmaphobic's avatar
dogmaphobic committed
    QStringList currentPorts;
Don Gagne's avatar
Don Gagne committed
    QList<QGCSerialPortInfo> portList = QGCSerialPortInfo::availablePorts();
Don Gagne's avatar
Don Gagne committed

Don Gagne's avatar
Don Gagne committed
    foreach (QGCSerialPortInfo portInfo, portList) {
Don Gagne's avatar
Don Gagne committed
        qCDebug(LinkManagerVerboseLog) << "-----------------------------------------------------";
        qCDebug(LinkManagerVerboseLog) << "portName:          " << portInfo.portName();
        qCDebug(LinkManagerVerboseLog) << "systemLocation:    " << portInfo.systemLocation();
        qCDebug(LinkManagerVerboseLog) << "description:       " << portInfo.description();
        qCDebug(LinkManagerVerboseLog) << "manufacturer:      " << portInfo.manufacturer();
        qCDebug(LinkManagerVerboseLog) << "serialNumber:      " << portInfo.serialNumber();
        qCDebug(LinkManagerVerboseLog) << "vendorIdentifier:  " << portInfo.vendorIdentifier();
        qCDebug(LinkManagerVerboseLog) << "productIdentifier: " << portInfo.productIdentifier();

dogmaphobic's avatar
dogmaphobic committed
        // Save port name
        currentPorts << portInfo.systemLocation();
Don Gagne's avatar
Don Gagne committed

        QGCSerialPortInfo::BoardType_t boardType = portInfo.boardType();

        if (boardType != QGCSerialPortInfo::BoardTypeUnknown) {
            if (portInfo.isBootloader()) {
                // Don't connect to bootloader
                continue;
            }
            
Don Gagne's avatar
Don Gagne committed
            if (!_autoconnectConfigurationsContainsPort(portInfo.systemLocation())) {
                SerialConfiguration* pSerialConfig = NULL;

Don Gagne's avatar
Don Gagne committed
                switch (boardType) {
                case QGCSerialPortInfo::BoardTypePX4FMUV1:
                case QGCSerialPortInfo::BoardTypePX4FMUV2:
Don Gagne's avatar
Don Gagne committed
                    if (_autoconnectPixhawk) {
                        pSerialConfig = new SerialConfiguration(QString("Pixhawk on %1").arg(portInfo.portName().trimmed()));
                    }
Don Gagne's avatar
Don Gagne committed
                    break;
                case QGCSerialPortInfo::BoardTypeAeroCore:
Don Gagne's avatar
Don Gagne committed
                    if (_autoconnectPixhawk) {
                        pSerialConfig = new SerialConfiguration(QString("AeroCore on %1").arg(portInfo.portName().trimmed()));
                    }
Don Gagne's avatar
Don Gagne committed
                    break;
                case QGCSerialPortInfo::BoardTypePX4Flow:
Don Gagne's avatar
Don Gagne committed
                    if (_autoconnectPX4Flow) {
                        pSerialConfig = new SerialConfiguration(QString("PX4Flow on %1").arg(portInfo.portName().trimmed()));
                    }
Don Gagne's avatar
Don Gagne committed
                    break;
                case QGCSerialPortInfo::BoardType3drRadio:
Don Gagne's avatar
Don Gagne committed
                    if (_autoconnect3DRRadio) {
                        pSerialConfig = new SerialConfiguration(QString("3DR Radio on %1").arg(portInfo.portName().trimmed()));
                    }
                    break;
Don Gagne's avatar
Don Gagne committed
                default:
                    qWarning() << "Internal error";
Don Gagne's avatar
Don Gagne committed
                    continue;
dogmaphobic's avatar
dogmaphobic committed
                }
Don Gagne's avatar
Don Gagne committed
                if (pSerialConfig) {
                    qCDebug(LinkManagerLog) << "New auto-connect port added: " << pSerialConfig->name() << portInfo.systemLocation();
                    pSerialConfig->setBaud(boardType == QGCSerialPortInfo::BoardType3drRadio ? 57600 : 115200);
                    pSerialConfig->setDynamic(true);
                    pSerialConfig->setPortName(portInfo.systemLocation());

                    _autoconnectConfigurations.append(pSerialConfig);

                    createConnectedLink(pSerialConfig, true /* persistenLink */);
                }
dogmaphobic's avatar
dogmaphobic committed
    // Now we go through the current configuration list and make sure any dynamic config has gone away
    QList<LinkConfiguration*>  _confToDelete;
Don Gagne's avatar
Don Gagne committed
    for (int i=0; i<_autoconnectConfigurations.count(); i++) {
        SerialConfiguration* linkConfig = _autoconnectConfigurations.value<SerialConfiguration*>(i);

        if (linkConfig) {
            if (!currentPorts.contains(linkConfig->portName())) {
                _confToDelete.append(linkConfig);
dogmaphobic's avatar
dogmaphobic committed
            }
Don Gagne's avatar
Don Gagne committed
        } else {
            qWarning() << "Internal error";
dogmaphobic's avatar
dogmaphobic committed
        }
    }
Don Gagne's avatar
Don Gagne committed

    // Now disconnect/remove all links that are gone
    foreach (LinkConfiguration* pDeleteConfig, _confToDelete) {
        LinkInterface* link = pDeleteConfig->link();
        if (link) {
            disconnectLink(link, true /* disconnectAutoconnectLink */);
        }
        _autoconnectConfigurations.removeOne(pDeleteConfig);
        delete pDeleteConfig;
dogmaphobic's avatar
dogmaphobic committed
#endif
Don Gagne's avatar
Don Gagne committed
bool LinkManager::anyConnectedLinks(void)
{
    bool found = false;
    for (int i=0; i<_links.count(); i++) {
        LinkInterface* link = _links.value<LinkInterface*>(i);

        if (link && link->isConnected()) {
            found = true;
            break;
        }
    }
    return found;
}

bool LinkManager::anyNonAutoconnectConnectedLinks(void)
Don Gagne's avatar
Don Gagne committed
    // FIXME: Should remove this duplication with anyConnectedLinks
    bool found = false;
Don Gagne's avatar
Don Gagne committed
    for (int i=0; i<_links.count(); i++) {
        LinkInterface* link = _links.value<LinkInterface*>(i);

        if (link && !link->autoconnect() && link->isConnected()) {
Don Gagne's avatar
Don Gagne committed
bool LinkManager::anyActiveLinks(void)
Don Gagne's avatar
Don Gagne committed
    for (int i=0; i<_links.count(); i++) {
        LinkInterface* link = _links.value<LinkInterface*>(i);

        if (link && link->active()) {
Don Gagne's avatar
Don Gagne committed
bool LinkManager::anyNonAutoconnectActiveLinks(void)
Don Gagne's avatar
Don Gagne committed
    // FIXME: Should remove this duplication with anyActiveLinks
    bool found = false;
    for (int i=0; i<_links.count(); i++) {
Don Gagne's avatar
Don Gagne committed
        LinkInterface* link = _links.value<LinkInterface*>(i);

        if (link && !link->autoconnect() && link->active()) {
            found = true;
            break;
        }
    }
    return found;
}

void LinkManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType)
{
    if (!link->active() && !_ignoreVehicleIds.contains(vehicleId)) {
        qCDebug(LinkManagerLog) << "New heartbeat on link:vehicleId:vehicleMavlinkVersion:vehicleFirmwareType:vehicleType "
                                << link->getName()
                                << vehicleId
                                << vehicleMavlinkVersion
                                << vehicleFirmwareType
                                << vehicleType;

        if (vehicleId == _mavlinkProtocol->getSystemId()) {
            _app->showToolBarMessage(QString("Warning: A vehicle is using the same system id as QGroundControl: %1").arg(vehicleId));
        }

        QSettings settings;
        bool mavlinkVersionCheck = settings.value("VERSION_CHECK_ENABLED", true).toBool();
        if (mavlinkVersionCheck && vehicleMavlinkVersion != MAVLINK_VERSION) {
            _ignoreVehicleIds += vehicleId;
            _app->showToolBarMessage(QString("The MAVLink protocol version on vehicle #%1 and QGroundControl differ! "
                                                 "It is unsafe to use different MAVLink versions. "
                                                 "QGroundControl therefore refuses to connect to vehicle #%1, which sends MAVLink version %2 (QGroundControl uses version %3).").arg(vehicleId).arg(vehicleMavlinkVersion).arg(MAVLINK_VERSION));
            return;
        }

        bool previousAnyActiveLinks = anyActiveLinks();
        bool previousAnyNonAutoconnectActiveLinks = anyNonAutoconnectActiveLinks();

        link->setActive(true);
        emit linkActive(link, vehicleId, vehicleFirmwareType, vehicleType);

        if (!previousAnyActiveLinks) {
            emit anyActiveLinksChanged(true);
Don Gagne's avatar
Don Gagne committed
        if (!previousAnyNonAutoconnectActiveLinks && anyNonAutoconnectActiveLinks()) {
            emit anyNonAutoconnectActiveLinksChanged(true);
        }
    }
}

void LinkManager::shutdown(void)
{
    setConnectionsSuspended("Shutdown");
    disconnectAll(true /* disconnectAutoconnectLink */);
}

void LinkManager::setAutoconnectUDP(bool autoconnect)
{
    if (_autoconnectUDP != autoconnect) {
        QSettings settings;

        settings.beginGroup(_settingsGroup);
        settings.setValue(_autoconnectUDPKey, autoconnect);

        _autoconnectUDP = autoconnect;
        emit autoconnectUDPChanged(autoconnect);
Don Gagne's avatar
Don Gagne committed
}

void LinkManager::setAutoconnectPixhawk(bool autoconnect)
{
    if (_autoconnectPixhawk != autoconnect) {
        QSettings settings;

        settings.beginGroup(_settingsGroup);
        settings.setValue(_autoconnectPixhawkKey, autoconnect);

        _autoconnectPixhawk = autoconnect;
        emit autoconnectPixhawkChanged(autoconnect);
    }

}

void LinkManager::setAutoconnect3DRRadio(bool autoconnect)
{
    if (_autoconnect3DRRadio != autoconnect) {
        QSettings settings;

        settings.beginGroup(_settingsGroup);
        settings.setValue(_autoconnect3DRRadioKey, autoconnect);

        _autoconnect3DRRadio = autoconnect;
        emit autoconnect3DRRadioChanged(autoconnect);
    }

}

void LinkManager::setAutoconnectPX4Flow(bool autoconnect)
{
    if (_autoconnectPX4Flow != autoconnect) {
        QSettings settings;

        settings.beginGroup(_settingsGroup);
        settings.setValue(_autoconnectPX4FlowKey, autoconnect);

        _autoconnectPX4Flow = autoconnect;
        emit autoconnectPX4FlowChanged(autoconnect);
    }