Commit 1dffdbf1 authored by DonLakeFlyer's avatar DonLakeFlyer

Waypoint now supports camera section

parent 2f1e631c
......@@ -449,6 +449,7 @@ HEADERS += \
src/JsonHelper.h \
src/LogCompressor.h \
src/MG.h \
src/MissionManager/CameraSection.h \
src/MissionManager/ComplexMissionItem.h \
src/MissionManager/FixedWingLandingComplexItem.h \
src/MissionManager/GeoFenceController.h \
......@@ -626,6 +627,7 @@ SOURCES += \
src/Joystick/JoystickManager.cc \
src/JsonHelper.cc \
src/LogCompressor.cc \
src/MissionManager/CameraSection.cc \
src/MissionManager/ComplexMissionItem.cc \
src/MissionManager/FixedWingLandingComplexItem.cc \
src/MissionManager/GeoFenceController.cc \
......
......@@ -49,6 +49,7 @@
<file alias="PX4FlowSensor.qml">src/VehicleSetup/PX4FlowSensor.qml</file>
<file alias="QGroundControl/Controls/AnalyzePage.qml">src/AnalyzeView/AnalyzePage.qml</file>
<file alias="QGroundControl/Controls/AppMessages.qml">src/QmlControls/AppMessages.qml</file>
<file alias="QGroundControl/Controls/CameraSection.qml">src/MissionEditor/CameraSection.qml</file>
<file alias="QGroundControl/Controls/ClickableColor.qml">src/QmlControls/ClickableColor.qml</file>
<file alias="QGroundControl/Controls/DropButton.qml">src/QmlControls/DropButton.qml</file>
<file alias="QGroundControl/Controls/ExclusiveGroupItem.qml">src/QmlControls/ExclusiveGroupItem.qml</file>
......@@ -192,6 +193,7 @@
<file alias="RallyPoint.FactMetaData.json">src/MissionManager/RallyPoint.FactMetaData.json</file>
<file alias="FWLandingPattern.FactMetaData.json">src/MissionManager/FWLandingPattern.FactMetaData.json</file>
<file alias="USBBoardInfo.json">src/comm/USBBoardInfo.json</file>
<file alias="CameraSection.FactMetaData.json">src/MissionManager/CameraSection.FactMetaData.json</file>
<file alias="MissionSettings.FactMetaData.json">src/MissionManager/MissionSettings.FactMetaData.json</file>
<file alias="Vehicle/VehicleFact.json">src/Vehicle/VehicleFact.json</file>
<file alias="Vehicle/BatteryFact.json">src/Vehicle/BatteryFact.json</file>
......
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.2
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
// Camera section for mission item editors
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
property alias exclusiveGroup: cameraSectionHeader.exclusiveGroup
property alias showSpacer: cameraSectionHeader.showSpacer
property alias checked: cameraSectionHeader.checked
property var _camera: missionItem.cameraSection
property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 16
property real _margin: ScreenTools.defaultFontPixelWidth / 2
SectionHeader {
id: cameraSectionHeader
text: qsTr("Camera")
checked: false
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: cameraSectionHeader.checked
FactComboBox {
id: cameraActionCombo
anchors.left: parent.left
anchors.right: parent.right
fact: _camera.cameraAction
indexModel: false
}
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: ScreenTools.defaultFontPixelWidth
visible: cameraActionCombo.currentIndex == 1
QGCLabel {
text: qsTr("Time")
Layout.fillWidth: true
}
FactTextField {
fact: _camera.cameraPhotoIntervalTime
Layout.preferredWidth: _fieldWidth
}
}
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: ScreenTools.defaultFontPixelWidth
visible: cameraActionCombo.currentIndex == 2
QGCLabel {
text: qsTr("Distance")
Layout.fillWidth: true
}
FactTextField {
fact: _camera.cameraPhotoIntervalDistance
Layout.preferredWidth: _fieldWidth
}
}
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: ScreenTools.defaultFontPixelWidth / 2
rowSpacing: 0
columns: 3
Item { width: 1; height: 1 }
QGCLabel { text: qsTr("Pitch") }
QGCLabel { text: qsTr("Yaw") }
QGCCheckBox {
id: gimbalCheckBox
text: qsTr("Gimbal")
checked: _camera.specifyGimbal
onClicked: _camera.specifyGimbal = checked
Layout.fillWidth: true
}
FactTextField {
fact: _camera.gimbalPitch
implicitWidth: ScreenTools.defaultFontPixelWidth * 9
enabled: gimbalCheckBox.checked
}
FactTextField {
fact: _camera.gimbalYaw
implicitWidth: ScreenTools.defaultFontPixelWidth * 9
enabled: gimbalCheckBox.checked
}
}
}
}
......@@ -579,7 +579,7 @@ QGCView {
spacing: _margin / 2
orientation: ListView.Vertical
model: missionController.visualItems
cacheBuffer: height * 2
cacheBuffer: Math.max(height * 2, 0)
clip: true
currentIndex: _currentMissionIndex
highlightMoveDuration: 250
......
......@@ -19,12 +19,6 @@ Rectangle {
visible: missionItem.isCurrentItem
radius: _radius
ExclusiveGroup {
id: sectionHeaderExclusiverGroup
}
property ExclusiveGroup sectionHeaderGroup: ScreenTools.isShortScreen ? sectionHeaderExclusiverGroup : null
Loader {
id: deferedload
active: valuesRect.visible
......@@ -64,7 +58,6 @@ Rectangle {
id: plannedHomePositionSection
text: qsTr("Planned Home Position")
showSpacer: false
exclusiveGroup: sectionHeaderGroup
}
Column {
......@@ -125,7 +118,6 @@ Rectangle {
text: qsTr("Vehicle Info")
visible: _multipleFirmware && _showOfflineEditingCombos
checked: false
exclusiveGroup: sectionHeaderGroup
}
GridLayout {
......@@ -183,7 +175,6 @@ Rectangle {
id: missionDefaultsSectionHeader
text: qsTr("Mission Defaults")
checked: false
exclusiveGroup: sectionHeaderGroup
}
Column {
......@@ -233,89 +224,8 @@ Rectangle {
*/
}
SectionHeader {
id: cameraSectionHeader
text: qsTr("Camera")
checked: false
exclusiveGroup: sectionHeaderGroup
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: cameraSectionHeader.checked
FactComboBox {
id: cameraActionCombo
anchors.left: parent.left
anchors.right: parent.right
fact: missionItem.cameraAction
indexModel: false
}
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: ScreenTools.defaultFontPixelWidth
visible: cameraActionCombo.currentIndex == 1
QGCLabel {
text: qsTr("Time")
Layout.fillWidth: true
}
FactTextField {
fact: missionItem.cameraPhotoIntervalTime
Layout.preferredWidth: _fieldWidth
}
}
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: ScreenTools.defaultFontPixelWidth
visible: cameraActionCombo.currentIndex == 2
QGCLabel {
text: qsTr("Distance")
Layout.fillWidth: true
}
FactTextField {
fact: missionItem.cameraPhotoIntervalDistance
Layout.preferredWidth: _fieldWidth
}
}
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: 0
rowSpacing: 0
columns: 3
Item { width: 1; height: 1 }
QGCLabel { text: qsTr("Pitch") }
QGCLabel { text: qsTr("Yaw") }
QGCCheckBox {
id: gimbalCheckBox
text: qsTr("Gimbal")
checked: missionItem.specifyGimbal
onClicked: missionItem.specifyGimbal = checked
Layout.fillWidth: true
}
FactTextField {
fact: missionItem.gimbalPitch
implicitWidth: ScreenTools.defaultFontPixelWidth * 9
enabled: gimbalCheckBox.checked
}
FactTextField {
fact: missionItem.gimbalYaw
implicitWidth: ScreenTools.defaultFontPixelWidth * 9
enabled: gimbalCheckBox.checked
}
}
CameraSection {
checked: missionItem.cameraSection.settingsSpecified
}
QGCLabel {
......
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.2
import QtGraphicalEffects 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
......@@ -41,10 +42,14 @@ QGCMouseArea {
id: label
Layout.fillWidth: true
Image {
QGCColoredImage {
id: image
width: label.height / 2
height: width
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
source: "/qmlimages/arrow-down.png"
color: qgcPal.text
visible: !_root.checked
}
}
......
......@@ -27,6 +27,7 @@ Rectangle {
anchors.left: valuesRect.left
anchors.right: valuesRect.right
anchors.top: valuesRect.top
sourceComponent: Component {
Item {
id: valuesItem
......@@ -114,6 +115,11 @@ Rectangle {
fact: object
}
}
CameraSection {
checked: missionItem.cameraSection.settingsSpecified
visible: missionItem.cameraSection.available
}
} // Column
} // Item
} // Component
......
[
{
"name": "CameraAction",
"shortDescription": "Specify whether the camera should take photos or video",
"type": "uint32",
"enumStrings": "Continue current action,Take photos (time),Take photos (distance),Take video",
"enumValues": "0,1,2,3",
"defaultValue": 0
},
{
"name": "CameraPhotoIntervalDistance",
"shortDescription": "Specify the distance between each photo",
"type": "double",
"units": "m",
"min": 0,
"decimalPlaces": 1,
"defaultValue": 1
},
{
"name": "CameraPhotoIntervalTime",
"shortDescription": "Specify the time between each photo",
"type": "uint32",
"units": "secs",
"min": 1,
"decimalPlaces": 0,
"defaultValue": 10
},
{
"name": "GimbalPitch",
"shortDescription": "Gimbal pitch rotation.",
"type": "double",
"units": "deg",
"min": 0.0,
"max": 360.0,
"decimalPlaces": 0,
"defaultValue": 0
},
{
"name": "GimbalYaw",
"shortDescription": "Gimbal yaw rotation.",
"type": "double",
"units": "deg",
"min": 0.0,
"max": 360.0,
"decimalPlaces": 0,
"defaultValue": 0
}
]
/****************************************************************************
*
* (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.
*
****************************************************************************/
#include "CameraSection.h"
#include "SimpleMissionItem.h"
QGC_LOGGING_CATEGORY(CameraSectionLog, "CameraSectionLog")
const char* CameraSection::_gimbalPitchName = "GimbalPitch";
const char* CameraSection::_gimbalYawName = "GimbalYaw";
const char* CameraSection::_cameraActionName = "CameraAction";
const char* CameraSection::_cameraPhotoIntervalDistanceName = "CameraPhotoIntervalDistance";
const char* CameraSection::_cameraPhotoIntervalTimeName = "CameraPhotoIntervalTime";
QMap<QString, FactMetaData*> CameraSection::_metaDataMap;
CameraSection::CameraSection(QObject* parent)
: QObject(parent)
, _available(false)
, _settingsSpecified(false)
, _specifyGimbal(false)
, _gimbalYawFact (0, _gimbalYawName, FactMetaData::valueTypeDouble)
, _gimbalPitchFact (0, _gimbalPitchName, FactMetaData::valueTypeDouble)
, _cameraActionFact (0, _cameraActionName, FactMetaData::valueTypeDouble)
, _cameraPhotoIntervalDistanceFact (0, _cameraPhotoIntervalDistanceName, FactMetaData::valueTypeDouble)
, _cameraPhotoIntervalTimeFact (0, _cameraPhotoIntervalTimeName, FactMetaData::valueTypeUint32)
, _dirty(false)
{
if (_metaDataMap.isEmpty()) {
_metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraSection.FactMetaData.json"), NULL /* metaDataParent */);
}
_gimbalPitchFact.setMetaData (_metaDataMap[_gimbalPitchName]);
_gimbalYawFact.setMetaData (_metaDataMap[_gimbalYawName]);
_cameraActionFact.setMetaData (_metaDataMap[_cameraActionName]);
_cameraPhotoIntervalDistanceFact.setMetaData (_metaDataMap[_cameraPhotoIntervalDistanceName]);
_cameraPhotoIntervalTimeFact.setMetaData (_metaDataMap[_cameraPhotoIntervalTimeName]);
_gimbalPitchFact.setRawValue (_gimbalPitchFact.rawDefaultValue());
_gimbalYawFact.setRawValue (_gimbalYawFact.rawDefaultValue());
_cameraActionFact.setRawValue (_cameraActionFact.rawDefaultValue());
_cameraPhotoIntervalDistanceFact.setRawValue (_cameraPhotoIntervalDistanceFact.rawDefaultValue());
_cameraPhotoIntervalTimeFact.setRawValue (_cameraPhotoIntervalTimeFact.rawDefaultValue());
connect(this, &CameraSection::specifyGimbalChanged, this, &CameraSection::_setDirtyAndUpdateMissionItemCount);
connect(&_cameraActionFact, &Fact::valueChanged, this, &CameraSection::_setDirtyAndUpdateMissionItemCount);
connect(&_gimbalPitchFact, &Fact::valueChanged, this, &CameraSection::_setDirty);
connect(&_gimbalYawFact, &Fact::valueChanged, this, &CameraSection::_setDirty);
connect(&_cameraPhotoIntervalDistanceFact, &Fact::valueChanged, this, &CameraSection::_setDirty);
connect(&_cameraPhotoIntervalTimeFact, &Fact::valueChanged, this, &CameraSection::_setDirty);
}
void CameraSection::setSpecifyGimbal(bool specifyGimbal)
{
if (specifyGimbal != _specifyGimbal) {
_specifyGimbal = specifyGimbal;
emit specifyGimbalChanged(specifyGimbal);
}
}
int CameraSection::missionItemCount(void) const
{
int itemCount = 0;
if (_specifyGimbal) {
itemCount++;
}
if (_cameraActionFact.rawValue().toInt() != CameraActionNone) {
itemCount++;
}
return itemCount;
}
void CameraSection::setDirty(bool dirty)
{
if (_dirty != dirty) {
_dirty = dirty;
emit dirtyChanged(_dirty);
}
}
void CameraSection::appendMissionItems(QList<MissionItem*>& items, QObject* missionItemParent, int nextSequenceNumber)
{
// IMPORTANT NOTE: If anything changes here you must also change CameraSection::scanForMissionSettings
if (_specifyGimbal) {
MissionItem* item = new MissionItem(nextSequenceNumber++,
MAV_CMD_DO_MOUNT_CONTROL,
MAV_FRAME_MISSION,
_gimbalPitchFact.rawValue().toDouble(),
0, // Gimbal roll
_gimbalYawFact.rawValue().toDouble(),
0, 0, 0, // param 4-6 not used
MAV_MOUNT_MODE_MAVLINK_TARGETING,
true, // autoContinue
false, // isCurrentItem
missionItemParent);
items.append(item);
}
if (_cameraActionFact.rawValue().toInt() != CameraActionNone) {
MissionItem* item = NULL;
switch (_cameraActionFact.rawValue().toInt()) {
case TakePhotosIntervalTime:
item = new MissionItem(nextSequenceNumber++,
MAV_CMD_IMAGE_START_CAPTURE,
MAV_FRAME_MISSION,
_cameraPhotoIntervalTimeFact.rawValue().toInt(), // Interval
0, // Unlimited photo count
-1, // Max resolution
0, 0, // param 4-5 not used
0, // Camera ID
0, // param 7 not used
true, // autoContinue
false, // isCurrentItem
missionItemParent);
break;
case TakePhotoIntervalDistance:
item = new MissionItem(nextSequenceNumber++,
MAV_CMD_DO_SET_CAM_TRIGG_DIST,
MAV_FRAME_MISSION,
_cameraPhotoIntervalDistanceFact.rawValue().toDouble(), // Trigger distance
0, 0, 0, 0, 0, 0, // param 2-7 not used
true, // autoContinue
false, // isCurrentItem
missionItemParent);
break;
case TakeVideo:
item = new MissionItem(nextSequenceNumber++,
MAV_CMD_VIDEO_START_CAPTURE,
MAV_FRAME_MISSION,
0, // Camera ID
-1, // Max fps
-1, // Max resolution
0, 0, 0, 0, // param 5-7 not used
true, // autoContinue
false, // isCurrentItem
missionItemParent);
break;
}
if (item) {
items.append(item);
}
}
}
bool CameraSection::scanForCameraSection(QmlObjectListModel* visualItems, int scanIndex)
{
bool foundGimbal = false;
bool foundCameraAction = false;
bool stopLooking = false;
qCDebug(CameraSectionLog) << "CameraSection::scanForCameraSection" << visualItems->count() << scanIndex;
// Scan through the initial mission items for possible mission settings
while (!stopLooking && visualItems->count() > scanIndex) {
SimpleMissionItem* item = visualItems->value<SimpleMissionItem*>(scanIndex);
if (!item) {
// We hit a complex item there can be no more possible mission settings
return foundGimbal || foundCameraAction;
}
MissionItem& missionItem = item->missionItem();
qCDebug(CameraSectionLog) << item->command() << missionItem.param1() << missionItem.param2() << missionItem.param3() << missionItem.param4() << missionItem.param5() << missionItem.param6() << missionItem.param7() ;
// See CameraSection::appendMissionItems for specs on what compomises a known camera section item
switch ((MAV_CMD)item->command()) {
case MAV_CMD_DO_MOUNT_CONTROL:
if (!foundGimbal && missionItem.param2() == 0 && missionItem.param4() == 0 && missionItem.param5() == 0 && missionItem.param6() == 0 && missionItem.param7() == MAV_MOUNT_MODE_MAVLINK_TARGETING) {
foundGimbal = true;
setSpecifyGimbal(true);
gimbalPitch()->setRawValue(missionItem.param1());
gimbalYaw()->setRawValue(missionItem.param3());
visualItems->removeAt(scanIndex)->deleteLater();
} else {
stopLooking = true;
}
break;
case MAV_CMD_IMAGE_START_CAPTURE:
if (!foundCameraAction && missionItem.param1() != 0 && missionItem.param2() == 0 && missionItem.param3() == -1 && missionItem.param4() == 0 && missionItem.param5() == 0 && missionItem.param6() == 0 && missionItem.param7() == 0) {
foundCameraAction = true;
cameraAction()->setRawValue(TakePhotosIntervalTime);
cameraPhotoIntervalTime()->setRawValue(missionItem.param1());
visualItems->removeAt(scanIndex)->deleteLater();
} else {
stopLooking = true;
}
break;
case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
if (!foundCameraAction && missionItem.param1() != 0 && missionItem.param2() == 0 && missionItem.param3() == 0 && missionItem.param4() == 0 && missionItem.param5() == 0 && missionItem.param6() == 0 && missionItem.param7() == 0) {
foundCameraAction = true;
cameraAction()->setRawValue(TakePhotoIntervalDistance);
cameraPhotoIntervalDistance()->setRawValue(missionItem.param1());
visualItems->removeAt(scanIndex)->deleteLater();
} else {
stopLooking = true;
}
break;
case MAV_CMD_VIDEO_START_CAPTURE:
if (!foundCameraAction && missionItem.param1() == 0 && missionItem.param2() == -1 && missionItem.param3() == -1 && missionItem.param4() == 0 && missionItem.param5() == 0 && missionItem.param6() == 0 && missionItem.param7() == 0) {
foundCameraAction = true;
cameraAction()->setRawValue(TakeVideo);
visualItems->removeAt(scanIndex)->deleteLater();
} else {
stopLooking = true;
}
break;
default:
stopLooking = true;
break;
}
}
qCDebug(CameraSectionLog) << foundGimbal << foundCameraAction;
_settingsSpecified = foundGimbal || foundCameraAction;
emit settingsSpecifiedChanged(_settingsSpecified);
return foundGimbal || foundCameraAction;
}
void CameraSection::_setDirty(void)
{
setDirty(true);
}
void CameraSection::_setDirtyAndUpdateMissionItemCount(void)
{
emit missionItemCountChanged(missionItemCount());
setDirty(true);
}
void CameraSection::setAvailable(bool available)
{
if (_available != available) {
_available = available;
emit availableChanged(available);
}
}
/****************************************************************************
*
* (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.
*
****************************************************************************/
#ifndef CameraSection_H
#define CameraSection_H
#include "ComplexMissionItem.h"
#include "MissionItem.h"
#include "Fact.h"
Q_DECLARE_LOGGING_CATEGORY(CameraSectionLog)
class CameraSection : public QObject
{
Q_OBJECT
public:
CameraSection(QObject* parent = NULL);
enum CameraAction {
CameraActionNone,
TakePhotosIntervalTime,
TakePhotoIntervalDistance,
TakeVideo
};
Q_ENUMS(CameraAction)
Q_PROPERTY(bool available READ available WRITE setAvailable NOTIFY availableChanged)
Q_PROPERTY(bool settingsSpecified MEMBER _settingsSpecified NOTIFY settingsSpecifiedChanged)
Q_PROPERTY(bool specifyGimbal READ specifyGimbal WRITE setSpecifyGimbal NOTIFY specifyGimbalChanged)
Q_PROPERTY(Fact* gimbalPitch READ gimbalPitch CONSTANT)
Q_PROPERTY(Fact* gimbalYaw READ gimbalYaw CONSTANT)
Q_PROPERTY(Fact* cameraAction READ cameraAction CONSTANT)
Q_PROPERTY(Fact* cameraPhotoIntervalTime READ cameraPhotoIntervalTime CONSTANT)
Q_PROPERTY(Fact* cameraPhotoIntervalDistance READ cameraPhotoIntervalDistance CONSTANT)
bool available (void) const { return _available; }
void setAvailable (bool available);
bool specifyGimbal (void) const { return _specifyGimbal; }
Fact* gimbalYaw (void) { return &_gimbalYawFact; }
Fact* gimbalPitch (void) { return &_gimbalPitchFact; }
Fact* cameraAction (void) { return &_cameraActionFact; }
Fact* cameraPhotoIntervalTime (void) { return &_cameraPhotoIntervalTimeFact; }
Fact* cameraPhotoIntervalDistance (void) { return &_cameraPhotoIntervalDistanceFact; }
/// Scans the loaded items for the section items
/// @param visualItems Item list
/// @param scanIndex Index to start scanning from
/// @return true: camera section found
bool scanForCameraSection(QmlObjectListModel* visualItems, int scanIndex);
/// Appends the mission items associated with this section
/// @param items List to append to
/// @param missionItemParent QObject parent for created MissionItems
/// @param nextSequenceNumber Sequence number for first item
void appendMissionItems(QList<MissionItem*>& items, QObject* missionItemParent, int nextSequenceNumber);
void setSpecifyGimbal (bool specifyGimbal);
bool dirty (void) const { return _dirty; }
void setDirty (bool dirty);
/// Returns the number of mission items represented by this section.
/// Signals: missionItemCountChanged on change
int missionItemCount(void) const;
signals:
void availableChanged (bool available);
void settingsSpecifiedChanged (bool settingsSpecified);
void dirtyChanged (bool dirty);
bool specifyGimbalChanged (bool specifyGimbal);