You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
436 lines
22 KiB
436 lines
22 KiB
/**************************************************************************** |
|
* |
|
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> |
|
* |
|
* QGroundControl is licensed according to the terms in the file |
|
* COPYING.md in the root of the source code directory. |
|
* |
|
****************************************************************************/ |
|
|
|
#include "CameraSection.h" |
|
#include "SimpleMissionItem.h" |
|
#include "FirmwarePlugin.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"; |
|
const char* CameraSection::_cameraModeName = "CameraMode"; |
|
|
|
QMap<QString, FactMetaData*> CameraSection::_metaDataMap; |
|
|
|
CameraSection::CameraSection(Vehicle* vehicle, QObject* parent) |
|
: Section(vehicle, parent) |
|
, _available(false) |
|
, _settingsSpecified(false) |
|
, _specifyGimbal(false) |
|
, _specifyCameraMode(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) |
|
, _cameraModeFact (0, _cameraModeName, 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]); |
|
_cameraModeFact.setMetaData (_metaDataMap[_cameraModeName]); |
|
|
|
_gimbalPitchFact.setRawValue (_gimbalPitchFact.rawDefaultValue()); |
|
_gimbalYawFact.setRawValue (_gimbalYawFact.rawDefaultValue()); |
|
_cameraActionFact.setRawValue (_cameraActionFact.rawDefaultValue()); |
|
_cameraPhotoIntervalDistanceFact.setRawValue (_cameraPhotoIntervalDistanceFact.rawDefaultValue()); |
|
_cameraPhotoIntervalTimeFact.setRawValue (_cameraPhotoIntervalTimeFact.rawDefaultValue()); |
|
_cameraModeFact.setRawValue (_cameraModeFact.rawDefaultValue()); |
|
|
|
connect(this, &CameraSection::specifyGimbalChanged, this, &CameraSection::_specifyChanged); |
|
connect(this, &CameraSection::specifyCameraModeChanged, this, &CameraSection::_specifyChanged); |
|
|
|
connect(&_cameraActionFact, &Fact::valueChanged, this, &CameraSection::_cameraActionChanged); |
|
|
|
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); |
|
connect(&_cameraModeFact, &Fact::valueChanged, this, &CameraSection::_setDirty); |
|
connect(this, &CameraSection::specifyGimbalChanged, this, &CameraSection::_setDirty); |
|
connect(this, &CameraSection::specifyCameraModeChanged, this, &CameraSection::_setDirty); |
|
|
|
connect(&_gimbalYawFact, &Fact::valueChanged, this, &CameraSection::_updateSpecifiedGimbalYaw); |
|
} |
|
|
|
void CameraSection::setSpecifyGimbal(bool specifyGimbal) |
|
{ |
|
if (specifyGimbal != _specifyGimbal) { |
|
_specifyGimbal = specifyGimbal; |
|
emit specifyGimbalChanged(specifyGimbal); |
|
} |
|
} |
|
|
|
void CameraSection::setSpecifyCameraMode(bool specifyCameraMode) |
|
{ |
|
if (specifyCameraMode != _specifyCameraMode) { |
|
_specifyCameraMode = specifyCameraMode; |
|
emit specifyCameraModeChanged(specifyCameraMode); |
|
} |
|
} |
|
|
|
int CameraSection::itemCount(void) const |
|
{ |
|
int itemCount = 0; |
|
|
|
if (_specifyGimbal) { |
|
itemCount++; |
|
} |
|
if (_specifyCameraMode) { |
|
itemCount++; |
|
} |
|
if (_cameraActionFact.rawValue().toInt() != CameraActionNone) { |
|
itemCount++; |
|
} |
|
|
|
return itemCount; |
|
} |
|
|
|
void CameraSection::setDirty(bool dirty) |
|
{ |
|
if (_dirty != dirty) { |
|
_dirty = dirty; |
|
emit dirtyChanged(_dirty); |
|
} |
|
} |
|
|
|
void CameraSection::appendSectionItems(QList<MissionItem*>& items, QObject* missionItemParent, int& nextSequenceNumber) |
|
{ |
|
// IMPORTANT NOTE: If anything changes here you must also change CameraSection::scanForSection |
|
|
|
if (_specifyCameraMode) { |
|
MissionItem* item = new MissionItem(nextSequenceNumber++, |
|
MAV_CMD_SET_CAMERA_MODE, |
|
MAV_FRAME_MISSION, |
|
0, // camera id, all cameras |
|
_cameraModeFact.rawValue().toDouble(), |
|
NAN, NAN, NAN, NAN, NAN, // param 3-7 unused |
|
true, // autoContinue |
|
false, // isCurrentItem |
|
missionItemParent); |
|
items.append(item); |
|
} |
|
|
|
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, |
|
0, // Camera ID, all cameras |
|
_cameraPhotoIntervalTimeFact.rawValue().toInt(), // Interval |
|
0, // Unlimited photo count |
|
-1, // Max horizontal resolution |
|
-1, // Max vertical resolution |
|
0, 0, // param 6-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 = 0, all cameras |
|
-1, // Max FPS |
|
-1, // Max horizontal resolution |
|
-1, // Max vertical resolution |
|
0, // Np CAMERA_CAPTURE_STATUS streaming |
|
0, 0, // param 6-7 not used |
|
true, // autoContinue |
|
false, // isCurrentItem |
|
missionItemParent); |
|
break; |
|
|
|
case StopTakingVideo: |
|
item = new MissionItem(nextSequenceNumber++, |
|
MAV_CMD_VIDEO_STOP_CAPTURE, |
|
MAV_FRAME_MISSION, |
|
0, // Camera ID, all cameras |
|
0, 0, 0, 0, 0, 0, // param 2-7 not used |
|
true, // autoContinue |
|
false, // isCurrentItem |
|
missionItemParent); |
|
break; |
|
|
|
case StopTakingPhotos: |
|
item = new MissionItem(nextSequenceNumber++, |
|
MAV_CMD_DO_SET_CAM_TRIGG_DIST, |
|
MAV_FRAME_MISSION, |
|
0, // Trigger distance = 0 means stop |
|
0, 0, 0, 0, 0, 0, // param 2-7 not used |
|
true, // autoContinue |
|
false, // isCurrentItem |
|
missionItemParent); |
|
items.append(item); |
|
item = new MissionItem(nextSequenceNumber++, |
|
MAV_CMD_IMAGE_STOP_CAPTURE, |
|
MAV_FRAME_MISSION, |
|
0, // camera id, all cameras |
|
0, 0, 0, 0, 0, 0, // param 2-7 not used |
|
true, // autoContinue |
|
false, // isCurrentItem |
|
missionItemParent); |
|
break; |
|
|
|
case TakePhoto: |
|
item = new MissionItem(nextSequenceNumber++, |
|
MAV_CMD_IMAGE_START_CAPTURE, |
|
MAV_FRAME_MISSION, |
|
0, // camera id = 0, all cameras |
|
0, // Interval (none) |
|
1, // Take 1 photo |
|
-1, // Max horizontal resolution |
|
-1, // Max vertical resolution |
|
0, 0, // param 6-7 not used |
|
true, // autoContinue |
|
false, // isCurrentItem |
|
missionItemParent); |
|
break; |
|
} |
|
if (item) { |
|
items.append(item); |
|
} |
|
} |
|
} |
|
|
|
bool CameraSection::scanForSection(QmlObjectListModel* visualItems, int scanIndex) |
|
{ |
|
bool foundGimbal = false; |
|
bool foundCameraAction = false; |
|
bool foundCameraMode = false; |
|
bool stopLooking = false; |
|
|
|
qCDebug(CameraSectionLog) << "CameraSection::scanForCameraSection" << visualItems->count() << scanIndex; |
|
|
|
if (!_available || scanIndex >= visualItems->count()) { |
|
return false; |
|
} |
|
|
|
// 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: |
|
// This could possibly be TakePhotosIntervalTime or TakePhoto |
|
if (!foundCameraAction && |
|
// TakePhotosIntervalTime matching |
|
((missionItem.param1() == 0 && missionItem.param2() >= 1 && missionItem.param3() == 0 && missionItem.param4() == -1 && missionItem.param5() == -1 && missionItem.param6() == 0 && missionItem.param7() == 0) || |
|
// TakePhoto matching |
|
(missionItem.param1() == 0 && missionItem.param2() == 0 && missionItem.param3() == 1 && missionItem.param4() == -1 && missionItem.param5() == -1 && missionItem.param6() == 0 && missionItem.param7() == 0))) { |
|
foundCameraAction = true; |
|
if (missionItem.param2() == 0) { |
|
cameraAction()->setRawValue(TakePhoto); |
|
} else { |
|
cameraAction()->setRawValue(TakePhotosIntervalTime); |
|
cameraPhotoIntervalTime()->setRawValue(missionItem.param2()); |
|
} |
|
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) { |
|
// At this point we don't know if we have a stop taking photos pair, or just a distance trigger |
|
|
|
if (missionItem.param1() == 0 && scanIndex < visualItems->count() - 1) { |
|
// Possible stop taking photos pair |
|
SimpleMissionItem* nextItem = visualItems->value<SimpleMissionItem*>(scanIndex + 1); |
|
if (nextItem) { |
|
MissionItem& nextMissionItem = nextItem->missionItem(); |
|
if (nextMissionItem.command() == MAV_CMD_IMAGE_STOP_CAPTURE && nextMissionItem.param1() == 0 && nextMissionItem.param2() == 0 && nextMissionItem.param3() == 0 && nextMissionItem.param4() == 0 && nextMissionItem.param5() == 0 && nextMissionItem.param6() == 0 && nextMissionItem.param7() == 0) { |
|
// We found a stop taking photos pair |
|
foundCameraAction = true; |
|
cameraAction()->setRawValue(StopTakingPhotos); |
|
visualItems->removeAt(scanIndex)->deleteLater(); |
|
visualItems->removeAt(scanIndex)->deleteLater(); |
|
stopLooking = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
// We didn't find a stop taking photos pair, check for trigger distance |
|
if (missionItem.param1() > 0) { |
|
foundCameraAction = true; |
|
cameraAction()->setRawValue(TakePhotoIntervalDistance); |
|
cameraPhotoIntervalDistance()->setRawValue(missionItem.param1()); |
|
visualItems->removeAt(scanIndex)->deleteLater(); |
|
stopLooking = true; |
|
break; |
|
} |
|
} |
|
stopLooking = true; |
|
break; |
|
|
|
case MAV_CMD_VIDEO_START_CAPTURE: |
|
if (!foundCameraAction && missionItem.param1() == 0 && missionItem.param2() == -1 && missionItem.param3() == -1 && missionItem.param4() == -1 && missionItem.param5() == 0 && missionItem.param6() == 0 && missionItem.param7() == 0) { |
|
foundCameraAction = true; |
|
cameraAction()->setRawValue(TakeVideo); |
|
visualItems->removeAt(scanIndex)->deleteLater(); |
|
} else { |
|
stopLooking = true; |
|
} |
|
break; |
|
|
|
case MAV_CMD_VIDEO_STOP_CAPTURE: |
|
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(StopTakingVideo); |
|
visualItems->removeAt(scanIndex)->deleteLater(); |
|
} else { |
|
stopLooking = true; |
|
} |
|
break; |
|
|
|
case MAV_CMD_SET_CAMERA_MODE: |
|
// We specifically don't test param 5/6/7 since we don't have NaN persistence for those fields |
|
if (!foundCameraMode && missionItem.param1() == 0 && (missionItem.param2() == 0 || missionItem.param2() == 1) && qIsNaN(missionItem.param3())) { |
|
foundCameraMode = true; |
|
setSpecifyCameraMode(true); |
|
cameraMode()->setRawValue(missionItem.param2()); |
|
visualItems->removeAt(scanIndex)->deleteLater(); |
|
} else { |
|
stopLooking = true; |
|
} |
|
break; |
|
|
|
default: |
|
stopLooking = true; |
|
break; |
|
} |
|
} |
|
|
|
qCDebug(CameraSectionLog) << foundGimbal << foundCameraAction << foundCameraMode; |
|
|
|
_settingsSpecified = foundGimbal || foundCameraAction || foundCameraMode; |
|
emit settingsSpecifiedChanged(_settingsSpecified); |
|
|
|
return _settingsSpecified; |
|
} |
|
|
|
void CameraSection::_setDirty(void) |
|
{ |
|
setDirty(true); |
|
} |
|
|
|
void CameraSection::_setDirtyAndUpdateItemCount(void) |
|
{ |
|
emit itemCountChanged(itemCount()); |
|
setDirty(true); |
|
} |
|
|
|
void CameraSection::setAvailable(bool available) |
|
{ |
|
if (_available != available) { |
|
_available = available; |
|
emit availableChanged(available); |
|
} |
|
} |
|
|
|
double CameraSection::specifiedGimbalYaw(void) const |
|
{ |
|
return _specifyGimbal ? _gimbalYawFact.rawValue().toDouble() : std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
|
|
void CameraSection::_updateSpecifiedGimbalYaw(void) |
|
{ |
|
emit specifiedGimbalYawChanged(specifiedGimbalYaw()); |
|
} |
|
|
|
void CameraSection::_updateSettingsSpecified(void) |
|
{ |
|
bool newSettingsSpecified = _specifyGimbal || _specifyCameraMode || _cameraActionFact.rawValue().toInt() != CameraActionNone; |
|
if (newSettingsSpecified != _settingsSpecified) { |
|
_settingsSpecified = newSettingsSpecified; |
|
emit settingsSpecifiedChanged(newSettingsSpecified); |
|
} |
|
} |
|
|
|
void CameraSection::_specifyChanged(void) |
|
{ |
|
_setDirtyAndUpdateItemCount(); |
|
_updateSettingsSpecified(); |
|
} |
|
|
|
void CameraSection::_cameraActionChanged(void) |
|
{ |
|
_setDirtyAndUpdateItemCount(); |
|
_updateSettingsSpecified(); |
|
} |
|
|
|
bool CameraSection::cameraModeSupported(void) const |
|
{ |
|
return _vehicle->firmwarePlugin()->supportedMissionCommands().contains(MAV_CMD_SET_CAMERA_MODE); |
|
}
|
|
|