APMAirframeComponentController.cc 13.1 KB
Newer Older
1 2
/****************************************************************************
 *
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

10 11 12 13
#include "APMAirframeComponentController.h"
#include "QGCMAVLink.h"
#include "MultiVehicleManager.h"
#include "QGCApplication.h"
Don Gagne's avatar
Don Gagne committed
14
#include "QGCFileDownload.h"
15
#include "ParameterManager.h"
16 17
#include "ArduCopterFirmwarePlugin.h"
#include "ArduRoverFirmwarePlugin.h"
18 19 20

#include <QVariant>
#include <QQmlProperty>
Don Gagne's avatar
Don Gagne committed
21
#include <QStandardPaths>
Don Gagne's avatar
Don Gagne committed
22
#include <QDir>
Don Gagne's avatar
Don Gagne committed
23 24
#include <QJsonParseError>
#include <QJsonObject>
25

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
// These should match the ArduCopter FRAME_CLASS parameter enum meta data
#define FRAME_CLASS_UNDEFINED       0
#define FRAME_CLASS_QUAD            1
#define FRAME_CLASS_HEX             2
#define FRAME_CLASS_OCTA            3
#define FRAME_CLASS_OCTAQUAD        4
#define FRAME_CLASS_Y6              5
#define FRAME_CLASS_HELI            6
#define FRAME_CLASS_TRI             7
#define FRAME_CLASS_SINGLECOPTER    8
#define FRAME_CLASS_COAXCOPTER      9
#define FRAME_CLASS_BICOPTER        10
#define FRAME_CLASS_HELI_DUAL       11
#define FRAME_CLASS_DODECAHEXA      12
#define FRAME_CLASS_HELIQUAD        13

// These should match the ArduCopter FRAME_TYPE parameter enum meta data
#define FRAME_TYPE_PLUS         0
#define FRAME_TYPE_X            1
#define FRAME_TYPE_V            2
#define FRAME_TYPE_H            3
#define FRAME_TYPE_V_TAIL       4
#define FRAME_TYPE_A_TAIL       5
#define FRAME_TYPE_Y6B          10
#define FRAME_TYPE_Y6F          11
#define FRAME_TYPE_BETAFLIGHTX  12
#define FRAME_TYPE_DJIX         13
#define FRAME_TYPE_CLOCKWISEX   14

// These should match the Rover FRAME_CLASS parameter enum meta data
#define FRAME_CLASS_ROVER       1
#define FRAME_CLASS_BOAT        2
#define FRAME_CLASS_BALANCEBOT  3

// These should match the Rover FRAME_TYPE parameter enum meta data
#define FRAME_TYPE_UNDEFINED    0
#define FRAME_TYPE_OMNI3        1
#define FRAME_TYPE_OMNIX        2
#define FRAME_TYPE_OMNIPLUS     3

typedef struct {
    int         frameClass;
    int         frameType;
    const char* imageResource;
} FrameToImageInfo_t;

static const FrameToImageInfo_t s_rgFrameToImageCopter[] = {
    { FRAME_CLASS_QUAD,         FRAME_TYPE_X,       "QuadRotorX" },             // Default
    { FRAME_CLASS_QUAD,         FRAME_TYPE_PLUS,    "QuadRotorPlus" },
    { FRAME_CLASS_QUAD,         FRAME_TYPE_V,       "QuadRotorWide" },
    { FRAME_CLASS_QUAD,         FRAME_TYPE_H,       "QuadRotorH" },
    { FRAME_CLASS_QUAD,         FRAME_TYPE_V_TAIL,  "QuadRotorVTail" },
    { FRAME_CLASS_QUAD,         FRAME_TYPE_A_TAIL,  "QuadRotorATail" },

    { FRAME_CLASS_HEX,          FRAME_TYPE_X,       "HexaRotorX" },             // Default
    { FRAME_CLASS_HEX,          FRAME_TYPE_PLUS,    "HexaRotorPlus" },

    { FRAME_CLASS_OCTA,         FRAME_TYPE_X,       "OctoRotorX" },             // Default
    { FRAME_CLASS_OCTA,         FRAME_TYPE_PLUS,    "OctoRotorPlus" },
    { FRAME_CLASS_OCTA,         FRAME_TYPE_V,       "AirframeUnknown" },
    { FRAME_CLASS_OCTA,         FRAME_TYPE_H,       "AirframeUnknown" },

    { FRAME_CLASS_OCTAQUAD,     FRAME_TYPE_X,       "OctoRotorXCoaxial" },      // Default
    { FRAME_CLASS_OCTAQUAD,     FRAME_TYPE_PLUS,    "OctoRotorPlusCoaxial" },
    { FRAME_CLASS_OCTAQUAD,     FRAME_TYPE_V,       "AirframeUnknown" },
    { FRAME_CLASS_OCTAQUAD,     FRAME_TYPE_H,       "AirframeUnknown" },

    { FRAME_CLASS_Y6,           FRAME_TYPE_Y6B,     "Y6B" },                    // Default
    { FRAME_CLASS_Y6,           FRAME_TYPE_Y6F,     "AirframeUnknown" },
    { FRAME_CLASS_Y6,           -1,                 "Y6A" },

    { FRAME_CLASS_DODECAHEXA,   FRAME_TYPE_X,       "AirframeUnknown" },        // Default
    { FRAME_CLASS_DODECAHEXA,   FRAME_TYPE_PLUS,    "AirframeUnknown" },

    { FRAME_CLASS_HELI,         -1,                 "Helicopter" },
    { FRAME_CLASS_TRI,          -1,                 "YPlus" },
};

static const FrameToImageInfo_t s_rgFrameToImageRover[] = {
    { FRAME_CLASS_ROVER,    -1, "Rover" },
    { FRAME_CLASS_BOAT,     -1, "Boat" },
};

/// Returns the image resource for the frameClass, frameType pair
///     @param[in,out] frameType Specified frame type, or -1 to match first item in list (frameType found will be returned)
static QString s_findImageResourceCopter(int frameClass, int& frameType)
{
    for (size_t i=0; i<sizeof(s_rgFrameToImageCopter)/sizeof(s_rgFrameToImageCopter[0]); i++) {
        const FrameToImageInfo_t* pFrameToImageInfo = &s_rgFrameToImageCopter[i];
115

116 117 118 119 120 121
        if ((pFrameToImageInfo->frameClass == frameClass && frameType == -1) ||
                (pFrameToImageInfo->frameClass == frameClass && pFrameToImageInfo->frameType == frameType)) {
            frameType = pFrameToImageInfo->frameType;
            return pFrameToImageInfo->imageResource;
        }
    }
122

123 124 125 126
    return QStringLiteral("AirframeUnknown");
}

static QString s_findImageResourceRover(int frameClass, int frameType)
127
{
128
    Q_UNUSED(frameType);
129

130 131
    for (size_t i=0; i<sizeof(s_rgFrameToImageRover)/sizeof(s_rgFrameToImageRover[0]); i++) {
        const FrameToImageInfo_t* pFrameToImageInfo = &s_rgFrameToImageRover[i];
132

133 134 135
        if (pFrameToImageInfo->frameClass == frameClass) {
            return pFrameToImageInfo->imageResource;
        }
136
    }
137 138

    return QStringLiteral("AirframeUnknown");
139 140
}

141 142 143 144
APMAirframeComponentController::APMAirframeComponentController(void)
    : _frameClassFact   (getParameterFact(FactSystem::defaultComponentId, QStringLiteral("FRAME_CLASS"), false /* reportMissing */))
    , _frameTypeFact    (getParameterFact(FactSystem::defaultComponentId, QStringLiteral("FRAME_TYPE"), false /* reportMissing */))
    , _frameClassModel  (new QmlObjectListModel(this))
