LinkManager.cc 27.9 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
#include "LinkManager.h"
41
#include "QGCApplication.h"
Don Gagne's avatar
Don Gagne committed
42 43
#include "UDPLink.h"
#include "TCPLink.h"
dogmaphobic's avatar
dogmaphobic committed
44
#ifdef QGC_ENABLE_BLUETOOTH
dogmaphobic's avatar
dogmaphobic committed
45 46
#include "BluetoothLink.h"
#endif
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 60 61 62 63 64 65
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

66 67
LinkManager::LinkManager(QGCApplication* app)
    : QGCTool(app)
68 69 70
    , _configUpdateSuspended(false)
    , _configurationsLoaded(false)
    , _connectionsSuspended(false)
71
    , _mavlinkChannelsUsedBitMask(0)
72
    , _mavlinkProtocol(NULL)
Don Gagne's avatar
Don Gagne committed
73 74 75 76 77
    , _autoconnectUDP(true)
    , _autoconnectPixhawk(true)
    , _autoconnect3DRRadio(true)
    , _autoconnectPX4Flow(true)

pixhawk's avatar
pixhawk committed
78
{
Don Gagne's avatar
Don Gagne committed
79 80 81 82 83
    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;
84

Don Gagne's avatar
Don Gagne committed
85 86 87 88 89
    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
90 91 92 93
}

LinkManager::~LinkManager()
{
94

pixhawk's avatar
pixhawk committed
95 96
}

97 98 99 100 101 102
void LinkManager::setToolbox(QGCToolbox *toolbox)
{
   QGCTool::setToolbox(toolbox);

   _mavlinkProtocol = _toolbox->mavlinkProtocol();

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

106 107
}

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

Don Gagne's avatar
Don Gagne committed
150
LinkInterface* LinkManager::createConnectedLink(const QString& name)
151 152 153
{
    Q_ASSERT(name.isEmpty() == false);
    for(int i = 0; i < _linkConfigurations.count(); i++) {
Don Gagne's avatar
Don Gagne committed
154
        LinkConfiguration* conf = _linkConfigurations.value<LinkConfiguration*>(i);
155
        if(conf && conf->name() == name)
Don Gagne's avatar
Don Gagne committed
156
            return createConnectedLink(conf);
157 158 159 160
    }
    return NULL;
}

161
void LinkManager::_addLink(LinkInterface* link)
pixhawk's avatar
pixhawk committed
162
{
Don Gagne's avatar
Don Gagne committed
163 164 165 166
    if (thread() != QThread::currentThread()) {
        qWarning() << "_deleteLink called from incorrect thread";
        return;
    }
167

Don Gagne's avatar
Don Gagne committed
168 169 170
    if (!link) {
        return;
    }
171

Don Gagne's avatar
Don Gagne committed
172
    if (!_links.contains(link)) {
173 174
        bool channelSet = false;

175 176 177
        // Find a mavlink channel to use for this link
        for (int i=0; i<32; i++) {
            if (!(_mavlinkChannelsUsedBitMask && 1 << i)) {
178
                mavlink_reset_channel_status(i);
179 180
                link->_setMavlinkChannel(i);
                _mavlinkChannelsUsedBitMask |= i << i;
181
                channelSet = true;
182 183 184
                break;
            }
        }
185

186 187 188 189
        if (!channelSet) {
            qWarning() << "Ran out of mavlink channels";
        }

Don Gagne's avatar
Don Gagne committed
190
        _links.append(link);
191 192
        emit newLink(link);
    }
193

Don Gagne's avatar
Don Gagne committed
194 195
    connect(link, &LinkInterface::communicationError,   _app,               &QGCApplication::criticalMessageBoxOnMainThread);
    connect(link, &LinkInterface::bytesReceived,        _mavlinkProtocol,   &MAVLinkProtocol::receiveBytes);
196

197
    _mavlinkProtocol->resetMetadataForLink(link);
198

199
    connect(link, &LinkInterface::connected,    this, &LinkManager::_linkConnected);
200
    connect(link, &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected);
201
}
pixhawk's avatar
pixhawk committed
202

