WimaController.cc 18.8 KB
Newer Older
1
#include "WimaController.h"
2

3 4 5
const char* WimaController::wimaFileExtension   = "wima";
const char* WimaController::areaItemsName       = "AreaItems";
const char* WimaController::missionItemsName    = "MissionItems";
6

7 8 9 10 11 12 13 14 15
WimaController::WimaController(QObject *parent)
    : QObject               (parent)
    , _readyForSaveSend     (false)
    , _currentPolygonIndex  (-1)
    , _container            (nullptr)
    , _joinedArea           (this)
    , _opArea               (this)
    , _serArea              (this)
    , _corridor             (this)
16 17 18 19
{
    connect(this, &WimaController::currentPolygonIndexChanged, this, &WimaController::recalcPolygonInteractivity);
}

20
QmlObjectListModel* WimaController::visualItems()
21
{
22
    return &_visualItems;
23 24
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
25 26 27 28
QStringList WimaController::loadNameFilters() const
{
    QStringList filters;

29
    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
Valentin Platzgummer's avatar
Valentin Platzgummer committed
30 31
               tr("All Files (*.*)");
    return filters;
32 33 34 35 36
}

QStringList WimaController::saveNameFilters() const
{
    QStringList filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
37

38 39
    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
    return filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
40 41
}

42 43
QGeoCoordinate WimaController::joinedAreaCenter() const
{
44
    return _joinedArea.center();
45 46
}

47
WimaArea WimaController::joinedArea() const
48
{
49
    return _joinedArea;
50 51
}

52 53 54 55 56 57 58 59 60 61 62 63 64 65
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
    emit masterControllerChanged();
}

void WimaController::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
    emit missionControllerChanged();
}

void WimaController::setCurrentPolygonIndex(int index)
{
66
    if(index >= 0 && index < _visualItems.count() && index != _currentPolygonIndex){
67 68 69 70 71 72
        _currentPolygonIndex = index;

        emit currentPolygonIndexChanged(index);
    }
}

73 74
void WimaController::setDataContainer(WimaDataContainer *container)
{
75
    if (_container == nullptr && container != nullptr) {
76 77 78 79 80 81 82 83
        _container = container;

        emit dataContainerChanged();
    }
}

void WimaController::startWimaController(bool flyView)
{
84

85 86
}

87 88
void WimaController::removeArea(int index)
{
89 90
    if(index >= 0 && index < _visualItems.count()){
        WimaArea* area = qobject_cast<WimaArea*>(_visualItems.removeAt(index));
91 92 93 94 95

        if ( area == nullptr) {
            qWarning("WimaController::removeArea(): nullptr catched, internal error.");
            return;
        }
96
        area->clear();
97

98 99
        emit visualItemsChanged();

100
        if (_visualItems.count() == 0) {
101 102 103 104 105 106
            // this branch is reached if all items are removed
            // to guarentee proper behavior, _currentPolygonIndex must be set to a invalid value, as on constructor init.
            _currentPolygonIndex = -1;
            return;
        }

107 108
        if(_currentPolygonIndex >= _visualItems.count()){
            setCurrentPolygonIndex(_visualItems.count() - 1);
109 110 111 112 113 114 115 116 117
        }else{
            recalcPolygonInteractivity(_currentPolygonIndex);
        }
    }else{
        qWarning("Index out of bounds!");
    }

}