145
{
146
    _fillFrameClasses();
147 148
}

149
APMAirframeComponentController::~APMAirframeComponentController()
150
{
151

152 153
}

154
void APMAirframeComponentController::_fillFrameClasses()
155
{
156 157 158 159 160 161 162 163 164 165 166
    FirmwarePlugin* fwPlugin = _vehicle->firmwarePlugin();

    if (qobject_cast<ArduCopterFirmwarePlugin*>(fwPlugin)) {
        QList<int> frameTypeNotSupported;

        frameTypeNotSupported << FRAME_CLASS_HELI
                              << FRAME_CLASS_SINGLECOPTER
                              << FRAME_CLASS_COAXCOPTER
                              << FRAME_CLASS_BICOPTER
                              << FRAME_CLASS_HELI_DUAL
                              << FRAME_CLASS_HELIQUAD;
167

168 169 170
        for (int i=1; i<_frameClassFact->enumStrings().count(); i++) {
            QString frameClassName =    _frameClassFact->enumStrings()[i];
            int     frameClass =        _frameClassFact->enumValues()[i].toInt();
171

172 173 174 175
            if (frameClass == FRAME_CLASS_HELI) {
                // Heli requires it's own firmware variant. You can't switch to Heli from a Copter variant firmware.
                continue;
            }
176

177 178 179 180 181 182 183
            _frameClassModel->append(new APMFrameClass(frameClassName, true /* copter */, frameClass, _frameTypeFact, _frameClassModel));
        }
    } else if (qobject_cast<ArduRoverFirmwarePlugin*>(fwPlugin)) {
        for (int i=1; i<_frameClassFact->enumStrings().count(); i++) {
            QString frameClassName =    _frameClassFact->enumStrings()[i];
            int     frameClass =        _frameClassFact->enumValues()[i].toInt();
            _frameClassModel->append(new APMFrameClass(frameClassName, false /* copter */, frameClass, _frameTypeFact, _frameClassModel));
184 185 186 187
        }
    }
}