Don Gagne's avatar
Don Gagne committed
203
void LinkManager::disconnectAll(void)
pixhawk's avatar
pixhawk committed
204
{
Don Gagne's avatar
Don Gagne committed
205 206
    // 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
207
        disconnectLink(_links.value<LinkInterface*>(i));
208
    }
pixhawk's avatar
pixhawk committed
209 210 211 212
}

bool LinkManager::connectLink(LinkInterface* link)
{
213
    Q_ASSERT(link);
214

215 216 217 218
    if (_connectionsSuspendedMsg()) {
        return false;
    }

219
    return link->_connect();
pixhawk's avatar
pixhawk committed
220 221
}

Don Gagne's avatar
Don Gagne committed
222
void LinkManager::disconnectLink(LinkInterface* link)
pixhawk's avatar
pixhawk committed
223
{
224
    Q_ASSERT(link);
Don Gagne's avatar
Don Gagne committed
225

Don Gagne's avatar
Don Gagne committed
226 227
    link->_disconnect();
    LinkConfiguration* config = link->getLinkConfiguration();
228 229 230 231
    if (config) {
        if (_autoconnectConfigurations.contains(config)) {
            config->setLink(NULL);
        }
232
    }
Don Gagne's avatar
Don Gagne committed
233
    _deleteLink(link);
234 235 236 237
    if (_autoconnectConfigurations.contains(config)) {
        _autoconnectConfigurations.removeOne(config);
        delete config;
    }
pixhawk's avatar
pixhawk committed
238 239
}

240
void LinkManager::_deleteLink(LinkInterface* link)
241
{
Don Gagne's avatar
Don Gagne committed
242 243 244 245 246 247 248 249
    if (thread() != QThread::currentThread()) {
        qWarning() << "_deleteLink called from incorrect thread";
        return;
    }

    if (!link) {
        return;
    }
250

251 252
    // Free up the mavlink channel associated with this link
    _mavlinkChannelsUsedBitMask &= ~(1 << link->getMavlinkChannel());
253

Don Gagne's avatar
Don Gagne committed
254 255
    _links.removeOne(link);
    delete link;
256

Don Gagne's avatar
Don Gagne committed
257
    // Emit removal of link
258
    emit linkDeleted(link);
pixhawk's avatar
pixhawk committed
259 260
}

261 262 263 264 265
/// @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) {
266
        qgcApp()->showMessage(QString("Connect not allowed: %1").arg(_connectionsSuspendedReason));
267 268 269 270 271 272 273 274 275 276 277 278
        return true;
    } else {
        return false;
    }
}

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

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

void LinkManager::_linkDisconnected(void)
{
    emit linkDisconnected((LinkInterface*)sender());
}
289 290 291 292 293 294 295 296 297 298

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

void LinkManager::saveLinkConfigurationList()
{
    QSettings settings;
    settings.remove(LinkConfiguration::settingsRoot());
299 300
    int trueCount = 0;
    for (int i = 0; i < _linkConfigurations.count(); i++) {
Don Gagne's avatar
Don Gagne committed
301 302 303 304 305
        LinkConfiguration* linkConfig = _linkConfigurations.value<LinkConfiguration*>(i);
        if (linkConfig) {
            if(!linkConfig->isDynamic())
            {
                QString root = LinkConfiguration::settingsRoot();
306
                root += QString("/Link%1").arg(trueCount++);
Don Gagne's avatar
Don Gagne committed
307 308
                settings.setValue(root + "/name", linkConfig->name());
                settings.setValue(root + "/type", linkConfig->type());
309
                settings.setValue(root + "/auto", linkConfig->isAutoConnect());
Don Gagne's avatar
Don Gagne committed
310 311 312 313 314
                // Have the instance save its own values
                linkConfig->saveSettings(settings, root);
            }
        } else {
            qWarning() << "Internal error";
dogmaphobic's avatar
dogmaphobic committed
315
        }
316
    }
dogmaphobic's avatar
dogmaphobic committed
317
    QString root(LinkConfiguration::settingsRoot());
318 319
    settings.setValue(root + "/count", trueCount);
    emit linkConfigurationsChanged();
320 321 322 323
}