118
bool WimaController::addGOperationArea()
119
{
120
    if (!_visualItems.contains(&_opArea)) {
121
        _visualItems.append(&_opArea);
122

123 124
        int newIndex = _visualItems.count()-1;
        setCurrentPolygonIndex(newIndex);
125

126 127 128 129 130
        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
131 132
}

133
bool WimaController::addServiceArea()
134
{
135
    if (!_visualItems.contains(&_serArea)) {
136
        _visualItems.append(&_serArea);
137

138 139
        int newIndex = _visualItems.count()-1;
        setCurrentPolygonIndex(newIndex);
140

141 142 143 144 145
        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
146 147
}

148
bool WimaController::addVehicleCorridor()
149
{
150
    if (!_visualItems.contains(&_corridor)) {
151
        _visualItems.append(&_corridor);
152

153 154
        int newIndex = _visualItems.count()-1;
        setCurrentPolygonIndex(newIndex);
155

156 157 158 159 160
        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
161 162
}

163
void WimaController::removeAll()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
164 165
{
    bool changesApplied = false;
166
    while (_visualItems.count() > 0) {
167
        removeArea(0);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
168 169 170
        changesApplied = true;
    }

171 172
    _missionController->removeAll();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
173 174 175 176 177 178 179
    _currentFile = "";

    emit currentFileChanged();
    if ( changesApplied )
         emit visualItemsChanged();
}

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
void WimaController::startMission()
{

}

void WimaController::abortMission()
{

}

void WimaController::pauseMission()
{

}

void WimaController::resumeMission()
{

}

200 201 202
bool WimaController::updateMission()
{

203 204
    setReadyForSaveSend(false);
    #define debug 0
205

206 207 208 209
    if ( !recalcJoinedArea()) {
        qgcApp()->showMessage(tr("Not able to join areas. Areas must be overlapping"));
        return false;
    }
210

211 212 213
    #if debug
        _visualItems.append(&_joinedArea);
    #endif
Valentin Platzgummer's avatar
Valentin Platzgummer committed
214

215 216 217 218 219 220 221 222 223
    // reset visual items
    _missionController->removeAll();
    QmlObjectListModel* missionItems = _missionController->visualItems();
    // set home position to serArea center
    MissionSettingsItem* settingsItem= qobject_cast<MissionSettingsItem*>(missionItems->get(0));
    if (settingsItem == nullptr){
        qWarning("WimaController::updateMission(): settingsItem == nullptr");
        return false;
    }
224

225 226 227 228 229 230 231 232 233 234
    // set altitudes, temporary measure to solve bugs
    QGeoCoordinate center = _serArea.center();
    center.setAltitude(0);
    _serArea.setCenter(center);
    center = _opArea.center();
    center.setAltitude(0);
    _opArea.setCenter(center);
    center = _corridor.center();
    center.setAltitude(0);
    _corridor.setCenter(center);
235

236

237 238
    // set HomePos. to serArea center
    settingsItem->setCoordinate(_serArea.center());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
239

240 241 242
    // create take off position item
    int sequenceNumber = _missionController->insertSimpleMissionItem(_serArea.center(), missionItems->count());
    _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
243

244

245 246 247 248 249 250 251 252 253 254 255
    // create survey item, will be extened with more()-> mission types in the future
    _missionController->insertComplexMissionItem(_missionController->surveyComplexItemName(), _opArea.center(), missionItems->count());
    SurveyComplexItem* survey = qobject_cast<SurveyComplexItem*>(missionItems->get(missionItems->count()-1));
    if (survey == nullptr){
        qWarning("WimaController::updateMission(): survey == nullptr");
        return false;
    } else {
        survey->surveyAreaPolygon()->clear();
        survey->surveyAreaPolygon()->appendVertices(_opArea.coordinateList());
        //survey->
    }
256

257 258 259 260 261 262 263 264 265 266 267 268
    // calculate path from take off to opArea
    QGeoCoordinate start = _serArea.center();
    QGeoCoordinate end = survey->visualTransectPoints().first().value<QGeoCoordinate>();
    QList<QGeoCoordinate> path;
    if ( !WimaArea::dijkstraPath(start, end, _joinedArea, path)) {
        qgcApp()->showMessage(tr("Not able to calculate the path from takeoff position to measurement area."));
        return false;
    }
    for (int i = 1; i < path.count()-1; i++) {
        sequenceNumber = _missionController->insertSimpleMissionItem(path.value(i), missionItems->count()-1);
        _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
    }
269

270 271 272 273 274 275 276 277 278 279
    // calculate return path
    start   = survey->visualTransectPoints().last().value<QGeoCoordinate>();
    end     = _serArea.center();
    path.clear();
    if ( ! WimaArea::dijkstraPath(start, end, _joinedArea, path)) {
        qgcApp()->showMessage(tr("Not able to calculate the path from measurement area to landing position."));
        return false;
    }
    for (int i = 1; i < path.count()-1; i++) {
        sequenceNumber = _missionController->insertSimpleMissionItem(path.value(i), missionItems->count());
280
        _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
281
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
282

283 284 285 286 287 288 289
    // create land position item
    sequenceNumber = _missionController->insertSimpleMissionItem(_serArea.center(), missionItems->count());
    _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
    SimpleMissionItem* landItem = qobject_cast<SimpleMissionItem*>(missionItems->get(missionItems->count()-1));
    if (landItem == nullptr){
        qWarning("WimaController::updateMission(): landItem == nullptr");
        return false;
290
    } else {
291 292 293 294 295
        Vehicle* controllerVehicle = _masterController->controllerVehicle();
        MAV_CMD landCmd = controllerVehicle->vtol() ? MAV_CMD_NAV_VTOL_LAND : MAV_CMD_NAV_LAND;
        if (controllerVehicle->firmwarePlugin()->supportedMissionCommands().contains(landCmd)) {
            landItem->setCommand(landCmd);
        }
296
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
297

298 299 300
    updateContainer();
    setReadyForSaveSend(true);
    return true;
301 302
}

303
void WimaController::saveToCurrent()
304
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
305
    saveToFile(_currentFile);
306 307
}

308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
void WimaController::saveToFile(const QString& filename)
{
    if (filename.isEmpty()) {
        return;
    }

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

    QFile file(planFilename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qgcApp()->showMessage(tr("Plan save error %1 : %2").arg(filename).arg(file.errorString()));
        _currentFile.clear();
        emit currentFileChanged();
    } else {
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
        FileType fileType = FileType::WimaFile;
        if ( planFilename.contains(QString(".%1").arg(wimaFileExtension)) ) {
            fileType = FileType::WimaFile;
        } else if ( planFilename.contains(QString(".%1").arg(AppSettings::planFileExtension)) ) {
            fileType = FileType::PlanFile;
        } else {
            if ( planFilename.contains(".") ) {
                qgcApp()->showMessage(tr("File format not supported"));
            } else {
                qgcApp()->showMessage(tr("File without file extension not accepted."));
                return;
            }
        }

        QJsonDocument saveDoc = saveToJson(fileType);
340 341 342 343 344 345 346 347
        file.write(saveDoc.toJson());
        if(_currentFile != planFilename) {
            _currentFile = planFilename;
            emit currentFileChanged();
        }
    }
}

348
bool WimaController::loadFromCurrent()
349
{
350 351 352 353 354
    return loadFromFile(_currentFile);
}

bool WimaController::loadFromFile(const QString &filename)
{
355
    #define debug 0
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
    QString errorString;
    QString errorMessage = tr("Error loading Plan file (%1). %2").arg(filename).arg("%1");

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

    QFileInfo fileInfo(filename);
    QFile file(filename);

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

    if(fileInfo.suffix() == wimaFileExtension) {
        QJsonDocument   jsonDoc;
        QByteArray      bytes = file.readAll();
375

376 377 378 379 380 381
        if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return false;
        }

        QJsonObject json = jsonDoc.object();
382 383
        // AreaItems
        QJsonArray areaArray = json[areaItemsName].toArray();
384
        _visualItems.clear();
385

386 387
        int validAreaCounter = 0;
        for( int i = 0; i < areaArray.size() && validAreaCounter < 3; i++) {
388 389 390
            QJsonObject jsonArea = areaArray[i].toObject();

            if (jsonArea.contains(WimaArea::areaTypeName) && jsonArea[WimaArea::areaTypeName].isString()) {
391
                if ( jsonArea[WimaArea::areaTypeName] == WimaGOperationArea::wimaGOperationAreaName) {
392
                    print(_opArea);
393
                    bool success = _opArea.loadFromJson(jsonArea, errorString);
394
                    print(_opArea);
395 396 397 398 399 400

                    if ( !success ) {
                        qgcApp()->showMessage(errorMessage.arg(errorString));
                        return false;
                    }

401
                    validAreaCounter++;
402
                    _visualItems.append(&_opArea);
403 404
                    emit visualItemsChanged();
                } else if ( jsonArea[WimaArea::areaTypeName] == WimaServiceArea::wimaServiceAreaName) {
405
                    bool success = _serArea.loadFromJson(jsonArea, errorString);
406 407 408 409 410 411

                    if ( !success ) {
                        qgcApp()->showMessage(errorMessage.arg(errorString));
                        return false;
                    }

412
                    validAreaCounter++;
413
                    _visualItems.append(&_serArea);
414 415
                    emit visualItemsChanged();
                } else if ( jsonArea[WimaArea::areaTypeName] == WimaVCorridor::wimaVCorridorName) {
416
                    bool success = _corridor.loadFromJson(jsonArea, errorString);
417 418 419 420 421 422

                    if ( !success ) {
                        qgcApp()->showMessage(errorMessage.arg(errorString));
                        return false;
                    }

423
                    validAreaCounter++;
424
                    _visualItems.append(&_corridor);
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
                    emit visualItemsChanged();
                } else {
                    errorString += QString(tr("%s not supported.\n").arg(WimaArea::areaTypeName));
                    qgcApp()->showMessage(errorMessage.arg(errorString));
                    return false;
                }
            } else {
                errorString += QString(tr("Invalid or non existing entry for %s.\n").arg(WimaArea::areaTypeName));
                return false;
            }
        }

        _currentFile.sprintf("%s/%s.%s", fileInfo.path().toLocal8Bit().data(), fileInfo.completeBaseName().toLocal8Bit().data(), wimaFileExtension);

        emit currentFileChanged();
440
        recalcJoinedArea();
441

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
        // MissionItems
        // extrac MissionItems part
        QJsonDocument missionJsonDoc = QJsonDocument(json[missionItemsName].toObject());
        // create temporary file with missionItems
        QFile temporaryFile;
        QString cropedFileName = filename.section("/",0,-2);
        #if debug
            qWarning() << cropedFileName;
        #endif
        QString temporaryFileName;
        for (int i = 0; ; i++) {
            temporaryFileName = cropedFileName.append("/temp%1.%2").arg(i).arg(AppSettings::planFileExtension);

            if ( !QFile::exists(temporaryFileName) ) {
                temporaryFile.setFileName(temporaryFileName);
                if ( temporaryFile.open(QIODevice::WriteOnly | QIODevice::Text) ) {
                    break;
                }
            }

            if ( i > 1000) {
                qWarning("WimaController::loadFromFile(): not able to create temporary file.");
                return false;
            }
        }

        temporaryFile.write(missionJsonDoc.toJson());

        // load from temporary file
        _masterController->loadFromFile(temporaryFileName);

        // remove temporary file
        if ( !temporaryFile.remove() ){
            qWarning("WimaController::loadFromFile(): not able to remove temporary file.");
        }

478 479
        return true;

480 481 482 483
    } else if ( fileInfo.suffix() == AppSettings::planFileExtension ){
        _masterController->loadFromFile(filename);

        return true;// might be wrong return value
484 485 486
    } else {
        errorString += QString(tr("File extension not supported.\n"));
        qgcApp()->showMessage(errorMessage.arg(errorString));
487

488 489
        return false;
    }
490 491
}

492
void WimaController::recalcPolygonInteractivity(int index)
493
{
494 495 496 497 498
    if (index >= 0 && index < _visualItems.count()) {
        resetAllInteractive();
        WimaArea* interactivePoly = qobject_cast<WimaArea*>(_visualItems.get(index));
        interactivePoly->setInteractive(true);
    }
499
}
500

501
bool WimaController::recalcJoinedArea()
502
{
503 504 505
    // join service area, op area and corridor
    _joinedArea = _serArea;
    _joinedArea.join(_corridor);
506

507
    if ( !_joinedArea.join(_opArea) )
508
        return false; // this happens if all areas are pairwise disjoint
509 510 511 512
    else {
        emit joinedAreaChanged() ;
        return true;
    }
513 514 515 516


}

517
void WimaController::updateContainer()
518
{
519 520
    // Sets the pointers (inside _container) to the areas (of this).
    // Should be called only (once) after a _container has been assigned.
521
    if (_container != nullptr) {
522 523 524 525
        _container->setOpArea(&_opArea);
        _container->setSerArea(&_serArea);
        _container->setCorridor(&_corridor);
        _container->setJoinedArea(&_joinedArea);
526 527
    } else {
        qWarning("WimaController::uploadToContainer(): no container assigned.");
528
    }
529 530
}

531
void WimaController::resetAllInteractive()
532
{
533
    // Marks all areas as inactive (area.interactive == false)
534
    int itemCount = _visualItems.count();
535 536
    if (itemCount > 0){
        for (int i = 0; i < itemCount; i++) {
537
            WimaArea* iteratorPoly = qobject_cast<WimaArea*>(_visualItems.get(i));
538 539
            iteratorPoly->setInteractive(false);
        }
540 541 542
    }
}

543 544 545 546 547
void WimaController::setInteractive()
{
    recalcPolygonInteractivity(_currentPolygonIndex);
}

548
QJsonDocument WimaController::saveToJson(FileType fileType)
549
{
550 551
    /// This function save all areas (of WimaController) and all mission items (of MissionController) to a QJsonDocument
    /// @param fileType is either WimaFile or PlanFile (enum), if fileType == PlanFile only mission items are stored
552
    QJsonObject json;
553

554 555
    if ( fileType == FileType::WimaFile ) {
        QJsonArray jsonArray;
556

557
        for (int i = 0; i < _visualItems.count(); i++) {
558
            QJsonObject json;
559

560
            WimaArea* area = qobject_cast<WimaArea*>(_visualItems.get(i));
561

562 563 564 565
            if (area == nullptr) {
                qWarning("WimaController::saveToJson(): Internal error, area == nullptr!");
                return QJsonDocument();
            }
566

567
            // check the type of area, create and append the JsonObject to the JsonArray once determined
568 569 570 571 572 573 574 575 576 577 578 579 580
            WimaGOperationArea* opArea =  qobject_cast<WimaGOperationArea*>(area);
            if (opArea != nullptr) {
                opArea->saveToJson(json);
                jsonArray.append(json);
                continue;
            }

            WimaServiceArea* serArea =  qobject_cast<WimaServiceArea*>(area);
            if (serArea != nullptr) {
                serArea->saveToJson(json);
                jsonArray.append(json);
                continue;
            }
581

582 583 584 585 586 587 588 589 590
            WimaVCorridor* corridor =  qobject_cast<WimaVCorridor*>(area);
            if (corridor != nullptr) {
                corridor->saveToJson(json);
                jsonArray.append(json);
                continue;
            }

            // if non of the obove branches was trigger, type must be WimaArea
            area->saveToJson(json);
591 592 593
            jsonArray.append(json);
        }

594 595 596 597 598 599
        json[areaItemsName] = jsonArray;
        json[missionItemsName] = _masterController->saveToJson().object();

        return QJsonDocument(json);
    } else if (fileType == FileType::PlanFile) {
        return _masterController->saveToJson();
600 601
    }

602
    return QJsonDocument(json);
603 604
}

605 606 607 608 609 610 611 612
void WimaController::setReadyForSaveSend(bool ready)
{
    if (ready != _readyForSaveSend) {
        _readyForSaveSend = ready;
        emit readyForSaveSendChanged(ready);
    }
}

613 614