MavlinkLogManager.cc 22 KB
Newer Older
Gus Grubba's avatar
Gus Grubba committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/****************************************************************************
 *
 *   (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 "MavlinkLogManager.h"
#include "QGCApplication.h"
#include <QQmlContext>
#include <QQmlProperty>
#include <QQmlEngine>
#include <QtQml>
#include <QSettings>
#include <QHttpPart>
#include <QNetworkReply>
#include <QFile>
#include <QFileInfo>

QGC_LOGGING_CATEGORY(MavlinkLogManagerLog, "MavlinkLogManagerLog")

24 25 26 27 28 29 30
static const char* kEmailAddressKey         = "MavlinkLogEmail";
static const char* kDescriptionsKey         = "MavlinkLogDescription";
static const char* kDefaultDescr            = "QGroundControl Session";
static const char* kPx4URLKey               = "MavlinkLogURL";
static const char* kDefaultPx4URL           = "http://logs.px4.io/upload";
static const char* kEnableAutoUploadKey     = "EnableAutoUploadKey";
static const char* kEnableAutoStartKey      = "EnableAutoStartKey";
31
static const char* kEnableDeletetKey        = "EnableDeleteKey";
Gus Grubba's avatar
Gus Grubba committed
32 33
static const char* kUlogExtension           = ".ulg";
static const char* kSidecarExtension        = ".uploaded";
Gus Grubba's avatar
Gus Grubba committed
34 35

//-----------------------------------------------------------------------------
Gus Grubba's avatar
Gus Grubba committed
36
MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& filePath, bool newFile)
Gus Grubba's avatar
Gus Grubba committed
37 38 39 40 41
    : _manager(manager)
    , _size(0)
    , _selected(false)
    , _uploading(false)
    , _progress(0)
Gus Grubba's avatar
Gus Grubba committed
42
    , _writing(false)
Gus Grubba's avatar
Gus Grubba committed
43
    , _uploaded(false)
Gus Grubba's avatar
Gus Grubba committed
44 45 46
{
    QFileInfo fi(filePath);
    _name = fi.baseName();
Gus Grubba's avatar
Gus Grubba committed
47 48 49 50 51 52 53
    if(!newFile) {
        _size = (quint32)fi.size();
        QString sideCar = filePath;
        sideCar.replace(kUlogExtension, kSidecarExtension);
        QFileInfo sc(sideCar);
        _uploaded = sc.exists();
    }
Gus Grubba's avatar
Gus Grubba committed
54 55
}

56 57 58 59 60 61 62 63
//-----------------------------------------------------------------------------
void
MavlinkLogFiles::setSize(quint32 size)
{
    _size = size;
    emit sizeChanged();
}

Gus Grubba's avatar
Gus Grubba committed
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
//-----------------------------------------------------------------------------
void
MavlinkLogFiles::setSelected(bool selected)
{
    _selected = selected;
    emit selectedChanged();
    emit _manager->selectedCountChanged();
}

//-----------------------------------------------------------------------------
void
MavlinkLogFiles::setUploading(bool uploading)
{
    _uploading = uploading;
    emit uploadingChanged();
}

//-----------------------------------------------------------------------------
void
MavlinkLogFiles::setProgress(qreal progress)
{
    _progress = progress;
    emit progressChanged();
}

89 90 91 92 93 94 95 96
//-----------------------------------------------------------------------------
void
MavlinkLogFiles::setWriting(bool writing)
{
    _writing = writing;
    emit writingChanged();
}

Gus Grubba's avatar
Gus Grubba committed
97 98 99 100 101 102 103 104
//-----------------------------------------------------------------------------
void
MavlinkLogFiles::setUploaded(bool uploaded)
{
    _uploaded = uploaded;
    emit uploadedChanged();
}

Gus Grubba's avatar
Gus Grubba committed
105 106 107 108 109 110 111 112 113 114 115
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CurrentRunningLog::close()
{
    if(fd) {
        fclose(fd);
        fd = NULL;
    }
}

//-----------------------------------------------------------------------------
Gus Grubba's avatar
Gus Grubba committed
116 117 118
//-----------------------------------------------------------------------------
MavlinkLogManager::MavlinkLogManager(QGCApplication* app)
    : QGCTool(app)
119 120
    , _enableAutoUpload(true)
    , _enableAutoStart(true)
Gus Grubba's avatar
Gus Grubba committed
121 122
    , _nam(NULL)
    , _currentLogfile(NULL)
123 124
    , _vehicle(NULL)
    , _logRunning(false)
Gus Grubba's avatar
Gus Grubba committed
125
    , _loggingDisabled(false)
126
    , _currentSavingFile(NULL)
Gus Grubba's avatar
Gus Grubba committed
127
    , _sequence(0)
128
    , _deleteAfterUpload(false)
Gus Grubba's avatar
Gus Grubba committed
129 130 131 132 133 134
{
    //-- Get saved settings
    QSettings settings;
    setEmailAddress(settings.value(kEmailAddressKey, QString()).toString());
    setDescription(settings.value(kDescriptionsKey, QString(kDefaultDescr)).toString());
    setUploadURL(settings.value(kPx4URLKey, QString(kDefaultPx4URL)).toString());
135 136
    setEnableAutoUpload(settings.value(kEnableAutoUploadKey, true).toBool());
    setEnableAutoStart(settings.value(kEnableAutoStartKey, true).toBool());
137
    setDeleteAfterUpload(settings.value(kEnableDeletetKey, false).toBool());
Gus Grubba's avatar
Gus Grubba committed
138 139 140 141
    //-- Logging location
    _logPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    _logPath += "/MavlinkLogs";
    if(!QDir(_logPath).exists()) {
Gus Grubba's avatar
Gus Grubba committed
142
        if(!QDir().mkpath(_logPath)) {
Gus Grubba's avatar
Gus Grubba committed
143
            qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log download path:" << _logPath;
Gus Grubba's avatar
Gus Grubba committed
144
            _loggingDisabled = true;
Gus Grubba's avatar
Gus Grubba committed
145 146
        }
    }
Gus Grubba's avatar
Gus Grubba committed
147 148
    if(!_loggingDisabled) {
        //-- Load current list of logs
Gus Grubba's avatar
Gus Grubba committed
149 150 151
        QString filter = "*";
        filter += kUlogExtension;
        QDirIterator it(_logPath, QStringList() << filter, QDir::Files);
Gus Grubba's avatar
Gus Grubba committed
152
        while(it.hasNext()) {
153
            _insertNewLog(new MavlinkLogFiles(this, it.next()));
Gus Grubba's avatar
Gus Grubba committed
154 155
        }
        qCDebug(MavlinkLogManagerLog) << "Mavlink logs directory:" << _logPath;
Gus Grubba's avatar
Gus Grubba committed
156 157 158 159 160 161 162 163 164 165 166
    }
}

//-----------------------------------------------------------------------------
MavlinkLogManager::~MavlinkLogManager()
{
    _logFiles.clear();
}

//-----------------------------------------------------------------------------
void
Gus Grubba's avatar
Gus Grubba committed
167
MavlinkLogManager::setToolbox(QGCToolbox* toolbox)
Gus Grubba's avatar
Gus Grubba committed
168
{
Gus Grubba's avatar
Gus Grubba committed
169 170 171 172 173 174
    QGCTool::setToolbox(toolbox);
    QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
    qmlRegisterUncreatableType<MavlinkLogManager>("QGroundControl.MavlinkLogManager", 1, 0, "MavlinkLogManager", "Reference only");
    if(!_loggingDisabled) {
        connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MavlinkLogManager::_activeVehicleChanged);
    }
Gus Grubba's avatar
Gus Grubba committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::setEmailAddress(QString email)
{
    _emailAddress = email;
    QSettings settings;
    settings.setValue(kEmailAddressKey, email);
    emit emailAddressChanged();
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::setDescription(QString description)
{
    _description = description;
    QSettings settings;
    settings.setValue(kDescriptionsKey, description);
    emit descriptionChanged();
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::setUploadURL(QString url)
{
    _uploadURL = url;
    if(_uploadURL.isEmpty()) {
        _uploadURL = kDefaultPx4URL;
    }
    QSettings settings;
    settings.setValue(kPx4URLKey, _uploadURL);
    emit uploadURLChanged();
}

//-----------------------------------------------------------------------------
void
212 213 214 215 216 217 218 219 220 221 222
MavlinkLogManager::setEnableAutoUpload(bool enable)
{
    _enableAutoUpload = enable;
    QSettings settings;
    settings.setValue(kEnableAutoUploadKey, enable);
    emit enableAutoUploadChanged();
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::setEnableAutoStart(bool enable)
Gus Grubba's avatar
Gus Grubba committed
223
{
224
    _enableAutoStart = enable;
Gus Grubba's avatar
Gus Grubba committed
225
    QSettings settings;
226 227
    settings.setValue(kEnableAutoStartKey, enable);
    emit enableAutoStartChanged();
Gus Grubba's avatar
Gus Grubba committed
228 229
}

230 231 232 233 234 235 236 237 238 239
//-----------------------------------------------------------------------------
void
MavlinkLogManager::setDeleteAfterUpload(bool enable)
{
    _deleteAfterUpload = enable;
    QSettings settings;
    settings.setValue(kEnableDeletetKey, enable);
    emit deleteAfterUploadChanged();
}

Gus Grubba's avatar
Gus Grubba committed
240 241
//-----------------------------------------------------------------------------
bool
242
MavlinkLogManager::uploading()
Gus Grubba's avatar
Gus Grubba committed
243 244 245 246 247 248 249 250 251 252 253
{
    return _currentLogfile != NULL;
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::uploadLog()
{
    if(_currentLogfile) {
        _currentLogfile->setUploading(false);
    }
Gus Grubba's avatar
Gus Grubba committed
254
    for(int i = 0; i < _logFiles.count(); i++) {
Gus Grubba's avatar
Gus Grubba committed
255 256 257 258
        _currentLogfile = qobject_cast<MavlinkLogFiles*>(_logFiles.get(i));
        Q_ASSERT(_currentLogfile);
        if(_currentLogfile->selected()) {
            _currentLogfile->setSelected(false);
Gus Grubba's avatar
Gus Grubba committed
259 260 261 262 263 264 265 266
            if(!_currentLogfile->uploaded() && !_emailAddress.isEmpty() && !_uploadURL.isEmpty()) {
                _currentLogfile->setUploading(true);
                _currentLogfile->setProgress(0.0);
                QString filePath = _makeFilename(_currentLogfile->name());
                _sendLog(filePath);
                emit uploadingChanged();
                return;
            }
Gus Grubba's avatar
Gus Grubba committed
267 268 269
        }
    }
    _currentLogfile = NULL;
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    emit uploadingChanged();
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::_insertNewLog(MavlinkLogFiles* newLog)
{
    //-- Simpler than trying to sort this thing
    int count = _logFiles.count();
    if(!count) {
        _logFiles.append(newLog);
    } else {
        for(int i = 0; i < count; i++) {
            MavlinkLogFiles* f = qobject_cast<MavlinkLogFiles*>(_logFiles.get(i));
            if(newLog->name() < f->name()) {
                _logFiles.insert(i, newLog);
                return;
            }
        }
        _logFiles.append(newLog);
    }
Gus Grubba's avatar
Gus Grubba committed
291 292
}

Gus Grubba's avatar
Gus Grubba committed
293 294 295 296 297 298 299 300 301 302 303 304 305 306
//-----------------------------------------------------------------------------
int
MavlinkLogManager::_getFirstSelected()
{
    for(int i = 0; i < _logFiles.count(); i++) {
        MavlinkLogFiles* f = qobject_cast<MavlinkLogFiles*>(_logFiles.get(i));
        Q_ASSERT(f);
        if(f->selected()) {
            return i;
        }
    }
    return -1;
}

Gus Grubba's avatar
Gus Grubba committed
307 308 309 310
//-----------------------------------------------------------------------------
void
MavlinkLogManager::deleteLog()
{
Gus Grubba's avatar
Gus Grubba committed
311 312 313 314 315
    while (true) {
        int idx = _getFirstSelected();
        if(idx < 0) {
            break;
        }
316 317
        MavlinkLogFiles* log = qobject_cast<MavlinkLogFiles*>(_logFiles.get(idx));
        _deleteLog(log);
Gus Grubba's avatar
Gus Grubba committed
318
    }
Gus Grubba's avatar
Gus Grubba committed
319 320
}

321 322 323 324
//-----------------------------------------------------------------------------
void
MavlinkLogManager::_deleteLog(MavlinkLogFiles* log)
{
Gus Grubba's avatar
Gus Grubba committed
325
    QString filePath = _makeFilename(log->name());
326 327 328 329
    QFile gone(filePath);
    if(!gone.remove()) {
        qCWarning(MavlinkLogManagerLog) << "Could not delete Mavlink log file:" << _logPath;
    }
Gus Grubba's avatar
Gus Grubba committed
330 331 332 333 334 335 336
    //-- Remove sidecar file (if any)
    filePath.replace(kUlogExtension, kSidecarExtension);
    QFile sgone(filePath);
    if(sgone.exists()) {
        sgone.remove();
    }
    //-- Remove file from list and delete record
337 338 339 340 341
    _logFiles.removeOne(log);
    delete log;
    emit logFilesChanged();
}

Gus Grubba's avatar
Gus Grubba committed
342 343 344 345
//-----------------------------------------------------------------------------
void
MavlinkLogManager::cancelUpload()
{
Gus Grubba's avatar
Gus Grubba committed
346
    for(int i = 0; i < _logFiles.count(); i++) {
Gus Grubba's avatar
Gus Grubba committed
347 348 349 350 351 352 353 354 355 356 357
        MavlinkLogFiles* pLogFile = qobject_cast<MavlinkLogFiles*>(_logFiles.get(i));
        Q_ASSERT(pLogFile);
        if(pLogFile->selected() && pLogFile != _currentLogfile) {
            pLogFile->setSelected(false);
        }
    }
    if(_currentLogfile) {
        emit abortUpload();
    }
}

358 359 360 361 362
//-----------------------------------------------------------------------------
void
MavlinkLogManager::startLogging()
{
    if(_vehicle) {
Gus Grubba's avatar
Gus Grubba committed
363 364 365 366 367
        if(_createNewLog()) {
            _vehicle->startMavlinkLog();
            _logRunning = true;
            emit logRunningChanged();
        }
368 369 370 371 372 373 374 375
    }
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::stopLogging()
{
    if(_vehicle) {
376
        //-- Tell vehicle to stop sending logs
377
        _vehicle->stopMavlinkLog();
378 379 380 381 382 383 384 385 386 387 388
        if(_currentSavingFile) {
            _currentSavingFile->close();
            if(_currentSavingFile->record) {
                _currentSavingFile->record->setWriting(false);
                if(_enableAutoUpload) {
                    //-- Queue log for auto upload (set selected flag)
                    _currentSavingFile->record->setSelected(true);
                    if(!uploading()) {
                        uploadLog();
                    }
                }
Gus Grubba's avatar
Gus Grubba committed
389
            }
390 391 392 393
            delete _currentSavingFile;
            _currentSavingFile = NULL;
            _logRunning = false;
            emit logRunningChanged();
Gus Grubba's avatar
Gus Grubba committed
394
        }
395 396 397
    }
}

Gus Grubba's avatar
Gus Grubba committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
//-----------------------------------------------------------------------------
QHttpPart
create_form_part(const QString& name, const QString& value)
{
    QHttpPart formPart;
    formPart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"").arg(name));
    formPart.setBody(value.toUtf8());
    return formPart;
}

//-----------------------------------------------------------------------------
bool
MavlinkLogManager::_sendLog(const QString& logFile)
{
    QString defaultDescription = _description;
    if(_description.isEmpty()) {
        qCWarning(MavlinkLogManagerLog) << "Log description missing. Using defaults.";
        defaultDescription = kDefaultDescr;
    }
    if(_emailAddress.isEmpty()) {
        qCCritical(MavlinkLogManagerLog) << "User email missing.";
        return false;
    }
    if(_uploadURL.isEmpty()) {
        qCCritical(MavlinkLogManagerLog) << "Upload URL missing.";
        return false;
    }
    QFileInfo fi(logFile);
    if(!fi.exists()) {
        qCCritical(MavlinkLogManagerLog) << "Log file missing:" << logFile;
        return false;
    }
Gus Grubba's avatar
Gus Grubba committed
430
    QFile* file = new QFile(logFile);
Gus Grubba's avatar
Gus Grubba committed
431
    if(!file || !file->open(QIODevice::ReadOnly)) {
Gus Grubba's avatar
Gus Grubba committed
432 433 434
        if(file) {
            delete file;
        }
Gus Grubba's avatar
Gus Grubba committed
435 436 437 438
        qCCritical(MavlinkLogManagerLog) << "Could not open log file:" << logFile;
        return false;
    }
    if(!_nam) {
Gus Grubba's avatar
Gus Grubba committed
439
        _nam = new QNetworkAccessManager(this);
Gus Grubba's avatar
Gus Grubba committed
440 441 442 443 444 445
    }
    QNetworkProxy savedProxy = _nam->proxy();
    QNetworkProxy tempProxy;
    tempProxy.setType(QNetworkProxy::DefaultProxy);
    _nam->setProxy(tempProxy);
    //-- Build POST request
Gus Grubba's avatar
Gus Grubba committed
446
    QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
Gus Grubba's avatar
Gus Grubba committed
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
    QHttpPart emailPart = create_form_part("email", _emailAddress);
    QHttpPart descriptionPart = create_form_part("description", _description);
    QHttpPart sourcePart = create_form_part("source", "QGroundControl");
    QHttpPart versionPart = create_form_part("version", _app->applicationVersion());
    QHttpPart logPart;
    logPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream");
    logPart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"filearg\"; filename=\"%1\"").arg(fi.fileName()));
    logPart.setBodyDevice(file);
    //-- Assemble request and POST it
    multiPart->append(emailPart);
    multiPart->append(descriptionPart);
    multiPart->append(sourcePart);
    multiPart->append(versionPart);
    multiPart->append(logPart);
    file->setParent(multiPart);
    QNetworkRequest request(_uploadURL);
463
#if QT_VERSION > 0x050600
Gus Grubba's avatar
Gus Grubba committed
464
    request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
465
#endif
Gus Grubba's avatar
Gus Grubba committed
466
    QNetworkReply* reply = _nam->post(request, multiPart);
Gus Grubba's avatar
Gus Grubba committed
467 468 469 470 471 472 473 474 475 476 477 478
    connect(reply, &QNetworkReply::finished,  this, &MavlinkLogManager::_uploadFinished);
    connect(this, &MavlinkLogManager::abortUpload, reply, &QNetworkReply::abort);
    //connect(reply, &QNetworkReply::readyRead, this, &MavlinkLogManager::_dataAvailable);
    connect(reply, &QNetworkReply::uploadProgress, this, &MavlinkLogManager::_uploadProgress);
    multiPart->setParent(reply);
    qCDebug(MavlinkLogManagerLog) << "Log" << fi.baseName() << "Uploading." << fi.size() << "bytes.";
    _nam->setProxy(savedProxy);
    return true;
}

//-----------------------------------------------------------------------------
bool
Gus Grubba's avatar
Gus Grubba committed
479
MavlinkLogManager::_processUploadResponse(int http_code, QByteArray& data)
Gus Grubba's avatar
Gus Grubba committed
480 481 482 483 484 485 486 487 488 489
{
    qCDebug(MavlinkLogManagerLog) << "Uploaded response:" << QString::fromUtf8(data);
    emit readyRead(data);
    return http_code == 200;
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::_dataAvailable()
{
Gus Grubba's avatar
Gus Grubba committed
490
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
Gus Grubba's avatar
Gus Grubba committed
491 492 493 494 495 496 497 498 499 500 501
    if(!reply) {
        return;
    }
    QByteArray data = reply->readAll();
    qCDebug(MavlinkLogManagerLog) << "Uploaded response data:" << QString::fromUtf8(data);
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::_uploadFinished()
{
Gus Grubba's avatar
Gus Grubba committed
502
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
Gus Grubba's avatar
Gus Grubba committed
503 504 505 506 507 508 509 510
    if(!reply) {
        return;
    }
    const int http_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    QByteArray data = reply->readAll();
    if(_processUploadResponse(http_code, data)) {
        qCDebug(MavlinkLogManagerLog) << "Log uploaded.";
        emit succeed();
511 512 513 514 515
        if(_deleteAfterUpload) {
            if(_currentLogfile) {
                _deleteLog(_currentLogfile);
                _currentLogfile = NULL;
            }
Gus Grubba's avatar
Gus Grubba committed
516 517 518 519 520 521 522 523 524 525 526
        } else {
            if(_currentLogfile) {
                _currentLogfile->setUploaded(true);
                //-- Write side-car file to flag it as uploaded
                QString sideCar = _makeFilename(_currentLogfile->name());
                sideCar.replace(kUlogExtension, kSidecarExtension);
                FILE* f = fopen(sideCar.toLatin1().data(), "wb");
                if(f) {
                    fclose(f);
                }
            }
527
        }
Gus Grubba's avatar
Gus Grubba committed
528
    } else {
529
        qCWarning(MavlinkLogManagerLog) << QString("Log Upload Error: %1 status: %2").arg(reply->errorString(), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString());
Gus Grubba's avatar
Gus Grubba committed
530 531 532 533 534 535 536 537 538 539 540 541 542
        emit failed();
    }
    reply->deleteLater();
    //-- Next (if any)
    uploadLog();
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::_uploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
    if(bytesTotal) {
        qreal progress = (qreal)bytesSent / (qreal)bytesTotal;
Gus Grubba's avatar
Gus Grubba committed
543
        if(_currentLogfile) {
Gus Grubba's avatar
Gus Grubba committed
544
            _currentLogfile->setProgress(progress);
Gus Grubba's avatar
Gus Grubba committed
545
        }
Gus Grubba's avatar
Gus Grubba committed
546 547 548
    }
    qCDebug(MavlinkLogManagerLog) << bytesSent << "of" << bytesTotal;
}
549 550 551 552 553 554 555 556 557

//-----------------------------------------------------------------------------
void
MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle)
{
    //-- TODO: This is not quite right. This is being used to detect when a vehicle
    //   connects/disconnects. In reality, if QGC is connected to multiple vehicles,
    //   this is called each time the user switches from one vehicle to another. So
    //   far, I'm working on the assumption that multiple vehicles is a rare exception.
Gus Grubba's avatar
Gus Grubba committed
558
    //   For now, we only handle one log download at a time.
559
    // Disconnect the previous one (if any)
Gus Grubba's avatar
Gus Grubba committed
560
    if(_vehicle) {
561 562 563 564 565 566
        disconnect(_vehicle, &Vehicle::armedChanged,   this, &MavlinkLogManager::_armedChanged);
        disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData);
        _vehicle = NULL;
        emit canStartLogChanged();
    }
    // Connect new system
Gus Grubba's avatar
Gus Grubba committed
567
    if(vehicle) {
568 569 570 571 572 573 574 575 576
        _vehicle = vehicle;
        connect(_vehicle, &Vehicle::armedChanged,   this, &MavlinkLogManager::_armedChanged);
        connect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData);
        emit canStartLogChanged();
    }
}

//-----------------------------------------------------------------------------
void
Gus Grubba's avatar
Gus Grubba committed
577 578
MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool /*acked*/)
{
Gus Grubba's avatar
Gus Grubba committed
579
    if(_currentSavingFile && _currentSavingFile->fd) {
Gus Grubba's avatar
Gus Grubba committed
580 581 582 583 584 585 586 587 588
        if(sequence != _sequence) {
            qCWarning(MavlinkLogManagerLog) << "Dropped Mavlink log data";
            if(first_message < 255) {
                data += first_message;
                length -= first_message;
            } else {
                return;
            }
        }
589 590 591 592
        if(fwrite(data, 1, length, _currentSavingFile->fd) != (size_t)length) {
            qCCritical(MavlinkLogManagerLog) << "Error writing Mavlink log file:" << _currentSavingFile->fileName;
            delete _currentSavingFile;
            _currentSavingFile = NULL;
Gus Grubba's avatar
Gus Grubba committed
593 594 595 596 597
            _logRunning = false;
            _vehicle->stopMavlinkLog();
            emit logRunningChanged();
        }
    } else {
598
        length = 0;
Gus Grubba's avatar
Gus Grubba committed
599 600
        qCWarning(MavlinkLogManagerLog) << "Mavlink log data received when not expected.";
    }
601 602 603 604 605 606 607
    //-- Update file size
    if(_currentSavingFile) {
        if(_currentSavingFile->record) {
            quint32 size = _currentSavingFile->record->size() + length;
            _currentSavingFile->record->setSize(size);
        }
    }
Gus Grubba's avatar
Gus Grubba committed
608 609 610 611 612 613
    _sequence = sequence + 1;
}

