QGCSerialPortInfo.cc 12.8 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

#include "QGCSerialPortInfo.h"
12 13 14 15 16 17
#include "JsonHelper.h"

#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
18 19 20

QGC_LOGGING_CATEGORY(QGCSerialPortInfoLog, "QGCSerialPortInfoLog")

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
bool         QGCSerialPortInfo::_jsonLoaded =           false;
const char*  QGCSerialPortInfo::_jsonFileTypeValue =    "USBBoardInfo";
const char*  QGCSerialPortInfo::_jsonBoardInfoKey =     "boardInfo";
const char*  QGCSerialPortInfo::_jsonBoardFallbackKey = "boardFallback";
const char*  QGCSerialPortInfo::_jsonVendorIDKey =      "vendorID";
const char*  QGCSerialPortInfo::_jsonProductIDKey =     "productID";
const char*  QGCSerialPortInfo::_jsonBoardClassKey =    "boardClass";
const char*  QGCSerialPortInfo::_jsonNameKey =          "name";
const char*  QGCSerialPortInfo::_jsonRegExpKey =        "regExp";
const char*  QGCSerialPortInfo::_jsonAndroidOnlyKey =   "androidOnly";

const QGCSerialPortInfo::BoardClassString2BoardType_t QGCSerialPortInfo::_rgBoardClass2BoardType[] = {
    { "Pixhawk",    QGCSerialPortInfo::BoardTypePixhawk },
    { "PX4 Flow",   QGCSerialPortInfo::BoardTypePX4Flow },
    { "RTK GPS",    QGCSerialPortInfo::BoardTypeRTKGPS },
    { "SiK Radio",  QGCSerialPortInfo::BoardTypeSiKRadio },
    { "OpenPilot",  QGCSerialPortInfo::BoardTypeOpenPilot },
Don Gagne's avatar
Don Gagne committed
38 39
};

40 41 42
QList<QGCSerialPortInfo::BoardInfo_t>       QGCSerialPortInfo::_boardInfoList;
QList<QGCSerialPortInfo::BoardFallback_t>   QGCSerialPortInfo::_boardFallbackList;

Don Gagne's avatar
Don Gagne committed
43 44 45 46 47 48
QGCSerialPortInfo::QGCSerialPortInfo(void) :
    QSerialPortInfo()
{

}

49 50 51 52 53 54
QGCSerialPortInfo::QGCSerialPortInfo(const QSerialPort & port) :
    QSerialPortInfo(port)
{

}

55
void QGCSerialPortInfo::_loadJsonData(void)
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    if (_jsonLoaded) {
        return;
    }
    _jsonLoaded = true;

    QFile file(QStringLiteral(":/json/USBBoardInfo.json"));

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "Unable to open board info json:" << file.errorString();
        return;
    }

    QByteArray  bytes = file.readAll();

    QJsonParseError jsonParseError;
    QJsonDocument   jsonDoc(QJsonDocument::fromJson(bytes, &jsonParseError));
    if (jsonParseError.error != QJsonParseError::NoError) {
        qWarning() << "Unable to parse board info json:" << jsonParseError.errorString();
        return;
    }
    QJsonObject json = jsonDoc.object();

    int fileVersion;
    QString errorString;
    if (!JsonHelper::validateQGCJsonFile(json,
                                         _jsonFileTypeValue,    // expected file type
                                         1,                     // minimum supported version
                                         1,                     // maximum supported version
                                         fileVersion,
                                         errorString)) {
        qWarning() << errorString;
        return;
    }

    // Validate root object keys
    QList<JsonHelper::KeyValidateInfo> rootKeyInfoList = {
        { _jsonBoardInfoKey,        QJsonValue::Array, true },
        { _jsonBoardFallbackKey,    QJsonValue::Array, true },
    };
    if (!JsonHelper::validateKeys(json, rootKeyInfoList, errorString)) {
        qWarning() << errorString;
        return;
    }

    // Load board info used to detect known board from vendor/product id

    QList<JsonHelper::KeyValidateInfo> boardKeyInfoList = {
        { _jsonVendorIDKey,     QJsonValue::Double, true },
        { _jsonProductIDKey,    QJsonValue::Double, true },
        { _jsonBoardClassKey,   QJsonValue::String, true },
        { _jsonNameKey,         QJsonValue::String, true },
    };

    QJsonArray rgBoardInfo = json[_jsonBoardInfoKey].toArray();
    for (int i=0; i<rgBoardInfo.count(); i++) {
        const QJsonValue& jsonValue = rgBoardInfo[i];
        if (!jsonValue.isObject()) {
            qWarning() << "Entry in boardInfo array is not object";
            return;
        }

        QJsonObject  boardObject = jsonValue.toObject();
        if (!JsonHelper::validateKeys(boardObject, boardKeyInfoList, errorString)) {
            qWarning() << errorString;
            return;
        }

        BoardInfo_t boardInfo;
        boardInfo.vendorId = boardObject[_jsonVendorIDKey].toInt();
        boardInfo.productId = boardObject[_jsonProductIDKey].toInt();
        boardInfo.name = boardObject[_jsonNameKey].toString();
        boardInfo.boardType = _boardClassStringToType(boardObject[_jsonBoardClassKey].toString());

        if (boardInfo.boardType == BoardTypeUnknown) {
            qWarning() << "Bad board class" << boardObject[_jsonBoardClassKey].toString();
            return;
        }

        _boardInfoList.append(boardInfo);
136 137
    }