void LinkManager::loadLinkConfigurationList()
{
324
    bool linksChanged = false;
325
#ifdef QT_DEBUG
326
    bool mockPresent  = false;
327
#endif
328 329 330 331 332 333 334 335 336 337
    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();
338
                if((LinkConfiguration::LinkType)type < LinkConfiguration::TypeLast) {
339 340 341 342
                    if(settings.contains(root + "/name")) {
                        QString name = settings.value(root + "/name").toString();
                        if(!name.isEmpty()) {
                            LinkConfiguration* pLink = NULL;
343 344
                            bool autoConnect = settings.value(root + "/auto").toBool();
                            switch((LinkConfiguration::LinkType)type) {
dogmaphobic's avatar
dogmaphobic committed
345
#ifndef __ios__
346 347 348
                                case LinkConfiguration::TypeSerial:
                                    pLink = (LinkConfiguration*)new SerialConfiguration(name);
                                    break;
dogmaphobic's avatar
dogmaphobic committed
349
#endif
350 351 352
                                case LinkConfiguration::TypeUdp:
                                    pLink = (LinkConfiguration*)new UDPConfiguration(name);
                                    break;
353 354 355
                                case LinkConfiguration::TypeTcp:
                                    pLink = (LinkConfiguration*)new TCPConfiguration(name);
                                    break;
dogmaphobic's avatar
dogmaphobic committed
356
#ifdef QGC_ENABLE_BLUETOOTH
dogmaphobic's avatar
dogmaphobic committed
357 358 359 360
                                case LinkConfiguration::TypeBluetooth:
                                    pLink = (LinkConfiguration*)new BluetoothConfiguration(name);
                                    break;
#endif
dogmaphobic's avatar
dogmaphobic committed
361
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
362 363 364
                                case LinkConfiguration::TypeLogReplay:
                                    pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name);
                                    break;
dogmaphobic's avatar
dogmaphobic committed
365
#endif
366
#ifdef QT_DEBUG
367 368
                                case LinkConfiguration::TypeMock:
                                    pLink = (LinkConfiguration*)new MockConfiguration(name);
369
                                    mockPresent = true;
370
                                    break;
371
#endif
372 373 374
                                default:
                                case LinkConfiguration::TypeLast:
                                    break;
375 376
                            }
                            if(pLink) {
377 378
                                //-- Have the instance load its own values
                                pLink->setAutoConnect(autoConnect);
379
                                pLink->loadSettings(settings, root);
Don Gagne's avatar
Don Gagne committed
380
                                _linkConfigurations.append(pLink);
381
                                linksChanged = true;
382 383
                            }
                        } else {
384
                            qWarning() << "Link Configuration" << root << "has an empty name." ;
385 386
                        }
                    } else {
387
                        qWarning() << "Link Configuration" << root << "has no name." ;
388 389
                    }
                } else {
390
                    qWarning() << "Link Configuration" << root << "an invalid type: " << type;
391 392
                }
            } else {
393
                qWarning() << "Link Configuration" << root << "has no type." ;
394 395 396
            }
        }
    }
397
    // Debug buids always add MockLink automatically (if one is not already there)
398
#ifdef QT_DEBUG
399 400 401 402 403 404 405
    if(!mockPresent)
    {
        MockConfiguration* pMock = new MockConfiguration("Mock Link PX4");
        pMock->setDynamic(true);
        _linkConfigurations.append(pMock);
        linksChanged = true;
    }
