PlanMasterController.cc 21.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "PlanMasterController.h"
#include "QGCApplication.h"
12
#include "QGCCorePlugin.h"
13
#include "MultiVehicleManager.h"
14
#include "SettingsManager.h"
15 16
#include "AppSettings.h"
#include "JsonHelper.h"
17
#include "MissionManager.h"
18
#include "KML.h"
19

20
#include <QDomDocument>
21
#include <QJsonDocument>
Don Gagne's avatar
Don Gagne committed
22
#include <QFileInfo>
23

DonLakeFlyer's avatar
DonLakeFlyer committed
24 25
QGC_LOGGING_CATEGORY(PlanMasterControllerLog, "PlanMasterControllerLog")

26 27 28 29 30
const int   PlanMasterController::kPlanFileVersion =            1;
const char* PlanMasterController::kPlanFileType =               "Plan";
const char* PlanMasterController::kJsonMissionObjectKey =       "mission";
const char* PlanMasterController::kJsonGeoFenceObjectKey =      "geoFence";
const char* PlanMasterController::kJsonRallyPointsObjectKey =   "rallyPoints";
31 32

PlanMasterController::PlanMasterController(QObject* parent)
33 34
    : QObject               (parent)
    , _multiVehicleMgr      (qgcApp()->toolbox()->multiVehicleManager())
35 36 37 38
    , _controllerVehicle    (new Vehicle(
        static_cast<MAV_AUTOPILOT>(qgcApp()->toolbox()->settingsManager()->appSettings()->offlineEditingFirmwareType()->rawValue().toInt()),
        static_cast<MAV_TYPE>(qgcApp()->toolbox()->settingsManager()->appSettings()->offlineEditingVehicleType()->rawValue().toInt()),
        qgcApp()->toolbox()->firmwarePluginManager()))
39 40 41 42 43 44 45 46 47 48
    , _managerVehicle       (_controllerVehicle)
    , _flyView              (true)
    , _offline              (true)
    , _missionController    (this)
    , _geoFenceController   (this)
    , _rallyPointController (this)
    , _loadGeoFence         (false)
    , _loadRallyPoints      (false)
    , _sendGeoFence         (false)
    , _sendRallyPoints      (false)
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
{
    connect(&_missionController,    &MissionController::dirtyChanged,       this, &PlanMasterController::dirtyChanged);
    connect(&_geoFenceController,   &GeoFenceController::dirtyChanged,      this, &PlanMasterController::dirtyChanged);
    connect(&_rallyPointController, &RallyPointController::dirtyChanged,    this, &PlanMasterController::dirtyChanged);

    connect(&_missionController,    &MissionController::containsItemsChanged,       this, &PlanMasterController::containsItemsChanged);
    connect(&_geoFenceController,   &GeoFenceController::containsItemsChanged,      this, &PlanMasterController::containsItemsChanged);
    connect(&_rallyPointController, &RallyPointController::containsItemsChanged,    this, &PlanMasterController::containsItemsChanged);

    connect(&_missionController,    &MissionController::syncInProgressChanged,      this, &PlanMasterController::syncInProgressChanged);
    connect(&_geoFenceController,   &GeoFenceController::syncInProgressChanged,     this, &PlanMasterController::syncInProgressChanged);
    connect(&_rallyPointController, &RallyPointController::syncInProgressChanged,   this, &PlanMasterController::syncInProgressChanged);
}

PlanMasterController::~PlanMasterController()
{

}

68
void PlanMasterController::start(bool flyView)
69
{
70 71 72 73
    _flyView = flyView;
    _missionController.start(_flyView);
    _geoFenceController.start(_flyView);
    _rallyPointController.start(_flyView);
74 75 76 77 78 79 80

    connect(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &PlanMasterController::_activeVehicleChanged);
    _activeVehicleChanged(_multiVehicleMgr->activeVehicle());
}

