/*=================================================================== QGroundControl Open Source Ground Control Station (c) 2009, 2010 QGROUNDCONTROL PROJECT This file is part of the QGROUNDCONTROL project QGROUNDCONTROL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QGROUNDCONTROL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with QGROUNDCONTROL. If not, see . ======================================================================*/ #include "MissionCommands.h" #include "FactMetaData.h" #include "Vehicle.h" #include "FirmwarePluginManager.h" #include "QGCApplication.h" #include "QGroundControlQmlGlobal.h" #include #include #include #include #include #include QGC_LOGGING_CATEGORY(MissionCommandsLog, "MissionCommandsLog") const QString MissionCommands::_categoryJsonKey (QStringLiteral("category")); const QString MissionCommands::_decimalPlacesJsonKey (QStringLiteral("decimalPlaces")); const QString MissionCommands::_defaultJsonKey (QStringLiteral("default")); const QString MissionCommands::_descriptionJsonKey (QStringLiteral("description")); const QString MissionCommands::_enumStringsJsonKey (QStringLiteral("enumStrings")); const QString MissionCommands::_enumValuesJsonKey (QStringLiteral("enumValues")); const QString MissionCommands::_friendlyEditJsonKey (QStringLiteral("friendlyEdit")); const QString MissionCommands::_friendlyNameJsonKey (QStringLiteral("friendlyName")); const QString MissionCommands::_idJsonKey (QStringLiteral("id")); const QString MissionCommands::_labelJsonKey (QStringLiteral("label")); const QString MissionCommands::_mavCmdInfoJsonKey (QStringLiteral("mavCmdInfo")); const QString MissionCommands::_param1JsonKey (QStringLiteral("param1")); const QString MissionCommands::_param2JsonKey (QStringLiteral("param2")); const QString MissionCommands::_param3JsonKey (QStringLiteral("param3")); const QString MissionCommands::_param4JsonKey (QStringLiteral("param4")); const QString MissionCommands::_paramJsonKeyFormat (QStringLiteral("param%1")); const QString MissionCommands::_rawNameJsonKey (QStringLiteral("rawName")); const QString MissionCommands::_standaloneCoordinateJsonKey (QStringLiteral("standaloneCoordinate")); const QString MissionCommands::_specifiesCoordinateJsonKey (QStringLiteral("specifiesCoordinate")); const QString MissionCommands::_unitsJsonKey (QStringLiteral("units")); const QString MissionCommands::_versionJsonKey (QStringLiteral("version")); const QString MissionCommands::_degreesConvertUnits (QStringLiteral("degreesConvert")); const QString MissionCommands::_degreesUnits (QStringLiteral("degrees")); MissionCommands::MissionCommands(QGCApplication* app) : QGCTool(app) { } void MissionCommands::setToolbox(QGCToolbox* toolbox) { QGCTool::setToolbox(toolbox); _loadMavCmdInfoJson(); _createFirmwareSpecificLists(); } bool MissionCommands::_validateKeyTypes(QJsonObject& jsonObject, const QStringList& keys, const QList& types) { for (int i=0; i types; keys << _idJsonKey << _rawNameJsonKey << _friendlyNameJsonKey << _descriptionJsonKey << _standaloneCoordinateJsonKey << _specifiesCoordinateJsonKey <<_friendlyEditJsonKey << _param1JsonKey << _param2JsonKey << _param3JsonKey << _param4JsonKey << _categoryJsonKey; types << QJsonValue::Double << QJsonValue::String << QJsonValue::String<< QJsonValue::String << QJsonValue::Bool << QJsonValue::Bool << QJsonValue::Bool << QJsonValue::Object << QJsonValue::Object << QJsonValue::Object << QJsonValue::Object << QJsonValue::String; if (!_validateKeyTypes(jsonObject, keys, types)) { return; } MavCmdInfo* mavCmdInfo = new MavCmdInfo(this); mavCmdInfo->_command = (MAV_CMD) jsonObject.value(_idJsonKey).toInt(); mavCmdInfo->_category = jsonObject.value(_categoryJsonKey).toString("Advanced"); mavCmdInfo->_rawName = jsonObject.value(_rawNameJsonKey).toString(); mavCmdInfo->_friendlyName = jsonObject.value(_friendlyNameJsonKey).toString(QString()); mavCmdInfo->_description = jsonObject.value(_descriptionJsonKey).toString(QString()); mavCmdInfo->_standaloneCoordinate = jsonObject.value(_standaloneCoordinateJsonKey).toBool(false); mavCmdInfo->_specifiesCoordinate = jsonObject.value(_specifiesCoordinateJsonKey).toBool(false); mavCmdInfo->_friendlyEdit = jsonObject.value(_friendlyEditJsonKey).toBool(false); qCDebug(MissionCommandsLog) << "Command" << mavCmdInfo->_command << mavCmdInfo->_category << mavCmdInfo->_rawName << mavCmdInfo->_friendlyName << mavCmdInfo->_description << mavCmdInfo->_standaloneCoordinate << mavCmdInfo->_specifiesCoordinate << mavCmdInfo->_friendlyEdit; if (_mavCmdInfoMap.contains((MAV_CMD)mavCmdInfo->command())) { qWarning() << "Duplicate command" << mavCmdInfo->command(); return; } _mavCmdInfoMap[mavCmdInfo->_command] = mavCmdInfo; // Read params for (int i=1; i<=7; i++) { QString paramKey = QString(_paramJsonKeyFormat).arg(i); if (jsonObject.contains(paramKey)) { QJsonObject paramObject = jsonObject.value(paramKey).toObject(); // Validate key types QStringList keys; QList types; keys << _defaultJsonKey << _decimalPlacesJsonKey << _enumStringsJsonKey << _enumValuesJsonKey << _labelJsonKey << _unitsJsonKey; types << QJsonValue::Double << QJsonValue::Double << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::String; if (!_validateKeyTypes(paramObject, keys, types)) { return; } mavCmdInfo->_friendlyEdit = true; // Assume friendly edit if we have params if (!paramObject.contains(_labelJsonKey)) { qWarning() << "param object missing label key" << mavCmdInfo->rawName() << paramKey; return; } MavCmdParamInfo* paramInfo = new MavCmdParamInfo(this); paramInfo->_label = paramObject.value(_labelJsonKey).toString(); paramInfo->_defaultValue = paramObject.value(_defaultJsonKey).toDouble(0.0); paramInfo->_decimalPlaces = paramObject.value(_decimalPlacesJsonKey).toInt(FactMetaData::defaultDecimalPlaces); paramInfo->_enumStrings = paramObject.value(_enumStringsJsonKey).toString().split(",", QString::SkipEmptyParts); paramInfo->_param = i; paramInfo->_units = paramObject.value(_unitsJsonKey).toString(); QStringList enumValues = paramObject.value(_enumValuesJsonKey).toString().split(",", QString::SkipEmptyParts); foreach (QString enumValue, enumValues) { bool convertOk; double value = enumValue.toDouble(&convertOk); if (!convertOk) { qWarning() << "Bad enumValue" << enumValue; return; } paramInfo->_enumValues << QVariant(value); } if (paramInfo->_enumValues.count() != paramInfo->_enumStrings.count()) { qWarning() << "enum strings/values count mismatch" << paramInfo->_enumStrings.count() << paramInfo->_enumValues.count(); return; } qCDebug(MissionCommandsLog) << "Param" << paramInfo->_label << paramInfo->_defaultValue << paramInfo->_decimalPlaces << paramInfo->_param << paramInfo->_units << paramInfo->_enumStrings << paramInfo->_enumValues; mavCmdInfo->_paramInfoMap[i] = paramInfo; } } // We don't add categories till down here, since friendly edit isn't valid till here if (mavCmdInfo->_command != MAV_CMD_NAV_LAST) { // Don't add fake home postion command to categories if (mavCmdInfo->friendlyEdit()) { // Only friendly edit commands go in category list. We use MAV_AUTOPILOT_GENERIC key to store full list. if (!_categoryToMavCmdInfoListMap.contains(MAV_AUTOPILOT_GENERIC) || !_categoryToMavCmdInfoListMap[MAV_AUTOPILOT_GENERIC].contains(mavCmdInfo->category())) { qCDebug(MissionCommandsLog) << "Adding new category"; _categoryToMavCmdInfoListMap[MAV_AUTOPILOT_GENERIC][mavCmdInfo->category()] = new QmlObjectListModel(this); } _categoryToMavCmdInfoListMap[MAV_AUTOPILOT_GENERIC][mavCmdInfo->category()]->append(mavCmdInfo); } } if (mavCmdInfo->friendlyEdit()) { if (mavCmdInfo->description().isEmpty()) { qWarning() << "Missing description" << mavCmdInfo->rawName(); return; } if (mavCmdInfo->rawName() == mavCmdInfo->friendlyName()) { qWarning() << "Missing friendly name" << mavCmdInfo->rawName() << mavCmdInfo->friendlyName(); return; } } } } MAV_AUTOPILOT MissionCommands::_firmwareTypeFromVehicle(Vehicle* vehicle) const { if (vehicle) { return vehicle->firmwareType(); } else { QSettings settings; // FIXME: Hack duplicated code from QGroundControlQmlGlobal. Had to do this for now since // QGroundControlQmlGlobal is not available from C++ side. return (MAV_AUTOPILOT)settings.value("OfflineEditingFirmwareType", MAV_AUTOPILOT_ARDUPILOTMEGA).toInt(); } } QString MissionCommands::categoryFromCommand(MavlinkQmlSingleton::Qml_MAV_CMD command) const { return _mavCmdInfoMap[(MAV_CMD)command]->category(); } QVariant MissionCommands::getCommandsForCategory(Vehicle* vehicle, const QString& category) const { return QVariant::fromValue(_categoryToMavCmdInfoListMap[_firmwareTypeFromVehicle(vehicle)][category]); } const QStringList MissionCommands::categories(Vehicle* vehicle) const { QStringList list; foreach (QString category, _categoryToMavCmdInfoListMap[_firmwareTypeFromVehicle(vehicle)].keys()) { list << category; } return list; } void MissionCommands::_createFirmwareSpecificLists(void) { QList firmwareList; firmwareList << MAV_AUTOPILOT_PX4 << MAV_AUTOPILOT_ARDUPILOTMEGA; foreach (MAV_AUTOPILOT firmwareType, firmwareList) { FirmwarePlugin* plugin = _toolbox->firmwarePluginManager()->firmwarePluginForAutopilot(firmwareType, MAV_TYPE_QUADROTOR); QList cmdList = plugin->supportedMissionCommands(); foreach (MAV_CMD command, cmdList) { MavCmdInfo* mavCmdInfo = _mavCmdInfoMap[command]; if (mavCmdInfo->friendlyEdit()) { if (!_categoryToMavCmdInfoListMap.contains(firmwareType) || !_categoryToMavCmdInfoListMap[firmwareType].contains(mavCmdInfo->category())) { qCDebug(MissionCommandsLog) << "Adding new category" << firmwareType; _categoryToMavCmdInfoListMap[firmwareType][mavCmdInfo->category()] = new QmlObjectListModel(this); } _categoryToMavCmdInfoListMap[firmwareType][mavCmdInfo->category()]->append(mavCmdInfo); } else { qWarning() << "Attempt to add non friendly edit supported command"; } } } }