APMAirframeComponentController.cc 11.5 KB
Newer Older
1 2 3 4 5 6 7 8 9
/****************************************************************************
 *
 *   (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.
 *
 ****************************************************************************/

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
// These should match the ArduCopter FRAME_CLASS parameter enum meta data
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#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

42
// These should match the ArduCopter FRAME_TYPE parameter enum meta data
43 44 45 46 47 48 49 50 51 52 53 54
#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

55 56 57 58 59 60 61 62 63 64 65
// 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

66 67 68 69 70 71
typedef struct {
    int         frameClass;
    int         frameType;
    const char* imageResource;
} FrameToImageInfo_t;

72
static const FrameToImageInfo_t s_rgFrameToImageCopter[] = {
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
    { FRAME_CLASS_QUAD,     FRAME_TYPE_PLUS,    "QuadRotorPlus" },
    { FRAME_CLASS_QUAD,     FRAME_TYPE_X,       "QuadRotorX" },
    { 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_PLUS,    "HexaRotorPlus" },
    { FRAME_CLASS_HEX,      FRAME_TYPE_X,       "HexaRotorX" },
    { FRAME_CLASS_OCTA,     FRAME_TYPE_PLUS,    "OctoRotorPlus" },
    { FRAME_CLASS_OCTA,     FRAME_TYPE_X,       "OctoRotorX" },
    { FRAME_CLASS_OCTAQUAD, FRAME_TYPE_PLUS,    "OctoRotorPlusCoaxial" },
    { FRAME_CLASS_OCTAQUAD, FRAME_TYPE_X,       "OctoRotorXCoaxial" },
    { FRAME_CLASS_Y6,       FRAME_TYPE_Y6B,     "Y6B" },
    { FRAME_CLASS_Y6,       FRAME_TYPE_Y6F,     "AirframeUnknown" },
    { FRAME_CLASS_Y6,       -1,                 "Y6A" },
    { FRAME_CLASS_HELI,     -1,                 "Helicopter" },
    { FRAME_CLASS_TRI,      -1,                 "YPlus" },
};

92 93 94 95 96 97
static const FrameToImageInfo_t s_rgFrameToImageRover[] = {
    { FRAME_CLASS_ROVER,    -1, "Rover" },
    { FRAME_CLASS_BOAT,     -1, "Boat" },
};

static QString s_findImageResourceCopter(int frameClass, int frameType)
98
{
99 100
    for (size_t i=0; i<sizeof(s_rgFrameToImageCopter)/sizeof(s_rgFrameToImageCopter[0]); i++) {
        const FrameToImageInfo_t* pFrameToImageInfo = &s_rgFrameToImageCopter[i];
101 102 103 104 105 106

        if (pFrameToImageInfo->frameClass == frameClass && pFrameToImageInfo->frameType == frameType) {
            return pFrameToImageInfo->imageResource;
        } else if (pFrameToImageInfo->frameClass == frameClass && pFrameToImageInfo->frameType == -1) {
            return pFrameToImageInfo->imageResource;
        }
107
    }
108

109
    return QStringLiteral("AirframeUnknown");
110 111
}

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
static QString s_findImageResourceRover(int frameClass, int frameType)
{
    Q_UNUSED(frameType);

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

        if (pFrameToImageInfo->frameClass == frameClass) {
            return pFrameToImageInfo->imageResource;
        }
    }

    return QStringLiteral("AirframeUnknown");
}

127 128
APMAirframeComponentController::APMAirframeComponentController(void)
    : _frameClassFact   (getParameterFact(FactSystem::defaultComponentId, QStringLiteral("FRAME_CLASS")))
129
    , _frameTypeFact    (getParameterFact(FactSystem::defaultComponentId, QStringLiteral("FRAME_TYPE"), false /* reportMissing */))
130
    , _frameClassModel  (new QmlObjectListModel(this))
131
{
132
    _fillFrameClasses();
133 134
}

135
APMAirframeComponentController::~APMAirframeComponentController()
136
{
137

138 139
}

140
void APMAirframeComponentController::_fillFrameClasses()
141
{
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    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;

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

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

            if (frameTypeNotSupported.contains(frameClass)) {
                defaultFrameType = -1;
            } else {
                defaultFrameType = FRAME_TYPE_X;
            }
            _frameClassModel->append(new APMFrameClass(frameClassName, true /* copter */, frameClass, _frameTypeFact, defaultFrameType, _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();
            int     defaultFrameType;

            if (_frameTypeFact) {
                defaultFrameType = FRAME_TYPE_UNDEFINED;
            } else {
                defaultFrameType = -1;
            }
            _frameClassModel->append(new APMFrameClass(frameClassName, false /* copter */, frameClass, _frameTypeFact, defaultFrameType, _frameClassModel));
183 184 185 186
        }
    }
}

