WimaPlaner.cc 29.5 KB
Newer Older
1
 #include "WimaPlaner.h"
2

Valentin Platzgummer's avatar
Valentin Platzgummer committed
3
4
#include "CircularSurveyComplexItem.h"

5
6


7
8
9
10
11
const char* WimaPlaner::wimaFileExtension   = "wima";
const char* WimaPlaner::areaItemsName       = "AreaItems";
const char* WimaPlaner::missionItemsName    = "MissionItems";

WimaPlaner::WimaPlaner(QObject *parent)
12
13
    : QObject                       (parent)
    , _currentAreaIndex             (-1)
14
    , _wimaBridge                   (nullptr)
15
    , _joinedArea                   (this)
16
    , _joinedAreaValid              (false)
17
18
19
20
21
    , _measurementArea              (this)
    , _serviceArea                  (this)
    , _corridor                     (this)
    , _circularSurvey               (nullptr)
    , _surveyRefChanging            (false)
22
23
24
    , _measurementAreaChanging      (false)
    , _corridorChanging             (false)
    , _serviceAreaChanging          (false)
25
    , _syncronizedWithController    (false)
26
    , _readyForSync                 (false)
27
{
28
29
30
31
    _lastMeasurementAreaPath    = _measurementArea.path();
    _lastServiceAreaPath        = _serviceArea.path();
    _lastCorridorPath           = _corridor.path();

32
33
34
    connect(this,           &WimaPlaner::currentPolygonIndexChanged,    this, &WimaPlaner::recalcPolygonInteractivity);
    connect(&_updateTimer,  &QTimer::timeout,                           this, &WimaPlaner::updateTimerSlot);
    connect(this,           &WimaPlaner::joinedAreaValidChanged,        this, &WimaPlaner::updateMission);
35

36
    _updateTimer.setInterval(300); // 300 ms means: max update time 2*300 ms
37
    _updateTimer.start();
38

Valentin Platzgummer's avatar
rc1    
Valentin Platzgummer committed
39
    // for debugging and testing purpose, remove if not needed anymore
40
41
    connect(&_autoLoadTimer, &QTimer::timeout, this, &WimaPlaner::autoLoadMission);
    _autoLoadTimer.setSingleShot(true);
42
    _autoLoadTimer.start(300);
43
44
45
46
47

    _calcArrivalAndReturnPathTimer.setInterval(100);
    _calcArrivalAndReturnPathTimer.setSingleShot(true);
    connect(&_calcArrivalAndReturnPathTimer, &QTimer::timeout, this, &WimaPlaner::calcArrivalAndReturnPath);

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
}

QmlObjectListModel* WimaPlaner::visualItems()
{
    return &_visualItems;
}

QStringList WimaPlaner::loadNameFilters() const
{
    QStringList filters;

    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
               tr("All Files (*.*)");
    return filters;
}

QStringList WimaPlaner::saveNameFilters() const
{
    QStringList filters;

    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
    return filters;
}

QGeoCoordinate WimaPlaner::joinedAreaCenter() const
{
    return _joinedArea.center();
}

void WimaPlaner::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
    emit masterControllerChanged();
}

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

void WimaPlaner::setCurrentPolygonIndex(int index)
{
    if(index >= 0 && index < _visualItems.count() && index != _currentAreaIndex){
        _currentAreaIndex = index;

        emit currentPolygonIndexChanged(index);
    }
}

98
void WimaPlaner::setWimaBridge(WimaBridge *bridge)
99
{
100
101
102
    if (bridge != nullptr) {
        _wimaBridge = bridge;
        emit wimaBridgeChanged();
103
104
105
    }
}

106
107
108
109
110
bool WimaPlaner::syncronizedWithController()
{
    return _syncronizedWithController;
}

111
112
113
114
115
bool WimaPlaner::readyForSync()
{
    return _readyForSync;
}

116
117
118
119
120
WimaPlaner *WimaPlaner::thisPointer()
{
    return this;
}

