Commit a3b3e582 authored by Gus Grubba's avatar Gus Grubba

Merge branch 'master' of https://github.com/mavlink/qgroundcontrol into foo

# Conflicts:
#	src/FlightDisplay/GuidedActionsController.qml
#	src/PlanView/PlanView.qml
parents 6cc84cd5 28b58527
......@@ -50,23 +50,11 @@ build_script:
test_script:
- if "%CONFIG%" EQU "debug" ( %SHADOW_BUILD_DIR%\debug\qgroundcontrol --unittest )
for:
-
branches:
only:
- master
artifacts:
artifacts:
- path: QGroundControl-installer.exe
name: qgcinstaller
-
branches:
only:
- Stable_V3.5
artifacts:
- path: QGroundControl-installer.exe
name: qgcinstaller
- path: symbols\**\*.*_
name: symbols
- path: build_windows_install\release\qgroundcontrol.pdb
name: pdb
deploy:
# deploy continuous builds to s3
......@@ -101,19 +89,19 @@ deploy:
# appveyor_repo_tag: false
# deploy release symbols to s3
- provider: S3
access_key_id:
secure: IGAojLMqokL+76DbdulmWDA3MTsxEBBi3ReVVSqTy9c=
secret_access_key:
secure: RiYqaR+3T2PMNz2j5ur8LCA6H/Zfd4jTX33CZE5iBxm+zaz4QLs25p0B7prpaoNN
bucket: qgroundcontrol
region: us-west-2
set_public: true
folder: releasesyms
artifact: symbols
on:
CONFIG: installer
appveyor_repo_tag: true
# - provider: S3
# access_key_id:
# secure: IGAojLMqokL+76DbdulmWDA3MTsxEBBi3ReVVSqTy9c=
# secret_access_key:
# secure: RiYqaR+3T2PMNz2j5ur8LCA6H/Zfd4jTX33CZE5iBxm+zaz4QLs25p0B7prpaoNN
# bucket: qgroundcontrol
# region: us-west-2
# set_public: true
# folder: releasesyms
# artifact: symbols
# on:
# CONFIG: installer
# appveyor_repo_tag: true
# deploy tagged releases to Github releases
- provider: GitHub
......@@ -155,3 +143,18 @@ deploy:
on:
CONFIG: installer
appveyor_repo_tag: true
# deploy pdb for tagged releases to s3 latest folder
- provider: S3
access_key_id:
secure: IGAojLMqokL+76DbdulmWDA3MTsxEBBi3ReVVSqTy9c=
secret_access_key:
secure: RiYqaR+3T2PMNz2j5ur8LCA6H/Zfd4jTX33CZE5iBxm+zaz4QLs25p0B7prpaoNN
bucket: qgroundcontrol
region: us-west-2
set_public: true
folder: latest
artifact: pdb
on:
CONFIG: installer
appveyor_repo_tag: true
......@@ -6,6 +6,7 @@ Note: This file only contains high level features or important fixes.
### 3.6.0 - Daily Build
* Plan/Pattern: Support named presets to simplify commonly used settings setup. Currently only supported by Survey.
* ArduCopter: Handle 3.7 parameter name change from CH#_OPT to RC#_OPTION.
* Improved support for flashing/connecting to ChibiOS bootloaders boards.
* Making the camera API available to all firmwares, not just PX4.
......@@ -13,6 +14,7 @@ Note: This file only contains high level features or important fixes.
* Major rewrite and bug fix pass through Structure Scan. Previous version had such bad problems that it can no longer be supported. Plans with Structure Scan will need to be recreated. New QGC will not load old Structure Scan plans.
### 3.5.1 - Not yet released
* Add ArduPilot CubeBlack Service Bulletin check
* Fix visibility of PX4/ArduPilot logo in toolbar
* Fix tile set count but in OfflineMaps which would cause image and elevation tile set to have incorrect counts and be incorrectly marked as download incomplete.
......
......@@ -210,6 +210,7 @@
</qresource>
<qresource prefix="/json">
<file alias="APMMavlinkStreamRate.SettingsGroup.json">src/Settings/APMMavlinkStreamRate.SettingsGroup.json</file>
<file alias="BreachReturn.FactMetaData.json">src/MissionManager/BreachReturn.FactMetaData.json</file>
<file alias="CameraCalc.FactMetaData.json">src/MissionManager/CameraCalc.FactMetaData.json</file>
<file alias="CameraSpec.FactMetaData.json">src/MissionManager/CameraSpec.FactMetaData.json</file>
<file alias="CorridorScan.SettingsGroup.json">src/MissionManager/CorridorScan.SettingsGroup.json</file>
......
......@@ -29,6 +29,11 @@
#include "APMSubFrameComponent.h"
#include "ESP8266Component.h"
#include "APMHeliComponent.h"
#include "QGCApplication.h"
#if !defined(NO_SERIAL_LINK) && !defined(__android__)
#include <QSerialPortInfo>
#endif
/// This is the AutoPilotPlugin implementatin for the MAV_AUTOPILOT_ARDUPILOT type.
APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent)
......@@ -50,6 +55,10 @@ APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent)
, _heliComponent (NULL)
{
APMAirframeLoader::loadAirframeFactMetaData();
#if !defined(NO_SERIAL_LINK) && !defined(__android__)
connect(vehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &APMAutoPilotPlugin::_checkForBadCubeBlack);
#endif
}
APMAutoPilotPlugin::~APMAutoPilotPlugin()
......@@ -170,3 +179,34 @@ QString APMAutoPilotPlugin::prerequisiteSetup(VehicleComponent* component) const
return QString();
}
#if !defined(NO_SERIAL_LINK) && !defined(__android__)
/// The following code is executed when the Vehicle is parameter ready. It checks for the service bulletin against Cube Blacks.
void APMAutoPilotPlugin::_checkForBadCubeBlack(void)
{
bool cubeBlackFound = false;
for (const QVariant& varLink: _vehicle->links()) {
SerialLink* serialLink = varLink.value<SerialLink*>();
if (serialLink && QSerialPortInfo(*serialLink->_hackAccessToPort()).description().contains(QStringLiteral("CubeBlack"))) {
cubeBlackFound = true;
}
}
if (!cubeBlackFound) {
return;
}
ParameterManager* paramMgr = _vehicle->parameterManager();
QString paramAcc3("INS_ACC3_ID");
QString paramGyr3("INS_GYR3_ID");
QString paramEnableMask("INS_ENABLE_MASK");
if (paramMgr->parameterExists(-1, paramAcc3) && paramMgr->getParameter(-1, paramAcc3)->rawValue().toInt() == 0 &&
paramMgr->parameterExists(-1, paramGyr3) && paramMgr->getParameter(-1, paramGyr3)->rawValue().toInt() == 0 &&
paramMgr->parameterExists(-1, paramEnableMask) && paramMgr->getParameter(-1, paramEnableMask)->rawValue().toInt() >= 7) {
qgcApp()->showMessage(tr("WARNING: The flight board you are using has a critical service bulletin against ti which advise against flying. https://discuss.cubepilot.org/t/sb-0000002-critical-service-bulletin-for-cubes-purchased-between-january-2019-to-present-do-not-fly/406"));
}
}
#endif
......@@ -59,6 +59,11 @@ protected:
ESP8266Component* _esp8266Component;
APMHeliComponent* _heliComponent;
#if !defined(NO_SERIAL_LINK) && !defined(__android__)
private slots:
void _checkForBadCubeBlack(void);
#endif
private:
QVariantList _components;
};
......
......@@ -44,6 +44,8 @@ SetupPage {
property bool _batt2ParamsAvailable: controller.parameterExists(-1, "BATT2_CAPACITY")
property bool _showBatt1Reboot: _batt1MonitorEnabled && !_batt1ParamsAvailable
property bool _showBatt2Reboot: _batt2MonitorEnabled && !_batt2ParamsAvailable
property bool _escCalibrationAvailable: controller.parameterExists(-1, "ESC_CALIBRATION")
property Fact _escCalibration: controller.getParameterFact(-1, "ESC_CALIBRATION", false /* reportMissing */)
property string _restartRequired: qsTr("Requires vehicle reboot")
......@@ -216,6 +218,61 @@ SetupPage {
}
}
}
Column {
spacing: _margins / 2
visible: _escCalibrationAvailable
QGCLabel {
text: qsTr("ESC Calibration")
font.family: ScreenTools.demiboldFontFamily
}
Rectangle {
width: escCalibrationHolder.x + escCalibrationHolder.width + _margins
height: escCalibrationHolder.y + escCalibrationHolder.height + _margins
color: ggcPal.windowShade
Column {
id: escCalibrationHolder
x: _margins
y: _margins
spacing: _margins
Column {
spacing: _margins
QGCLabel {
text: qsTr("WARNING: Remove props prior to calibration!")
color: qgcPal.warningText
}
Row {
spacing: _margins
QGCButton {
text: qsTr("Calibrate")
enabled: _escCalibration.rawValue === 0
onClicked: _escCalibration.rawValue = 3
}
Column {
enabled: _escCalibration.rawValue === 3
QGCLabel { text: _escCalibration.rawValue === 3 ? qsTr("Now perform these steps:") : qsTr("Click Calibrate to start, then:") }
QGCLabel { text: qsTr("- Disconnect USB and battery so flight controller powers down") }
QGCLabel { text: qsTr("- Connect the battery") }
QGCLabel { text: qsTr("- The arming tone will be played (if the vehicle has a buzzer attached)") }
QGCLabel { text: qsTr("- If using a flight controller with a safety button press it until it displays solid red") }
QGCLabel { text: qsTr("- You will hear a musical tone then two beeps") }
QGCLabel { text: qsTr("- A few seconds later you should hear a number of beeps (one for each battery cell you’re using)") }
QGCLabel { text: qsTr("- And finally a single long beep indicating the end points have been set and the ESC is calibrated") }
QGCLabel { text: qsTr("- Disconnect the battery and power up again normally") }
}
}
}
}
}
}
} // Flow
} // Component - powerPageComponent
......
......@@ -897,9 +897,9 @@ void APMFirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoord
vehicle->missionManager()->writeArduPilotGuidedMissionItem(coordWithAltitude, false /* altChangeOnly */);
}
void APMFirmwarePlugin::guidedModeRTL(Vehicle* vehicle)
void APMFirmwarePlugin::guidedModeRTL(Vehicle* vehicle, bool smartRTL)
{
_setFlightModeAndValidate(vehicle, rtlFlightMode());
_setFlightModeAndValidate(vehicle, smartRTL ? smartRTLFlightMode() : rtlFlightMode());
}
void APMFirmwarePlugin::guidedModeChangeAltitude(Vehicle* vehicle, double altitudeChange)
......
......@@ -86,9 +86,10 @@ public:
bool isGuidedMode (const Vehicle* vehicle) const override;
QString gotoFlightMode (void) const override { return QStringLiteral("Guided"); }
QString rtlFlightMode (void) const override { return QString("RTL"); }
QString smartRTLFlightMode (void) const override { return QString("Smart RTL"); }
QString missionFlightMode (void) const override { return QString("Auto"); }
void pauseVehicle (Vehicle* vehicle) override;
void guidedModeRTL (Vehicle* vehicle) override;
void guidedModeRTL (Vehicle* vehicle, bool smartRTL) override;
void guidedModeChangeAltitude (Vehicle* vehicle, double altitudeChange) override;
bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message) override;
void adjustOutgoingMavlinkMessage (Vehicle* vehicle, LinkInterface* outgoingLink, mavlink_message_t* message) override;
......
......@@ -23,26 +23,25 @@ APMCopterMode::APMCopterMode(uint32_t mode, bool settable) :
APMCustomMode(mode, settable)
{
QMap<uint32_t,QString> enumToString;
enumToString.insert(STABILIZE, "Stabilize");
enumToString.insert(ACRO, "Acro");
enumToString.insert(ALT_HOLD, "Altitude Hold");
enumToString.insert(AUTO, "Auto");
enumToString.insert(GUIDED, "Guided");
enumToString.insert(LOITER, "Loiter");
enumToString.insert(RTL, "RTL");
enumToString.insert(CIRCLE, "Circle");
enumToString.insert(LAND, "Land");
enumToString.insert(DRIFT, "Drift");
enumToString.insert(SPORT, "Sport");
enumToString.insert(FLIP, "Flip");
enumToString.insert(AUTOTUNE, "Autotune");
enumToString.insert(POS_HOLD, "Position Hold");
enumToString.insert(BRAKE, "Brake");
enumToString.insert(THROW, "Throw");
enumToString.insert(AVOID_ADSB,"Avoid ADSB");
enumToString.insert(GUIDED_NOGPS,"Guided No GPS");
enumToString.insert(SAFE_RTL,"Smart RTL");
enumToString.insert(STABILIZE, "Stabilize");
enumToString.insert(ACRO, "Acro");
enumToString.insert(ALT_HOLD, "Altitude Hold");
enumToString.insert(AUTO, "Auto");
enumToString.insert(GUIDED, "Guided");
enumToString.insert(LOITER, "Loiter");
enumToString.insert(RTL, "RTL");
enumToString.insert(CIRCLE, "Circle");
enumToString.insert(LAND, "Land");
enumToString.insert(DRIFT, "Drift");
enumToString.insert(SPORT, "Sport");
enumToString.insert(FLIP, "Flip");
enumToString.insert(AUTOTUNE, "Autotune");
enumToString.insert(POS_HOLD, "Position Hold");
enumToString.insert(BRAKE, "Brake");
enumToString.insert(THROW, "Throw");
enumToString.insert(AVOID_ADSB, "Avoid ADSB");
enumToString.insert(GUIDED_NOGPS, "Guided No GPS");
enumToString.insert(SAFE_RTL, "Smart RTL");
setEnumToStringMapping(enumToString);
}
......
......@@ -65,8 +65,9 @@ public:
QString pauseFlightMode (void) const override { return QString("Brake"); }
QString landFlightMode (void) const override { return QString("Land"); }
QString takeControlFlightMode (void) const override { return QString("Loiter"); }
bool vehicleYawsToNextWaypointInMission (const Vehicle* vehicle) const final;
QString autoDisarmParameter (Vehicle* vehicle) final { Q_UNUSED(vehicle); return QStringLiteral("DISARM_DELAY"); }
bool vehicleYawsToNextWaypointInMission (const Vehicle* vehicle) const override;
QString autoDisarmParameter (Vehicle* vehicle) override { Q_UNUSED(vehicle); return QStringLiteral("DISARM_DELAY"); }
bool supportsSmartRTL (void) const override { return true; }
private:
static bool _remapParamNameIntialized;
......
......@@ -49,8 +49,9 @@ public:
QString pauseFlightMode (void) const override { return QString("Hold"); }
void guidedModeChangeAltitude (Vehicle* vehicle, double altitudeChange) final;
int remapParamNameHigestMinorVersionNumber (int majorVersionNumber) const final;
const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap(void) const final { return _remapParamName; }
bool supportsNegativeThrust(void) final;
const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap(void) const final { return _remapParamName; }
bool supportsNegativeThrust (void) final;
bool supportsSmartRTL (void) const override { return true; }
private:
static bool _remapParamNameIntialized;
......
......@@ -240,10 +240,11 @@ void FirmwarePlugin::pauseVehicle(Vehicle* vehicle)
qgcApp()->showMessage(guided_mode_not_supported_by_vehicle);
}
void FirmwarePlugin::guidedModeRTL(Vehicle* vehicle)
void FirmwarePlugin::guidedModeRTL(Vehicle* vehicle, bool smartRTL)
{
// Not supported by generic vehicle
Q_UNUSED(vehicle);
Q_UNUSED(smartRTL);
qgcApp()->showMessage(guided_mode_not_supported_by_vehicle);
}
......
......@@ -105,6 +105,11 @@ public:
/// Returns the flight mode for RTL
virtual QString rtlFlightMode(void) const { return QString(); }
/// Returns the flight mode for Smart RTL
virtual QString smartRTLFlightMode(void) const { return QString(); }
virtual bool supportsSmartRTL(void) const { return false; }
/// Returns the flight mode for Land
virtual QString landFlightMode(void) const { return QString(); }
......@@ -125,7 +130,7 @@ public:
virtual void pauseVehicle(Vehicle* vehicle);
/// Command vehicle to return to launch
virtual void guidedModeRTL(Vehicle* vehicle);
virtual void guidedModeRTL(Vehicle* vehicle, bool smartRTL);
/// Command vehicle to land at current location
virtual void guidedModeLand(Vehicle* vehicle);
......
......@@ -351,8 +351,9 @@ void PX4FirmwarePlugin::pauseVehicle(Vehicle* vehicle)
NAN);
}
void PX4FirmwarePlugin::guidedModeRTL(Vehicle* vehicle)
void PX4FirmwarePlugin::guidedModeRTL(Vehicle* vehicle, bool smartRTL)
{
Q_UNUSED(smartRTL);
_setFlightModeAndValidate(vehicle, _rtlFlightMode);
}
......
......@@ -44,7 +44,7 @@ public:
QString takeControlFlightMode (void) const override { return _manualFlightMode; }
QString gotoFlightMode (void) const override { return _holdFlightMode; }
void pauseVehicle (Vehicle* vehicle) override;
void guidedModeRTL (Vehicle* vehicle) override;
void guidedModeRTL (Vehicle* vehicle, bool smartRTL) override;
void guidedModeLand (Vehicle* vehicle) override;
void guidedModeTakeoff (Vehicle* vehicle, double takeoffAltRel) override;
void guidedModeGotoLocation (Vehicle* vehicle, const QGeoCoordinate& gotoCoord) override;
......
......@@ -1083,7 +1083,7 @@ This parameter controls the time constant of the decay</short_desc>
</parameter>
<parameter default="1" name="EKF2_AID_MASK" type="INT32">
<short_desc>Integer bitmask controlling data fusion and aiding methods</short_desc>
<long_desc>Set bits in the following positions to enable: 0 : Set to true to use GPS data if available 1 : Set to true to use optical flow data if available 2 : Set to true to inhibit IMU bias estimation 3 : Set to true to enable vision position fusion 4 : Set to true to enable vision yaw fusion. Cannot be used if bit position 7 is true. 5 : Set to true to enable multi-rotor drag specific force fusion 6 : set to true if the EV observations are in a non NED reference frame and need to be rotated before being used 7 : Set to true to enable GPS yaw fusion. Cannot be used if bit position 4 is true.</long_desc>
<long_desc>Set bits in the following positions to enable: 0 : Set to true to use GPS data if available 1 : Set to true to use optical flow data if available 2 : Set to true to inhibit IMU delta velocity bias estimation 3 : Set to true to enable vision position fusion 4 : Set to true to enable vision yaw fusion. Cannot be used if bit position 7 is true. 5 : Set to true to enable multi-rotor drag specific force fusion 6 : set to true if the EV observations are in a non NED reference frame and need to be rotated before being used 7 : Set to true to enable GPS yaw fusion. Cannot be used if bit position 4 is true.</long_desc>
<min>0</min>
<max>255</max>
<reboot_required>true</reboot_required>
......@@ -4471,15 +4471,6 @@ default 1.5 turns per second</short_desc>
<decimal>2</decimal>
<increment>0.01</increment>
</parameter>
<parameter default="45.0" name="MC_YAWRAUTO_MAX" type="FLOAT">
<short_desc>Max yaw rate in auto mode</short_desc>
<long_desc>Limit for yaw rate, has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation.</long_desc>
<min>0.0</min>
<max>360.0</max>
<unit>deg/s</unit>
<decimal>1</decimal>
<increment>5</increment>
</parameter>
<parameter default="2.8" name="MC_YAW_P" type="FLOAT">
<short_desc>Yaw P gain</short_desc>
<long_desc>Yaw proportional gain, i.e. desired angular speed in rad/s for error 1 rad.</long_desc>
......@@ -4496,6 +4487,15 @@ default 1.5 turns per second</short_desc>
<decimal>2</decimal>
<increment>0.01</increment>
</parameter>
<parameter default="45.0" name="MPC_YAWRAUTO_MAX" type="FLOAT">
<short_desc>Max yaw rate in auto mode</short_desc>
<long_desc>Limit the rate of change of the yaw setpoint in autonomous mode to avoid large control output and mixer saturation.</long_desc>
<min>0.0</min>
<max>360.0</max>
<unit>deg/s</unit>
<decimal>1</decimal>
<increment>5</increment>
</parameter>
</group>
<group name="Multicopter Position Control">
<parameter default="10.0" name="MPC_ACC_DOWN_MAX" type="FLOAT">
......
......@@ -35,6 +35,8 @@ Rectangle {
property var actionData
property bool hideTrigger: false
property var mapIndicator
property alias optionText: optionCheckBox.text
property alias optionChecked: optionCheckBox.checked
property real _margins: ScreenTools.defaultFontPixelWidth
property bool _emergencyAction: action === guidedController.actionEmergencyStop
......@@ -101,6 +103,13 @@ Rectangle {
wrapMode: Text.WordWrap
}
QGCCheckBox {
id: optionCheckBox
anchors.horizontalCenter: parent.horizontalCenter
text: ""
visible: text !== ""
}
// Action confirmation control
SliderSwitch {
id: slider
......@@ -115,7 +124,7 @@ Rectangle {
altitudeSlider.visible = false
}
hideTrigger = false
guidedController.executeAction(_root.action, _root.actionData, altitudeChange)
guidedController.executeAction(_root.action, _root.actionData, altitudeChange, _root.optionChecked)
if (mapIndicator) {
mapIndicator.actionConfirmed()
mapIndicator = undefined
......
......@@ -208,7 +208,7 @@ Item {
on_FlightModeChanged: {
_vehiclePaused = activeVehicle ? _flightMode === activeVehicle.pauseFlightMode : false
_vehicleInRTLMode = activeVehicle ? _flightMode === activeVehicle.rtlFlightMode : false
_vehicleInRTLMode = activeVehicle ? _flightMode === activeVehicle.rtlFlightMode || _flightMode === activeVehicle.smartRTLFlightMode : false
_vehicleInLandMode = activeVehicle ? _flightMode === activeVehicle.landFlightMode : false
_vehicleInMissionMode = activeVehicle ? _flightMode === activeVehicle.missionFlightMode : false // Must be last to get correct signalling for showStartMission popups
}
......@@ -221,6 +221,7 @@ Item {
confirmDialog.actionData = actionData
confirmDialog.hideTrigger = true
confirmDialog.mapIndicator = mapIndicator
confirmDialog.optionText = ""
_actionData = actionData
switch (actionCode) {
case actionArm:
......@@ -284,6 +285,10 @@ Item {
case actionRTL:
confirmDialog.title = rtlTitle
confirmDialog.message = rtlMessage
if (activeVehicle.supportsSmartRTL) {
confirmDialog.optionText = qsTr("Smart RTL")
confirmDialog.optionChecked = false
}
confirmDialog.hideTrigger = Qt.binding(function() { return !showRTL })
break;
case actionChangeAlt:
......@@ -344,12 +349,12 @@ Item {
}
// Executes the specified action
function executeAction(actionCode, actionData, actionAltitudeChange) {
function executeAction(actionCode, actionData, actionAltitudeChange, optionChecked) {
var i;
var rgVehicle;
switch (actionCode) {
case actionRTL:
activeVehicle.guidedModeRTL()
activeVehicle.guidedModeRTL(optionChecked)
break
case actionLand:
activeVehicle.guidedModeLand()
......
[
{
"name": "Latitude",
"shortDescription": "Latitude of breach return point position",
"type": "double",
"decimalPlaces": 7
},
{
"name": "Longitude",
"shortDescription": "Longitude of breach return point position",
"type": "double",
"decimalPlaces": 7
},
{
"name": "Altitude",
"shortDescription": "Altitude of breach return point position (Rel)",
"type": "double",
"decimalPlaces": 2,
"units": "m"
}
]
......@@ -95,7 +95,7 @@ void CameraCalc::_cameraNameChanged(void)
// Validate known camera name
bool foundKnownCamera = false;
CameraMetaData* cameraMetaData = NULL;
CameraMetaData* cameraMetaData = nullptr;
if (!isManualCamera() && !isCustomCamera()) {
for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) {
cameraMetaData = _knownCameraList[cameraIndex].value<CameraMetaData*>();
......@@ -201,11 +201,12 @@ void CameraCalc::save(QJsonObject& json) const
}
}
bool CameraCalc::load(const QJsonObject& json, QString& errorString)
bool CameraCalc::load(const QJsonObject& json, bool forPresets, bool cameraSpecInPreset, QString& errorString)
{
qDebug() << "CameraCalc::load" << forPresets << cameraSpecInPreset;
QJsonObject v1Json = json;
if (!v1Json.contains(JsonHelper::jsonVersionKey)) {
if (!json.contains(JsonHelper::jsonVersionKey)) {
// Version 0 file. Differences from Version 1 for conversion:
// JsonHelper::jsonVersionKey not stored
// _jsonCameraSpecTypeKey stores CameraSpecType
......@@ -231,18 +232,13 @@ bool CameraCalc::load(const QJsonObject& json, QString& errorString)
{ adjustedFootprintSideName, QJsonValue::Double, true },
{ adjustedFootprintFrontalName, QJsonValue::Double, true },
{ distanceToSurfaceName, QJsonValue::Double, true },
{ distanceToSurfaceRelativeName, QJsonValue::Bool, true },
{ distanceToSurfaceRelativeName, QJsonValue::Bool, true },
};
if (!JsonHelper::validateKeys(v1Json, keyInfoList1, errorString)) {
return false;
}
_disableRecalc = true;
_cameraNameFact.setRawValue (v1Json[cameraNameName].toString());
_adjustedFootprintSideFact.setRawValue (v1Json[adjustedFootprintSideName].toDouble());
_adjustedFootprintFrontalFact.setRawValue (v1Json[adjustedFootprintFrontalName].toDouble());
_distanceToSurfaceFact.setRawValue (v1Json[distanceToSurfaceName].toDouble());
_disableRecalc = !forPresets;
_distanceToSurfaceRelative = v1Json[distanceToSurfaceRelativeName].toBool();
......@@ -259,13 +255,40 @@ bool CameraCalc::load(const QJsonObject& json, QString& errorString)
}
_valueSetIsDistanceFact.setRawValue (v1Json[valueSetIsDistanceName].toBool());
_imageDensityFact.setRawValue (v1Json[imageDensityName].toDouble());
_frontalOverlapFact.setRawValue (v1Json[frontalOverlapName].toDouble());
_sideOverlapFact.setRawValue (v1Json[sideOverlapName].toDouble());
}
if (!CameraSpec::load(v1Json, errorString)) {
_disableRecalc = false;
return false;
if (forPresets) {
if (isManualCamera()) {
_distanceToSurfaceFact.setRawValue(v1Json[distanceToSurfaceName].toDouble());
} else {
if (_valueSetIsDistanceFact.rawValue().toBool()) {
_distanceToSurfaceFact.setRawValue(v1Json[distanceToSurfaceName].toDouble());
} else {
_imageDensityFact.setRawValue(v1Json[imageDensityName].toDouble());
}
if (cameraSpecInPreset) {
_cameraNameFact.setRawValue(v1Json[cameraNameName].toString());
if (!CameraSpec::load(v1Json, errorString)) {
_disableRecalc = false;
return false;
}
}
}
} else {
_cameraNameFact.setRawValue (v1Json[cameraNameName].toString());
_adjustedFootprintSideFact.setRawValue (v1Json[adjustedFootprintSideName].toDouble());
_adjustedFootprintFrontalFact.setRawValue (v1Json[adjustedFootprintFrontalName].toDouble());
_distanceToSurfaceFact.setRawValue (v1Json[distanceToSurfaceName].toDouble());
if (!isManualCamera()) {
_imageDensityFact.setRawValue(v1Json[imageDensityName].toDouble());
if (!CameraSpec::load(v1Json, errorString)) {
_disableRecalc = false;
return false;
}
}
}
......
......@@ -70,7 +70,7 @@ public:
void setDistanceToSurfaceRelative (bool distanceToSurfaceRelative);
void save(QJsonObject& json) const;
bool load(const QJsonObject& json, QString& errorString);
bool load(const QJsonObject& json, bool forPresets, bool cameraSpecInPreset, QString& errorString);
static const char* cameraNameName;
static const char* valueSetIsDistanceName;
......
......@@ -8,11 +8,21 @@
****************************************************************************/
#include "ComplexMissionItem.h"
#include "QGCApplication.h"
#include <QSettings>
const char* ComplexMissionItem::jsonComplexItemTypeKey = "complexItemType";
const char* ComplexMissionItem::_presetSettingsKey = "_presets";
const char* ComplexMissionItem::_presetNameKey = "complexItemPresetName";
const char* ComplexMissionItem::_saveCameraInPresetKey = "complexItemCameraSavedInPreset";
const char* ComplexMissionItem::_builtInPresetKey = "complexItemBuiltInPreset";
ComplexMissionItem::ComplexMissionItem(Vehicle* vehicle, bool flyView, QObject* parent)
: VisualMissionItem(vehicle, flyView, parent)
: VisualMissionItem (vehicle, flyView, parent)
, _cameraInPreset (true)
, _builtInPreset (false)
{
}
......@@ -21,5 +31,114 @@ const ComplexMissionItem& ComplexMissionItem::operator=(const ComplexMissionItem
{
VisualMissionItem::operator=(other);
_currentPreset = other._currentPreset;
_cameraInPreset = other._cameraInPreset;
_builtInPreset = other._builtInPreset;
return *this;
}
QStringList ComplexMissionItem::presetNames(void)
{
QStringList names;
QSettings settings;
settings.beginGroup(presetsSettingsGroup());
settings.beginGroup(_presetSettingsKey);
return settings.childKeys();
}
void ComplexMissionItem::loadPreset(const QString& name)
{
Q_UNUSED(name);
qgcApp()->showMessage(tr("This Pattern does not support Presets."));
}
void ComplexMissionItem::savePreset(const QString& name)
{
Q_UNUSED(name);
qgcApp()->showMessage(tr("This Pattern does not support Presets."));
}
void ComplexMissionItem::clearCurrentPreset(void)
{
_currentPreset.clear();
emit currentPresetChanged(_currentPreset);
}
void ComplexMissionItem::deleteCurrentPreset(void)
{
qDebug() << "deleteCurrentPreset" << _currentPreset;
if (!_currentPreset.isEmpty()) {
QSettings settings;
settings.beginGroup(presetsSettingsGroup());
settings.beginGroup(_presetSettingsKey);
settings.remove(_currentPreset);
emit presetNamesChanged();
clearCurrentPreset();
}
}
void ComplexMissionItem::_savePresetJson(const QString& name, QJsonObject& presetObject)
{
presetObject[_presetNameKey] = name;
QSettings settings;
settings.beginGroup(presetsSettingsGroup());
settings.beginGroup(_presetSettingsKey);
settings.setValue(name, QJsonDocument(presetObject).toBinaryData());
emit presetNamesChanged();
_currentPreset = name;
emit currentPresetChanged(name);
}
QJsonObject ComplexMissionItem::_loadPresetJson(const QString& name)
{
QSettings settings;
settings.beginGroup(presetsSettingsGroup());
settings.beginGroup(_presetSettingsKey);
return QJsonDocument::fromBinaryData(settings.value(name).toByteArray()).object();
}
void ComplexMissionItem::_saveItem(QJsonObject& saveObject)
{
qDebug() << "_saveItem" << _cameraInPreset;
saveObject[_presetNameKey] = _currentPreset;
saveObject[_saveCameraInPresetKey] = _cameraInPreset;
saveObject[_builtInPresetKey] = _builtInPreset;
}
void ComplexMissionItem::_loadItem(const QJsonObject& saveObject)
{
_currentPreset = saveObject[_presetNameKey].toString();
_cameraInPreset = saveObject[_saveCameraInPresetKey].toBool(false);
_builtInPreset = saveObject[_builtInPresetKey].toBool(false);
if (!presetNames().contains(_currentPreset)) {
_currentPreset.clear();
}
emit cameraInPresetChanged (_cameraInPreset);
emit currentPresetChanged (_currentPreset);
emit builtInPresetChanged (_builtInPreset);
}
void ComplexMissionItem::setCameraInPreset(bool cameraInPreset)
{
if (cameraInPreset != _cameraInPreset) {
_cameraInPreset = cameraInPreset;
emit cameraInPresetChanged(_cameraInPreset);
}
}
void ComplexMissionItem::setBuiltInPreset(bool builtInPreset)
{
if (builtInPreset != _builtInPreset) {
_builtInPreset = builtInPreset;
emit builtInPresetChanged(_builtInPreset);
}
}
......@@ -13,6 +13,8 @@
#include "VisualMissionItem.h"
#include "QGCGeo.h"
#include <QSettings>
class ComplexMissionItem : public VisualMissionItem
{
Q_OBJECT
......@@ -22,7 +24,12 @@ public:
const ComplexMissionItem& operator=(const ComplexMissionItem& other);
Q_PROPERTY(double complexDistance READ complexDistance NOTIFY complexDistanceChanged)
Q_PROPERTY(double complexDistance READ complexDistance NOTIFY complexDistanceChanged)
Q_PROPERTY(bool presetsSupported READ presetsSupported CONSTANT)
Q_PROPERTY(QStringList presetNames READ presetNames NOTIFY presetNamesChanged)
Q_PROPERTY(QString currentPreset READ currentPreset NOTIFY currentPresetChanged)
Q_PROPERTY(bool cameraInPreset READ cameraInPreset WRITE setCameraInPreset NOTIFY cameraInPresetChanged)
Q_PROPERTY(bool builtInPreset READ builtInPreset WRITE setBuiltInPreset NOTIFY builtInPresetChanged)
/// @return The distance covered the complex mission item in meters.
/// Signals complexDistanceChanged
......@@ -35,12 +42,38 @@ public:
/// @return true: load success, false: load failed, errorString set
virtual bool load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString) = 0;
/// Loads the specified preset into the complex item.
/// @param name Preset name.
Q_INVOKABLE virtual void loadPreset(const QString& name);
/// Saves the current state of the complex item as the named preset.
/// @param name User visible name for preset. Will replace existing preset if already exists.
Q_INVOKABLE virtual void savePreset(const QString& name);
Q_INVOKABLE void clearCurrentPreset(void);
Q_INVOKABLE void deleteCurrentPreset(void);
/// Get the point of complex mission item furthest away from a coordinate
/// @param other QGeoCoordinate to which distance is calculated
/// @return the greatest distance from any point of the complex item to some coordinate
/// Signals greatestDistanceToChanged
virtual double greatestDistanceTo(const QGeoCoordinate &other) const = 0;
/// Returns the list of currently saved presets for this complex item type.
/// @param name User visible name for preset. Will replace existing preset if already exists.
virtual QStringList presetNames(void);
/// Returns the name of the settings group for presets.
/// Empty string signals no support for presets.
virtual QString presetsSettingsGroup(void) { return QString(); }
bool presetsSupported (void) { return !presetsSettingsGroup().isEmpty(); }
QString currentPreset (void) const { return _currentPreset; }
bool cameraInPreset (void) const { return _cameraInPreset; }
bool builtInPreset (void) const { return _builtInPreset; }
void setCameraInPreset (bool cameraInPreset);
void setBuiltInPreset (bool builtInPreset);
/// This mission item attribute specifies the type of the complex item.
static const char* jsonComplexItemTypeKey;
......@@ -48,6 +81,29 @@ signals:
void complexDistanceChanged (void);
void boundingCubeChanged (void);
void greatestDistanceToChanged (void);
void presetNamesChanged (void);
void currentPresetChanged (QString currentPreset);
void cameraInPresetChanged (bool cameraInPreset);
void builtInPresetChanged (bool builtInPreset);
protected:
void _saveItem (QJsonObject& saveObject);
void _loadItem (const QJsonObject& saveObject);
void _savePresetJson (const QString& name, QJsonObject& presetObject);
QJsonObject _loadPresetJson (const QString& name);
QMap<QString, FactMetaData*> _metaDataMap;
QString _currentPreset;
SettingsFact _saveCameraInPresetFact;
bool _cameraInPreset;
bool _builtInPreset;
static const char* _presetSettingsKey;
static const char* _presetNameKey;
static const char* _saveCameraInPresetKey;
static const char* _builtInPresetKey;
};
#endif
......@@ -62,7 +62,7 @@ void CorridorScanComplexItem::save(QJsonArray& planItems)
{
QJsonObject saveObject;
_save(saveObject);
TransectStyleComplexItem::_save(saveObject);
saveObject[JsonHelper::jsonVersionKey] = 2;
saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue;
......@@ -115,7 +115,7 @@ bool CorridorScanComplexItem::load(const QJsonObject& complexObject, int sequenc
setSequenceNumber(sequenceNumber);
if (!_load(complexObject, errorString)) {
if (!_load(complexObject, false /* forPresets */, errorString)) {
_ignoreRecalc = false;
return false;
}
......
......@@ -21,30 +21,51 @@
#include "QGCQGeoCoordinate.h"
#include "AppSettings.h"
#include "PlanMasterController.h"
#include "SettingsManager.h"
#include "AppSettings.h"
#include <QJsonDocument>
#include <QJsonArray>
QGC_LOGGING_CATEGORY(GeoFenceControllerLog, "GeoFenceControllerLog")
QMap<QString, FactMetaData*> GeoFenceController::_metaDataMap;
const char* GeoFenceController::_jsonFileTypeValue = "GeoFence";
const char* GeoFenceController::_jsonBreachReturnKey = "breachReturn";
const char* GeoFenceController::_jsonPolygonsKey = "polygons";
const char* GeoFenceController::_jsonCirclesKey = "circles";
const char* GeoFenceController::_breachReturnAltitudeFactName = "Altitude";
const char* GeoFenceController::_px4ParamCircularFence = "GF_MAX_HOR_DIST";
GeoFenceController::GeoFenceController(PlanMasterController* masterController, QObject* parent)
: PlanElementController (masterController, parent)
, _geoFenceManager (_managerVehicle->geoFenceManager())
, _dirty (false)
: PlanElementController (masterController, parent)
, _geoFenceManager (_managerVehicle->geoFenceManager())
, _dirty (false)
, _breachReturnAltitudeFact (0, _breachReturnAltitudeFactName, FactMetaData::valueTypeDouble)
, _breachReturnDefaultAltitude (qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble())
, _itemsRequested (false)
, _px4ParamCircularFenceFact(NULL)
, _px4ParamCircularFenceFact(nullptr)
{
if (_metaDataMap.isEmpty()) {
_metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/BreachReturn.FactMetaData.json"), nullptr /* metaDataParent */);
}
_breachReturnAltitudeFact.setMetaData(_metaDataMap[_breachReturnAltitudeFactName]);
_breachReturnAltitudeFact.setRawValue(_breachReturnDefaultAltitude);
connect(&_polygons, &QmlObjectListModel::countChanged, this, &GeoFenceController::_updateContainsItems);
connect(&_circles, &QmlObjectListModel::countChanged, this, &GeoFenceController::_updateContainsItems);
managerVehicleChanged(_managerVehicle);
connect(this, &GeoFenceController::breachReturnPointChanged, this, &GeoFenceController::_setDirty);
connect(&_breachReturnAltitudeFact, &Fact::rawValueChanged, this, &GeoFenceController::_setDirty);
connect(&_polygons, &QmlObjectListModel::dirtyChanged, this, &GeoFenceController::_setDirty);
connect(&_circles, &QmlObjectListModel::dirtyChanged, this, &GeoFenceController::_setDirty);
}
GeoFenceController::~GeoFenceController()
......@@ -74,26 +95,19 @@ void GeoFenceController::setBreachReturnPoint(const QGeoCoordinate& breachReturn
}
}
void GeoFenceController::_signalAll(void)
{
emit breachReturnPointChanged(breachReturnPoint());
emit dirtyChanged(dirty());
emit supportedChanged(supported());
}
void GeoFenceController::managerVehicleChanged(Vehicle* managerVehicle)
{
if (_managerVehicle) {
_geoFenceManager->disconnect(this);
_managerVehicle->disconnect(this);
_managerVehicle->parameterManager()->disconnect(this);
_managerVehicle = NULL;
_geoFenceManager = NULL;
_managerVehicle = nullptr;
_geoFenceManager = nullptr;
}
_managerVehicle = managerVehicle;
if (!_managerVehicle) {
qWarning() << "GeoFenceController::managerVehicleChanged managerVehicle=NULL";
qWarning() << "GeoFenceController::managerVehicleChanged managerVehicle=nullptr";
return;
}
......@@ -109,7 +123,6 @@ void GeoFenceController::managerVehicleChanged(Vehicle* managerVehicle)
_parametersReady();
emit supportedChanged(supported());
_signalAll();
}
bool GeoFenceController::load(const QJsonObject& json, QString& errorString)
......@@ -128,6 +141,7 @@ bool GeoFenceController::load(const QJsonObject& json, QString& errorString)
{ JsonHelper::jsonVersionKey, QJsonValue::Double, true },
{ _jsonCirclesKey, QJsonValue::Array, true },
{ _jsonPolygonsKey, QJsonValue::Array, true },
{ _jsonBreachReturnKey, QJsonValue::Array, false },
};
if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) {
return false;
......@@ -139,7 +153,7 @@ bool GeoFenceController::load(const QJsonObject& json, QString& errorString)
}
QJsonArray jsonPolygonArray = json[_jsonPolygonsKey].toArray();
for (const QJsonValue& jsonPolygonValue: jsonPolygonArray) {
for (const QJsonValue jsonPolygonValue: jsonPolygonArray) {
if (jsonPolygonValue.type() != QJsonValue::Object) {
errorString = tr("GeoFence polygon not stored as object");
return false;
......@@ -153,7 +167,7 @@ bool GeoFenceController::load(const QJsonObject& json, QString& errorString)
}
QJsonArray jsonCircleArray = json[_jsonCirclesKey].toArray();
for (const QJsonValue& jsonCircleValue: jsonCircleArray) {
for (const QJsonValue jsonCircleValue: jsonCircleArray) {
if (jsonCircleValue.type() != QJsonValue::Object) {
errorString = tr("GeoFence circle not stored as object");
return false;
......@@ -166,8 +180,18 @@ bool GeoFenceController::load(const QJsonObject& json, QString& errorString)
_circles.append(fenceCircle);
}
if (json.contains(_jsonBreachReturnKey)) {
if (!JsonHelper::loadGeoCoordinate(json[_jsonBreachReturnKey], true /* altitudeRequred */, _breachReturnPoint, errorString)) {
return false;
}
_breachReturnAltitudeFact.setRawValue(_breachReturnPoint.altitude());
} else {
_breachReturnPoint = QGeoCoordinate();
_breachReturnAltitudeFact.setRawValue(_breachReturnDefaultAltitude);
}
emit breachReturnPointChanged(_breachReturnPoint);
setDirty(false);
_signalAll();
return true;
}
......@@ -193,6 +217,14 @@ void GeoFenceController::save(QJsonObject& json)
jsonCircleArray.append(jsonCircle);
}
json[_jsonCirclesKey] = jsonCircleArray;
if (_breachReturnPoint.isValid()) {
QJsonValue jsonCoordinate;
_breachReturnPoint.setAltitude(_breachReturnAltitudeFact.rawValue().toDouble());
JsonHelper::saveGeoCoordinate(_breachReturnPoint, true /* writeAltitude */, jsonCoordinate);
json[_jsonBreachReturnKey] = jsonCoordinate;
}
}
void GeoFenceController::removeAll(void)
......@@ -300,6 +332,11 @@ void GeoFenceController::_setReturnPointFromManager(QGeoCoordinate breachReturnP
{
_breachReturnPoint = breachReturnPoint;
emit breachReturnPointChanged(_breachReturnPoint);
if (_breachReturnPoint.isValid()) {
_breachReturnAltitudeFact.setRawValue(_breachReturnPoint.altitude());
} else {
_breachReturnAltitudeFact.setRawValue(_breachReturnDefaultAltitude);
}
}
void GeoFenceController::_managerLoadComplete(void)
......@@ -310,7 +347,6 @@ void GeoFenceController::_managerLoadComplete(void)
_setReturnPointFromManager(_geoFenceManager->breachReturnPoint());
_setFenceFromManager(_geoFenceManager->polygons(), _geoFenceManager->circles());
setDirty(false);
_signalAll();
emit loadComplete();
}
_itemsRequested = false;
......@@ -471,7 +507,7 @@ void GeoFenceController::_parametersReady(void)
{
if (_px4ParamCircularFenceFact) {
_px4ParamCircularFenceFact->disconnect(this);
_px4ParamCircularFenceFact = NULL;
_px4ParamCircularFenceFact = nullptr;
}
if (_managerVehicle->isOfflineEditingVehicle() || !_managerVehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _px4ParamCircularFence)) {
......
......@@ -27,12 +27,13 @@ class GeoFenceController : public PlanElementController
Q_OBJECT
public:
GeoFenceController(PlanMasterController* masterController, QObject* parent = NULL);
GeoFenceController(PlanMasterController* masterController, QObject* parent = nullptr);
~GeoFenceController();
Q_PROPERTY(QmlObjectListModel* polygons READ polygons CONSTANT)
Q_PROPERTY(QmlObjectListModel* circles READ circles CONSTANT)
Q_PROPERTY(QGeoCoordinate breachReturnPoint READ breachReturnPoint WRITE setBreachReturnPoint NOTIFY breachReturnPointChanged)
Q_PROPERTY(QmlObjectListModel* polygons READ polygons CONSTANT)
Q_PROPERTY(QmlObjectListModel* circles READ circles CONSTANT)
Q_PROPERTY(QGeoCoordinate breachReturnPoint READ breachReturnPoint WRITE setBreachReturnPoint NOTIFY breachReturnPointChanged)
Q_PROPERTY(Fact* breachReturnAltitude READ breachReturnAltitude CONSTANT)
// Hack to expose PX4 circular fence controlled by GF_MAX_HOR_DIST
Q_PROPERTY(double paramCircularFence READ paramCircularFence NOTIFY paramCircularFenceChanged)
......@@ -58,7 +59,8 @@ public:
/// Clears the interactive bit from all fence items
Q_INVOKABLE void clearAllInteractive(void);
double paramCircularFence(void);
double paramCircularFence (void);
Fact* breachReturnAltitude(void) { return &_breachReturnAltitudeFact; }
// Overrides from PlanElementController
bool supported (void) const final;
......@@ -101,16 +103,19 @@ private slots:
private:
void _init(void);
void _signalAll(void);
GeoFenceManager* _geoFenceManager;
bool _dirty;
QmlObjectListModel _polygons;
QmlObjectListModel _circles;
QGeoCoordinate _breachReturnPoint;
Fact _breachReturnAltitudeFact;
double _breachReturnDefaultAltitude;
bool _itemsRequested;
Fact* _px4ParamCircularFenceFact;
static QMap<QString, FactMetaData*> _metaDataMap;
static const char* _px4ParamCircularFence;
static const int _jsonCurrentVersion = 2;
......@@ -119,6 +124,8 @@ private:
static const char* _jsonBreachReturnKey;
static const char* _jsonPolygonsKey;
static const char* _jsonCirclesKey;
static const char* _breachReturnAltitudeFactName;
};
#endif
......@@ -51,8 +51,6 @@ void GeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn,
QmlObjectListModel& polygons,
QmlObjectListModel& circles)
{
Q_UNUSED(breachReturn);
QList<MissionItem*> fenceItems;
_sendPolygons.clear();
......@@ -64,6 +62,7 @@ void GeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn,
for (int i=0; i<circles.count(); i++) {
_sendCircles.append(*circles.value<QGCFenceCircle*>(i));
}
_breachReturnPoint = breachReturn;
for (int i=0; i<_sendPolygons.count(); i++) {
const QGCFencePolygon& polygon = _sendPolygons[i];
......@@ -103,12 +102,30 @@ void GeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn,
fenceItems.append(item);
}
if (_breachReturnPoint.isValid()) {
MissionItem* item = new MissionItem(0,
MAV_CMD_NAV_FENCE_RETURN_POINT,
MAV_FRAME_GLOBAL_RELATIVE_ALT,
0, 0, 0, 0, // param 1-4 unused
breachReturn.latitude(),
breachReturn.longitude(),
breachReturn.altitude(),
false, // autocontinue
false, // isCurrentItem
this); // parent
fenceItems.append(item);
}
// Plan manager takes control of MissionItems, so no need to delete
_planManager.writeMissionItems(fenceItems);
}
void GeoFenceManager::removeAll(void)
{
_polygons.clear();
_circles.clear();
_breachReturnPoint = QGeoCoordinate();
_planManager.removeAll();
}
......@@ -117,6 +134,7 @@ void GeoFenceManager::_sendComplete(bool error)
if (error) {
_polygons.clear();
_circles.clear();
_breachReturnPoint = QGeoCoordinate();
} else {
_polygons = _sendPolygons;
_circles = _sendCircles;
......@@ -174,6 +192,8 @@ void GeoFenceManager::_planManagerLoadComplete(bool removeAllRequested)
}
QGCFenceCircle circle(QGeoCoordinate(item->param5(), item->param6()), item->param1(), command == MAV_CMD_NAV_FENCE_CIRCLE_INCLUSION /* inclusion */);
_circles.append(circle);
} else if (command == MAV_CMD_NAV_FENCE_RETURN_POINT) {
_breachReturnPoint = QGeoCoordinate(item->param5(), item->param6(), item->param7());
} else {
emit error(UnsupportedCommand, tr("GeoFence load: Unsupported command %1").arg(item->command()));
break;
......@@ -183,6 +203,7 @@ void GeoFenceManager::_planManagerLoadComplete(bool removeAllRequested)
if (loadFailed) {
_polygons.clear();
_circles.clear();
_breachReturnPoint = QGeoCoordinate();
}
emit loadComplete();
......
......@@ -72,6 +72,7 @@ void RallyPointManager::sendToVehicle(const QList<QGeoCoordinate>& rgPoints)
void RallyPointManager::removeAll(void)
{
_rgPoints.clear();
_planManager.removeAll();
}
......
......@@ -83,6 +83,9 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, bool flyVie
connect(&_cameraCalc, &CameraCalc::isManualCameraChanged, this, &StructureScanComplexItem::_updateGimbalPitch);
connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_recalcScanDistance);
connect(&_flightPolygon, &QGCMapPolygon::pathChanged, this, &StructureScanComplexItem::_recalcScanDistance);
_recalcLayerInfo();
if (!kmlOrShpFile.isEmpty()) {
......@@ -93,14 +96,6 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, bool flyVie
setDirty(false);
}
void StructureScanComplexItem::_setScanDistance(double scanDistance)
{
if (!qFuzzyCompare(_scanDistance, scanDistance)) {
_scanDistance = scanDistance;
emit complexDistanceChanged();
}
}
void StructureScanComplexItem::_setCameraShots(int cameraShots)
{
if (_cameraShots != cameraShots) {
......@@ -218,7 +213,7 @@ bool StructureScanComplexItem::load(const QJsonObject& complexObject, int sequen
setSequenceNumber(sequenceNumber);
// Load CameraCalc first since it will trigger camera name change which will trounce gimbal angles
if (!_cameraCalc.load(complexObject[_jsonCameraCalcKey].toObject(), errorString)) {
if (!_cameraCalc.load(complexObject[_jsonCameraCalcKey].toObject(), false /* forPresets */, false /* cameraSpecInPreset */, errorString)) {
return false;
}
......@@ -519,6 +514,7 @@ void StructureScanComplexItem::_rebuildFlightPolygon(void)
} else {
_entryVertex = savedEntryVertex;
}
emit coordinateChanged(coordinate());
emit exitCoordinateChanged(exitCoordinate());
}
......@@ -598,3 +594,26 @@ void StructureScanComplexItem::_signalTopBottomAltChanged(void)
emit topFlightAltChanged();
emit bottomFlightAltChanged();
}
void StructureScanComplexItem::_recalcScanDistance()
{
double scanDistance = 0;
QList<QGeoCoordinate> vertices = _flightPolygon.coordinateList();
for (int i=0; i<vertices.count() - 1; i++) {
scanDistance += vertices[i].distanceTo(vertices[i+1]);
}
scanDistance *= _layersFact.rawValue().toInt();
double surfaceHeight = qMax(_structureHeightFact.rawValue().toDouble() - _scanBottomAltFact.rawValue().toDouble(), 0.0);
scanDistance += surfaceHeight;
if (!qFuzzyCompare(_scanDistance, scanDistance)) {
_scanDistance = scanDistance;
emit complexDistanceChanged();
}
qCDebug(StructureScanComplexItemLog) << "StructureScanComplexItem--_recalcScanDistance layers: "
<< _layersFact.rawValue().toInt() << " structure height: " << surfaceHeight
<< " scanDistance: " << _scanDistance;
}
......@@ -125,11 +125,11 @@ private slots:
void _recalcLayerInfo (void);
void _updateLastSequenceNumber (void);
void _updateGimbalPitch (void);
void _signalTopBottomAltChanged (void);
void _signalTopBottomAltChanged (void);
void _recalcScanDistance (void);
private:
void _setExitCoordinate(const QGeoCoordinate& coordinate);
void _setScanDistance(double scanDistance);
void _setCameraShots(int cameraShots);
double _triggerDistance(void) const;
......
......@@ -108,7 +108,21 @@ void SurveyComplexItem::save(QJsonArray& planItems)
{
QJsonObject saveObject;
_save(saveObject);
_saveWorker(saveObject);
planItems.append(saveObject);
}
void SurveyComplexItem::savePreset(const QString& name)
{
QJsonObject saveObject;
_saveWorker(saveObject);
_savePresetJson(name, saveObject);
}
void SurveyComplexItem::_saveWorker(QJsonObject& saveObject)
{
TransectStyleComplexItem::_save(saveObject);
saveObject[JsonHelper::jsonVersionKey] = 5;
saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue;
......@@ -120,8 +134,15 @@ void SurveyComplexItem::save(QJsonArray& planItems)
// Polygon shape
_surveyAreaPolygon.saveToJson(saveObject);
}
planItems.append(saveObject);
void SurveyComplexItem::loadPreset(const QString& name)
{
QString errorString;
QJsonObject presetObject = _loadPresetJson(name);
_loadV4V5(presetObject, 0, errorString, 5, true /* forPresets */);
_rebuildTransects();
}
bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString)
......@@ -141,7 +162,7 @@ bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumbe
}
if (version == 4 || version == 5) {
if (!_loadV4V5(complexObject, sequenceNumber, errorString, version)) {
if (!_loadV4V5(complexObject, sequenceNumber, errorString, version, false /* forPresets */)) {
return false;
}
......@@ -171,7 +192,7 @@ bool SurveyComplexItem::load(const QJsonObject& complexObject, int sequenceNumbe
return true;
}
bool SurveyComplexItem::_loadV4V5(const QJsonObject& complexObject, int sequenceNumber, QString& errorString, int version)
bool SurveyComplexItem::_loadV4V5(const QJsonObject& complexObject, int sequenceNumber, QString& errorString, int version, bool forPresets)
{
QList<JsonHelper::KeyValidateInfo> keyInfoList = {
{ VisualMissionItem::jsonTypeKey, QJsonValue::String, true },
......@@ -197,16 +218,18 @@ bool SurveyComplexItem::_loadV4V5(const QJsonObject& complexObject, int sequence
return false;
}
_ignoreRecalc = true;
_ignoreRecalc = !forPresets;
setSequenceNumber(sequenceNumber);
if (!forPresets) {
setSequenceNumber(sequenceNumber);
if (!_surveyAreaPolygon.loadFromJson(complexObject, true /* required */, errorString)) {
_surveyAreaPolygon.clear();
return false;
if (!_surveyAreaPolygon.loadFromJson(complexObject, true /* required */, errorString)) {
_surveyAreaPolygon.clear();
return false;
}
}
if (!_load(complexObject, errorString)) {
if (!TransectStyleComplexItem::_load(complexObject, forPresets, errorString)) {
_ignoreRecalc = false;
return false;
}
......@@ -214,7 +237,7 @@ bool SurveyComplexItem::_loadV4V5(const QJsonObject& complexObject, int sequence
_gridAngleFact.setRawValue (complexObject[_jsonGridAngleKey].toDouble());
_flyAlternateTransectsFact.setRawValue (complexObject[_jsonFlyAlternateTransectsKey].toBool(false));
if(version == 5) {
if (version == 5) {
_splitConcavePolygonsFact.setRawValue (complexObject[_jsonSplitConcavePolygonsKey].toBool(true));
}
......@@ -282,7 +305,7 @@ bool SurveyComplexItem::_loadV3(const QJsonObject& complexObject, int sequenceNu
_turnAroundDistanceFact.setRawValue (gridObject[_jsonV3TurnaroundDistKey].toDouble());
if (gridObject.contains(_jsonEntryPointKey)) {
_entryPoint = gridObject[_jsonEntryPointKey].toDouble();
_entryPoint = gridObject[_jsonEntryPointKey].toInt();
} else {
_entryPoint = EntryLocationTopRight;
}
......
......@@ -39,6 +39,9 @@ public:
// Overrides from ComplexMissionItem
bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final;
QString mapVisualQML (void) const final { return QStringLiteral("SurveyMapVisual.qml"); }
QString presetsSettingsGroup(void) { return settingsGroup; }
void savePreset (const QString& name);
void loadPreset (const QString& name);
// Overrides from TransectStyleComplexItem
void save (QJsonArray& planItems) final;
......@@ -114,7 +117,8 @@ private:
double _turnaroundDistance(void) const;
bool _hoverAndCaptureEnabled(void) const;
bool _loadV3(const QJsonObject& complexObject, int sequenceNumber, QString& errorString);
bool _loadV4V5(const QJsonObject& complexObject, int sequenceNumber, QString& errorString, int version);
bool _loadV4V5(const QJsonObject& complexObject, int sequenceNumber, QString& errorString, int version, bool forPresets);
void _saveWorker(QJsonObject& complexObject);
void _rebuildTransectsPhase1Worker(bool refly);
void _rebuildTransectsPhase1WorkerSinglePolygon(bool refly);
void _rebuildTransectsPhase1WorkerSplitPolygons(bool refly);
......
......@@ -132,6 +132,8 @@ void TransectStyleComplexItem::setDirty(bool dirty)
void TransectStyleComplexItem::_save(QJsonObject& complexObject)
{
ComplexMissionItem::_saveItem(complexObject);
QJsonObject innerObject;
innerObject[JsonHelper::jsonVersionKey] = 1;
......@@ -183,8 +185,9 @@ void TransectStyleComplexItem::setSequenceNumber(int sequenceNumber)
}
}
bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString& errorString)
bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, bool forPresets, QString& errorString)
{
ComplexMissionItem::_loadItem(complexObject);
QList<JsonHelper::KeyValidateInfo> keyInfoList = {
{ _jsonTransectStyleComplexItemKey, QJsonValue::Object, true },
......@@ -211,8 +214,8 @@ bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString&
{ hoverAndCaptureName, QJsonValue::Bool, true },
{ refly90DegreesName, QJsonValue::Bool, true },
{ _jsonCameraCalcKey, QJsonValue::Object, true },
{ _jsonVisualTransectPointsKey, QJsonValue::Array, true },
{ _jsonItemsKey, QJsonValue::Array, true },
{ _jsonVisualTransectPointsKey, QJsonValue::Array, !forPresets },
{ _jsonItemsKey, QJsonValue::Array, !forPresets },
{ _jsonFollowTerrainKey, QJsonValue::Bool, true },
{ _jsonCameraShotsKey, QJsonValue::Double, false }, // Not required since it was missing from initial implementation
};
......@@ -220,28 +223,30 @@ bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString&
return false;
}
// Load visual transect points
if (!JsonHelper::loadGeoCoordinateArray(innerObject[_jsonVisualTransectPointsKey], false /* altitudeRequired */, _visualTransectPoints, errorString)) {
return false;
}
_coordinate = _visualTransectPoints.count() ? _visualTransectPoints.first().value<QGeoCoordinate>() : QGeoCoordinate();
_exitCoordinate = _visualTransectPoints.count() ? _visualTransectPoints.last().value<QGeoCoordinate>() : QGeoCoordinate();
// Load generated mission items
_loadedMissionItemsParent = new QObject(this);
QJsonArray missionItemsJsonArray = innerObject[_jsonItemsKey].toArray();
for (const QJsonValue& missionItemJson: missionItemsJsonArray) {
MissionItem* missionItem = new MissionItem(_loadedMissionItemsParent);
if (!missionItem->load(missionItemJson.toObject(), 0 /* sequenceNumber */, errorString)) {
_loadedMissionItemsParent->deleteLater();
_loadedMissionItemsParent = NULL;
if (!forPresets) {
// Load visual transect points
if (!JsonHelper::loadGeoCoordinateArray(innerObject[_jsonVisualTransectPointsKey], false /* altitudeRequired */, _visualTransectPoints, errorString)) {
return false;
}
_loadedMissionItems.append(missionItem);
_coordinate = _visualTransectPoints.count() ? _visualTransectPoints.first().value<QGeoCoordinate>() : QGeoCoordinate();
_exitCoordinate = _visualTransectPoints.count() ? _visualTransectPoints.last().value<QGeoCoordinate>() : QGeoCoordinate();
// Load generated mission items
_loadedMissionItemsParent = new QObject(this);
QJsonArray missionItemsJsonArray = innerObject[_jsonItemsKey].toArray();
for (const QJsonValue missionItemJson: missionItemsJsonArray) {
MissionItem* missionItem = new MissionItem(_loadedMissionItemsParent);
if (!missionItem->load(missionItemJson.toObject(), 0 /* sequenceNumber */, errorString)) {
_loadedMissionItemsParent->deleteLater();
_loadedMissionItemsParent = nullptr;
return false;
}
_loadedMissionItems.append(missionItem);
}
}
// Load CameraCalc data
if (!_cameraCalc.load(innerObject[_jsonCameraCalcKey].toObject(), errorString)) {
if (!_cameraCalc.load(innerObject[_jsonCameraCalcKey].toObject(), forPresets, cameraInPreset(), errorString)) {
return false;
}
......@@ -435,7 +440,7 @@ void TransectStyleComplexItem::_reallyQueryTransectsPathHeightInfo(void)
// Let the signal fall on the floor
disconnect(_terrainPolyPathQuery, &TerrainPolyPathQuery::terrainDataReceived, this, &TransectStyleComplexItem::_polyPathTerrainData);
#endif
_terrainPolyPathQuery = NULL;
_terrainPolyPathQuery = nullptr;
}
// Append all transects into a single PolyPath query
......@@ -479,7 +484,7 @@ void TransectStyleComplexItem::_polyPathTerrainData(bool success, const QList<Te
qWarning() << "TransectStyleComplexItem::_polyPathTerrainData _terrainPolyPathQuery != sender()";
}
disconnect(_terrainPolyPathQuery, &TerrainPolyPathQuery::terrainDataReceived, this, &TransectStyleComplexItem::_polyPathTerrainData);
_terrainPolyPathQuery = NULL;
_terrainPolyPathQuery = nullptr;
}
bool TransectStyleComplexItem::readyForSave(void) const
......
......@@ -141,7 +141,7 @@ protected:
virtual void _recalcCameraShots (void) = 0;
void _save (QJsonObject& saveObject);
bool _load (const QJsonObject& complexObject, QString& errorString);
bool _load (const QJsonObject& complexObject, bool forPresets, QString& errorString);
void _setExitCoordinate (const QGeoCoordinate& coordinate);
void _setCameraShots (int cameraShots);
double _triggerDistance (void) const;
......
......@@ -14,37 +14,53 @@ Column {
anchors.right: parent.right
spacing: _margin
visible: !usingPreset || !cameraSpecifiedInPreset
property var cameraCalc
property bool vehicleFlightIsFrontal: true
property string distanceToSurfaceLabel
property int distanceToSurfaceAltitudeMode: QGroundControl.AltitudeModeNone
property string frontalDistanceLabel
property string sideDistanceLabel
property bool usingPreset: false
property bool cameraSpecifiedInPreset: false
property real _margin: ScreenTools.defaultFontPixelWidth / 2
property int _cameraIndex: 1
property string _cameraName: cameraCalc.cameraName.value
property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5
property var _cameraList: [ ]
property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
property var _vehicleCameraList: _vehicle ? _vehicle.staticCameraList : []
property bool _cameraComboFilled: false
readonly property int _gridTypeManual: 0
readonly property int _gridTypeCustomCamera: 1
readonly property int _gridTypeCamera: 2
Component.onCompleted: {
Component.onCompleted: _fillCameraCombo()
on_CameraNameChanged: _updateSelectedCamera()
function _fillCameraCombo() {
_cameraComboFilled = true
_cameraList.push(cameraCalc.manualCameraName)
_cameraList.push(cameraCalc.customCameraName)
for (var i=0; i<_vehicle.staticCameraList.length; i++) {
_cameraList.push(_vehicle.staticCameraList[i].name)
}
gridTypeCombo.model = _cameraList
var knownCameraIndex = gridTypeCombo.find(cameraCalc.cameraName.value)
if (knownCameraIndex !== -1) {
gridTypeCombo.currentIndex = knownCameraIndex
} else {
console.log("Internal error: Known camera not found", cameraCalc.cameraName.value)
gridTypeCombo.currentIndex = _gridTypeCustomCamera
_updateSelectedCamera()
}
function _updateSelectedCamera() {
if (_cameraComboFilled) {
var knownCameraIndex = gridTypeCombo.find(_cameraName)
if (knownCameraIndex !== -1) {
gridTypeCombo.currentIndex = knownCameraIndex
} else {
console.log("Internal error: Known camera not found", _cameraName)
gridTypeCombo.currentIndex = _gridTypeCustomCamera
}
}
}
......@@ -179,6 +195,7 @@ Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: !usingPreset
Item { Layout.fillWidth: true }
QGCLabel {
Layout.preferredWidth: _root._fieldWidth
......@@ -194,6 +211,7 @@ Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: !usingPreset
QGCLabel { text: qsTr("Overlap"); Layout.fillWidth: true }
FactTextField {
Layout.preferredWidth: _root._fieldWidth
......@@ -210,6 +228,7 @@ Column {
text: qsTr("Select one:")
Layout.preferredWidth: parent.width
Layout.columnSpan: 2
visible: !usingPreset
}
GridLayout {
......@@ -218,6 +237,7 @@ Column {
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: !usingPreset
QGCRadioButton {
id: fixedDistanceRadio
......@@ -248,33 +268,6 @@ Column {
Layout.fillWidth: true
}
}
// Calculated values
/*
Taking these out for now since they take up so much space. May come back at a later time.
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel { text: frontalDistanceLabel }
FactTextField {
Layout.fillWidth: true
fact: cameraCalc.adjustedFootprintFrontal
enabled: false
}
QGCLabel { text: sideDistanceLabel }
FactTextField {
Layout.fillWidth: true
fact: cameraCalc.adjustedFootprintSide
enabled: false
}
} // GridLayout
*/
} // Column - Camera spec based ui
// No camera spec ui
......
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.2
import QtPositioning 5.2
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
......@@ -53,7 +54,7 @@ QGCFlickable {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: ScreenTools.defaultFontPixelHeight / 2
spacing: _margin
QGCLabel {
anchors.left: parent.left
......@@ -66,10 +67,10 @@ QGCFlickable {
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: ScreenTools.defaultFontPixelHeight / 2
visible: myGeoFenceController.supported
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: myGeoFenceController.supported
Repeater {
model: myGeoFenceController.params
......@@ -114,9 +115,8 @@ QGCFlickable {
}
QGCButton {
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("Polygon Fence")
Layout.fillWidth: true
text: qsTr("Polygon Fence")
onClicked: {
var rect = Qt.rect(flightMap.centerViewport.x, flightMap.centerViewport.y, flightMap.centerViewport.width, flightMap.centerViewport.height)
......@@ -127,9 +127,8 @@ QGCFlickable {
}
QGCButton {
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("Circular Fence")
Layout.fillWidth: true
text: qsTr("Circular Fence")
onClicked: {
var rect = Qt.rect(flightMap.centerViewport.x, flightMap.centerViewport.y, flightMap.centerViewport.width, flightMap.centerViewport.height)
......@@ -150,11 +149,10 @@ QGCFlickable {
}
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columns: 3
flow: GridLayout.TopToBottom
visible: polygonSection.checked && myGeoFenceController.polygons.count > 0
Layout.fillWidth: true
columns: 3
flow: GridLayout.TopToBottom
visible: polygonSection.checked && myGeoFenceController.polygons.count > 0
QGCLabel {
text: qsTr("Inclusion")
......@@ -224,11 +222,11 @@ QGCFlickable {
}
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columns: 4
flow: GridLayout.TopToBottom
visible: polygonSection.checked && myGeoFenceController.circles.count > 0
anchors.left: parent.left
anchors.right: parent.right
columns: 4
flow: GridLayout.TopToBottom
visible: polygonSection.checked && myGeoFenceController.circles.count > 0
QGCLabel {
text: qsTr("Inclusion")
......@@ -302,6 +300,46 @@ QGCFlickable {
}
}
} // GridLayout
SectionHeader {
id: breachReturnSection
text: qsTr("Breach Return Point")
}
QGCButton {
text: qsTr("Add Breach Return Point")
visible: breachReturnSection.visible && !myGeoFenceController.breachReturnPoint.isValid
anchors.left: parent.left
anchors.right: parent.right
onClicked: myGeoFenceController.breachReturnPoint = flightMap.center
}
QGCButton {
text: qsTr("Remove Breach Return Point")
visible: breachReturnSection.visible && myGeoFenceController.breachReturnPoint.isValid
anchors.left: parent.left
anchors.right: parent.right
onClicked: myGeoFenceController.breachReturnPoint = QtPositioning.coordinate()
}
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: breachReturnSection.visible && myGeoFenceController.breachReturnPoint.isValid
QGCLabel {
text: qsTr("Altitude")
}
AltitudeFactTextField {
fact: myGeoFenceController.breachReturnAltitude
altitudeMode: QGroundControl.AltitudeModeRelative
}
}
}
}
}
......
......@@ -28,8 +28,8 @@ Item {
property bool planView: false ///< true: visuals showing in plan view
property var homePosition
//property var _breachReturnPointComponent
//property var _mouseAreaComponent
property var _breachReturnPointComponent
property var _breachReturnDragComponent
property var _paramCircleFenceComponent
property var _polygons: myGeoFenceController.polygons
property var _circles: myGeoFenceController.circles
......@@ -75,30 +75,19 @@ Item {
}
Component.onCompleted: {
//_breachReturnPointComponent = breachReturnPointComponent.createObject(map)
//map.addMapItem(_breachReturnPointComponent)
//_mouseAreaComponent = mouseAreaComponent.createObject(map)
_breachReturnPointComponent = breachReturnPointComponent.createObject(map)
map.addMapItem(_breachReturnPointComponent)
_breachReturnDragComponent = breachReturnDragComponent.createObject(map, { "itemIndicator": _breachReturnPointComponent })
_paramCircleFenceComponent = paramCircleFenceComponent.createObject(map)
map.addMapItem(_paramCircleFenceComponent)
}
Component.onDestruction: {
//_breachReturnPointComponent.destroy()
//_mouseAreaComponent.destroy()
_breachReturnPointComponent.destroy()
_breachReturnDragComponent.destroy()
_paramCircleFenceComponent.destroy()
}
// Mouse area to capture breach return point coordinate
Component {
id: mouseAreaComponent
MouseArea {
anchors.fill: map
visible: interactive
onClicked: myGeoFenceController.breachReturnPoint = map.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
}
}
Instantiator {
model: _polygons
......@@ -144,6 +133,19 @@ Item {
}
}
Component {
id: breachReturnDragComponent
MissionItemIndicatorDrag {
mapControl: map
itemCoordinate: myGeoFenceController.breachReturnPoint
//visible: itemCoordinate.isValid
onItemCoordinateChanged: myGeoFenceController.breachReturnPoint = itemCoordinate
}
}
// Breach return point
Component {
id: breachReturnPointComponent
......@@ -155,7 +157,8 @@ Item {
coordinate: myGeoFenceController.breachReturnPoint
sourceItem: MissionItemIndexLabel {
label: "B"
label: qsTr("B", "Breach Return Point item indicator")
checked: true
}
}
}
......
This diff is collapsed.
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
......@@ -29,6 +28,8 @@ Rectangle {
property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5
property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
property real _cameraMinTriggerInterval: missionItem.cameraCalc.minTriggerInterval.rawValue
property string _currentPreset: missionItem.currentPreset
property bool _usingPreset: _currentPreset !== ""
function polygonCaptureStarted() {
missionItem.clearPolygon()
......@@ -66,6 +67,97 @@ Rectangle {
visible: missionItem.cameraShots > 0 && _cameraMinTriggerInterval !== 0 && _cameraMinTriggerInterval > missionItem.timeBetweenShots
}
QGCLabel {
text: qsTr("Presets")
}
QGCComboBox {
id: presetCombo
anchors.left: parent.left
anchors.right: parent.right
model: _presetList
property var _presetList: []
readonly property int _indexCustom: 0
readonly property int _indexCreate: 1
readonly property int _indexDelete: 2
readonly property int _indexLabel: 3
readonly property int _indexFirstPreset: 4
Component.onCompleted: _updateList()
onActivated: {
if (index == _indexCustom) {
missionItem.clearCurrentPreset()
} else if (index == _indexCreate) {
rootQgcView.showDialog(savePresetDialog, qsTr("Save Preset"), rootQgcView.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
} else if (index == _indexDelete) {
if (missionItem.builtInPreset) {
rootQgcView.showMessage(qsTr("Delete Preset"), qsTr("This preset cannot be deleted."), StandardButton.Ok)
} else {
missionItem.deleteCurrentPreset()
}
} else if (index >= _indexFirstPreset) {
missionItem.loadPreset(textAt(index))
} else {
_selectCurrentPreset()
}
}
Connections {
target: missionItem
onPresetNamesChanged: presetCombo._updateList()
onCurrentPresetChanged: presetCombo._selectCurrentPreset()
}
// There is some major strangeness going on with programatically changing the index of a combo box in this scenario.
// If you just set currentIndex directly it will just change back 1o -1 magically. Has something to do with resetting
// model on the fly I think. But not sure. To work around I delay the currentIndex changes to let things unwind.
Timer {
id: delayedIndexChangeTimer
interval: 10
property int newIndex
onTriggered: presetCombo.currentIndex = newIndex
}
function delayedIndexChange(index) {
delayedIndexChangeTimer.newIndex = index
delayedIndexChangeTimer.start()
}
function _updateList() {
_presetList = []
_presetList.push(qsTr("Custom (specify all settings)"))
_presetList.push(qsTr("Save Settings As Preset"))
_presetList.push(qsTr("Delete Current Preset"))
if (missionItem.presetNames.length !== 0) {
_presetList.push(qsTr("Presets:"))
}
for (var i=0; i<missionItem.presetNames.length; i++) {
_presetList.push(missionItem.presetNames[i])
}
model = _presetList
_selectCurrentPreset()
}
function _selectCurrentPreset() {
if (_usingPreset) {
var newIndex = find(_currentPreset)
if (newIndex !== -1) {
delayedIndexChange(newIndex)
return
}
}
delayedIndexChange(presetCombo._indexCustom)
}
}
CameraCalc {
cameraCalc: missionItem.cameraCalc
vehicleFlightIsFrontal: true
......@@ -73,6 +165,8 @@ Rectangle {
distanceToSurfaceAltitudeMode: missionItem.followTerrain ? QGroundControl.AltitudeModeAboveTerrain : QGroundControl.AltitudeModeRelative
frontalDistanceLabel: qsTr("Trigger Dist")
sideDistanceLabel: qsTr("Spacing")
usingPreset: _usingPreset
cameraSpecifiedInPreset: missionItem.cameraInPreset
}
SectionHeader {
......@@ -109,23 +203,27 @@ Rectangle {
updateValueWhileDragging: true
}
QGCLabel { text: qsTr("Turnaround dist") }
QGCLabel {
text: qsTr("Turnaround dist")
visible: !_usingPreset
}
FactTextField {
fact: missionItem.turnAroundDistance
Layout.fillWidth: true
visible: !_usingPreset
}
}
QGCButton {
text: qsTr("Rotate Entry Point")
onClicked: missionItem.rotateEntryPoint();
}
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: transectsHeader.checked
QGCButton {
text: qsTr("Rotate Entry Point")
onClicked: missionItem.rotateEntryPoint();
}
visible: transectsHeader.checked && !_usingPreset
/*
Temporarily removed due to bug https://github.com/mavlink/qgroundcontrol/issues/7005
......@@ -187,13 +285,15 @@ Rectangle {
id: terrainHeader
text: qsTr("Terrain")
checked: missionItem.followTerrain
visible: !_usingPreset
}
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: terrainHeader.checked
visible: terrainHeader.checked && !_usingPreset
QGCCheckBox {
id: followsTerrainCheckBox
......@@ -236,4 +336,45 @@ Rectangle {
TransectStyleComplexItemStats { }
} // Column
Component {
id: savePresetDialog
QGCViewDialog {
function accept() {
if (presetNameField.text != "") {
missionItem.savePreset(presetNameField.text)
hideDialog()
}
}
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: ScreenTools.defaultFontPixelHeight
QGCLabel {
Layout.fillWidth: true
text: qsTr("Save the current settings as a named preset.")
wrapMode: Text.WordWrap
}
QGCLabel {
text: qsTr("Preset Name")
}
QGCTextField {
id: presetNameField
Layout.fillWidth: true
text: _currentPreset
}
QGCCheckBox {
text: qsTr("Save Camera In Preset")
checked: missionItem.cameraInPreset
onClicked: missionItem.cameraInPreset = checked
}
}
}
}
} // Rectangle
......@@ -2975,13 +2975,13 @@ QString Vehicle::gotoFlightMode() const
return _firmwarePlugin->gotoFlightMode();
}
void Vehicle::guidedModeRTL(void)
void Vehicle::guidedModeRTL(bool smartRTL)
{
if (!guidedModeSupported()) {
qgcApp()->showMessage(guided_mode_not_supported_by_vehicle);
return;
}
_firmwarePlugin->guidedModeRTL(this);
_firmwarePlugin->guidedModeRTL(this, smartRTL);
}
void Vehicle::guidedModeLand(void)
......@@ -3602,6 +3602,16 @@ QString Vehicle::rtlFlightMode(void) const
return _firmwarePlugin->rtlFlightMode();
}
QString Vehicle::smartRTLFlightMode(void) const
{
return _firmwarePlugin->smartRTLFlightMode();
}
bool Vehicle::supportsSmartRTL(void) const
{
return _firmwarePlugin->supportsSmartRTL();
}
QString Vehicle::landFlightMode(void) const
{
return _firmwarePlugin->landFlightMode();
......
......@@ -600,6 +600,8 @@ public:
Q_PROPERTY(QString missionFlightMode READ missionFlightMode CONSTANT)
Q_PROPERTY(QString pauseFlightMode READ pauseFlightMode CONSTANT)
Q_PROPERTY(QString rtlFlightMode READ rtlFlightMode CONSTANT)
Q_PROPERTY(QString smartRTLFlightMode READ smartRTLFlightMode CONSTANT)
Q_PROPERTY(bool supportsSmartRTL READ supportsSmartRTL CONSTANT)
Q_PROPERTY(QString landFlightMode READ landFlightMode CONSTANT)
Q_PROPERTY(QString takeControlFlightMode READ takeControlFlightMode CONSTANT)
Q_PROPERTY(QString firmwareTypeString READ firmwareTypeString NOTIFY firmwareTypeChanged)
......@@ -698,7 +700,7 @@ public:
Q_INVOKABLE void disconnectInactiveVehicle(void);
/// Command vehicle to return to launch
Q_INVOKABLE void guidedModeRTL(void);
Q_INVOKABLE void guidedModeRTL(bool smartRTL);
/// Command vehicle to land at current location
Q_INVOKABLE void guidedModeLand(void);
......@@ -919,6 +921,8 @@ public:
QString missionFlightMode () const;
QString pauseFlightMode () const;
QString rtlFlightMode () const;
QString smartRTLFlightMode () const;
bool supportsSmartRTL () const;
QString landFlightMode () const;
QString takeControlFlightMode () const;
double defaultCruiseSpeed () const { return _defaultCruiseSpeed; }
......
......@@ -145,6 +145,9 @@ public:
bool connect(void);
bool disconnect(void);
/// Don't even think of calling this method!
QSerialPort* _hackAccessToPort(void) { return _port; }
private slots:
/**
* @brief Write a number of bytes to the interface.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment