/**************************************************************************** * * (c) 2009-2016 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ #include "APMGeoFenceManager.h" #include "Vehicle.h" #include "FirmwarePlugin.h" #include "MAVLinkProtocol.h" #include "QGCApplication.h" #include "ParameterManager.h" #include "QmlObjectListModel.h" #include "QGCQGeoCoordinate.h" const char* APMGeoFenceManager::_fenceTotalParam = "FENCE_TOTAL"; const char* APMGeoFenceManager::_fenceActionParam = "FENCE_ACTION"; const char* APMGeoFenceManager::_fenceEnableParam = "FENCE_ENABLE"; APMGeoFenceManager::APMGeoFenceManager(Vehicle* vehicle) : GeoFenceManager(vehicle) , _fenceSupported(false) , _circleEnabled(false) , _polygonSupported(false) , _polygonEnabled(false) , _breachReturnSupported(vehicle->fixedWing()) , _firstParamLoadComplete(false) , _readTransactionInProgress(false) , _writeTransactionInProgress(false) , _fenceTypeFact(NULL) , _fenceEnableFact(NULL) , _circleRadiusFact(NULL) { connect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &APMGeoFenceManager::_mavlinkMessageReceived); connect(_vehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &APMGeoFenceManager::_parametersReady); if (_vehicle->parameterManager()->parametersReady()) { _parametersReady(); } } APMGeoFenceManager::~APMGeoFenceManager() { } void APMGeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn, QmlObjectListModel& polygon) { if (_vehicle->isOfflineEditingVehicle()) { return; } if (_readTransactionInProgress) { _sendError(InternalError, QStringLiteral("Geo-Fence write attempted while read in progress.")); return; } if (!_fenceSupported) { return; } // Validate int validatedPolygonCount = 0; if (polygon.count() >= 3) { validatedPolygonCount = polygon.count(); } if (polygon.count() > std::numeric_limits::max()) { _sendError(TooManyPoints, QStringLiteral("Geo-Fence polygon has too many points: %1.").arg(_polygon.count())); validatedPolygonCount = 0; } _breachReturnPoint = breachReturn; _polygon.clear(); if (validatedPolygonCount) { for (int i=0; i(i)->coordinate()); } } // Total point count, +1 polygon close in last index, +1 for breach in index 0 _cWriteFencePoints = validatedPolygonCount ? validatedPolygonCount + 1 + 1 : 0; _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceTotalParam)->setRawValue(_cWriteFencePoints); // FIXME: No validation of correct fence received for (uint8_t index=0; index<_cWriteFencePoints; index++) { _sendFencePoint(index); } emit loadComplete(_breachReturnPoint, _polygon); } void APMGeoFenceManager::loadFromVehicle(void) { if (_vehicle->isOfflineEditingVehicle() || _readTransactionInProgress) { return; } _breachReturnPoint = QGeoCoordinate(); _polygon.clear(); if (!_fenceSupported) { return; } // Point 0: Breach return point (always sent, but supported by ArduPlane only) // Point [1,N]: Polygon points // Point N+1: Close polygon point (same as point 1) int cFencePoints = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceTotalParam)->rawValue().toInt(); int minFencePoints = 5; qCDebug(GeoFenceManagerLog) << "APMGeoFenceManager::loadFromVehicle" << cFencePoints; if (cFencePoints == 0) { // No fence emit loadComplete(_breachReturnPoint, _polygon); return; } if (cFencePoints < 0 || (cFencePoints > 0 && cFencePoints < minFencePoints)) { _sendError(TooFewPoints, QStringLiteral("Geo-Fence information from Vehicle has too few points: %1").arg(cFencePoints)); return; } if (cFencePoints > std::numeric_limits::max()) { _sendError(TooManyPoints, QStringLiteral("Geo-Fence information from Vehicle has too many points: %1").arg(cFencePoints)); return; } _readTransactionInProgress = true; _cReadFencePoints = cFencePoints; _currentFencePoint = 0; _requestFencePoint(_currentFencePoint); } /// Called when a new mavlink message for out vehicle is received void APMGeoFenceManager::_mavlinkMessageReceived(const mavlink_message_t& message) { if (message.msgid == MAVLINK_MSG_ID_FENCE_POINT) { mavlink_fence_point_t fencePoint; mavlink_msg_fence_point_decode(&message, &fencePoint); qCDebug(GeoFenceManagerLog) << "From vehicle fence_point: idx:lat:lng" << fencePoint.idx << fencePoint.lat << fencePoint.lng; if (fencePoint.idx != _currentFencePoint) { // FIXME: Protocol out of whack _readTransactionInProgress = false; emit inProgressChanged(inProgress()); qCWarning(GeoFenceManagerLog) << "Indices out of sync" << fencePoint.idx << _currentFencePoint; return; } if (fencePoint.idx == 0) { _breachReturnPoint = QGeoCoordinate(fencePoint.lat, fencePoint.lng); qCDebug(GeoFenceManagerLog) << "From vehicle: breach return point" << _breachReturnPoint; _requestFencePoint(++_currentFencePoint); } else if (fencePoint.idx < _cReadFencePoints - 1) { QGeoCoordinate polyCoord(fencePoint.lat, fencePoint.lng); _polygon.append(polyCoord); qCDebug(GeoFenceManagerLog) << "From vehicle: polygon point" << fencePoint.idx << polyCoord; if (fencePoint.idx < _cReadFencePoints - 2) { // Still more points to request _requestFencePoint(++_currentFencePoint); } else { // We've finished collecting fence points qCDebug(GeoFenceManagerLog) << "Fence point load complete"; _readTransactionInProgress = false; emit loadComplete(_breachReturnPoint, _polygon); emit inProgressChanged(inProgress()); } } } } void APMGeoFenceManager::_requestFencePoint(uint8_t pointIndex) { mavlink_message_t msg; MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); qCDebug(GeoFenceManagerLog) << "APMGeoFenceManager::_requestFencePoint" << pointIndex; mavlink_msg_fence_fetch_point_pack_chan(mavlink->getSystemId(), mavlink->getComponentId(), _vehicle->priorityLink()->mavlinkChannel(), &msg, _vehicle->id(), _vehicle->defaultComponentId(), pointIndex); _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); } void APMGeoFenceManager::_sendFencePoint(uint8_t pointIndex) { mavlink_message_t msg; MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); QGeoCoordinate fenceCoord; if (pointIndex == 0) { fenceCoord = breachReturnPoint(); } else if (pointIndex == _cWriteFencePoints - 1) { // Polygon close point fenceCoord = _polygon[0]; } else { // Polygon point fenceCoord = _polygon[pointIndex - 1]; } // Total point count, +1 polygon close in last index, +1 for breach in index 0 uint8_t totalPointCount = _polygon.count() + 1 + 1; mavlink_msg_fence_point_pack_chan(mavlink->getSystemId(), mavlink->getComponentId(), _vehicle->priorityLink()->mavlinkChannel(), &msg, _vehicle->id(), _vehicle->defaultComponentId(), pointIndex, // Index of point to set totalPointCount, fenceCoord.latitude(), fenceCoord.longitude()); _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); } bool APMGeoFenceManager::inProgress(void) const { return _readTransactionInProgress || _writeTransactionInProgress; } void APMGeoFenceManager::_setCircleEnabled(bool circleEnabled) { if (circleEnabled != _circleEnabled) { _circleEnabled = circleEnabled; emit circleEnabledChanged(circleEnabled); } } void APMGeoFenceManager::_setPolygonEnabled(bool polygonEnabled) { if (polygonEnabled != _polygonEnabled) { _polygonEnabled = polygonEnabled; emit polygonEnabledChanged(polygonEnabled); } } void APMGeoFenceManager::_updateEnabledFlags(void) { bool fenceEnabled = false; if (_fenceSupported) { if (_fenceEnableFact) { fenceEnabled = _fenceEnableFact->rawValue().toBool(); } else { fenceEnabled = true; } bool newCircleEnabled = fenceEnabled && _fenceTypeFact && (_fenceTypeFact->rawValue().toInt() & 2); _setCircleEnabled(newCircleEnabled); bool newPolygonEnabled = _fenceSupported && fenceEnabled && ((_vehicle->multiRotor() && _fenceTypeFact && (_fenceTypeFact->rawValue().toInt() & 4)) || _vehicle->fixedWing()); _setPolygonEnabled(newPolygonEnabled); } else { _setCircleEnabled(false); _setPolygonEnabled(false); } } void APMGeoFenceManager::_parametersReady(void) { if (!_firstParamLoadComplete) { _firstParamLoadComplete = true; _fenceSupported = _vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceTotalParam) && _vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceActionParam) && !qgcApp()->runningUnitTests(); if (_fenceSupported) { QStringList paramNames; QStringList paramLabels; _polygonSupported = true; if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceEnableParam)) { _fenceEnableFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceEnableParam); connect(_fenceEnableFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_updateEnabledFlags); } if (_vehicle->multiRotor()) { _breachReturnSupported = false; _fenceTypeFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FENCE_TYPE")); connect(_fenceTypeFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_updateEnabledFlags); _circleRadiusFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FENCE_RADIUS")); paramNames << QStringLiteral("FENCE_ENABLE") << QStringLiteral("FENCE_TYPE") << QStringLiteral("FENCE_ACTION") << QStringLiteral("FENCE_ALT_MAX") << QStringLiteral("FENCE_RADIUS") << QStringLiteral("FENCE_MARGIN"); paramLabels << QStringLiteral("Enabled:") << QStringLiteral("Type:") << QStringLiteral("Breach Action:") << QStringLiteral("Max Altitude:") << QStringLiteral("Radius:") << QStringLiteral("Margin:"); } if (_vehicle->fixedWing()) { _breachReturnSupported = true; paramNames << QStringLiteral("FENCE_ACTION") << QStringLiteral("FENCE_MINALT") << QStringLiteral("FENCE_MAXALT") << QStringLiteral("FENCE_RETALT") << QStringLiteral("FENCE_AUTOENABLE") << QStringLiteral("FENCE_RET_RALLY"); paramLabels << QStringLiteral("Breach Action:") << QStringLiteral("Min Altitude:") << QStringLiteral("Max Altitude:") << QStringLiteral("Return Altitude:") << QStringLiteral("Auto-Enable:") << QStringLiteral("Return to Rally:"); } _params.clear(); _paramLabels.clear(); for (int i=0; iparameterManager()->parameterExists(FactSystem::defaultComponentId, paramName)) { Fact* paramFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, paramName); _params << QVariant::fromValue(paramFact); _paramLabels << paramLabels[i]; } } emit paramsChanged(_params); emit paramLabelsChanged(_paramLabels); emit breachReturnSupportedChanged(_breachReturnSupported); emit polygonSupportedChanged(_polygonSupported); } _updateEnabledFlags(); } } void APMGeoFenceManager::removeAll(void) { QmlObjectListModel emptyPolygon; sendToVehicle(_breachReturnPoint, emptyPolygon); }