121
122
123
124
125
126
127
128
129
130
void WimaPlaner::removeArea(int index)
{
    if(index >= 0 && index < _visualItems.count()){
        WimaArea* area = qobject_cast<WimaArea*>(_visualItems.removeAt(index));

        if ( area == nullptr) {
            qWarning("WimaPlaner::removeArea(): nullptr catched, internal error.");
            return;
        }
        area->clear();
131
        area->borderPolygon()->clear();
132
133
134
135
136
137

        emit visualItemsChanged();

        if (_visualItems.count() == 0) {
            // this branch is reached if all items are removed
            // to guarentee proper behavior, _currentAreaIndex must be set to a invalid value, as on constructor init.
138
            resetAllInteractive();
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
            _currentAreaIndex = -1;
            return;
        }

        if(_currentAreaIndex >= _visualItems.count()){
            setCurrentPolygonIndex(_visualItems.count() - 1);
        }else{
            recalcPolygonInteractivity(_currentAreaIndex);
        }
    }else{
        qWarning("Index out of bounds!");
    }

}

154
bool WimaPlaner::addMeasurementArea()
155
{
156
157
    if (!_visualItems.contains(&_measurementArea)) {
        _visualItems.append(&_measurementArea);
158
159
160
161
162
163
164
165
166
167
168
169
170

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

        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
}

bool WimaPlaner::addServiceArea()
{
171
172
    if (!_visualItems.contains(&_serviceArea)) {
        _visualItems.append(&_serviceArea);
173
174
175
176
177
178
179
180
181
182
183

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

        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
}

184
bool WimaPlaner::addCorridor()
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
{
    if (!_visualItems.contains(&_corridor)) {
        _visualItems.append(&_corridor);

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

        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
}

void WimaPlaner::removeAll()
{
    bool changesApplied = false;
    while (_visualItems.count() > 0) {
        removeArea(0);
        changesApplied = true;
    }

    _missionController->removeAll();

    _currentFile = "";

211
212
213
214
    _circularSurvey = nullptr;
    _surveyRefChanging = false;


215
216
217
218
219
220
221
    emit currentFileChanged();
    if ( changesApplied )
         emit visualItemsChanged();
}

bool WimaPlaner::updateMission()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
222

223
   setReadyForSync(false);
224

225
    if ( !_joinedAreaValid)
226
227
228
        return false;


Valentin Platzgummer's avatar
Valentin Platzgummer committed
229
230
231
    // extract old survey data
    QmlObjectListModel* missionItems        = _missionController->visualItems();

232
    int surveyIndex = missionItems->indexOf(_circularSurvey);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
233

234
    // create survey item if not yet present
235
    if (surveyIndex == -1) {
236
        _missionController->insertComplexMissionItem(_missionController->circularSurveyComplexItemName(), _measurementArea.center(), missionItems->count());
237
        _circularSurvey = qobject_cast<CircularSurveyComplexItem*>(missionItems->get(missionItems->count()-1));
238

239
        if (_circularSurvey == nullptr){
240
241
242
243
            qWarning("WimaPlaner::updateMission(): survey == nullptr");
            return false;
        }

244

245
246
        // establish connections
        _circularSurvey->setRefPoint(_measurementArea.center());
247
248
        _lastSurveyRefPoint = _measurementArea.center();
        _surveyRefChanging = false;
249
        _circularSurvey->setIsInitialized(true); // prevents reinitialisation from gui
250
        connect(_circularSurvey, &TransectStyleComplexItem::missionItemReady, this, &WimaPlaner::calcArrivalAndReturnPath);
251
252
    }

253
254
255
    // update survey area
    _circularSurvey->surveyAreaPolygon()->clear();
    _circularSurvey->surveyAreaPolygon()->appendVertices(_measurementArea.coordinateList());
256
    _circularSurvey->comprehensiveUpdate();
257

258
    setReadyForSync(true);
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
    return true;
}

void WimaPlaner::saveToCurrent()
{
    saveToFile(_currentFile);
}

void WimaPlaner::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 {
        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);
        file.write(saveDoc.toJson());
        if(_currentFile != planFilename) {
            _currentFile = planFilename;
            emit currentFileChanged();
        }
    }
}

