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

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);
}