Newer
Older
Valentin Platzgummer
committed
#include "WimaPlaner.h"
const char* WimaPlaner::wimaFileExtension = "wima";
const char* WimaPlaner::areaItemsName = "AreaItems";
const char* WimaPlaner::missionItemsName = "MissionItems";
WimaPlaner::WimaPlaner(QObject *parent)
: QObject (parent)
Valentin Platzgummer
committed
, _missionReady (false)
, _currentAreaIndex (-1)
, _container (nullptr)
, _joinedArea (this)
Valentin Platzgummer
committed
, _measurementArea (this)
, _serviceArea (this)
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
, _corridor (this)
{
connect(this, &WimaPlaner::currentPolygonIndexChanged, this, &WimaPlaner::recalcPolygonInteractivity);
}
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);
}
}
void WimaPlaner::setDataContainer(WimaDataContainer *container)
{
Valentin Platzgummer
committed
if (container != nullptr) {
if (_container != nullptr) {
disconnect(this, &WimaPlaner::missionReadyChanged, _container, &WimaDataContainer::setDataValid);
}
Valentin Platzgummer
committed
connect(this, &WimaPlaner::missionReadyChanged, _container, &WimaDataContainer::setDataValid);
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
emit dataContainerChanged();
}
}
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();
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.
_currentAreaIndex = -1;
return;
}
if(_currentAreaIndex >= _visualItems.count()){
setCurrentPolygonIndex(_visualItems.count() - 1);
}else{
recalcPolygonInteractivity(_currentAreaIndex);
}
}else{
qWarning("Index out of bounds!");
}
}
Valentin Platzgummer
committed
bool WimaPlaner::addMeasurementArea()
Valentin Platzgummer
committed
if (!_visualItems.contains(&_measurementArea)) {
_visualItems.append(&_measurementArea);
int newIndex = _visualItems.count()-1;
setCurrentPolygonIndex(newIndex);
emit visualItemsChanged();
return true;
} else {
return false;
}
}
bool WimaPlaner::addServiceArea()
{
Valentin Platzgummer
committed
if (!_visualItems.contains(&_serviceArea)) {
_visualItems.append(&_serviceArea);
int newIndex = _visualItems.count()-1;
setCurrentPolygonIndex(newIndex);
emit visualItemsChanged();
return true;
} else {
return false;
}
}
Valentin Platzgummer
committed
bool WimaPlaner::addCorridor()
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
{
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 = "";
emit currentFileChanged();
if ( changesApplied )
emit visualItemsChanged();
}
void WimaPlaner::startMission()
{
}
void WimaPlaner::abortMission()
{
}
void WimaPlaner::pauseMission()
{
}
void WimaPlaner::resumeMission()
{
}
bool WimaPlaner::updateMission()
{
#if TEST_FUN
TestPlanimetryCalculus::test();
TestPolygonCalculus::test();
#endif
Valentin Platzgummer
committed
QString errorString;
setMissionReady(false);
Valentin Platzgummer
committed
if ( !recalcJoinedArea(errorString)) {
qgcApp()->showMessage(tr(errorString.toLocal8Bit().data()));
return false;
}
#if debug
_visualItems.append(&_joinedArea);
#endif
// extract old survey data
QmlObjectListModel* missionItems = _missionController->visualItems();
CircularSurveyComplexItem* OldSurveyPt = nullptr;
QGeoCoordinate oldSurveyRef;
double oldSurveyDeltaR = 0;
double oldSurveyDeltaAlpha = 0;
bool oldSurveyExists = false;
for (int i = 0; i < _missionController->visualItems()->count(); i++) {
OldSurveyPt = qobject_cast<CircularSurveyComplexItem*>(missionItems->get(i));
if ( OldSurveyPt != nullptr) {
oldSurveyRef = OldSurveyPt->refPoint();
oldSurveyDeltaR = OldSurveyPt->deltaR()->rawValue().toDouble();
oldSurveyDeltaAlpha = OldSurveyPt->deltaAlpha()->rawValue().toDouble();
oldSurveyExists = true;
break;
}
}
// reset visual items
_missionController->removeAll();
missionItems = _missionController->visualItems();
// set home position to serArea center
MissionSettingsItem* settingsItem= qobject_cast<MissionSettingsItem*>(missionItems->get(0));
if (settingsItem == nullptr){
qWarning("WimaPlaner::updateMission(): settingsItem == nullptr");
return false;
}
// set altitudes, temporary measure to solve bugs
Valentin Platzgummer
committed
QGeoCoordinate center = _serviceArea.center();
Valentin Platzgummer
committed
_serviceArea.setCenter(center);
center = _measurementArea.center();
Valentin Platzgummer
committed
_measurementArea.setCenter(center);
center = _corridor.center();
center.setAltitude(0);
_corridor.setCenter(center);
// set HomePos. to serArea center
Valentin Platzgummer
committed
settingsItem->setCoordinate(_serviceArea.center());
// create take off position item
Valentin Platzgummer
committed
int sequenceNumber = _missionController->insertSimpleMissionItem(_serviceArea.center(), missionItems->count());
_missionController->setCurrentPlanViewIndex(sequenceNumber, true);
// create survey item, will be extened with more()-> mission types in the future
_missionController->insertComplexMissionItem(_missionController->circularSurveyComplexItemName(), _measurementArea.center(), missionItems->count());
CircularSurveyComplexItem* survey = qobject_cast<CircularSurveyComplexItem*>(missionItems->get(missionItems->count()-1));
if (survey == nullptr){
qWarning("WimaPlaner::updateMission(): survey == nullptr");
return false;
} else {
if ( oldSurveyExists ) {
survey->setRefPoint(oldSurveyRef);
survey->deltaR()->setRawValue(oldSurveyDeltaR);
survey->deltaAlpha()->setRawValue(oldSurveyDeltaAlpha);
} else {
survey->setRefPoint(_measurementArea.center());
}
survey->setAutoGenerated(true); // prevents reinitialisation from gui
survey->surveyAreaPolygon()->clear();
Valentin Platzgummer
committed
survey->surveyAreaPolygon()->appendVertices(_measurementArea.coordinateList());
// calculate path from take off to opArea
if (survey->visualTransectPoints().size() == 0) {
qWarning("WimaPlaner::updateMission(): survey no points.");
return false;
}
Valentin Platzgummer
committed
QGeoCoordinate start = _serviceArea.center();
if (!_visualItems.contains(&_joinedArea))
_visualItems.append(&_joinedArea);
QList<QGeoCoordinate> path;
if ( !calcShortestPath(start, end, path)) {
qgcApp()->showMessage( QString(tr("Not able to calculate the path from takeoff position to measurement area.")).toLocal8Bit().data());
// path.clear();
// path.append(end);
// path.append(start);
// path.append(end);
// path.append(end);
for (int i = 1; i < path.count()-1; i++) {
sequenceNumber = _missionController->insertSimpleMissionItem(path.value(i), missionItems->count()-1);
_missionController->setCurrentPlanViewIndex(sequenceNumber, true);
}
// calculate return path
Valentin Platzgummer
committed
end = _serviceArea.center();
if ( !calcShortestPath(start, end, path)) {
qgcApp()->showMessage(QString(tr("Not able to calculate the path from measurement area to landing position.")).toLocal8Bit().data());
// path.clear();
// path.append(end);
// path.append(start);
// path.append(end);
// path.append(end);
for (int i = 1; i < path.count()-1; i++) {
sequenceNumber = _missionController->insertSimpleMissionItem(path.value(i), missionItems->count());
_missionController->setCurrentPlanViewIndex(sequenceNumber, true);
}
// create land position item
Valentin Platzgummer
committed
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::updateMission(): landItem == nullptr");
return false;
} else {
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);
}
}
Valentin Platzgummer
committed
pushToContainer(); // exchange plan data with the WimaController via the _container
setMissionReady(true);
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
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()) {
if ( jsonArea[WimaArea::areaTypeName] == WimaMeasurementArea::WimaMeasurementAreaName) {
Valentin Platzgummer
committed
print(_measurementArea);
bool success = _measurementArea.loadFromJson(jsonArea, errorString);
print(_measurementArea);
if ( !success ) {
qgcApp()->showMessage(errorMessage.arg(errorString));
return false;
}
validAreaCounter++;
Valentin Platzgummer
committed
_visualItems.append(&_measurementArea);
emit visualItemsChanged();
} else if ( jsonArea[WimaArea::areaTypeName] == WimaServiceArea::wimaServiceAreaName) {
Valentin Platzgummer
committed
bool success = _serviceArea.loadFromJson(jsonArea, errorString);
if ( !success ) {
qgcApp()->showMessage(errorMessage.arg(errorString));
return false;
}
validAreaCounter++;
Valentin Platzgummer
committed
_visualItems.append(&_serviceArea);
emit visualItemsChanged();
} else if ( jsonArea[WimaArea::areaTypeName] == WimaCorridor::WimaCorridorName) {
bool success = _corridor.loadFromJson(jsonArea, errorString);
if ( !success ) {
qgcApp()->showMessage(errorMessage.arg(errorString));
return false;
}
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;
}
}
_currentFile.sprintf("%s/%s.%s", fileInfo.path().toLocal8Bit().data(), fileInfo.completeBaseName().toLocal8Bit().data(), wimaFileExtension);
emit currentFileChanged();
Valentin Platzgummer
committed
//recalcJoinedArea();
498
499
500
501
502
503
504
505
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
// 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("WimaPlaner::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("WimaPlaner::loadFromFile(): not able to remove temporary file.");
}
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));
interactivePoly->setInteractive(true);
}
}
Valentin Platzgummer
committed
bool WimaPlaner::recalcJoinedArea(QString &errorString)
Valentin Platzgummer
committed
// check if area paths form simple polygons
if ( !_serviceArea.isSimplePolygon() ) {
Valentin Platzgummer
committed
errorString.append(tr("Service area is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
return false;
}
if ( !_corridor.isSimplePolygon() && _corridor.count() > 0) {
Valentin Platzgummer
committed
errorString.append(tr("Corridor is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
return false;
}
if ( !_measurementArea.isSimplePolygon() ) {
Valentin Platzgummer
committed
errorString.append(tr("Measurement area is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
return false;
}
_joinedArea.setPath(_serviceArea.path());
_joinedArea.join(_corridor);
Valentin Platzgummer
committed
if ( !_joinedArea.join(_measurementArea) ) {
errorString.append(tr("Not able to join areas. Service area and measurement"
Valentin Platzgummer
committed
" must have a overlapping section, or be connected through a corridor."));
return false; // this happens if all areas are pairwise disjoint
Valentin Platzgummer
committed
}
// // remove if debugging finished
// WimaServiceArea *test = new WimaServiceArea(this);
// WimaServiceArea *test1 = new WimaServiceArea(this);
// WimaServiceArea *test2 = new WimaServiceArea(this);
// WimaServiceArea *test3 = new WimaServiceArea(this);
// Circle circle(25, this);
// using namespace GeoUtilities;
// test->setPath(toGeo(circle.approximateSektor(0.5, 1, 3), _joinedArea.center()));
// _visualItems.append(test);
// test1->setPath(toGeo(circle.approximateSektor(0.5, 1, -1), _joinedArea.center()));
// _visualItems.append(test1);
// test2->setPath(toGeo(circle.approximateSektor(0.5,-1, 3), _joinedArea.center()));
// _visualItems.append(test2);
// test3->setPath(toGeo(circle.approximateSektor(0.5, -1, -3), _joinedArea.center()));
// _visualItems.append(test3);
Valentin Platzgummer
committed
/*!
* \fn void WimaPlaner::pushToContainer()
* Pushes the \c WimaPlanData object generated by \c toPlanData() to the \c WimaDataContainer.
* Should be called only after \c updateMission() was successful.
*
* \sa WimaDataContainer, WimaPlanData
*/
void WimaPlaner::pushToContainer()
{
if (_container != nullptr) {
Valentin Platzgummer
committed
WimaPlanData planData = toPlanData();
_container->push(planData);
} else {
qWarning("WimaPlaner::uploadToContainer(): no container assigned.");
}
}
bool WimaPlaner::calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QList<QGeoCoordinate> &path)
{
using namespace GeoUtilities;
using namespace PolygonCalculus;
QList<QPointF> path2D;
bool retVal = PolygonCalculus::shortestPath(
toQPolygonF(toCartesian2D(_joinedArea.coordinateList(), /*origin*/ start)),
/*start point*/ QPointF(0,0),
/*destination*/ toCartesian2D(destination, start),
/*shortest path*/ path2D);
return retVal;
}
void WimaPlaner::resetAllInteractive()
Valentin Platzgummer
committed
{
// 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));
iteratorPoly->setInteractive(false);
}
}
}
void WimaPlaner::setInteractive()
{
recalcPolygonInteractivity(_currentAreaIndex);
}
Valentin Platzgummer
committed
/*!
* \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
*/
WimaPlanData WimaPlaner::toPlanData()
{
WimaPlanData planData;
planData.append(WimaMeasurementAreaData(_measurementArea));
planData.append(WimaServiceAreaData(_serviceArea));
planData.append(WimaCorridorData(_corridor));
planData.append(WimaJoinedAreaData(_joinedArea));
Valentin Platzgummer
committed
Valentin Platzgummer
committed
}
void WimaPlaner::setMissionReady(bool ready)
{
if(_missionReady != ready)
{
_missionReady = ready;
emit missionReadyChanged(_missionReady);
}
}
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
WimaMeasurementArea* opArea = qobject_cast<WimaMeasurementArea*>(area);
if (opArea != nullptr) {
opArea->saveToJson(json);
jsonArray.append(json);
continue;
}
Valentin Platzgummer
committed
WimaServiceArea* serArea = qobject_cast<WimaServiceArea*>(area);
if (serArea != nullptr) {
serArea->saveToJson(json);
jsonArray.append(json);
continue;
}
WimaCorridor* corridor = qobject_cast<WimaCorridor*>(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);
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);
Valentin Platzgummer
committed
}