bool WimaPlaner::loadFromCurrent()
{
    return loadFromFile(_currentFile);
}

bool WimaPlaner::loadFromFile(const QString &filename)
{
    #define debug 0
    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();

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

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

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

            if (jsonArea.contains(WimaArea::areaTypeName) && jsonArea[WimaArea::areaTypeName].isString()) {
Valentin Platzgummer's avatar
adding    
Valentin Platzgummer committed
350
                if ( jsonArea[WimaArea::areaTypeName] == WimaMeasurementArea::WimaMeasurementAreaName) {
351
                    bool success = _measurementArea.loadFromJson(jsonArea, errorString);
352
353
354
355
356
357

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

358
                    _lastMeasurementAreaPath    = _measurementArea.path(); // prevents error messages at this point
359
                    validAreaCounter++;
360
                    _visualItems.append(&_measurementArea);
361
362
                    emit visualItemsChanged();
                } else if ( jsonArea[WimaArea::areaTypeName] == WimaServiceArea::wimaServiceAreaName) {
363
                    bool success = _serviceArea.loadFromJson(jsonArea, errorString);
364
365
366
367
368
369

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

370
                    _lastServiceAreaPath        = _serviceArea.path(); // prevents error messages at this point
371
                    validAreaCounter++;
372
                    _visualItems.append(&_serviceArea);
373
                    emit visualItemsChanged();
Valentin Platzgummer's avatar
adding    
Valentin Platzgummer committed
374
                } else if ( jsonArea[WimaArea::areaTypeName] == WimaCorridor::WimaCorridorName) {
375
376
377
378
379
380
381
                    bool success = _corridor.loadFromJson(jsonArea, errorString);

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

382
                    _lastCorridorPath           = _corridor.path(); // prevents error messages at this point
383
384
385
386
387
388
389
390
391
392
393
394
                    validAreaCounter++;
                    _visualItems.append(&_corridor);
                    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;
            }
395
        }
396
397
398
399

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

        emit currentFileChanged();
Valentin Platzgummer's avatar
12312    
Valentin Platzgummer committed
400
401
402
403
404
405

        QJsonObject missionObject = json[missionItemsName].toObject();

        //qWarning() << json[missionItemsName].type();

        QJsonDocument missionJsonDoc = QJsonDocument(missionObject);
406
407
408
409
410
411
412
413
        // 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++) {
Valentin Platzgummer's avatar
12312    
Valentin Platzgummer committed
414
415
            temporaryFileName = cropedFileName + QString("/temp%1.%2").arg(i).arg(AppSettings::planFileExtension);
            // qWarning() << temporaryFileName;
416
417
418
419
420
421
422
423
424
425
426
427
428
429

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

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

Valentin Platzgummer's avatar
12312    
Valentin Platzgummer committed
430
        // qWarning() << missionJsonDoc.toVariant().toString();
431
        temporaryFile.write(missionJsonDoc.toJson());
Valentin Platzgummer's avatar
12312    
Valentin Platzgummer committed
432
        temporaryFile.close();
433
434
435
436

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

437
438
439
440
441
442
443
444
445
        QmlObjectListModel *missionItems = _missionController->visualItems();

        _circularSurvey = nullptr;
        for (int i = 0; i < missionItems->count(); i++) {
            _circularSurvey = missionItems->value<CircularSurveyComplexItem *>(i);
            if (_circularSurvey != nullptr) {

                _lastSurveyRefPoint = _circularSurvey->refPoint();
                _surveyRefChanging = false;
446
                _circularSurvey->setIsInitialized(true); // prevents reinitialisation from gui
447
                connect(_circularSurvey, &TransectStyleComplexItem::missionItemReady, this, &WimaPlaner::calcArrivalAndReturnPath);
448
449
450
451
                break;
            }
        }

452
        //if (_circularSurvey == nullptr)        
453
454
455
456
        if ( !recalcJoinedArea() )
            return false;
        if ( !updateMission() )
            return false;
457

458
459
460
461
462
        // remove temporary file
        if ( !temporaryFile.remove() ){
            qWarning("WimaPlaner::loadFromFile(): not able to remove temporary file.");
        }

463
        setSyncronizedWithController(false);
464

465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
        return true;

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

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

void WimaPlaner::recalcPolygonInteractivity(int index)
{
    if (index >= 0 && index < _visualItems.count()) {
        resetAllInteractive();
        WimaArea* interactivePoly = qobject_cast<WimaArea*>(_visualItems.get(index));
483
484
        if (interactivePoly != nullptr)
            interactivePoly->setWimaAreaInteractive(true);
485
486
487
    }
}

488
489
bool WimaPlaner::calcArrivalAndReturnPath()
{
490
491
    setReadyForSync(false);

492
493
494
495
496
497
498
499
500
501
    // extract old survey data
    QmlObjectListModel          *missionItems   = _missionController->visualItems();

    int surveyIndex = missionItems->indexOf(_circularSurvey);

    if (surveyIndex == -1) {
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): no survey item");
        return false;
    }

502
503
504
505
    bool restorePlanViewIndex = false;
    if (surveyIndex == _missionController->currentPlanViewIndex())
        restorePlanViewIndex = true;

506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
    // remove old arrival and return path
    int size = missionItems->count();
    for (int i = surveyIndex+1; i < size; i++)
        _missionController->removeMissionItem(surveyIndex+1);
    for (int i = surveyIndex-1; i > 1; i--)
        _missionController->removeMissionItem(i);

    // set home position to serArea center
    MissionSettingsItem* settingsItem= qobject_cast<MissionSettingsItem*>(missionItems->get(0));
    if (settingsItem == nullptr){
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): settingsItem == nullptr");
        return false;
    }

    // set altitudes, temporary measure to solve bugs
    QGeoCoordinate center = _serviceArea.center();
    center.setAltitude(0);
    _serviceArea.setCenter(center);
    center = _measurementArea.center();
    center.setAltitude(0);
    _measurementArea.setCenter(center);
    center = _corridor.center();
    center.setAltitude(0);
    _corridor.setCenter(center);
    // set HomePos. to serArea center
    settingsItem->setCoordinate(_serviceArea.center());

    // set takeoff position
    bool setCommandNeeded = false;
    if (missionItems->count() < 3) {
        setCommandNeeded = true;
        _missionController->insertSimpleMissionItem(_serviceArea.center(), 1);
    }
    SimpleMissionItem* takeOffItem = qobject_cast<SimpleMissionItem*>(missionItems->get(1));
    if (takeOffItem == nullptr){
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): takeOffItem == nullptr");
        return false;
    }
    if (setCommandNeeded)
        _missionController->setTakeoffCommand(*takeOffItem);
    takeOffItem->setCoordinate(_serviceArea.center());

    if (_circularSurvey->visualTransectPoints().size() == 0) {
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): survey no points.");
        return false;
    }

    // calculate path from take off to survey
    QGeoCoordinate start = _serviceArea.center();
    QGeoCoordinate end = _circularSurvey->coordinate();

    #ifdef QT_DEBUG