Don Gagne's avatar
Don Gagne committed
188 189 190 191 192 193 194 195
void APMAirframeComponentController::_loadParametersFromDownloadFile(const QString& downloadedParamFile)
{
    QFile parametersFile(downloadedParamFile);
    if (!parametersFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "Unable to open downloaded parameter file" << downloadedParamFile << parametersFile.errorString();
        qgcApp()->restoreOverrideCursor();
        return;
    }
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210

    QTextStream reader(&parametersFile);

    while (!reader.atEnd()) {
        QString line = reader.readLine().trimmed();
        if (line.isEmpty() || line.at(0) == QChar('#')) {
            continue;
        }

        QStringList aux = line.split(',');
        if (parameterExists(-1, aux.at(0))) {
            Fact *param = getParameterFact(-1, aux.at(0));
            param->setRawValue(QVariant::fromValue(aux.at(1)));
        }
    }
Don Gagne's avatar
Don Gagne committed
211
    qgcApp()->restoreOverrideCursor();
212
    _vehicle->parameterManager()->refreshAllParameters();
213 214
}

215
void APMAirframeComponentController::loadParameters(const QString& paramFile)
216
{
217
    qgcApp()->setOverrideCursor(Qt::WaitCursor);
218

219
    QString paramFileUrl = QStringLiteral("https://api.github.com/repos/ArduPilot/ardupilot/contents/Tools/Frame_params/%1?ref=master");
220

221 222 223
    QGCFileDownload* downloader = new QGCFileDownload(this);
    connect(downloader, &QGCFileDownload::downloadComplete, this, &APMAirframeComponentController::_githubJsonDownloadComplete);
    downloader->download(paramFileUrl.arg(paramFile));
224 225
}

226
void APMAirframeComponentController::_githubJsonDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg)
227
{
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    if (errorMsg.isEmpty()) {
        QFile jsonFile(localFile);
        if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
            qWarning() << "Unable to open github json file" << localFile << jsonFile.errorString();
            qgcApp()->restoreOverrideCursor();
            return;
        }
        QByteArray bytes = jsonFile.readAll();
        jsonFile.close();

        QJsonParseError jsonParseError;
        QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
        if (jsonParseError.error != QJsonParseError::NoError) {
            qWarning() <<  "Unable to open json document" << localFile << jsonParseError.errorString();
            qgcApp()->restoreOverrideCursor();
            return;
        }
        QJsonObject json = doc.object();
246

247 248 249 250 251 252 253
        QGCFileDownload* downloader = new QGCFileDownload(this);
        connect(downloader, &QGCFileDownload::downloadComplete, this, &APMAirframeComponentController::_paramFileDownloadComplete);
        downloader->download(json[QLatin1String("download_url")].toString());
    } else {
        qgcApp()->showAppMessage(tr("Param file github json download failed: %1").arg(errorMsg));
        qgcApp()->restoreOverrideCursor();
    }