406
#endif
407 408

    if(linksChanged) {
409
        emit linkConfigurationsChanged();
410 411
    }
    // Enable automatic Serial PX4/3DR Radio hunting
412 413 414
    _configurationsLoaded = true;
}

dogmaphobic's avatar
dogmaphobic committed
415
#ifndef __ios__
Don Gagne's avatar
Don Gagne committed
416
SerialConfiguration* LinkManager::_autoconnectConfigurationsContainsPort(const QString& portName)
417 418
{
    QString searchPort = portName.trimmed();
Don Gagne's avatar
Don Gagne committed
419 420 421 422 423 424 425

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

        if (linkConfig) {
            if (linkConfig->portName() == searchPort) {
                return linkConfig;
426
            }
Don Gagne's avatar
Don Gagne committed
427 428
        } else {
            qWarning() << "Internal error";
429 430 431 432
        }
    }
    return NULL;
}
dogmaphobic's avatar
dogmaphobic committed
433
#endif
434

Don Gagne's avatar
Don Gagne committed
435
void LinkManager::_updateAutoConnectLinks(void)
436
{
Don Gagne's avatar
Don Gagne committed
437
    if (_connectionsSuspended || qgcApp()->runningUnitTests()) {
438 439
        return;
    }
Don Gagne's avatar
Don Gagne committed
440

Don Gagne's avatar
Don Gagne committed
441 442 443 444 445 446 447 448 449 450
    // 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
451
        qCDebug(LinkManagerLog) << "New auto-connect UDP port added";
Don Gagne's avatar
Don Gagne committed
452 453 454
        UDPConfiguration* udpConfig = new UDPConfiguration(_defaultUPDLinkName);
        udpConfig->setLocalPort(QGC_UDP_LOCAL_PORT);
        udpConfig->setDynamic(true);
455
        _linkConfigurations.append(udpConfig);
Don Gagne's avatar
Don Gagne committed
456
        createConnectedLink(udpConfig);
457
        emit linkConfigurationsChanged();
Don Gagne's avatar
Don Gagne committed
458 459
    }

460
#ifndef __ios__
dogmaphobic's avatar
dogmaphobic committed
461
    QStringList currentPorts;
Don Gagne's avatar
Don Gagne committed
462
    QList<QGCSerialPortInfo> portList = QGCSerialPortInfo::availablePorts();
Don Gagne's avatar
Don Gagne committed
463

464
    // Iterate Comm Ports