138
    // Load board fallback info used to detect known boards from description string match
139

140 141 142 143 144
    QList<JsonHelper::KeyValidateInfo> fallbackKeyInfoList = {
        { _jsonRegExpKey,       QJsonValue::String, true },
        { _jsonBoardClassKey,   QJsonValue::String, true },
        { _jsonAndroidOnlyKey,  QJsonValue::Bool,   false },
    };
Don Gagne's avatar
Don Gagne committed
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
    QJsonArray rgBoardFallback = json[_jsonBoardFallbackKey].toArray();
    for (int i=0; i<rgBoardFallback.count(); i++) {
        const QJsonValue& jsonValue = rgBoardFallback[i];
        if (!jsonValue.isObject()) {
            qWarning() << "Entry in boardFallback array is not object";
            return;
        }

        QJsonObject  fallbackObject = jsonValue.toObject();
        if (!JsonHelper::validateKeys(fallbackObject, fallbackKeyInfoList, errorString)) {
            qWarning() << errorString;
            return;
        }

        BoardFallback_t boardFallback;
        boardFallback.regExp = fallbackObject[_jsonRegExpKey].toString();
        boardFallback.androidOnly = fallbackObject[_jsonAndroidOnlyKey].toBool(false);
        boardFallback.boardType = _boardClassStringToType(fallbackObject[_jsonBoardClassKey].toString());

        if (boardFallback.boardType == BoardTypeUnknown) {
            qWarning() << "Bad board class" << fallbackObject[_jsonBoardClassKey].toString();
            return;
        }

        _boardFallbackList.append(boardFallback);
    }
}

QGCSerialPortInfo::BoardType_t QGCSerialPortInfo::_boardClassStringToType(const QString& boardClass)
{
    for (size_t j=0; j<sizeof(_rgBoardClass2BoardType)/sizeof(_rgBoardClass2BoardType[0]); j++) {
        if (boardClass == _rgBoardClass2BoardType[j].classString) {
            return _rgBoardClass2BoardType[j].boardType;
179
            break;
Don Gagne's avatar
Don Gagne committed
180
        }
181 182
    }

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
    return BoardTypeUnknown;
}