Don Gagne's avatar
Don Gagne committed
187 188 189 190 191 192 193 194
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;
    }
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209

    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
210
    qgcApp()->restoreOverrideCursor();
211
    _vehicle->parameterManager()->refreshAllParameters();
212 213
}

Don Gagne's avatar
Don Gagne committed
214
void APMAirframeComponentController::loadParameters(const QString& paramFile)
215 216
{
    qgcApp()->setOverrideCursor(Qt::WaitCursor);
Don Gagne's avatar
Don Gagne committed
217 218 219 220 221 222 223

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

    QGCFileDownload* downloader = new QGCFileDownload(this);
    connect(downloader, &QGCFileDownload::downloadFinished, this, &APMAirframeComponentController::_githubJsonDownloadFinished);
    connect(downloader, &QGCFileDownload::error, this, &APMAirframeComponentController::_githubJsonDownloadError);
    downloader->download(paramFileUrl.arg(paramFile));
224 225
}

Don Gagne's avatar
Don Gagne committed
226
void APMAirframeComponentController::_githubJsonDownloadFinished(QString remoteFile, QString localFile)
227
{
Don Gagne's avatar
Don Gagne committed
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
    Q_UNUSED(remoteFile);

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

    QGCFileDownload* downloader = new QGCFileDownload(this);
    connect(downloader, &QGCFileDownload::downloadFinished, this, &APMAirframeComponentController::_paramFileDownloadFinished);
    connect(downloader, &QGCFileDownload::error, this, &APMAirframeComponentController::_paramFileDownloadError);
Don Gagne's avatar
Don Gagne committed
251
    downloader->download(json[QLatin1Literal("download_url")].toString());
Don Gagne's avatar
Don Gagne committed
252 253 254 255
}

void APMAirframeComponentController::_githubJsonDownloadError(QString errorMsg)
{
256
    qgcApp()->showMessage(tr("Param file github json download failed: %1").arg(errorMsg));
Don Gagne's avatar
Don Gagne committed
257
    qgcApp()->restoreOverrideCursor();
258 259
}

Don Gagne's avatar
Don Gagne committed
260 261 262 263 264 265 266 267 268
void APMAirframeComponentController::_paramFileDownloadFinished(QString remoteFile, QString localFile)
{
    Q_UNUSED(remoteFile);

    _loadParametersFromDownloadFile(localFile);
}

void APMAirframeComponentController::_paramFileDownloadError(QString errorMsg)
{
Don Gagne's avatar
Don Gagne committed
269
    qgcApp()->showMessage(tr("Param file download failed: %1").arg(errorMsg));
Don Gagne's avatar
Don Gagne committed
270 271
    qgcApp()->restoreOverrideCursor();
}
272

273
APMFrameClass::APMFrameClass(const QString& name, bool copter, int frameClass, Fact* frameTypeFact, int defaultFrameType, QObject* parent)
274 275
    : QObject               (parent)
    , _name                 (name)
276
    , _copter               (copter)
277 278 279 280 281
    , _frameClass           (frameClass)
    , _defaultFrameType     (defaultFrameType)
    , _frameTypeSupported   (defaultFrameType != -1)
    , _frameTypeFact        (frameTypeFact)
{
282 283 284 285
    if (frameTypeFact) {
        connect(frameTypeFact, &Fact::rawValueChanged, this, &APMFrameClass::imageResourceChanged);
        connect(frameTypeFact, &Fact::rawValueChanged, this, &APMFrameClass::frameTypeChanged);
    }
286 287 288 289 290 291 292 293 294
}

APMFrameClass::~APMFrameClass()
{

}

QString APMFrameClass::imageResource(void)
{
295 296 297 298 299 300 301 302 303 304
    QString imageResource;

    int frameType = _frameTypeFact ? _frameTypeFact->rawValue().toInt() : -1;

    if (_copter) {
        imageResource = s_findImageResourceCopter(_frameClass, frameType);
    } else {
        imageResource = s_findImageResourceRover(_frameClass, frameType);
    }
    return QStringLiteral("/qmlimages/Airframe/%1").arg(imageResource);
305
}