558
559
    //if (!_visualItems.contains(&_joinedArea))
        //_visualItems.append(&_joinedArea);
560
561
    #endif

562
    QVector<QGeoCoordinate> path;
563
564
565
566
    if ( !calcShortestPath(start, end, path)) {
        qgcApp()->showMessage( QString(tr("Not able to calculate the path from takeoff position to measurement area.")).toLocal8Bit().data());
        return false;
    }
567
    _arrivalPathLength = path.size()-1;
568
569
    int sequenceNumber = 0;
    for (int i = 1; i < path.count()-1; i++) {
570
        sequenceNumber = _missionController->insertSimpleMissionItem(path[i], missionItems->count()-1);
571
572
573
574
575
576
577
578
579
580
581
582
583
        _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
    }

    // calculate return path
    start   = _circularSurvey->exitCoordinate();
    end     = _serviceArea.center();
    path.clear();
    if ( !calcShortestPath(start, end, path)) {
        qgcApp()->showMessage(QString(tr("Not able to calculate the path from measurement area to landing position.")).toLocal8Bit().data());
        return false;
    }
    _returnPathLength = path.size()-1; // -1: fist item is last measurement point
    for (int i = 1; i < path.count()-1; i++) {
584
        sequenceNumber = _missionController->insertSimpleMissionItem(path[i], missionItems->count());
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
        _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
    }

    // create land position item
    sequenceNumber = _missionController->insertSimpleMissionItem(_serviceArea.center(), missionItems->count());
    _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
    SimpleMissionItem* landItem = qobject_cast<SimpleMissionItem*>(missionItems->get(missionItems->count()-1));
    if (landItem == nullptr){
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): landItem == nullptr");
        return false;
    } else {
        if (!_missionController->setLandCommand(*landItem))
            return false;
    }