//-----------------------------------------------------------------------------
bool
MavlinkLogManager::_createNewLog()
614
{
615 616 617
    if(_currentSavingFile) {
        delete _currentSavingFile;
        _currentSavingFile = NULL;
Gus Grubba's avatar
Gus Grubba committed
618
    }
619
    _currentSavingFile = new CurrentRunningLog;
Gus Grubba's avatar
Gus Grubba committed
620
    _currentSavingFile->fileName.sprintf("%s/%03d-%s%s",
621 622
        _logPath.toLatin1().data(),
        _vehicle->id(),
Gus Grubba's avatar
Gus Grubba committed
623 624
        QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLatin1().data(),
        kUlogExtension);
625 626
    _currentSavingFile->fd = fopen(_currentSavingFile->fileName.toLatin1().data(), "wb");
    if(_currentSavingFile->fd) {
Gus Grubba's avatar
Gus Grubba committed
627
        MavlinkLogFiles* newLog = new MavlinkLogFiles(this, _currentSavingFile->fileName, true);
628 629 630 631 632 633 634 635
        newLog->setWriting(true);
        _insertNewLog(newLog);
        _currentSavingFile->record = newLog;
        emit logFilesChanged();
    } else {
        qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log file:" << _currentSavingFile->fileName;
        delete _currentSavingFile;
        _currentSavingFile = NULL;
Gus Grubba's avatar
Gus Grubba committed
636 637
    }
    _sequence = 0;
638
    return _currentSavingFile != NULL;
639 640 641 642 643 644 645 646 647
}

//-----------------------------------------------------------------------------
void
MavlinkLogManager::_armedChanged(bool armed)
{
    if(_vehicle) {
        if(armed) {
            if(_enableAutoStart) {
Gus Grubba's avatar
Gus Grubba committed
648
                startLogging();
649 650 651
            }
        } else {
            if(_logRunning && _enableAutoStart) {
Gus Grubba's avatar
Gus Grubba committed
652
                stopLogging();
653 654 655 656
            }
        }
    }
}
Gus Grubba's avatar
Gus Grubba committed
657 658 659 660 661 662 663 664 665 666 667

//-----------------------------------------------------------------------------
QString
MavlinkLogManager::_makeFilename(const QString& baseName)
{
    QString filePath = _logPath;
    filePath += "/";
    filePath += baseName;
    filePath += kUlogExtension;
    return filePath;
}