地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

442 lines
14 KiB

/****************************************************************************
*
* (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.
*
****************************************************************************/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#include "GeoFenceController.h"
#include "Vehicle.h"
#include "FirmwarePlugin.h"
#include "MAVLinkProtocol.h"
#include "QGCApplication.h"
#include "ParameterManager.h"
#include "JsonHelper.h"
#ifndef __mobile__
#include "QGCFileDialog.h"
#endif
#include <QJsonDocument>
QGC_LOGGING_CATEGORY(GeoFenceControllerLog, "GeoFenceControllerLog")
const char* GeoFenceController::_jsonFileTypeValue = "GeoFence";
const char* GeoFenceController::_jsonBreachReturnKey = "breachReturn";
GeoFenceController::GeoFenceController(QObject* parent)
: PlanElementController(parent)
, _dirty(false)
{
}
GeoFenceController::~GeoFenceController()
{
}
void GeoFenceController::start(bool editMode)
{
qCDebug(GeoFenceControllerLog) << "start editMode" << editMode;
PlanElementController::start(editMode);
connect(&_polygon, &QGCMapPolygon::dirtyChanged, this, &GeoFenceController::_polygonDirtyChanged);
}
void GeoFenceController::setBreachReturnPoint(const QGeoCoordinate& breachReturnPoint)
{
if (_breachReturnPoint != breachReturnPoint) {
_breachReturnPoint = breachReturnPoint;
setDirty(true);
emit breachReturnPointChanged(breachReturnPoint);
}
}
void GeoFenceController::_signalAll(void)
{
emit fenceSupportedChanged(fenceSupported());
emit circleSupportedChanged(circleSupported());
emit polygonSupportedChanged(polygonSupported());
emit breachReturnSupportedChanged(breachReturnSupported());
emit breachReturnPointChanged(breachReturnPoint());
emit circleRadiusChanged(circleRadius());
emit paramsChanged(params());
emit paramLabelsChanged(paramLabels());
emit editorQmlChanged(editorQml());
emit dirtyChanged(dirty());
}
void GeoFenceController::_activeVehicleBeingRemoved(void)
{
_activeVehicle->geoFenceManager()->disconnect(this);
}
void GeoFenceController::_activeVehicleSet(void)
{
GeoFenceManager* geoFenceManager = _activeVehicle->geoFenceManager();
connect(geoFenceManager, &GeoFenceManager::circleSupportedChanged, this, &GeoFenceController::_setDirty);
connect(geoFenceManager, &GeoFenceManager::polygonSupportedChanged, this, &GeoFenceController::_setDirty);
connect(geoFenceManager, &GeoFenceManager::fenceSupportedChanged, this, &GeoFenceController::fenceSupportedChanged);
connect(geoFenceManager, &GeoFenceManager::circleSupportedChanged, this, &GeoFenceController::circleSupportedChanged);
connect(geoFenceManager, &GeoFenceManager::polygonSupportedChanged, this, &GeoFenceController::polygonSupportedChanged);
connect(geoFenceManager, &GeoFenceManager::breachReturnSupportedChanged, this, &GeoFenceController::breachReturnSupportedChanged);
connect(geoFenceManager, &GeoFenceManager::circleRadiusChanged, this, &GeoFenceController::circleRadiusChanged);
connect(geoFenceManager, &GeoFenceManager::paramsChanged, this, &GeoFenceController::paramsChanged);
connect(geoFenceManager, &GeoFenceManager::paramLabelsChanged, this, &GeoFenceController::paramLabelsChanged);
connect(geoFenceManager, &GeoFenceManager::loadComplete, this, &GeoFenceController::_loadComplete);
connect(geoFenceManager, &GeoFenceManager::inProgressChanged, this, &GeoFenceController::syncInProgressChanged);
if (!geoFenceManager->inProgress()) {
_loadComplete(geoFenceManager->breachReturnPoint(), geoFenceManager->polygon());
}
_signalAll();
}
bool GeoFenceController::_loadJsonFile(QJsonDocument& jsonDoc, QString& errorString)
{
QJsonObject json = jsonDoc.object();
// Check for required keys
QStringList requiredKeys;
requiredKeys << JsonHelper::jsonVersionKey << JsonHelper::jsonFileTypeKey;
if (!JsonHelper::validateRequiredKeys(json, requiredKeys, errorString)) {
return false;
}
#if 0
// Validate base key types
QStringList keyList;
QList<QJsonValue::Type> typeList;
keyList << jsonSimpleItemsKey << _jsonVersionKey << _jsonGroundStationKey << _jsonMavAutopilotKey << _jsonComplexItemsKey << _jsonPlannedHomePositionKey;
typeList << QJsonValue::Array << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Array << QJsonValue::Object;
if (!JsonHelper::validateKeyTypes(json, keyList, typeList, errorString)) {
return false;
}
#endif
// Version check
if (json[JsonHelper::jsonVersionKey].toString() != "1.0") {
errorString = QStringLiteral("QGroundControl does not support this file version");
return false;
}
if (!_activeVehicle->parameterManager()->loadFromJson(json, false /* required */, errorString)) {
return false;
}
if (breachReturnSupported()) {
if (json.contains(_jsonBreachReturnKey)
&& !JsonHelper::loadGeoCoordinate(json[_jsonBreachReturnKey], false /* altitudeRequired */, _breachReturnPoint, errorString)) {
return false;
}
} else {
_breachReturnPoint = QGeoCoordinate();
}
if (polygonSupported()) {
if (!_polygon.loadFromJson(json, false /* reauired */, errorString)) {
return false;
}
} else {
_polygon.clear();
}
_polygon.setDirty(false);
return true;
}
#if 0
// NYI
bool GeoFenceController::_loadTextFile(QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString)
{
bool addPlannedHomePosition = false;
QString firstLine = stream.readLine();
const QStringList& version = firstLine.split(" ");
bool versionOk = false;
if (version.size() == 3 && version[0] == "QGC" && version[1] == "WPL") {
if (version[2] == "110") {
// ArduPilot file, planned home position is already in position 0
versionOk = true;
} else if (version[2] == "120") {
// Old QGC file, no planned home position
versionOk = true;
addPlannedHomePosition = true;
}
}
if (versionOk) {
while (!stream.atEnd()) {
SimpleMissionItem* item = new SimpleMissionItem(_activeVehicle, this);
if (item->load(stream)) {
visualItems->append(item);
} else {
errorString = QStringLiteral("The mission file is corrupted.");
return false;
}
}
} else {
errorString = QStringLiteral("The mission file is not compatible with this version of QGroundControl.");
return false;
}
if (addPlannedHomePosition || visualItems->count() == 0) {
_addPlannedHomePosition(visualItems, true /* addToCenter */);
// Update sequence numbers in DO_JUMP commands to take into account added home position in index 0
for (int i=1; i<visualItems->count(); i++) {
SimpleMissionItem* item = qobject_cast<SimpleMissionItem*>(visualItems->get(i));
if (item && item->command() == MavlinkQmlSingleton::MAV_CMD_DO_JUMP) {
item->missionItem().setParam1((int)item->missionItem().param1() + 1);
}
}
}
return true;
}
#endif
void GeoFenceController::loadFromFile(const QString& filename)
{
QString errorString;
if (filename.isEmpty()) {
return;
}
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
errorString = file.errorString();
} else {
QJsonDocument jsonDoc;
QByteArray bytes = file.readAll();
if (JsonHelper::isJsonFile(bytes, jsonDoc)) {
_loadJsonFile(jsonDoc, errorString);
} else {
// FIXME: No MP file format support
qgcApp()->showMessage("GeoFence file is in incorrect format.");
return;
}
}
if (!errorString.isEmpty()) {
qgcApp()->showMessage(errorString);
}
_signalAll();
setDirty(true);
}
void GeoFenceController::loadFromFilePicker(void)
{
#ifndef __mobile__
QString filename = QGCFileDialog::getOpenFileName(NULL, "Select GeoFence File to load", QString(), "Fence file (*.fence);;All Files (*.*)");
if (filename.isEmpty()) {
return;
}
loadFromFile(filename);
#endif
}
void GeoFenceController::saveToFile(const QString& filename)
{
if (filename.isEmpty()) {
return;
}
QString fenceFilename = filename;
if (!QFileInfo(filename).fileName().contains(".")) {
fenceFilename += QString(".%1").arg(QGCApplication::fenceFileExtension);
}
QFile file(fenceFilename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qgcApp()->showMessage(file.errorString());
} else {
QJsonObject fenceFileObject; // top level json object
fenceFileObject[JsonHelper::jsonFileTypeKey] = _jsonFileTypeValue;
fenceFileObject[JsonHelper::jsonVersionKey] = QStringLiteral("1.0");
fenceFileObject[JsonHelper::jsonGroundStationKey] = JsonHelper::jsonGroundStationValue;
QStringList paramNames;
ParameterManager* paramMgr = _activeVehicle->parameterManager();
GeoFenceManager* fenceMgr = _activeVehicle->geoFenceManager();
QVariantList params = fenceMgr->params();
for (int i=0; i< params.count(); i++) {
paramNames.append(params[i].value<Fact*>()->name());
}
if (paramNames.count() > 0) {
paramMgr->saveToJson(paramMgr->defaultComponentId(), paramNames, fenceFileObject);
}
if (breachReturnSupported()) {
QJsonValue jsonBreachReturn;
JsonHelper::saveGeoCoordinate(_breachReturnPoint, false /* writeAltitude */, jsonBreachReturn);
fenceFileObject[_jsonBreachReturnKey] = jsonBreachReturn;
}
if (polygonSupported()) {
_polygon.saveToJson(fenceFileObject);
}
QJsonDocument saveDoc(fenceFileObject);
file.write(saveDoc.toJson());
}
setDirty(false);
}
void GeoFenceController::saveToFilePicker(void)
{
#ifndef __mobile__
QString filename = QGCFileDialog::getSaveFileName(NULL, "Select file to save GeoFence to", QString(), "Fence file (*.fence);;All Files (*.*)");
if (filename.isEmpty()) {
return;
}
saveToFile(filename);
#endif
}
void GeoFenceController::removeAll(void)
{
setBreachReturnPoint(QGeoCoordinate());
_polygon.clear();
}
void GeoFenceController::loadFromVehicle(void)
{
if (_activeVehicle->parameterManager()->parametersReady() && !syncInProgress()) {
_activeVehicle->geoFenceManager()->loadFromVehicle();
} else {
qCWarning(GeoFenceControllerLog) << "GeoFenceController::loadFromVehicle call at wrong time" << _activeVehicle->parameterManager()->parametersReady() << syncInProgress();
}
}
void GeoFenceController::sendToVehicle(void)
{
if (_activeVehicle->parameterManager()->parametersReady() && !syncInProgress()) {
_activeVehicle->geoFenceManager()->sendToVehicle(_breachReturnPoint, _polygon.coordinateList());
_polygon.setDirty(false);
setDirty(false);
} else {
qCWarning(GeoFenceControllerLog) << "GeoFenceController::loadFromVehicle call at wrong time" << _activeVehicle->parameterManager()->parametersReady() << syncInProgress();
}
}
bool GeoFenceController::syncInProgress(void) const
{
return _activeVehicle->geoFenceManager()->inProgress();
}
bool GeoFenceController::dirty(void) const
{
return _dirty | _polygon.dirty();
}
void GeoFenceController::setDirty(bool dirty)
{
if (dirty != _dirty) {
_dirty = dirty;
if (!dirty) {
_polygon.setDirty(dirty);
}
emit dirtyChanged(dirty);
}
}
void GeoFenceController::_polygonDirtyChanged(bool dirty)
{
if (dirty) {
setDirty(true);
}
}
bool GeoFenceController::fenceSupported(void) const
{
return _activeVehicle->geoFenceManager()->fenceSupported();
}
bool GeoFenceController::circleSupported(void) const
{
return _activeVehicle->geoFenceManager()->circleSupported();
}
bool GeoFenceController::polygonSupported(void) const
{
return _activeVehicle->geoFenceManager()->polygonSupported();
}
bool GeoFenceController::breachReturnSupported(void) const
{
return _activeVehicle->geoFenceManager()->breachReturnSupported();
}
void GeoFenceController::_setDirty(void)
{
setDirty(true);
}
void GeoFenceController::_setPolygonFromManager(const QList<QGeoCoordinate>& polygon)
{
_polygon.setPath(polygon);
_polygon.setDirty(false);
emit polygonPathChanged(_polygon.path());
}
void GeoFenceController::_setReturnPointFromManager(QGeoCoordinate breachReturnPoint)
{
_breachReturnPoint = breachReturnPoint;
emit breachReturnPointChanged(_breachReturnPoint);
}
float GeoFenceController::circleRadius(void) const
{
return _activeVehicle->geoFenceManager()->circleRadius();
}
QVariantList GeoFenceController::params(void) const
{
return _activeVehicle->geoFenceManager()->params();
}
QStringList GeoFenceController::paramLabels(void) const
{
return _activeVehicle->geoFenceManager()->paramLabels();
}
QString GeoFenceController::editorQml(void) const
{
return _activeVehicle->geoFenceManager()->editorQml();
}
void GeoFenceController::_loadComplete(const QGeoCoordinate& breachReturn, const QList<QGeoCoordinate>& polygon)
{
_setReturnPointFromManager(breachReturn);
_setPolygonFromManager(polygon);
setDirty(false);
emit loadComplete();
}
QString GeoFenceController::fileExtension(void) const
{
return QGCApplication::fenceFileExtension;
}