600
601
602
    if (restorePlanViewIndex)
        _missionController->setCurrentPlanViewIndex(missionItems->indexOf(_circularSurvey), false);
    setSyncronizedWithControllerFalse();
603
    setReadyForSync(true);
604

605
606
607
    return true;
}

608
bool WimaPlaner::recalcJoinedArea()
609
{
610
    setJoinedAreaValid(false);
611
612
613
614
    // check if at least service area and measurement area are available
    if (_visualItems.indexOf(&_serviceArea) == -1 || _visualItems.indexOf(&_measurementArea) == -1)
        return false;

615
    // check if area paths form simple polygons
616
    if ( !_serviceArea.isSimplePolygon() ) {
617
        qgcApp()->showMessage(tr("Service area is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
618
619
620
        return false;
    }

621
    if ( !_corridor.isSimplePolygon() && _corridor.count() > 0) {
622
        qgcApp()->showMessage(tr("Corridor is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
623
624
625
        return false;
    }

626
    if ( !_measurementArea.isSimplePolygon() ) {
627
        qgcApp()->showMessage(tr("Measurement area is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
628
629
630
        return false;
    }

631
    _joinedArea.setPath(_serviceArea.path());
632
    _joinedArea.join(_corridor);
633
    if ( !_joinedArea.join(_measurementArea) ) {
634
635
        /*qgcApp()->showMessage(tr("Not able to join areas. Service area and measurement"
                                 " must have a overlapping section, or be connected through a corridor."));*/
636
        return false; // this happens if all areas are pairwise disjoint
637
    }
638

Valentin Platzgummer's avatar
Valentin Platzgummer committed
639
    // join service area, op area and corridor
640
    setJoinedAreaValid(true);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
641
    return true;
642
643
}

644
void WimaPlaner::pushToWimaController()
645
{
646
    if (_wimaBridge != nullptr) {
647
648
        if (!_readyForSync)
            return;
649
        WimaPlanData planData = toPlanData();
650
        (void)_wimaBridge->setWimaPlanData(planData);
651
        setSyncronizedWithController(true);
652
653
654
655
656
    } else {
        qWarning("WimaPlaner::uploadToContainer(): no container assigned.");
    }
}

Valentin Platzgummer's avatar
123    
Valentin Platzgummer committed
657
658
659
bool WimaPlaner::calcShortestPath(const QGeoCoordinate &start,
                                  const QGeoCoordinate &destination,
                                  QVector<QGeoCoordinate> &path)
660
661
662
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
123    
Valentin Platzgummer committed
663
664
665
666
    QPolygonF polygon2D;
    toCartesianList(_joinedArea.coordinateList(), /*origin*/ start, polygon2D);
    QPointF start2D(0,0);
    QPointF end2D;
Valentin Platzgummer's avatar
123    
Valentin Platzgummer committed
667
    QVector<QPointF> path2D;
Valentin Platzgummer's avatar
123    
Valentin Platzgummer committed
668
669
670
    toCartesian(destination, start, end2D);
    bool retVal = PolygonCalculus::shortestPath( polygon2D, start2D, end2D, path2D);
    toGeoList(path2D, /*origin*/ start, path);
671
672
673
674

    return  retVal;
}

675
void WimaPlaner::resetAllInteractive()
676
{
677
678
679
680
681
    // Marks all areas as inactive (area.interactive == false)
    int itemCount = _visualItems.count();
    if (itemCount > 0){
        for (int i = 0; i < itemCount; i++) {
            WimaArea* iteratorPoly = qobject_cast<WimaArea*>(_visualItems.get(i));
682
            iteratorPoly->setWimaAreaInteractive(false);
683
684
685
686
687
688
689
690
691
        }
    }
}

void WimaPlaner::setInteractive()
{
    recalcPolygonInteractivity(_currentAreaIndex);
}

692
693
694
695
696
697
698
699
700
/*!
 * \fn WimaPlanData WimaPlaner::toPlanData()
 *
 * Returns a \c WimaPlanData object containing information about the current mission.
 * The \c WimaPlanData object holds only the data which is relevant for the \c WimaController class.
 * Should only be called if updateMission() was successful.
 *
 * \sa WimaController, WimaPlanData
 */
701
WimaPlanData WimaPlaner::toPlanData()
702
{
703
    WimaPlanData planData;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
704
705

    // store areas
706
707
708
709
    planData.append(WimaMeasurementAreaData(_measurementArea));
    planData.append(WimaServiceAreaData(_serviceArea));
    planData.append(WimaCorridorData(_corridor));
    planData.append(WimaJoinedAreaData(_joinedArea));
710

Valentin Platzgummer's avatar
Valentin Platzgummer committed
711
    // convert mission items to mavlink commands
712
    QObject deleteObject; // used to automatically delete content of rgMissionItems after this function call
Valentin Platzgummer's avatar
Valentin Platzgummer committed
713
    QList<MissionItem*> rgMissionItems;
Valentin Platzgummer's avatar
123    
Valentin Platzgummer committed
714
    QmlObjectListModel *visualItems = _missionController->visualItems();
715
716
717
718
    QmlObjectListModel visualItemsToCopy;
    for (int i = _arrivalPathLength+1; i < visualItems->count()-_returnPathLength; i++)
        visualItemsToCopy.append(visualItems->get(i));
    MissionController::convertToMissionItems(&visualItemsToCopy, rgMissionItems, &deleteObject);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
719

Valentin Platzgummer's avatar
Valentin Platzgummer committed
720
    // store mavlink commands
721
    planData.append(rgMissionItems);
722
723

    return planData;
724
725
}

726
void WimaPlaner::setSyncronizedWithController(bool sync)
727
{
728
729
730
    if (_syncronizedWithController != sync) {
        _syncronizedWithController = sync;
        emit syncronizedWithControllerChanged();
731
732
733
    }
}

734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
void WimaPlaner::setReadyForSync(bool ready)
{
    if( _readyForSync != ready) {
        _readyForSync = ready;

        emit readyForSyncChanged();
    }
}

void WimaPlaner::setJoinedAreaValid(bool valid)
{
    if (_joinedAreaValid != valid) {
        _joinedAreaValid = valid;

        emit joinedAreaValidChanged();
    }
}

752
753
754
755
756
757
void WimaPlaner::updateTimerSlot()
{
    // General operation of this function:
    // Check if parameter has changed, wait until it stops changing, update mission

    // circular survey reference point
758
759
760
761
762
763
764
765
766
767
768
769
770
771
//    if (_missionController != nullptr
//            && _missionController->visualItems()->indexOf(_circularSurvey) != -1
//            && _circularSurvey != nullptr)
//    {
//        if (_surveyRefChanging) {
//            if (_circularSurvey->refPoint() == _lastSurveyRefPoint) { // is it still changing?
//                calcArrivalAndReturnPath();
//                _surveyRefChanging = false;
//            }
//        } else {
//            if (_circularSurvey->refPoint() != _lastSurveyRefPoint) // does it started changing?
//                _surveyRefChanging = true;
//        }
//    }
772

773
774
775
    // measurementArea
    if (_measurementAreaChanging) {
        if (_measurementArea.path() == _lastMeasurementAreaPath) { // is it still changing?
776
777
778
            setReadyForSync(false);
            if ( recalcJoinedArea() && calcArrivalAndReturnPath() )
                setReadyForSync(true);
779
780
781
782
783
784
785
786
787
788
            _measurementAreaChanging = false;
        }
    } else {
        if (_measurementArea.path() != _lastMeasurementAreaPath) // does it started changing?
            _measurementAreaChanging = true;
    }

    // corridor
    if (_corridorChanging) {
        if (_corridor.path() == _lastCorridorPath) { // is it still changing?
789
790
791
            setReadyForSync(false);
            if ( recalcJoinedArea() && calcArrivalAndReturnPath() )
                setReadyForSync(true);
792
793
794
795
796
797
798
799
800
801
            _corridorChanging = false;
        }
    } else {
        if (_corridor.path() != _lastCorridorPath) // does it started changing?
            _corridorChanging = true;
    }

    // service area
    if (_serviceAreaChanging) {
        if (_serviceArea.path() == _lastServiceAreaPath) { // is it still changing?
802
803
804
            setReadyForSync(false);
            if ( recalcJoinedArea() && calcArrivalAndReturnPath() )
                setReadyForSync(true);
805
806
807
808
809
810
811
            _serviceAreaChanging = false;
        }
    } else {
        if (_serviceArea.path() != _lastServiceAreaPath) // does it started changing?
            _serviceAreaChanging = true;
    }

812
813

    // update old values
814
815
816
817
//    if (_missionController != nullptr
//            && _missionController->visualItems()->indexOf(_circularSurvey) != -1
//            && _circularSurvey != nullptr)
//        _lastSurveyRefPoint = _circularSurvey->refPoint();
818
819
820
821

    _lastMeasurementAreaPath    = _measurementArea.path();
    _lastCorridorPath           = _corridor.path();
    _lastServiceAreaPath        = _serviceArea.path();
822
823
}

824
825
826
827
828
void WimaPlaner::setSyncronizedWithControllerFalse()
{
    setSyncronizedWithController(false);
}

829
830
void WimaPlaner::autoLoadMission()
{
831
832
    loadFromFile("/home/valentin/Desktop/drones/qgroundcontrol/Paths/KlingenbachTest.wima");
    pushToWimaController();
833
834
}

835
836
837
838
839
void WimaPlaner::startCalcArrivalAndReturnTimer()
{
    _calcArrivalAndReturnPathTimer.start();
}

840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
QJsonDocument WimaPlaner::saveToJson(FileType fileType)
{
    /// This function save all areas (of WimaPlaner) 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
    QJsonObject json;

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

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

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

            if (area == nullptr) {
                qWarning("WimaPlaner::saveToJson(): Internal error, area == nullptr!");
                return QJsonDocument();
            }

            // check the type of area, create and append the JsonObject to the JsonArray once determined
Valentin Platzgummer's avatar
adding    
Valentin Platzgummer committed
860
            WimaMeasurementArea* opArea =  qobject_cast<WimaMeasurementArea*>(area);
861
862
863
864
865
            if (opArea != nullptr) {
                opArea->saveToJson(json);
                jsonArray.append(json);
                continue;
            }
866

867
868
869
870
871
872
873
            WimaServiceArea* serArea =  qobject_cast<WimaServiceArea*>(area);
            if (serArea != nullptr) {
                serArea->saveToJson(json);
                jsonArray.append(json);
                continue;
            }

Valentin Platzgummer's avatar
adding    
Valentin Platzgummer committed
874
            WimaCorridor* corridor =  qobject_cast<WimaCorridor*>(area);
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
            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);
            jsonArray.append(json);
        }

        json[areaItemsName] = jsonArray;
        json[missionItemsName] = _masterController->saveToJson().object();

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

    return QJsonDocument(json);
895
}
896
897
898