void PlanMasterController::startStaticActiveVehicle(Vehicle* vehicle)
{
81 82 83 84
    _flyView = true;
    _missionController.start(_flyView);
    _geoFenceController.start(_flyView);
    _rallyPointController.start(_flyView);
85 86 87 88 89
    _activeVehicleChanged(vehicle);
}

void PlanMasterController::_activeVehicleChanged(Vehicle* activeVehicle)
{
90 91 92
    if (_managerVehicle == activeVehicle) {
        // We are already setup for this vehicle
        return;
93 94
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
95 96
    qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged" << activeVehicle;

97 98 99 100 101 102 103 104 105 106
    if (_managerVehicle) {
        // Disconnect old vehicle
        disconnect(_managerVehicle->missionManager(),       &MissionManager::newMissionItemsAvailable,  this, &PlanMasterController::_loadMissionComplete);
        disconnect(_managerVehicle->geoFenceManager(),      &GeoFenceManager::loadComplete,             this, &PlanMasterController::_loadGeoFenceComplete);
        disconnect(_managerVehicle->rallyPointManager(),    &RallyPointManager::loadComplete,           this, &PlanMasterController::_loadRallyPointsComplete);
        disconnect(_managerVehicle->missionManager(),       &MissionManager::sendComplete,              this, &PlanMasterController::_sendMissionComplete);
        disconnect(_managerVehicle->geoFenceManager(),      &GeoFenceManager::sendComplete,             this, &PlanMasterController::_sendGeoFenceComplete);
        disconnect(_managerVehicle->rallyPointManager(),    &RallyPointManager::sendComplete,           this, &PlanMasterController::_sendRallyPointsComplete);
    }

107
    bool newOffline = false;
108
    if (activeVehicle == nullptr) {
109 110 111
        // Since there is no longer an active vehicle we use the offline controller vehicle as the manager vehicle
        _managerVehicle = _controllerVehicle;
        newOffline = true;
112
    } else {
113
        newOffline = false;
114 115 116 117 118 119 120 121
        _managerVehicle = activeVehicle;

        // Update controllerVehicle to the currently connected vehicle
        AppSettings* appSettings = qgcApp()->toolbox()->settingsManager()->appSettings();
        appSettings->offlineEditingFirmwareType()->setRawValue(AppSettings::offlineEditingFirmwareTypeFromFirmwareType(_managerVehicle->firmwareType()));
        appSettings->offlineEditingVehicleType()->setRawValue(AppSettings::offlineEditingVehicleTypeFromVehicleType(_managerVehicle->vehicleType()));

        // We use these signals to sequence upload and download to the multiple controller/managers
DonLakeFlyer's avatar
DonLakeFlyer committed
122 123 124 125 126 127
        connect(_managerVehicle->missionManager(),      &MissionManager::newMissionItemsAvailable,  this, &PlanMasterController::_loadMissionComplete);
        connect(_managerVehicle->geoFenceManager(),     &GeoFenceManager::loadComplete,             this, &PlanMasterController::_loadGeoFenceComplete);
        connect(_managerVehicle->rallyPointManager(),   &RallyPointManager::loadComplete,           this, &PlanMasterController::_loadRallyPointsComplete);
        connect(_managerVehicle->missionManager(),      &MissionManager::sendComplete,              this, &PlanMasterController::_sendMissionComplete);
        connect(_managerVehicle->geoFenceManager(),     &GeoFenceManager::sendComplete,             this, &PlanMasterController::_sendGeoFenceComplete);
        connect(_managerVehicle->rallyPointManager(),   &RallyPointManager::sendComplete,           this, &PlanMasterController::_sendRallyPointsComplete);
128
    }
129

130 131 132
    _missionController.managerVehicleChanged(_managerVehicle);
    _geoFenceController.managerVehicleChanged(_managerVehicle);
    _rallyPointController.managerVehicleChanged(_managerVehicle);
133

134 135 136 137 138 139 140
    // Vehicle changed so we need to signal everything
    _offline = newOffline;
    emit containsItemsChanged(containsItems());
    emit syncInProgressChanged();
    emit dirtyChanged(dirty());
    emit offlineChanged(offline());

141
    if (!_flyView) {
DonLakeFlyer's avatar
DonLakeFlyer committed
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
        if (!offline()) {
            // We are in Plan view and we have a newly connected vehicle:
            //  - If there is no plan available in Plan view show the one from the vehicle
            //  - Otherwise leave the current plan alone
            if (!containsItems()) {
                qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Plan view is empty so loading from manager";
                _showPlanFromManagerVehicle();
            }
        }
    } else {
        if (offline()) {
            // No more active vehicle, clear mission
            qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Fly view is offline clearing plan";
            removeAll();
        } else {
            // Fly view has changed to a new active vehicle, update to show correct mission
            qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Fly view is online so loading from manager";
            _showPlanFromManagerVehicle();
        }
161
    }
162 163 164 165
}

void PlanMasterController::loadFromVehicle(void)
{
166 167 168 169 170
    if (_managerVehicle->highLatencyLink()) {
        qgcApp()->showMessage(tr("Download not supported on high latency links."));
        return;
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
171 172
    if (offline()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while offline";
173
    } else if (_flyView) {
DonLakeFlyer's avatar
DonLakeFlyer committed
174 175 176 177
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called from Fly view";
    } else if (syncInProgress()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while syncInProgress";
    } else {
178
        _loadGeoFence = true;
179
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle calling _missionController.loadFromVehicle";
180 181 182
        _missionController.loadFromVehicle();
        setDirty(false);
    }
183 184
}

185

DonLakeFlyer's avatar
DonLakeFlyer committed
186
void PlanMasterController::_loadMissionComplete(void)
187
{
188
    if (!_flyView && _loadGeoFence) {
189 190
        _loadGeoFence = false;
        _loadRallyPoints = true;
191
        if (_geoFenceController.supported()) {
192
            qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadMissionComplete calling _geoFenceController.loadFromVehicle";
193 194 195 196 197 198
            _geoFenceController.loadFromVehicle();
        } else {
            qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadMissionComplete GeoFence not supported skipping";
            _geoFenceController.removeAll();
            _loadGeoFenceComplete();
        }
199
        setDirty(false);
DonLakeFlyer's avatar
DonLakeFlyer committed
200 201 202 203 204
    }
}

void PlanMasterController::_loadGeoFenceComplete(void)
{
205
    if (!_flyView && _loadRallyPoints) {
DonLakeFlyer's avatar
DonLakeFlyer committed
206
        _loadRallyPoints = false;
207
        if (_rallyPointController.supported()) {
208
            qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadGeoFenceComplete calling _rallyPointController.loadFromVehicle";
209 210 211 212 213 214
            _rallyPointController.loadFromVehicle();
        } else {
            qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadMissionComplete Rally Points not supported skipping";
            _rallyPointController.removeAll();
            _loadRallyPointsComplete();
        }
DonLakeFlyer's avatar
DonLakeFlyer committed
215 216 217 218 219 220
        setDirty(false);
    }
}

void PlanMasterController::_loadRallyPointsComplete(void)
{
221
    qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadRallyPointsComplete";
DonLakeFlyer's avatar
DonLakeFlyer committed
222 223 224 225
}

void PlanMasterController::_sendMissionComplete(void)
{
226
    if (_sendGeoFence) {
227 228
        _sendGeoFence = false;
        _sendRallyPoints = true;
229 230 231 232 233 234 235
        if (_geoFenceController.supported()) {
            qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start GeoFence sendToVehicle";
            _geoFenceController.sendToVehicle();
        } else {
            qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle GeoFence not supported skipping";
            _sendGeoFenceComplete();
        }
236 237 238 239
        setDirty(false);
    }
}

DonLakeFlyer's avatar
DonLakeFlyer committed
240
void PlanMasterController::_sendGeoFenceComplete(void)
241
{
242
    if (_sendRallyPoints) {
243
        _sendRallyPoints = false;
244 245 246 247 248 249 250
        if (_rallyPointController.supported()) {
            qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start rally sendToVehicle";
            _rallyPointController.sendToVehicle();
        } else {
            qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle Rally Points not support skipping";
            _sendRallyPointsComplete();
        }
251 252 253
    }
}

DonLakeFlyer's avatar
DonLakeFlyer committed
254 255
void PlanMasterController::_sendRallyPointsComplete(void)
{
256
    qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle Rally Point send complete";
DonLakeFlyer's avatar
DonLakeFlyer committed
257 258
}

259 260
void PlanMasterController::sendToVehicle(void)
{
261 262 263 264 265
    if (_managerVehicle->highLatencyLink()) {
        qgcApp()->showMessage(tr("Upload not supported on high latency links."));
        return;
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
266 267 268 269 270 271
    if (offline()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while offline";
    } else if (syncInProgress()) {
        qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while syncInProgress";
    } else {
        qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start mission sendToVehicle";
272 273 274 275
        _sendGeoFence = true;
        _missionController.sendToVehicle();
        setDirty(false);
    }
276 277 278 279 280
}

void PlanMasterController::loadFromFile(const QString& filename)
{
    QString errorString;
281
    QString errorMessage = tr("Error loading Plan file (%1). %2").arg(filename).arg("%1");
282 283 284 285 286

    if (filename.isEmpty()) {
        return;
    }

287
    QFileInfo fileInfo(filename);
288 289 290 291
    QFile file(filename);

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        errorString = file.errorString() + QStringLiteral(" ") + filename;
292
        qgcApp()->showMessage(errorMessage.arg(errorString));
293 294 295
        return;
    }

296 297
    bool success = false;
    if(fileInfo.suffix() == AppSettings::planFileExtension) {
298 299 300 301 302 303 304 305 306
        QJsonDocument   jsonDoc;
        QByteArray      bytes = file.readAll();

        if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

        QJsonObject json = jsonDoc.object();
307 308 309 310 311
        //-- Allow plugins to pre process the load
        qgcApp()->toolbox()->corePlugin()->preLoadFromJson(this, json);

        int version;
        if (!JsonHelper::validateQGCJsonFile(json, kPlanFileType, kPlanFileVersion, kPlanFileVersion, version, errorString)) {
312 313 314 315 316
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

        QList<JsonHelper::KeyValidateInfo> rgKeyInfo = {
317 318 319
            { kJsonMissionObjectKey,        QJsonValue::Object, true },
            { kJsonGeoFenceObjectKey,       QJsonValue::Object, true },
            { kJsonRallyPointsObjectKey,    QJsonValue::Object, true },
320 321 322 323 324 325
        };
        if (!JsonHelper::validateKeys(json, rgKeyInfo, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return;
        }

326 327 328
        if (!_missionController.load(json[kJsonMissionObjectKey].toObject(), errorString) ||
                !_geoFenceController.load(json[kJsonGeoFenceObjectKey].toObject(), errorString) ||
                !_rallyPointController.load(json[kJsonRallyPointsObjectKey].toObject(), errorString)) {
329
            qgcApp()->showMessage(errorMessage.arg(errorString));
330
        } else {
331 332
            //-- Allow plugins to post process the load
            qgcApp()->toolbox()->corePlugin()->postLoadFromJson(this, json);
333
            success = true;
334
        }
335
    } else if (fileInfo.suffix() == AppSettings::missionFileExtension) {
336 337
        if (!_missionController.loadJsonFile(file, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
338 339
        } else {
            success = true;
340
        }
341
    } else if (fileInfo.suffix() == AppSettings::waypointsFileExtension || fileInfo.suffix() == QStringLiteral("txt")) {
342 343
        if (!_missionController.loadTextFile(file, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
344 345
        } else {
            success = true;
346
        }
347 348 349 350 351 352 353 354
    } else {
        //-- TODO: What then?
    }

    if(success){
        _currentPlanFile.sprintf("%s/%s.%s", fileInfo.path().toLocal8Bit().data(), fileInfo.completeBaseName().toLocal8Bit().data(), AppSettings::planFileExtension);
    } else {
        _currentPlanFile.clear();
355
    }
356
    emit currentPlanFileChanged();
357

358
    if (!offline()) {
359
        setDirty(true);
360 361 362
    }
}

Gus Grubba's avatar
Gus Grubba committed
363 364 365
QJsonDocument PlanMasterController::saveToJson()
{
    QJsonObject planJson;
366
    qgcApp()->toolbox()->corePlugin()->preSaveToJson(this, planJson);
Gus Grubba's avatar
Gus Grubba committed
367 368 369
    QJsonObject missionJson;
    QJsonObject fenceJson;
    QJsonObject rallyJson;
370 371 372
    JsonHelper::saveQGCJsonFileHeader(planJson, kPlanFileType, kPlanFileVersion);
    //-- Allow plugin to preemptly add its own keys to mission
    qgcApp()->toolbox()->corePlugin()->preSaveToMissionJson(this, missionJson);
Gus Grubba's avatar
Gus Grubba committed
373
    _missionController.save(missionJson);
374 375
    //-- Allow plugin to add its own keys to mission
    qgcApp()->toolbox()->corePlugin()->postSaveToMissionJson(this, missionJson);
Gus Grubba's avatar
Gus Grubba committed
376 377
    _geoFenceController.save(fenceJson);
    _rallyPointController.save(rallyJson);
378 379 380 381
    planJson[kJsonMissionObjectKey] = missionJson;
    planJson[kJsonGeoFenceObjectKey] = fenceJson;
    planJson[kJsonRallyPointsObjectKey] = rallyJson;
    qgcApp()->toolbox()->corePlugin()->postSaveToJson(this, planJson);
Gus Grubba's avatar
Gus Grubba committed
382 383 384
    return QJsonDocument(planJson);
}

385 386 387 388 389 390 391 392
void
PlanMasterController::saveToCurrent()
{
    if(!_currentPlanFile.isEmpty()) {
        saveToFile(_currentPlanFile);
    }
}

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
void PlanMasterController::saveToFile(const QString& filename)
{
    if (filename.isEmpty()) {
        return;
    }

    QString planFilename = filename;
    if (!QFileInfo(filename).fileName().contains(".")) {
        planFilename += QString(".%1").arg(fileExtension());
    }

    QFile file(planFilename);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qgcApp()->showMessage(tr("Plan save error %1 : %2").arg(filename).arg(file.errorString()));
408 409
        _currentPlanFile.clear();
        emit currentPlanFileChanged();
410
    } else {
Gus Grubba's avatar
Gus Grubba committed
411
        QJsonDocument saveDoc = saveToJson();
412
        file.write(saveDoc.toJson());
413 414 415 416
        if(_currentPlanFile != planFilename) {
            _currentPlanFile = planFilename;
            emit currentPlanFileChanged();
        }
417 418
    }

419 420
    // Only clear dirty bit if we are offline
    if (offline()) {
421 422 423 424
        setDirty(false);
    }
}

425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
void PlanMasterController::saveToKml(const QString& filename)
{
    if (filename.isEmpty()) {
        return;
    }

    QString kmlFilename = filename;
    if (!QFileInfo(filename).fileName().contains(".")) {
        kmlFilename += QString(".%1").arg(kmlFileExtension());
    }

    QFile file(kmlFilename);

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qgcApp()->showMessage(tr("KML save error %1 : %2").arg(filename).arg(file.errorString()));
    } else {
        QDomDocument domDocument;
        _missionController.convertToKMLDocument(domDocument);
        QTextStream stream(&file);
        stream << domDocument.toString();
        file.close();
    }
}

449 450
void PlanMasterController::removeAll(void)
{
451 452 453
    _missionController.removeAll();
    _geoFenceController.removeAll();
    _rallyPointController.removeAll();
454 455 456 457
    if (_offline) {
        _missionController.setDirty(false);
        _geoFenceController.setDirty(false);
        _rallyPointController.setDirty(false);
458 459
        _currentPlanFile.clear();
        emit currentPlanFileChanged();
460
    }
461 462 463 464
}

void PlanMasterController::removeAllFromVehicle(void)
{
465 466
    if (!offline()) {
        _missionController.removeAllFromVehicle();
467 468 469 470 471 472
        if (_geoFenceController.supported()) {
            _geoFenceController.removeAllFromVehicle();
        }
        if (_rallyPointController.supported()) {
            _rallyPointController.removeAllFromVehicle();
        }
473
        setDirty(false);
474 475 476
    } else {
        qWarning() << "PlanMasterController::removeAllFromVehicle called while offline";
    }
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
}

bool PlanMasterController::containsItems(void) const
{
    return _missionController.containsItems() || _geoFenceController.containsItems() || _rallyPointController.containsItems();
}

bool PlanMasterController::dirty(void) const
{
    return _missionController.dirty() || _geoFenceController.dirty() || _rallyPointController.dirty();
}

void PlanMasterController::setDirty(bool dirty)
{
    _missionController.setDirty(dirty);
    _geoFenceController.setDirty(dirty);
    _rallyPointController.setDirty(dirty);
}

QString PlanMasterController::fileExtension(void) const
{
    return AppSettings::planFileExtension;
}

501 502 503 504 505
QString PlanMasterController::kmlFileExtension(void) const
{
    return AppSettings::kmlFileExtension;
}

506 507 508 509
QStringList PlanMasterController::loadNameFilters(void) const
{
    QStringList filters;

Don Gagne's avatar
Don Gagne committed
510
    filters << tr("Supported types (*.%1 *.%2 *.%3 *.%4)").arg(AppSettings::planFileExtension).arg(AppSettings::missionFileExtension).arg(AppSettings::waypointsFileExtension).arg("txt") <<
511 512 513 514 515 516 517 518 519 520 521 522 523
               tr("All Files (*.*)");
    return filters;
}


QStringList PlanMasterController::saveNameFilters(void) const
{
    QStringList filters;

    filters << tr("Plan Files (*.%1)").arg(fileExtension()) << tr("All Files (*.*)");
    return filters;
}

524
QStringList PlanMasterController::fileKmlFilters(void) const
525 526 527 528 529 530 531
{
    QStringList filters;

    filters << tr("KML Files (*.%1)").arg(kmlFileExtension()) << tr("All Files (*.*)");
    return filters;
}

532 533 534 535 536 537
void PlanMasterController::sendPlanToVehicle(Vehicle* vehicle, const QString& filename)
{
    // Use a transient PlanMasterController to accomplish this
    PlanMasterController* controller = new PlanMasterController();
    controller->startStaticActiveVehicle(vehicle);
    controller->loadFromFile(filename);
538
    controller->sendToVehicle();
539 540
    delete controller;
}
DonLakeFlyer's avatar
DonLakeFlyer committed
541 542 543

void PlanMasterController::_showPlanFromManagerVehicle(void)
{
544
    if (!_managerVehicle->initialPlanRequestComplete() && !syncInProgress()) {
545 546 547 548
        // Something went wrong with initial load. All controllers are idle, so just force it off
        _managerVehicle->forceInitialPlanRequestComplete();
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
549 550 551 552 553 554 555
    // The crazy if structure is to handle the load propogating by itself through the system
    if (!_missionController.showPlanFromManagerVehicle()) {
        if (!_geoFenceController.showPlanFromManagerVehicle()) {
            _rallyPointController.showPlanFromManagerVehicle();
        }
    }
}
556 557 558 559 560 561 562

bool PlanMasterController::syncInProgress(void) const
{
    return _missionController.syncInProgress() ||
            _geoFenceController.syncInProgress() ||
            _rallyPointController.syncInProgress();
}