Don Gagne's avatar
Don Gagne committed
465
    foreach (QGCSerialPortInfo portInfo, portList) {
Don Gagne's avatar
Don Gagne committed
466 467 468 469 470 471 472 473 474
        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
475 476
        // Save port name
        currentPorts << portInfo.systemLocation();
Don Gagne's avatar
Don Gagne committed
477 478 479 480 481 482

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

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

487 488 489 490 491 492 493
            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();
494 495
                _autoconnectWaitList[portInfo.systemLocation()] = 1;
            } else if (++_autoconnectWaitList[portInfo.systemLocation()] * _autoconnectUpdateTimerMSecs > _autoconnectConnectDelayMSecs) {
Don Gagne's avatar
Don Gagne committed
496 497
                SerialConfiguration* pSerialConfig = NULL;

498
                _autoconnectWaitList.remove(portInfo.systemLocation());
499

Don Gagne's avatar
Don Gagne committed
500 501 502
                switch (boardType) {
                case QGCSerialPortInfo::BoardTypePX4FMUV1:
                case QGCSerialPortInfo::BoardTypePX4FMUV2:
503
                case QGCSerialPortInfo::BoardTypePX4FMUV4:
Don Gagne's avatar
Don Gagne committed
504 505
                    if (_autoconnectPixhawk) {
                        pSerialConfig = new SerialConfiguration(QString("Pixhawk on %1").arg(portInfo.portName().trimmed()));
506
                        pSerialConfig->setUsbDirect(true);
Don Gagne's avatar
Don Gagne committed
507
                    }
Don Gagne's avatar
Don Gagne committed
508 509
                    break;
                case QGCSerialPortInfo::BoardTypeAeroCore:
Don Gagne's avatar
Don Gagne committed
510 511
                    if (_autoconnectPixhawk) {
                        pSerialConfig = new SerialConfiguration(QString("AeroCore on %1").arg(portInfo.portName().trimmed()));
512
                        pSerialConfig->setUsbDirect(true);
Don Gagne's avatar
Don Gagne committed
513
                    }
Don Gagne's avatar
Don Gagne committed
514 515
                    break;
                case QGCSerialPortInfo::BoardTypePX4Flow:
Don Gagne's avatar
Don Gagne committed
516 517 518
                    if (_autoconnectPX4Flow) {
                        pSerialConfig = new SerialConfiguration(QString("PX4Flow on %1").arg(portInfo.portName().trimmed()));
                    }
Don Gagne's avatar
Don Gagne committed
519 520
                    break;
                case QGCSerialPortInfo::BoardType3drRadio:
Don Gagne's avatar
Don Gagne committed
521 522 523 524
                    if (_autoconnect3DRRadio) {
                        pSerialConfig = new SerialConfiguration(QString("3DR Radio on %1").arg(portInfo.portName().trimmed()));
                    }
                    break;
Don Gagne's avatar
Don Gagne committed
525 526
                default:
                    qWarning() << "Internal error";
Don Gagne's avatar
Don Gagne committed
527
                    continue;
dogmaphobic's avatar
dogmaphobic committed
528
                }
Don Gagne's avatar
Don Gagne committed
529

Don Gagne's avatar
Don Gagne committed
530 531 532 533 534 535
                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
536
                    createConnectedLink(pSerialConfig);
Don Gagne's avatar
Don Gagne committed
537
                }
dogmaphobic's avatar
dogmaphobic committed
538 539 540
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
541

dogmaphobic's avatar
dogmaphobic committed
542 543
    // 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
544 545 546 547
    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
548 549 550 551 552 553 554
                // 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
555
            }
Don Gagne's avatar
Don Gagne committed
556 557
        } else {
            qWarning() << "Internal error";
dogmaphobic's avatar
dogmaphobic committed
558 559
        }
    }
Don Gagne's avatar
Don Gagne committed
560

Don Gagne's avatar
Don Gagne committed
561
    // Now remove all configs that are gone
Don Gagne's avatar
Don Gagne committed
562
    foreach (LinkConfiguration* pDeleteConfig, _confToDelete) {
Don Gagne's avatar
Don Gagne committed
563
        qCDebug(LinkManagerLog) << "Removing unused autoconnect config" << pDeleteConfig->name();
Don Gagne's avatar
Don Gagne committed
564 565
        _autoconnectConfigurations.removeOne(pDeleteConfig);
        delete pDeleteConfig;
566
    }
567
#endif // __ios__
568 569
}

Don Gagne's avatar
Don Gagne committed
570 571 572
void LinkManager::shutdown(void)
{
    setConnectionsSuspended("Shutdown");
Don Gagne's avatar
Don Gagne committed
573
    disconnectAll();
Don Gagne's avatar
Don Gagne committed
574 575 576 577 578 579 580 581 582 583 584 585
}

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

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

        _autoconnectUDP = autoconnect;
        emit autoconnectUDPChanged(autoconnect);
586
    }
Don Gagne's avatar
Don Gagne committed
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
}

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

629
}
630 631 632 633 634 635 636 637 638 639 640 641

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
642
#ifdef QGC_ENABLE_BLUETOOTH
dogmaphobic's avatar
dogmaphobic committed
643 644
        list += "Bluetooth";
#endif
dogmaphobic's avatar
dogmaphobic committed
645
#ifdef QT_DEBUG
646
        list += "Mock Link";
