Skip to content
Snippets Groups Projects
LinkManager.cc 30.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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 "QGCApplication.h"
    
    Don Gagne's avatar
    Don Gagne committed
    #include "UDPLink.h"
    #include "TCPLink.h"
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifdef QGC_ENABLE_BLUETOOTH
    
    dogmaphobic's avatar
    dogmaphobic committed
    #include "BluetoothLink.h"
    #endif
    
    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";
    
    Don Gagne's avatar
    Don Gagne committed
    const char* LinkManager::_defaultUPDLinkName =      "Default UDP Link";
    
    const int LinkManager::_autoconnectUpdateTimerMSecs =   1000;
    #ifdef Q_OS_WIN
    // Have to manually let the bootloader go by on Windows to get a working connect
    const int LinkManager::_autoconnectConnectDelayMSecs =  6000;
    #else
    const int LinkManager::_autoconnectConnectDelayMSecs =  1000;
    #endif
    
    
    LinkManager::LinkManager(QGCApplication* app)
        : QGCTool(app)
    
        , _configUpdateSuspended(false)
        , _configurationsLoaded(false)
        , _connectionsSuspended(false)
    
        , _mavlinkChannelsUsedBitMask(0)
    
        , _mavlinkProtocol(NULL)
    
    Don Gagne's avatar
    Don Gagne committed
        , _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);
    
    Don Gagne's avatar
    Don Gagne committed
        connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateAutoConnectLinks);
    
        _portListTimer.start(_autoconnectUpdateTimerMSecs); // timeout must be long enough to get past bootloader on second pass
    
    Don Gagne's avatar
    Don Gagne committed
    LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config)
    
    {
        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;
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifdef QGC_ENABLE_BLUETOOTH
    
    dogmaphobic's avatar
    dogmaphobic committed
            case LinkConfiguration::TypeBluetooth:
                pLink = new BluetoothLink(dynamic_cast<BluetoothConfiguration*>(config));
                break;
    #endif
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifndef __mobile__
    
    Don Gagne's avatar
    Don Gagne committed
            case LinkConfiguration::TypeLogReplay:
                pLink = new LogReplayLink(dynamic_cast<LogReplayLinkConfiguration*>(config));
                break;
    
    dogmaphobic's avatar
    dogmaphobic committed
    #endif
    
    #ifdef QT_DEBUG
    
            case LinkConfiguration::TypeMock:
                pLink = new MockLink(dynamic_cast<MockConfiguration*>(config));
                break;
    
            case LinkConfiguration::TypeLast:
            default:
                break;
    
            _addLink(pLink);
            connectLink(pLink);
    
    Don Gagne's avatar
    Don Gagne committed
    LinkInterface* LinkManager::createConnectedLink(const QString& name)
    
    {
        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);
    
    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);
    
    Don Gagne's avatar
    Don Gagne committed
        connect(link, &LinkInterface::communicationError,   _app,               &QGCApplication::criticalMessageBoxOnMainThread);
        connect(link, &LinkInterface::bytesReceived,        _mavlinkProtocol,   &MAVLinkProtocol::receiveBytes);
    
        _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(void)
    
    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--) {
    
    Don Gagne's avatar
    Don Gagne committed
            disconnectLink(_links.value<LinkInterface*>(i));
    
    pixhawk's avatar
    pixhawk committed
    }
    
    bool LinkManager::connectLink(LinkInterface* link)
    {
    
        if (_connectionsSuspendedMsg()) {
            return false;
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        bool previousAnyConnectedLinks = anyConnectedLinks();
    
    
        if (link->_connect()) {
    
    Don Gagne's avatar
    Don Gagne committed
            if (!previousAnyConnectedLinks) {
                emit anyConnectedLinksChanged(true);
            }
    
            return true;
        } else {
            return false;
        }
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    Don Gagne's avatar
    Don Gagne committed
    void LinkManager::disconnectLink(LinkInterface* link)
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Don Gagne's avatar
    Don Gagne committed
    
    
    Don Gagne's avatar
    Don Gagne committed
        link->_disconnect();
        LinkConfiguration* config = link->getLinkConfiguration();
    
        if (config) {
            if (_autoconnectConfigurations.contains(config)) {
                config->setLink(NULL);
            }
    
    Don Gagne's avatar
    Don Gagne committed
        _deleteLink(link);
    
        if (_autoconnectConfigurations.contains(config)) {
            _autoconnectConfigurations.removeOne(config);
            delete config;
        }
    
    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) {
    
            qgcApp()->showMessage(QString("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());
    
        int trueCount = 0;
        for (int i = 0; i < _linkConfigurations.count(); i++) {
    
    Don Gagne's avatar
    Don Gagne committed
            LinkConfiguration* linkConfig = _linkConfigurations.value<LinkConfiguration*>(i);
            if (linkConfig) {
                if(!linkConfig->isDynamic())
                {
                    QString root = LinkConfiguration::settingsRoot();
    
                    root += QString("/Link%1").arg(trueCount++);
    
    Don Gagne's avatar
    Don Gagne committed
                    settings.setValue(root + "/name", linkConfig->name());
                    settings.setValue(root + "/type", linkConfig->type());
    
                    settings.setValue(root + "/auto", linkConfig->isAutoConnect());
    
    Don Gagne's avatar
    Don Gagne committed
                    // Have the instance save its own values
                    linkConfig->saveSettings(settings, root);
                }
            } else {
                qWarning() << "Internal error";
    
    dogmaphobic's avatar
    dogmaphobic committed
            }
    
    dogmaphobic's avatar
    dogmaphobic committed
        QString root(LinkConfiguration::settingsRoot());
    
        settings.setValue(root + "/count", trueCount);
        emit linkConfigurationsChanged();
    
        bool linksChanged = false;
    
    #ifdef QT_DEBUG
    
        bool mockPresent  = false;
    
        QSettings settings;
        // 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((LinkConfiguration::LinkType)type < LinkConfiguration::TypeLast) {
    
                        if(settings.contains(root + "/name")) {
                            QString name = settings.value(root + "/name").toString();
                            if(!name.isEmpty()) {
                                LinkConfiguration* pLink = NULL;
    
                                bool autoConnect = settings.value(root + "/auto").toBool();
                                switch((LinkConfiguration::LinkType)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;
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifdef QGC_ENABLE_BLUETOOTH
    
    dogmaphobic's avatar
    dogmaphobic committed
                                    case LinkConfiguration::TypeBluetooth:
                                        pLink = (LinkConfiguration*)new BluetoothConfiguration(name);
                                        break;
    #endif
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifndef __mobile__
    
    Don Gagne's avatar
    Don Gagne committed
                                    case LinkConfiguration::TypeLogReplay:
                                        pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name);
                                        break;
    
    dogmaphobic's avatar
    dogmaphobic committed
    #endif
    
    #ifdef QT_DEBUG
    
                                    case LinkConfiguration::TypeMock:
                                        pLink = (LinkConfiguration*)new MockConfiguration(name);
    
                                        mockPresent = true;
    
                                    default:
                                    case LinkConfiguration::TypeLast:
                                        break;
    
                                    //-- Have the instance load its own values
                                    pLink->setAutoConnect(autoConnect);
    
    Don Gagne's avatar
    Don Gagne committed
                                    _linkConfigurations.append(pLink);
    
                                    linksChanged = true;
    
                                qWarning() << "Link Configuration" << root << "has an empty name." ;
    
                            qWarning() << "Link Configuration" << root << "has no name." ;
    
                        qWarning() << "Link Configuration" << root << "an invalid type: " << type;
    
                    qWarning() << "Link Configuration" << root << "has no type." ;
    
        // Debug buids always add MockLink automatically (if one is not already there)
    
    #ifdef QT_DEBUG
    
        if(!mockPresent)
        {
            MockConfiguration* pMock = new MockConfiguration("Mock Link PX4");
            pMock->setDynamic(true);
            _linkConfigurations.append(pMock);
            linksChanged = true;
        }
    
    
        if(linksChanged) {
    
            emit linkConfigurationsChanged();
    
        }
        // 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
    
    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
    
    
    Don Gagne's avatar
    Don Gagne committed
        // Re-add UDP if we need to
        bool foundUDP = false;
        for (int i=0; i<_links.count(); i++) {
            LinkConfiguration* linkConfig = _links.value<LinkInterface*>(i)->getLinkConfiguration();
            if (linkConfig->type() == LinkConfiguration::TypeUdp && linkConfig->name() == _defaultUPDLinkName) {
                foundUDP = true;
                break;
            }
        }
        if (!foundUDP) {
    
    Don Gagne's avatar
    Don Gagne committed
            qCDebug(LinkManagerLog) << "New auto-connect UDP port added";
    
    Don Gagne's avatar
    Don Gagne committed
            UDPConfiguration* udpConfig = new UDPConfiguration(_defaultUPDLinkName);
            udpConfig->setLocalPort(QGC_UDP_LOCAL_PORT);
            udpConfig->setDynamic(true);
    
            _linkConfigurations.append(udpConfig);
    
    Don Gagne's avatar
    Don Gagne committed
            createConnectedLink(udpConfig);
    
            emit linkConfigurationsChanged();
    
    #ifndef __ios__
    
    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
    
                    qCDebug(LinkManagerLog) << "Waiting for bootloader to finish" << portInfo.systemLocation();
    
    Don Gagne's avatar
    Don Gagne committed
                    continue;
                }
    
                if (_autoconnectConfigurationsContainsPort(portInfo.systemLocation())) {
                    qCDebug(LinkManagerVerboseLog) << "Skipping existing autoconnect" << portInfo.systemLocation();
                } else if (!_autoconnectWaitList.contains(portInfo.systemLocation())) {
                    // We don't connect to the port the first time we see it. The ability to correctly detect whether we
                    // are in the bootloader is flaky from a cross-platform standpoint. So by putting it on a wait list
                    // and only connect on the second pass we leave enough time for the board to boot up.
                    qCDebug(LinkManagerLog) << "Waiting for next autoconnect pass" << portInfo.systemLocation();
    
                    _autoconnectWaitList[portInfo.systemLocation()] = 1;
                } else if (++_autoconnectWaitList[portInfo.systemLocation()] * _autoconnectUpdateTimerMSecs > _autoconnectConnectDelayMSecs) {
    
    Don Gagne's avatar
    Don Gagne committed
                    SerialConfiguration* pSerialConfig = NULL;
    
    
                    _autoconnectWaitList.remove(portInfo.systemLocation());
    
    Don Gagne's avatar
    Don Gagne committed
                    switch (boardType) {
                    case QGCSerialPortInfo::BoardTypePX4FMUV1:
                    case QGCSerialPortInfo::BoardTypePX4FMUV2:
    
                    case QGCSerialPortInfo::BoardTypePX4FMUV4:
    
    Don Gagne's avatar
    Don Gagne committed
                        if (_autoconnectPixhawk) {
                            pSerialConfig = new SerialConfiguration(QString("Pixhawk on %1").arg(portInfo.portName().trimmed()));
    
                            pSerialConfig->setUsbDirect(true);
    
    Don Gagne's avatar
    Don Gagne committed
                        }
    
    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()));
    
                            pSerialConfig->setUsbDirect(true);
    
    Don Gagne's avatar
    Don Gagne committed
                        }
    
    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);
    
    Don Gagne's avatar
    Don Gagne committed
                        createConnectedLink(pSerialConfig);
    
    Don Gagne's avatar
    Don Gagne committed
                    }
    
    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())) {
    
    Don Gagne's avatar
    Don Gagne committed
                    // We don't remove links which are still connected even though at this point the cable may
                    // have been pulled. This is due to the fact that whether a serial port goes away from the
                    // list when the cable is pulled is OS dependant. By not disconnecting in this case, we keep
                    // things working the same across all OS.
                    if (!linkConfig->link() || !linkConfig->link()->isConnected()) {
                        _confToDelete.append(linkConfig);
                    }
    
    dogmaphobic's avatar
    dogmaphobic committed
                }
    
    Don Gagne's avatar
    Don Gagne committed
            } else {
                qWarning() << "Internal error";
    
    Don Gagne's avatar
    Don Gagne committed
    
    
    Don Gagne's avatar
    Don Gagne committed
        // Now remove all configs that are gone
    
    Don Gagne's avatar
    Don Gagne committed
        foreach (LinkConfiguration* pDeleteConfig, _confToDelete) {
    
    Don Gagne's avatar
    Don Gagne committed
            qCDebug(LinkManagerLog) << "Removing unused autoconnect config" << pDeleteConfig->name();
    
    Don Gagne's avatar
    Don Gagne committed
            _autoconnectConfigurations.removeOne(pDeleteConfig);
            delete pDeleteConfig;
    
    #endif // __ios__
    
    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::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
    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->showMessage(QString("Warning: A vehicle is using the same system id as QGroundControl: %1").arg(vehicleId));
    
    Don Gagne's avatar
    Don Gagne committed
            }
    
            QSettings settings;
            bool mavlinkVersionCheck = settings.value("VERSION_CHECK_ENABLED", true).toBool();
            if (mavlinkVersionCheck && vehicleMavlinkVersion != MAVLINK_VERSION) {
                _ignoreVehicleIds += vehicleId;
    
                _app->showMessage(QString("The MAVLink protocol version on vehicle #%1 and QGroundControl differ! "
    
    Don Gagne's avatar
    Don Gagne committed
                                                     "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();
    
            link->setActive(true);
            emit linkActive(link, vehicleId, vehicleFirmwareType, vehicleType);
    
            if (!previousAnyActiveLinks) {
                emit anyActiveLinksChanged(true);
    
    Don Gagne's avatar
    Don Gagne committed
        }
    }
    
    void LinkManager::shutdown(void)
    {
        setConnectionsSuspended("Shutdown");
    
    Don Gagne's avatar
    Don Gagne committed
        disconnectAll();
    
    Don Gagne's avatar
    Don Gagne committed
    }
    
    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);
        }
    
    
    
    QStringList LinkManager::linkTypeStrings(void) const
    {
        //-- Must follow same order as enum LinkType in LinkConfiguration.h
        static QStringList list;
        if(!list.size())
        {
    #ifndef __ios__
            list += "Serial";
    #endif
            list += "UDP";
            list += "TCP";
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifdef QGC_ENABLE_BLUETOOTH
    
    dogmaphobic's avatar
    dogmaphobic committed
            list += "Bluetooth";
    #endif
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifdef QT_DEBUG
    
            list += "Mock Link";
    
    dogmaphobic's avatar
    dogmaphobic committed
    #endif
    #ifndef __mobile__
    
            list += "Log Replay";
    
    dogmaphobic's avatar
    dogmaphobic committed
    #endif
    
    dogmaphobic's avatar
    dogmaphobic committed
            Q_ASSERT(list.size() == (int)LinkConfiguration::TypeLast);
    
    void LinkManager::_updateSerialPorts()
    
        _commPortList.clear();
        _commPortDisplayList.clear();
    
    #ifndef __ios__
    
        QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
        foreach (const QSerialPortInfo &info, portList)
    
            QString port = info.systemLocation().trimmed();
            _commPortList += port;
            _commPortDisplayList += SerialConfiguration::cleanPortDisplayname(port);
    
    }
    
    QStringList LinkManager::serialPortStrings(void)
    {
        if(!_commPortDisplayList.size())
        {
            _updateSerialPorts();
        }
        return _commPortDisplayList;
    }
    
    QStringList LinkManager::serialPorts(void)
    {
        if(!_commPortList.size())
        {
            _updateSerialPorts();
        }
    
        return _commPortList;
    }
    
    QStringList LinkManager::serialBaudRates(void)
    {
    #ifdef __ios__
        QStringList foo;
        return foo;
    #else
        return SerialConfiguration::supportedBaudRates();
    #endif
    }
    
    
    bool LinkManager::endConfigurationEditing(LinkConfiguration* config, LinkConfiguration* editedConfig)
    {
        Q_ASSERT(config != NULL);
        Q_ASSERT(editedConfig != NULL);
        _fixUnnamed(editedConfig);
        config->copyFrom(editedConfig);
        saveLinkConfigurationList();
        // Tell link about changes (if any)
        config->updateSettings();
        // Discard temporary duplicate
        delete editedConfig;
        return true;
    }
    
    bool LinkManager::endCreateConfiguration(LinkConfiguration* config)
    {
        Q_ASSERT(config != NULL);
        _fixUnnamed(config);
        _linkConfigurations.append(config);
        saveLinkConfigurationList();
        return true;
    }
    
    LinkConfiguration* LinkManager::createConfiguration(int type, const QString& name)
    {
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifndef __ios__
    
        if((LinkConfiguration::LinkType)type == LinkConfiguration::TypeSerial)
            _updateSerialPorts();
    
    dogmaphobic's avatar
    dogmaphobic committed
    #endif
    
        return LinkConfiguration::createSettings(type, name);
    }
    
    LinkConfiguration* LinkManager::startConfigurationEditing(LinkConfiguration* config)
    {
        Q_ASSERT(config != NULL);
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifndef __ios__
    
        if(config->type() == LinkConfiguration::TypeSerial)
            _updateSerialPorts();
    
    dogmaphobic's avatar
    dogmaphobic committed
    #endif
    
        return LinkConfiguration::duplicateSettings(config);
    }
    
    
    void LinkManager::_fixUnnamed(LinkConfiguration* config)
    {
        Q_ASSERT(config != NULL);
        //-- Check for "Unnamed"
        if (config->name() == "Unnamed") {
            switch(config->type()) {
    #ifndef __ios__
                case LinkConfiguration::TypeSerial: {
                    QString tname = dynamic_cast<SerialConfiguration*>(config)->portName();
    
    #ifdef Q_OS_WIN
    
                    tname.replace("\\\\.\\", "");
    #else
                    tname.replace("/dev/cu.", "");
                    tname.replace("/dev/", "");
    #endif
                    config->setName(QString("Serial Device on %1").arg(tname));
                    break;
                    }
    #endif
                case LinkConfiguration::TypeUdp:
                    config->setName(
                        QString("UDP Link on Port %1").arg(dynamic_cast<UDPConfiguration*>(config)->localPort()));
                    break;
                case LinkConfiguration::TypeTcp: {
                        TCPConfiguration* tconfig = dynamic_cast<TCPConfiguration*>(config);
                        if(tconfig) {
                            config->setName(
                                QString("TCP Link %1:%2").arg(tconfig->address().toString()).arg((int)tconfig->port()));
                        }
                    }
                    break;
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifdef QGC_ENABLE_BLUETOOTH
    
    dogmaphobic's avatar
    dogmaphobic committed
                case LinkConfiguration::TypeBluetooth: {
                        BluetoothConfiguration* tconfig = dynamic_cast<BluetoothConfiguration*>(config);
                        if(tconfig) {
    
                            config->setName(QString("%1 (Bluetooth Device)").arg(tconfig->device().name));
    
    dogmaphobic's avatar
    dogmaphobic committed
                        }
                    }
                    break;
    #endif
    
    dogmaphobic's avatar
    dogmaphobic committed
    #ifndef __mobile__
    
                case LinkConfiguration::TypeLogReplay: {
    
    dogmaphobic's avatar
    dogmaphobic committed
                        LogReplayLinkConfiguration* tconfig = dynamic_cast<LogReplayLinkConfiguration*>(config);
                        if(tconfig) {
                            config->setName(QString("Log Replay %1").arg(tconfig->logFilenameShort()));
                        }
    
    dogmaphobic's avatar
    dogmaphobic committed
    #endif
    
    #ifdef QT_DEBUG
                case LinkConfiguration::TypeMock:
                    config->setName(
                        QString("Mock Link"));
                    break;
    #endif
                case LinkConfiguration::TypeLast:
                default:
                    break;
            }
        }
    }
    
    void LinkManager::removeConfiguration(LinkConfiguration* config)
    {
        Q_ASSERT(config != NULL);
        LinkInterface* iface = config->link();
        if(iface) {
            disconnectLink(iface);
        }
        // Remove configuration
        _linkConfigurations.removeOne(config);
        delete config;
        // Save list
        saveLinkConfigurationList();
    }
    
    bool LinkManager::isAutoconnectLink(LinkInterface* link)
    {
        return _autoconnectConfigurations.contains(link->getLinkConfiguration());
    }
    
    dogmaphobic's avatar
    dogmaphobic committed
    
    bool LinkManager::isBluetoothAvailable(void)
    {
        return qgcApp()->isBluetoothAvailable();
    }