bool QGCSerialPortInfo::getBoardInfo(QGCSerialPortInfo::BoardType_t& boardType, QString& name) const
{
    _loadJsonData();

    if (isNull()) {
        return false;
    }

    for (int i=0; i<_boardInfoList.count(); i++) {
        const BoardInfo_t& boardInfo = _boardInfoList[i];

        if (vendorIdentifier() == boardInfo.vendorId && productIdentifier() == boardInfo.productId) {
            boardType = boardInfo.boardType;
            name = boardInfo.name;
            return true;
        }
    }

204 205 206
    if (boardType == BoardTypeUnknown) {
        // Fall back to port name matching which could lead to incorrect board mapping. But in some cases the
        // vendor and product id do not come through correctly so this is used as a last chance detection method.
207
<<<<<<< c4da69536e067addfbf394609e9369c1c2d00129
208 209 210 211 212 213 214 215 216

        for (int i=0; i<_boardFallbackList.count(); i++) {
            const BoardFallback_t& boardFallback = _boardFallbackList[i];

            if (description().contains(QRegExp(boardFallback.regExp, Qt::CaseInsensitive))) {
#ifndef __android
                if (boardFallback.androidOnly) {
                    continue;
                }
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
=======
        if (description() == "PX4 FMU v4.x" || description() == "PX4 BL FMU v4.x") {
            qCDebug(QGCSerialPortInfoLog) << "Found PX4 FMU V4 (by name matching fallback)";
            boardType = BoardTypePX4FMUV4;
        } else if (description() == "PX4 FMU v2.x" || description() == "PX4 BL FMU v2.x") {
            qCDebug(QGCSerialPortInfoLog) << "Found PX4 FMU V2 (by name matching fallback)";
            boardType = BoardTypePX4FMUV2;
        } else if (description() == "PX4 FMU v1.x" || description() == "PX4 BL FMU v1.x") {
            qCDebug(QGCSerialPortInfoLog) << "Found PX4 FMU V1 (by name matching fallback)";
            boardType = BoardTypePX4FMUV1;
        } else if (description().startsWith("PX4 FMU")) {
            qCDebug(QGCSerialPortInfoLog) << "Found PX4 FMU, assuming V2 (by name matching fallback)";
            boardType = BoardTypePX4FMUV2;
        } else if (description().contains(QRegExp("PX4.*Flow", Qt::CaseInsensitive))) {
            qCDebug(QGCSerialPortInfoLog) << "Found possible px4 flow camera (by name matching fallback)";
            boardType = BoardTypePX4Flow;
        } else if (description() == "MindPX FMU v2.x" || description() == "MindPX BL FMU v2.x") {
            qCDebug(QGCSerialPortInfoLog) << "Found MindPX FMU V2 (by name matching fallback)";
            boardType = BoardTypeMINDPXFMUV2;
        } else if (description() == "PX4 TAP v1.x" || description() == "PX4 BL TAP v1.x") {
            qCDebug(QGCSerialPortInfoLog) << "Found TAP V1 (by name matching fallback)";
            boardType = BoardTypeTAPV1;
        } else if (description() == "PX4 ASC v1.x" || description() == "PX4 BL ASC v1.x") {
            qCDebug(QGCSerialPortInfoLog) << "Found ASC V1 (by name matching fallback)";
            boardType = BoardTypeASCV1;
        } else if (description() == "PX4 Crazyflie v2.0" || description() == "Crazyflie BL") {
            qCDebug(QGCSerialPortInfoLog) << "Found Crazyflie 2.0 (by name matching fallback)";
            boardType = BoardTypeCrazyflie2;
        } else if (description() == "FT231X USB UART") {
            qCDebug(QGCSerialPortInfoLog) << "Found possible Radio (by name matching fallback)";
            boardType = BoardTypeSikRadio;
#ifdef __android__
        } else if (description().endsWith("USB UART")) {
            // This is a fairly broad fallbacks for radios which will also catch most FTDI devices. That would
            // cause problems on desktop due to incorrect connections. Since mobile is more anal about connecting
            // it will work fine here.
            boardType = BoardTypeSikRadio;
>>>>>>> Add firmware upgrade support for CF2
255
#endif
256 257 258 259
                boardType = boardFallback.boardType;
                name = _boardTypeToString(boardType);
                return true;
            }
260 261 262
        }
    }

263 264
    boardType = BoardTypeUnknown;
    return false;
265 266
}

267 268
QString QGCSerialPortInfo::_boardTypeToString(BoardType_t boardType)
{
Don Gagne's avatar
Don Gagne committed
269 270
    QString unknown = QObject::tr("Unknown");

271 272 273 274 275 276 277 278 279 280 281 282
    switch (boardType) {
    case BoardTypePixhawk:
        return QObject::tr("Pixhawk");
    case BoardTypeSiKRadio:
        return QObject::tr("SiK Radio");
    case BoardTypePX4Flow:
        return QObject::tr("PX4 Flow");
    case BoardTypeOpenPilot:
        return QObject::tr("OpenPilot");
    case BoardTypeRTKGPS:
        return QObject::tr("RTK GPS");
    case BoardTypeUnknown:
Don Gagne's avatar
Don Gagne committed
283
        return unknown;
284
    }
Don Gagne's avatar
Don Gagne committed
285 286

    return unknown;
287 288 289
}


290 291 292 293 294 295 296 297 298 299 300
QList<QGCSerialPortInfo> QGCSerialPortInfo::availablePorts(void)
{
    QList<QGCSerialPortInfo>    list;

    foreach(QSerialPortInfo portInfo, QSerialPortInfo::availablePorts()) {
        list << *((QGCSerialPortInfo*)&portInfo);
    }

    return list;
}

301 302 303 304 305 306 307 308 309 310 311 312 313
<<<<<<< c4da69536e067addfbf394609e9369c1c2d00129
=======
bool QGCSerialPortInfo::boardTypePixhawk(void) const
{
    BoardType_t boardType = this->boardType();

    return boardType == BoardTypePX4FMUV1 || boardType == BoardTypePX4FMUV2
            || boardType == BoardTypePX4FMUV4 || boardType == BoardTypeAeroCore
            || boardType == BoardTypeMINDPXFMUV2 || boardType == BoardTypeTAPV1
            || boardType == BoardTypeASCV1 || boardType == BoardTypeCrazyflie2;
}

>>>>>>> Add firmware upgrade support for CF2
314 315
bool QGCSerialPortInfo::isBootloader(void) const
{
316 317 318 319 320 321 322 323 324
    BoardType_t boardType;
    QString     name;

    if (getBoardInfo(boardType, name)) {
        // FIXME: Check SerialLink bootloade detect code which is different
        return boardType == BoardTypePixhawk && description().contains("BL");
    } else {
        return false;
    }
325
}
326 327 328

bool QGCSerialPortInfo::canFlash(void)
{
329 330
    BoardType_t boardType;
    QString     name;
331

332 333 334 335 336 337 338 339 340 341 342
    if (getBoardInfo(boardType, name)) {
        switch(boardType){
        case QGCSerialPortInfo::BoardTypePixhawk:
        case QGCSerialPortInfo::BoardTypePX4Flow:
        case QGCSerialPortInfo::BoardTypeSiKRadio:
            return true;
        default:
            return false;
        }
    } else {
        return false;
343
    }
344
}