dogmaphobic's avatar
dogmaphobic committed
647 648
#endif
#ifndef __mobile__
649
        list += "Log Replay";
dogmaphobic's avatar
dogmaphobic committed
650
#endif
dogmaphobic's avatar
dogmaphobic committed
651
        Q_ASSERT(list.size() == (int)LinkConfiguration::TypeLast);
652 653 654 655
    }
    return list;
}

656
void LinkManager::_updateSerialPorts()
657
{
658 659
    _commPortList.clear();
    _commPortDisplayList.clear();
660
#ifndef __ios__
661 662
    QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
    foreach (const QSerialPortInfo &info, portList)
663
    {
664 665 666
        QString port = info.systemLocation().trimmed();
        _commPortList += port;
        _commPortDisplayList += SerialConfiguration::cleanPortDisplayname(port);
667 668
    }
#endif
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
}

QStringList LinkManager::serialPortStrings(void)
{
    if(!_commPortDisplayList.size())
    {
        _updateSerialPorts();
    }
    return _commPortDisplayList;
}

QStringList LinkManager::serialPorts(void)
{
    if(!_commPortList.size())
    {
        _updateSerialPorts();
    }
686 687 688 689 690 691 692 693 694 695 696 697
    return _commPortList;
}

QStringList LinkManager::serialBaudRates(void)
{
#ifdef __ios__
    QStringList foo;
    return foo;
#else
    return SerialConfiguration::supportedBaudRates();
#endif
}
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723

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
724
#ifndef __ios__
725 726
    if((LinkConfiguration::LinkType)type == LinkConfiguration::TypeSerial)
        _updateSerialPorts();
dogmaphobic's avatar
dogmaphobic committed
727
#endif
728 729 730 731 732 733
    return LinkConfiguration::createSettings(type, name);
}

LinkConfiguration* LinkManager::startConfigurationEditing(LinkConfiguration* config)
{
    Q_ASSERT(config != NULL);
dogmaphobic's avatar
dogmaphobic committed
734
#ifndef __ios__
735 736
    if(config->type() == LinkConfiguration::TypeSerial)
        _updateSerialPorts();
dogmaphobic's avatar
dogmaphobic committed
737
#endif
738 739 740 741 742 743 744 745 746 747 748 749 750
    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();
751
#ifdef Q_OS_WIN
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
                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
773
#ifdef QGC_ENABLE_BLUETOOTH
dogmaphobic's avatar
dogmaphobic committed
774 775 776
            case LinkConfiguration::TypeBluetooth: {
                    BluetoothConfiguration* tconfig = dynamic_cast<BluetoothConfiguration*>(config);
                    if(tconfig) {
777
                        config->setName(QString("%1 (Bluetooth Device)").arg(tconfig->device().name));
dogmaphobic's avatar
dogmaphobic committed
778 779 780 781
                    }
                }
                break;
#endif
dogmaphobic's avatar
dogmaphobic committed
782
#ifndef __mobile__
783
            case LinkConfiguration::TypeLogReplay: {
dogmaphobic's avatar
dogmaphobic committed
784 785 786 787
                    LogReplayLinkConfiguration* tconfig = dynamic_cast<LogReplayLinkConfiguration*>(config);
                    if(tconfig) {
                        config->setName(QString("Log Replay %1").arg(tconfig->logFilenameShort()));
                    }
788 789
                }
                break;
dogmaphobic's avatar
dogmaphobic committed
790
#endif
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
#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();
}
817

818 819 820 821
bool LinkManager::isAutoconnectLink(LinkInterface* link)
{
    return _autoconnectConfigurations.contains(link->getLinkConfiguration());
}
dogmaphobic's avatar
dogmaphobic committed
822 823 824 825 826

bool LinkManager::isBluetoothAvailable(void)
{
    return qgcApp()->isBluetoothAvailable();
}