LinkManager.cc 24.4 KB
Newer Older
pixhawk's avatar
pixhawk committed
1
/*=====================================================================
lm's avatar
lm committed
2 3 4

QGroundControl Open Source Ground Control Station

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

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
pixhawk's avatar
pixhawk committed
10 11 12
    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
13 14

    QGROUNDCONTROL is distributed in the hope that it will be useful,
pixhawk's avatar
pixhawk committed
15 16 17
    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.
lm's avatar
lm committed
18

pixhawk's avatar
pixhawk committed
19
    You should have received a copy of the GNU General Public License
lm's avatar
lm committed
20 21
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

pixhawk's avatar
pixhawk committed
22
======================================================================*/
23

pixhawk's avatar
pixhawk committed
24 25 26 27 28 29 30 31 32 33
/**
 * @file
 *   @brief Brief Description
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QList>
#include <QApplication>
34
#include <QDebug>
dogmaphobic's avatar
dogmaphobic committed
35 36

#ifndef __ios__
Don Gagne's avatar
Don Gagne committed
37
#include "QGCSerialPortInfo.h"
dogmaphobic's avatar
dogmaphobic committed
38
#endif
39

40 41
#include "LinkManager.h"
#include "MainWindow.h"
Don Gagne's avatar
Don Gagne committed
42
#include "QGCMessageBox.h"
43
#include "QGCApplication.h"
44
#include "QGCApplication.h"
Don Gagne's avatar
Don Gagne committed
45 46
#include "UDPLink.h"
#include "TCPLink.h"
47

Don Gagne's avatar
Don Gagne committed
48
QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog")
Don Gagne's avatar
Don Gagne committed
49 50 51 52 53 54 55
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
56
const char* LinkManager::_defaultUPDLinkName =      "Default UDP Link";
57

58 59
LinkManager::LinkManager(QGCApplication* app)
    : QGCTool(app)
60 61 62
    , _configUpdateSuspended(false)
    , _configurationsLoaded(false)
    , _connectionsSuspended(false)
63
    , _mavlinkChannelsUsedBitMask(0)
64
    , _mavlinkProtocol(NULL)
Don Gagne's avatar
Don Gagne committed
65 66 67 68 69
    , _autoconnectUDP(true)
    , _autoconnectPixhawk(true)
    , _autoconnect3DRRadio(true)
    , _autoconnectPX4Flow(true)

pixhawk's avatar
pixhawk committed
70
{
Don Gagne's avatar
Don Gagne committed
71 72 73 74 75
    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;
76

Don Gagne's avatar
Don Gagne committed
77 78 79 80 81
    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
82 83 84 85
}

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

91 92 93 94 95
void LinkManager::setToolbox(QGCToolbox *toolbox)
{
   QGCTool::setToolbox(toolbox);

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

Don Gagne's avatar
Don Gagne committed
98
    connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateAutoConnectLinks);
99
    _portListTimer.start(6000); // timeout must be long enough to get past bootloader on second pass
100

101 102
}

Don Gagne's avatar
Don Gagne committed
103
LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config)
104 105 106 107
{
    Q_ASSERT(config);
    LinkInterface* pLink = NULL;
    switch(config->type()) {
dogmaphobic's avatar
dogmaphobic committed
108
#ifndef __ios__
109 110 111
        case LinkConfiguration::TypeSerial:
            pLink = new SerialLink(dynamic_cast<SerialConfiguration*>(config));
            break;
dogmaphobic's avatar
dogmaphobic committed
112
#endif
113 114 115
        case LinkConfiguration::TypeUdp:
            pLink = new UDPLink(dynamic_cast<UDPConfiguration*>(config));
            break;
116 117 118
        case LinkConfiguration::TypeTcp:
            pLink = new TCPLink(dynamic_cast<TCPConfiguration*>(config));
            break;
Don Gagne's avatar
Don Gagne committed
119 120 121
        case LinkConfiguration::TypeLogReplay:
            pLink = new LogReplayLink(dynamic_cast<LogReplayLinkConfiguration*>(config));
            break;
122
#ifdef QT_DEBUG
123 124 125
        case LinkConfiguration::TypeMock:
            pLink = new MockLink(dynamic_cast<MockConfiguration*>(config));
            break;
126
#endif
127 128 129
        case LinkConfiguration::TypeLast:
        default:
            break;
130 131
    }
    if(pLink) {
132 133
        _addLink(pLink);
        connectLink(pLink);
134 135 136 137
    }
    return pLink;
}

Don Gagne's avatar
Don Gagne committed
138
LinkInterface* LinkManager::createConnectedLink(const QString& name)
139 140 141
{
    Q_ASSERT(name.isEmpty() == false);
    for(int i = 0; i < _linkConfigurations.count(); i++) {
Don Gagne's avatar
Don Gagne committed
142
        LinkConfiguration* conf = _linkConfigurations.value<LinkConfiguration*>(i);
143
        if(conf && conf->name() == name)
Don Gagne's avatar
Don Gagne committed
144
            return createConnectedLink(conf);
145 146 147 148
    }
    return NULL;
}

149
void LinkManager::_addLink(LinkInterface* link)
pixhawk's avatar
pixhawk committed
150
{
Don Gagne's avatar
Don Gagne committed
151 152 153 154
    if (thread() != QThread::currentThread()) {
        qWarning() << "_deleteLink called from incorrect thread";
        return;
    }
155

Don Gagne's avatar
Don Gagne committed
156 157 158
    if (!link) {
        return;
    }
159

Don Gagne's avatar
Don Gagne committed
160
    if (!_links.contains(link)) {
161 162 163
        // Find a mavlink channel to use for this link
        for (int i=0; i<32; i++) {
            if (!(_mavlinkChannelsUsedBitMask && 1 << i)) {
164
                mavlink_reset_channel_status(i);
165 166 167 168 169
                link->_setMavlinkChannel(i);
                _mavlinkChannelsUsedBitMask |= i << i;
                break;
            }
        }
170

Don Gagne's avatar
Don Gagne committed
171
        _links.append(link);
172 173
        emit newLink(link);
    }
174

175 176
    // MainWindow may be around when doing things like running unit tests
    if (MainWindow::instance()) {
177
        connect(link, &LinkInterface::communicationError, _app, &QGCApplication::criticalMessageBoxOnMainThread);
178
    }
179

180 181 182 183
    connect(link, &LinkInterface::bytesReceived,    _mavlinkProtocol, &MAVLinkProtocol::receiveBytes);
    connect(link, &LinkInterface::connected,        _mavlinkProtocol, &MAVLinkProtocol::linkConnected);
    connect(link, &LinkInterface::disconnected,     _mavlinkProtocol, &MAVLinkProtocol::linkDisconnected);
    _mavlinkProtocol->resetMetadataForLink(link);
184

185
    connect(link, &LinkInterface::connected,    this, &LinkManager::_linkConnected);
186
    connect(link, &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected);
187
}
pixhawk's avatar
pixhawk committed
188

Don Gagne's avatar
Don Gagne committed
189
void LinkManager::disconnectAll(void)
pixhawk's avatar
pixhawk committed
190
{
Don Gagne's avatar
Don Gagne committed
191 192
    // 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
193
        disconnectLink(_links.value<LinkInterface*>(i));
194
    }
pixhawk's avatar
pixhawk committed
195 196 197 198
}

bool LinkManager::connectLink(LinkInterface* link)
{
199
    Q_ASSERT(link);
200

201 202 203 204
    if (_connectionsSuspendedMsg()) {
        return false;
    }

Don Gagne's avatar
Don Gagne committed
205 206
    bool previousAnyConnectedLinks = anyConnectedLinks();

207
    if (link->_connect()) {
Don Gagne's avatar
Don Gagne committed
208 209 210
        if (!previousAnyConnectedLinks) {
            emit anyConnectedLinksChanged(true);
        }
211 212 213 214
        return true;
    } else {
        return false;
    }
pixhawk's avatar
pixhawk committed
215 216
}

Don Gagne's avatar
Don Gagne committed
217
void LinkManager::disconnectLink(LinkInterface* link)
pixhawk's avatar
pixhawk committed
218
{
219
    Q_ASSERT(link);
Don Gagne's avatar
Don Gagne committed
220

Don Gagne's avatar
Don Gagne committed
221 222 223 224
    link->_disconnect();
    LinkConfiguration* config = link->getLinkConfiguration();
    if(config) {
        config->setLink(NULL);
225
    }
Don Gagne's avatar
Don Gagne committed
226
    _deleteLink(link);
pixhawk's avatar
pixhawk committed
227 228
}

229
void LinkManager::_deleteLink(LinkInterface* link)
230
{
Don Gagne's avatar
Don Gagne committed
231 232 233 234 235 236 237 238
    if (thread() != QThread::currentThread()) {
        qWarning() << "_deleteLink called from incorrect thread";
        return;
    }

    if (!link) {
        return;
    }
239

240 241
    // Free up the mavlink channel associated with this link
    _mavlinkChannelsUsedBitMask &= ~(1 << link->getMavlinkChannel());
242

Don Gagne's avatar
Don Gagne committed
243 244
    _links.removeOne(link);
    delete link;
245

Don Gagne's avatar
Don Gagne committed
246
    // Emit removal of link
247
    emit linkDeleted(link);
pixhawk's avatar
pixhawk committed
248 249
}

250 251 252 253 254
/// @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
255 256
        QGCMessageBox::information(tr("Connect not allowed"),
                                   tr("Connect not allowed: %1").arg(_connectionsSuspendedReason));
257 258 259 260 261 262 263 264 265 266 267 268
        return true;
    } else {
        return false;
    }
}

void LinkManager::setConnectionsSuspended(QString reason)
{
    _connectionsSuspended = true;
    _connectionsSuspendedReason = reason;
    Q_ASSERT(!reason.isEmpty());
}
269

270 271 272 273 274 275 276 277 278
void LinkManager::_linkConnected(void)
{
    emit linkConnected((LinkInterface*)sender());
}

void LinkManager::_linkDisconnected(void)
{
    emit linkDisconnected((LinkInterface*)sender());
}
279 280 281 282 283 284 285 286 287 288

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

void LinkManager::saveLinkConfigurationList()
{
    QSettings settings;
    settings.remove(LinkConfiguration::settingsRoot());
Don Gagne's avatar
Don Gagne committed
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304

    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
305
        }
306
    }
Don Gagne's avatar
Don Gagne committed
307

dogmaphobic's avatar
dogmaphobic committed
308
    QString root(LinkConfiguration::settingsRoot());
Don Gagne's avatar
Don Gagne committed
309
    settings.setValue(root + "/count", _linkConfigurations.count());
310 311 312 313 314
    emit linkConfigurationChanged();
}

void LinkManager::loadLinkConfigurationList()
{
315
    bool linksChanged = false;
316
    QSettings settings;
Don Gagne's avatar
Don Gagne committed
317

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    // 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
333
#ifndef __ios__
334 335 336
                                case LinkConfiguration::TypeSerial:
                                    pLink = (LinkConfiguration*)new SerialConfiguration(name);
                                    break;
dogmaphobic's avatar
dogmaphobic committed
337
#endif
338 339 340
                                case LinkConfiguration::TypeUdp:
                                    pLink = (LinkConfiguration*)new UDPConfiguration(name);
                                    break;
341 342 343
                                case LinkConfiguration::TypeTcp:
                                    pLink = (LinkConfiguration*)new TCPConfiguration(name);
                                    break;
Don Gagne's avatar
Don Gagne committed
344 345 346
                                case LinkConfiguration::TypeLogReplay:
                                    pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name);
                                    break;
347
#ifdef QT_DEBUG
348 349 350
                                case LinkConfiguration::TypeMock:
                                    pLink = (LinkConfiguration*)new MockConfiguration(name);
                                    break;
351
#endif
352 353 354 355
                            }
                            if(pLink) {
                                // Have the instance load its own values
                                pLink->loadSettings(settings, root);
Don Gagne's avatar
Don Gagne committed
356
                                _linkConfigurations.append(pLink);
357
                                linksChanged = true;
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
                            }
                        } 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." ;
            }
        }
    }
373

374 375
    // Debug buids always add MockLink automatically
#ifdef QT_DEBUG
376
    MockConfiguration* pMock = new MockConfiguration("Mock Link PX4");
377
    pMock->setDynamic(true);
Don Gagne's avatar
Don Gagne committed
378
    _linkConfigurations.append(pMock);
379
    linksChanged = true;
380
#endif
381 382 383 384

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

386
    // Enable automatic Serial PX4/3DR Radio hunting
387 388 389
    _configurationsLoaded = true;
}

dogmaphobic's avatar
dogmaphobic committed
390
#ifndef __ios__
Don Gagne's avatar
Don Gagne committed
391
SerialConfiguration* LinkManager::_autoconnectConfigurationsContainsPort(const QString& portName)
392 393
{
    QString searchPort = portName.trimmed();
Don Gagne's avatar
Don Gagne committed
394 395 396 397 398 399 400

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

        if (linkConfig) {
            if (linkConfig->portName() == searchPort) {
                return linkConfig;
401
            }
Don Gagne's avatar
Don Gagne committed
402 403
        } else {
            qWarning() << "Internal error";
404 405 406 407
        }
    }
    return NULL;
}
dogmaphobic's avatar
dogmaphobic committed
408
#endif
409

Don Gagne's avatar
Don Gagne committed
410
void LinkManager::_updateAutoConnectLinks(void)
411
{
Don Gagne's avatar
Don Gagne committed
412
    if (_connectionsSuspended || qgcApp()->runningUnitTests()) {
413 414
        return;
    }
Don Gagne's avatar
Don Gagne committed
415

Don Gagne's avatar
Don Gagne committed
416 417 418 419 420 421 422 423 424 425 426
    // 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
427
        qCDebug(LinkManagerLog) << "New auto-connect UDP port added";
Don Gagne's avatar
Don Gagne committed
428 429 430 431
        UDPConfiguration* udpConfig = new UDPConfiguration(_defaultUPDLinkName);
        udpConfig->setLocalPort(QGC_UDP_LOCAL_PORT);
        udpConfig->setDynamic(true);
        createConnectedLink(udpConfig);
Don Gagne's avatar
Don Gagne committed
432 433
    }

434
#ifndef __ios__
dogmaphobic's avatar
dogmaphobic committed
435
    QStringList currentPorts;
Don Gagne's avatar
Don Gagne committed
436
    QList<QGCSerialPortInfo> portList = QGCSerialPortInfo::availablePorts();
Don Gagne's avatar
Don Gagne committed
437

438
    // Iterate Comm Ports
Don Gagne's avatar
Don Gagne committed
439
    foreach (QGCSerialPortInfo portInfo, portList) {
Don Gagne's avatar
Don Gagne committed
440 441 442 443 444 445 446 447 448
        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
449 450
        // Save port name
        currentPorts << portInfo.systemLocation();
Don Gagne's avatar
Don Gagne committed
451 452 453 454 455 456

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

        if (boardType != QGCSerialPortInfo::BoardTypeUnknown) {
            if (portInfo.isBootloader()) {
                // Don't connect to bootloader
457
                qCDebug(LinkManagerLog) << "Waiting for bootloader to finish" << portInfo.systemLocation();
Don Gagne's avatar
Don Gagne committed
458 459
                continue;
            }
460

461 462 463 464 465 466 467 468 469
            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.append(portInfo.systemLocation());
            } else {
Don Gagne's avatar
Don Gagne committed
470 471
                SerialConfiguration* pSerialConfig = NULL;

472 473
                _autoconnectWaitList.removeOne(portInfo.systemLocation());

Don Gagne's avatar
Don Gagne committed
474 475 476
                switch (boardType) {
                case QGCSerialPortInfo::BoardTypePX4FMUV1:
                case QGCSerialPortInfo::BoardTypePX4FMUV2:
Don Gagne's avatar
Don Gagne committed
477 478 479
                    if (_autoconnectPixhawk) {
                        pSerialConfig = new SerialConfiguration(QString("Pixhawk on %1").arg(portInfo.portName().trimmed()));
                    }
Don Gagne's avatar
Don Gagne committed
480 481
                    break;
                case QGCSerialPortInfo::BoardTypeAeroCore:
Don Gagne's avatar
Don Gagne committed
482 483 484
                    if (_autoconnectPixhawk) {
                        pSerialConfig = new SerialConfiguration(QString("AeroCore on %1").arg(portInfo.portName().trimmed()));
                    }
Don Gagne's avatar
Don Gagne committed
485 486
                    break;
                case QGCSerialPortInfo::BoardTypePX4Flow:
Don Gagne's avatar
Don Gagne committed
487 488 489
                    if (_autoconnectPX4Flow) {
                        pSerialConfig = new SerialConfiguration(QString("PX4Flow on %1").arg(portInfo.portName().trimmed()));
                    }
Don Gagne's avatar
Don Gagne committed
490 491
                    break;
                case QGCSerialPortInfo::BoardType3drRadio:
Don Gagne's avatar
Don Gagne committed
492 493 494 495
                    if (_autoconnect3DRRadio) {
                        pSerialConfig = new SerialConfiguration(QString("3DR Radio on %1").arg(portInfo.portName().trimmed()));
                    }
                    break;
Don Gagne's avatar
Don Gagne committed
496 497
                default:
                    qWarning() << "Internal error";
Don Gagne's avatar
Don Gagne committed
498
                    continue;
dogmaphobic's avatar
dogmaphobic committed
499
                }
Don Gagne's avatar
Don Gagne committed
500

Don Gagne's avatar
Don Gagne committed
501 502 503 504 505 506 507 508
                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
509
                    createConnectedLink(pSerialConfig);
Don Gagne's avatar
Don Gagne committed
510
                }
dogmaphobic's avatar
dogmaphobic committed
511 512 513
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
514

dogmaphobic's avatar
dogmaphobic committed
515 516
    // 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
517 518 519 520 521
    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
522 523 524 525 526 527 528
                // 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
529
            }
Don Gagne's avatar
Don Gagne committed
530 531
        } else {
            qWarning() << "Internal error";
dogmaphobic's avatar
dogmaphobic committed
532 533
        }
    }
Don Gagne's avatar
Don Gagne committed
534

Don Gagne's avatar
Don Gagne committed
535
    // Now remove all configs that are gone
Don Gagne's avatar
Don Gagne committed
536
    foreach (LinkConfiguration* pDeleteConfig, _confToDelete) {
Don Gagne's avatar
Don Gagne committed
537
        qCDebug(LinkManagerLog) << "Removing unused autoconnect config" << pDeleteConfig->name();
Don Gagne's avatar
Don Gagne committed
538 539
        _autoconnectConfigurations.removeOne(pDeleteConfig);
        delete pDeleteConfig;
540
    }
541
#endif // __ios__
542 543
}

Don Gagne's avatar
Don Gagne committed
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
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)
559 560
{
    bool found = false;
Don Gagne's avatar
Don Gagne committed
561 562 563 564
    for (int i=0; i<_links.count(); i++) {
        LinkInterface* link = _links.value<LinkInterface*>(i);

        if (link && link->active()) {
565 566 567 568 569 570 571
            found = true;
            break;
        }
    }
    return found;
}

Don Gagne's avatar
Don Gagne committed
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
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();

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

        if (!previousAnyActiveLinks) {
            emit anyActiveLinksChanged(true);
603
        }
Don Gagne's avatar
Don Gagne committed
604 605 606 607 608 609
    }
}

void LinkManager::shutdown(void)
{
    setConnectionsSuspended("Shutdown");
Don Gagne's avatar
Don Gagne committed
610
    disconnectAll();
Don Gagne's avatar
Don Gagne committed
611 612 613 614 615 616 617 618 619 620 621 622
}

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

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

        _autoconnectUDP = autoconnect;
        emit autoconnectUDPChanged(autoconnect);
623
    }
Don Gagne's avatar
Don Gagne committed
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
}

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);
    }

666
}
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709

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";
        list += "Mock Link";
        list += "Log Replay";
    }
    return list;
}

QStringList LinkManager::serialPortStrings(void)
{
#ifndef __ios__
    if(!_commPortList.size())
    {
        QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
        foreach (const QSerialPortInfo &info, portList)
        {
            QString name = info.portName();
            _commPortList += name;
        }
    }
#endif
    return _commPortList;
}

QStringList LinkManager::serialBaudRates(void)
{
#ifdef __ios__
    QStringList foo;
    return foo;
#else
    return SerialConfiguration::supportedBaudRates();
#endif
}