254 255
}

256
void APMAirframeComponentController::_paramFileDownloadComplete(QString /*remoteFile*/, QString localFile, QString errorMsg)
257
{
258 259 260 261 262 263
    if (errorMsg.isEmpty()) {
        _loadParametersFromDownloadFile(localFile);
    } else {
        qgcApp()->showAppMessage(tr("Param file download failed: %1").arg(errorMsg));
        qgcApp()->restoreOverrideCursor();
    }
264 265
}

266 267 268 269 270 271 272 273
APMFrameClass::APMFrameClass(const QString& name, bool copter, int frameClass, Fact* frameTypeFact, QObject* parent)
    : QObject               (parent)
    , _name                 (name)
    , _copter               (copter)
    , _frameClass           (frameClass)
    , _defaultFrameType     (-1)
    , _frameTypeSupported   (false)
    , _frameTypeFact        (frameTypeFact)
274
{
275 276 277 278
    if (frameTypeFact) {
        connect(frameTypeFact, &Fact::rawValueChanged, this, &APMFrameClass::imageResourceChanged);
        connect(frameTypeFact, &Fact::rawValueChanged, this, &APMFrameClass::frameTypeChanged);
    }
279

280 281
    if (copter) {
        QList<int> rgSupportedFrameTypes;
282

283 284
        for (size_t i=0; i<sizeof(s_rgFrameToImageCopter)/sizeof(s_rgFrameToImageCopter[0]); i++) {
            const FrameToImageInfo_t* pFrameToImageInfo = &s_rgFrameToImageCopter[i];
285

286 287 288 289 290 291
            if (pFrameToImageInfo->frameClass == frameClass) {
                if (_defaultFrameType == -1) {
                    // Default frame type/icon is the first item found to match frameClass
                    _defaultFrameType = pFrameToImageInfo->frameType;
                    _imageResourceDefault = QStringLiteral("/qmlimages/Airframe/%1").arg(pFrameToImageInfo->imageResource);
                }
292

293 294 295 296 297 298 299 300 301
                if (pFrameToImageInfo->frameType != -1) {
                    // The list includes the supported frame types for the class
                    rgSupportedFrameTypes.append(pFrameToImageInfo->frameType);
                }
            }
        }
        if (_imageResourceDefault.isEmpty()) {
            _imageResourceDefault = QStringLiteral("/qmlimages/Airframe/AirframeUnknown");
        }
302

303 304 305 306 307 308 309 310 311
        // Filter the enums
        for (const int frameType: rgSupportedFrameTypes) {
            int index = frameTypeFact->enumValues().indexOf(frameType);
            if (index != -1) {
                _frameTypeEnumValues.append(frameType);
                _frameTypeEnumStrings.append(frameTypeFact->enumStrings()[index]);
            }
        }
    }
312

313 314
    // If the frameClass is not in the list then frame type is not supported
    _frameTypeSupported = _defaultFrameType != -1;
315 316
}

317
APMFrameClass::~APMFrameClass()
318
{
Don Gagne's avatar
Don Gagne committed
319

320 321
}

322
QString APMFrameClass::imageResource(void)
323
{
324
    QString imageResource;
Don Gagne's avatar
Don Gagne committed
325

326
    int frameType = _frameTypeFact ? _frameTypeFact->rawValue().toInt() : -1;
Don Gagne's avatar
Don Gagne committed
327

328 329 330 331
    if (_copter) {
        imageResource = s_findImageResourceCopter(_frameClass, frameType);
    } else {
        imageResource = s_findImageResourceRover(_frameClass, frameType);
Don Gagne's avatar
Don Gagne committed
332
    }
333
    return QStringLiteral("/qmlimages/Airframe/%1").arg(imageResource);
Don Gagne's avatar
Don Gagne committed
334
}