/**************************************************************************** * * (c) 2009-2020 QGROUNDCONTROL PROJECT * * 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 "SettingsManager.h" #include #include #include #include #include #include #include #include #include QGC_LOGGING_CATEGORY(MAVLinkLogManagerLog, "MAVLinkLogManagerLog") static const char* kMAVLinkLogGroup = "MAVLinkLogGroup"; static const char* kEmailAddressKey = "Email"; static const char* kDescriptionsKey = "Description"; static const char* kDefaultDescr = "QGroundControl Session"; static const char* kPx4URLKey = "LogURL"; static const char* kDefaultPx4URL = "https://logs.px4.io/upload"; static const char* kEnableAutoUploadKey = "EnableAutoUpload"; static const char* kEnableAutoStartKey = "EnableAutoStart"; static const char* kEnableDeletetKey = "EnableDelete"; static const char* kSidecarExtension = ".uploaded"; static const char* kVideoURLKey = "VideoURL"; static const char* kWindSpeedKey = "WindSpeed"; static const char* kRateKey = "RateKey"; static const char* kPublicLogKey = "PublicLog"; static const char* kFeedback = "feedback"; static const char* kVideoURL = "videoUrl"; //----------------------------------------------------------------------------- MAVLinkLogFiles::MAVLinkLogFiles(MAVLinkLogManager* manager, const QString& filePath, bool newFile) : _manager(manager) , _size(0) , _selected(false) , _uploading(false) , _progress(0) , _writing(false) , _uploaded(false) { QFileInfo fi(filePath); _name = fi.baseName(); if(!newFile) { _size = (quint32)fi.size(); QString sideCar = filePath; sideCar.replace(manager->logExtension(), kSidecarExtension); QFileInfo sc(sideCar); _uploaded = sc.exists(); } } //----------------------------------------------------------------------------- void MAVLinkLogFiles::setSize(quint32 size) { _size = size; emit sizeChanged(); } //----------------------------------------------------------------------------- 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(); } //----------------------------------------------------------------------------- void MAVLinkLogFiles::setWriting(bool writing) { _writing = writing; emit writingChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogFiles::setUploaded(bool uploaded) { _uploaded = uploaded; emit uploadedChanged(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- MAVLinkLogProcessor::MAVLinkLogProcessor() : _fd(nullptr) , _written(0) , _sequence(-1) , _numDrops(0) , _gotHeader(false) , _error(false) , _record(nullptr) { } //----------------------------------------------------------------------------- MAVLinkLogProcessor::~MAVLinkLogProcessor() { close(); } //----------------------------------------------------------------------------- void MAVLinkLogProcessor::close() { if(_fd) { fclose(_fd); _fd = nullptr; } } //----------------------------------------------------------------------------- bool MAVLinkLogProcessor::valid() { return (_fd != nullptr) && (_record != nullptr); } //----------------------------------------------------------------------------- bool MAVLinkLogProcessor::create(MAVLinkLogManager* manager, const QString path, uint8_t id) { _fileName.asprintf("%s/%03d-%s%s", path.toLatin1().data(), id, QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLocal8Bit().data(), manager->logExtension().toLocal8Bit().data()); _fd = fopen(_fileName.toLocal8Bit().data(), "wb"); if(_fd) { _record = new MAVLinkLogFiles(manager, _fileName, true); _record->setWriting(true); _sequence = -1; return true; } return false; } //----------------------------------------------------------------------------- bool MAVLinkLogProcessor::_checkSequence(uint16_t seq, int& num_drops) { num_drops = 0; //-- Check if a sequence is newer than the one previously received and if // there were dropped messages between the last one and this. if(_sequence == -1) { _sequence = seq; return true; } if((uint16_t)_sequence == seq) { return false; } if(seq > (uint16_t)_sequence) { // Account for wrap-arounds, sequence is 2 bytes if((seq - _sequence) > (1 << 15)) { // Assume reordered return false; } num_drops = seq - _sequence - 1; _numDrops += num_drops; _sequence = seq; return true; } else { if((_sequence - seq) > (1 << 15)) { num_drops = (1 << 16) - _sequence - 1 + seq; _numDrops += num_drops; _sequence = seq; return true; } return false; } } //----------------------------------------------------------------------------- void MAVLinkLogProcessor::_writeData(void* data, int len) { if(!_error) { _error = fwrite(data, 1, len, _fd) != (size_t)len; if(!_error) { _written += len; if(_record) { _record->setSize(_written); } } else { qCDebug(MAVLinkLogManagerLog) << "File IO error:" << len << "bytes into" << _fileName; } } } //----------------------------------------------------------------------------- QByteArray MAVLinkLogProcessor::_writeUlogMessage(QByteArray& data) { //-- Write ulog data w/o integrity checking, assuming data starts with a // valid ulog message. returns the remaining data at the end. while(data.length() > 2) { uint8_t* ptr = (uint8_t*)data.data(); int message_length = ptr[0] + (ptr[1] * 256) + 3; // 3 = ULog msg header if(message_length > data.length()) break; _writeData(data.data(), message_length); data.remove(0, message_length); } return data; } //----------------------------------------------------------------------------- bool MAVLinkLogProcessor::processStreamData(uint16_t sequence, uint8_t first_message, QByteArray data) { int num_drops = 0; _error = false; while(_checkSequence(sequence, num_drops)) { //-- The first 16 bytes need special treatment (this sounds awfully brittle) if(!_gotHeader) { if(data.size() < 16) { //-- Shouldn't happen but if it does, we might as well close shop. qCWarning(MAVLinkLogManagerLog) << "Corrupt log header. Canceling log download."; return false; } //-- Write header _writeData(data.data(), 16); data.remove(0, 16); _gotHeader = true; // What about data start offset now that we removed 16 bytes off the start? } if(_gotHeader && num_drops > 0) { if(num_drops > 25) num_drops = 25; //-- Hocus Pocus // Write a dropout message. We don't really know the actual duration, // so just use the number of drops * 10 ms uint8_t bogus[] = {2, 0, 79, 0, 0}; bogus[3] = num_drops * 10; _writeData(bogus, sizeof(bogus)); } if(num_drops > 0) { _writeUlogMessage(_ulogMessage); _ulogMessage.clear(); //-- If no useful information in this message. Drop it. if(first_message == 255) { break; } if(first_message > 0) { data.remove(0, first_message); first_message = 0; } } if(first_message == 255 && _ulogMessage.length() > 0) { _ulogMessage.append(data); break; } if(_ulogMessage.length()) { _writeData(_ulogMessage.data(), _ulogMessage.length()); if(first_message) { _writeData(data.left(first_message).data(), first_message); } _ulogMessage.clear(); } if(first_message) { data.remove(0, first_message); } _ulogMessage = _writeUlogMessage(data); break; } return !_error; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- MAVLinkLogManager::MAVLinkLogManager(QGCApplication* app, QGCToolbox* toolbox) : QGCTool(app, toolbox) , _enableAutoUpload(true) , _enableAutoStart(false) , _nam(nullptr) , _currentLogfile(nullptr) , _vehicle(nullptr) , _logRunning(false) , _loggingDisabled(false) , _logProcessor(nullptr) , _deleteAfterUpload(false) , _windSpeed(-1) , _publicLog(false) , _logginDenied(false) { //-- Get saved settings QSettings settings; settings.beginGroup(kMAVLinkLogGroup); setEmailAddress(settings.value(kEmailAddressKey, QString()).toString()); setDescription(settings.value(kDescriptionsKey, QString(kDefaultDescr)).toString()); setUploadURL(settings.value(kPx4URLKey, QString(kDefaultPx4URL)).toString()); setVideoURL(settings.value(kVideoURLKey, QString()).toString()); setEnableAutoUpload(settings.value(kEnableAutoUploadKey, true).toBool()); setEnableAutoStart(settings.value(kEnableAutoStartKey, false).toBool()); setDeleteAfterUpload(settings.value(kEnableDeletetKey, false).toBool()); setWindSpeed(settings.value(kWindSpeedKey, -1).toInt()); setRating(settings.value(kRateKey, "notset").toString()); setPublicLog(settings.value(kPublicLogKey, true).toBool()); } //----------------------------------------------------------------------------- MAVLinkLogManager::~MAVLinkLogManager() { _logFiles.clear(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setToolbox(QGCToolbox* toolbox) { QGCTool::setToolbox(toolbox); QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.MAVLinkLogManager", 1, 0, "MAVLinkLogManager", "Reference only"); //-- Logging location _ulogExtension = "."; _ulogExtension += qgcApp()->toolbox()->settingsManager()->appSettings()->logFileExtension; _logPath = qgcApp()->toolbox()->settingsManager()->appSettings()->logSavePath(); if(!QDir(_logPath).exists()) { if(!QDir().mkpath(_logPath)) { qCWarning(MAVLinkLogManagerLog) << "Could not create MAVLink log download path:" << _logPath; _loggingDisabled = true; } } if(!_loggingDisabled) { //-- Load current list of logs QString filter = "*"; filter += _ulogExtension; QDirIterator it(_logPath, QStringList() << filter, QDir::Files); while(it.hasNext()) { _insertNewLog(new MAVLinkLogFiles(this, it.next())); } qCDebug(MAVLinkLogManagerLog) << "MAVLink logs directory:" << _logPath; connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MAVLinkLogManager::_activeVehicleChanged); } } //----------------------------------------------------------------------------- void MAVLinkLogManager::setEmailAddress(QString email) { _emailAddress = email; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kEmailAddressKey, email); emit emailAddressChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setDescription(QString description) { _description = description; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kDescriptionsKey, description); emit descriptionChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setUploadURL(QString url) { _uploadURL = url; if(_uploadURL.isEmpty()) { _uploadURL = kDefaultPx4URL; } QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kPx4URLKey, _uploadURL); emit uploadURLChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setFeedback(QString fb) { _feedback = fb; emit feedbackChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setVideoURL(QString url) { _videoURL = url; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kVideoURLKey, url); emit videoURLChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setEnableAutoUpload(bool enable) { _enableAutoUpload = enable; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kEnableAutoUploadKey, enable); emit enableAutoUploadChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setEnableAutoStart(bool enable) { _enableAutoStart = enable; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kEnableAutoStartKey, enable); emit enableAutoStartChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setDeleteAfterUpload(bool enable) { _deleteAfterUpload = enable; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kEnableDeletetKey, enable); emit deleteAfterUploadChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setWindSpeed(int speed) { _windSpeed = speed; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kWindSpeedKey, speed); emit windSpeedChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setRating(QString rate) { _rating = rate; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kRateKey, rate); emit ratingChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::setPublicLog(bool pub) { _publicLog = pub; QSettings settings; settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kPublicLogKey, pub); emit publicLogChanged(); } //----------------------------------------------------------------------------- bool MAVLinkLogManager::uploading() { return _currentLogfile != nullptr; } //----------------------------------------------------------------------------- void MAVLinkLogManager::uploadLog() { if(_currentLogfile) { _currentLogfile->setUploading(false); } for(int i = 0; i < _logFiles.count(); i++) { _currentLogfile = qobject_cast(_logFiles.get(i)); if (_currentLogfile) { if(_currentLogfile->selected()) { _currentLogfile->setSelected(false); if(!_currentLogfile->uploaded() && !_emailAddress.isEmpty() && !_uploadURL.isEmpty()) { _currentLogfile->setUploading(true); _currentLogfile->setProgress(0.0); QString filePath = _makeFilename(_currentLogfile->name()); _sendLog(filePath); emit uploadingChanged(); return; } } } else { qWarning() << "Internal error"; } } _currentLogfile = nullptr; 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(_logFiles.get(i)); if(newLog->name() < f->name()) { _logFiles.insert(i, newLog); return; } } _logFiles.append(newLog); } } //----------------------------------------------------------------------------- int MAVLinkLogManager::_getFirstSelected() { for(int i = 0; i < _logFiles.count(); i++) { MAVLinkLogFiles* f = qobject_cast(_logFiles.get(i)); if (f) { if(f->selected()) { return i; } } else { qWarning() << "Internal error"; } } return -1; } //----------------------------------------------------------------------------- void MAVLinkLogManager::deleteLog() { while (true) { int idx = _getFirstSelected(); if(idx < 0) { break; } MAVLinkLogFiles* log = qobject_cast(_logFiles.get(idx)); _deleteLog(log); } } //----------------------------------------------------------------------------- void MAVLinkLogManager::_deleteLog(MAVLinkLogFiles* log) { QString filePath = _makeFilename(log->name()); QFile gone(filePath); if(!gone.remove()) { qCWarning(MAVLinkLogManagerLog) << "Could not delete MAVLink log file:" << _logPath; } //-- Remove sidecar file (if any) filePath.replace(_ulogExtension, kSidecarExtension); QFile sgone(filePath); if(sgone.exists()) { sgone.remove(); } //-- Remove file from list and delete record _logFiles.removeOne(log); delete log; emit logFilesChanged(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::cancelUpload() { for(int i = 0; i < _logFiles.count(); i++) { MAVLinkLogFiles* pLogFile = qobject_cast(_logFiles.get(i)); if (pLogFile) { if(pLogFile->selected() && pLogFile != _currentLogfile) { pLogFile->setSelected(false); } } else { qWarning() << "Internal error"; } } if(_currentLogfile) { emit abortUpload(); } } //----------------------------------------------------------------------------- void MAVLinkLogManager::startLogging() { //-- If we are allowed to persist data AppSettings* appSettings = qgcApp()->toolbox()->settingsManager()->appSettings(); if(!appSettings->disableAllPersistence()->rawValue().toBool()) { if(_vehicle && _vehicle->px4Firmware() && !_logginDenied) { if(_createNewLog()) { _vehicle->startMavlinkLog(); _logRunning = true; emit logRunningChanged(); } } } } //----------------------------------------------------------------------------- void MAVLinkLogManager::stopLogging() { if(_vehicle && _vehicle->px4Firmware()) { //-- Tell vehicle to stop sending logs _vehicle->stopMavlinkLog(); } if(_logProcessor) { _logProcessor->close(); if(_logProcessor->record()) { _logProcessor->record()->setWriting(false); if(_enableAutoUpload) { //-- Queue log for auto upload (set selected flag) _logProcessor->record()->setSelected(true); if(!uploading()) { uploadLog(); } } } delete _logProcessor; _logProcessor = nullptr; _logRunning = false; emit logRunningChanged(); } } //----------------------------------------------------------------------------- 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()) { qCWarning(MAVLinkLogManagerLog) << "User email missing."; return false; } if(_uploadURL.isEmpty()) { qCWarning(MAVLinkLogManagerLog) << "Upload URL missing."; return false; } QFileInfo fi(logFile); if(!fi.exists()) { qCWarning(MAVLinkLogManagerLog) << "Log file missing:" << logFile; return false; } QFile* file = new QFile(logFile); if(!file || !file->open(QIODevice::ReadOnly)) { delete file; file = nullptr; qCWarning(MAVLinkLogManagerLog) << "Could not open log file:" << logFile; return false; } if(!_nam) { _nam = new QNetworkAccessManager(this); } QNetworkProxy savedProxy = _nam->proxy(); QNetworkProxy tempProxy; tempProxy.setType(QNetworkProxy::DefaultProxy); _nam->setProxy(tempProxy); //-- Build POST request QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); 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 typePart = create_form_part("type", "flightreport"); QHttpPart windPart = create_form_part("windSpeed", QString::number(_windSpeed)); QHttpPart ratingPart = create_form_part("rating", _rating); QHttpPart publicPart = create_form_part("public", _publicLog ? "true" : "false"); //-- Assemble request and POST it multiPart->append(emailPart); multiPart->append(descriptionPart); multiPart->append(sourcePart); multiPart->append(versionPart); multiPart->append(typePart); multiPart->append(windPart); multiPart->append(ratingPart); multiPart->append(publicPart); //-- Optional QHttpPart feedbackPart; if(_feedback.isEmpty()) { feedbackPart = create_form_part(kFeedback, "None Given"); } else { feedbackPart = create_form_part(kFeedback, _feedback); } multiPart->append(feedbackPart); QHttpPart videoPart; if(_videoURL.isEmpty()) { videoPart = create_form_part(kVideoURL, "None"); } else { videoPart = create_form_part(kVideoURL, _videoURL); } multiPart->append(videoPart); //-- Actual Log File 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); multiPart->append(logPart); file->setParent(multiPart); QNetworkRequest request(_uploadURL); #if QT_VERSION > 0x050600 request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); #endif QNetworkReply* reply = _nam->post(request, multiPart); 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 MAVLinkLogManager::_processUploadResponse(int http_code, QByteArray& data) { qCDebug(MAVLinkLogManagerLog) << "Uploaded response:" << QString::fromUtf8(data); emit readyRead(data); return http_code == 200; } //----------------------------------------------------------------------------- void MAVLinkLogManager::_dataAvailable() { QNetworkReply* reply = qobject_cast(sender()); if(!reply) { return; } QByteArray data = reply->readAll(); qCDebug(MAVLinkLogManagerLog) << "Uploaded response data:" << QString::fromUtf8(data); } //----------------------------------------------------------------------------- void MAVLinkLogManager::_uploadFinished() { QNetworkReply* reply = qobject_cast(sender()); 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(); if(_deleteAfterUpload) { if(_currentLogfile) { _deleteLog(_currentLogfile); _currentLogfile = nullptr; } } else { if(_currentLogfile) { _currentLogfile->setUploaded(true); //-- Write side-car file to flag it as uploaded QString sideCar = _makeFilename(_currentLogfile->name()); sideCar.replace(_ulogExtension, kSidecarExtension); FILE* f = fopen(sideCar.toLatin1().data(), "wb"); if(f) { fclose(f); } } } } else { qCWarning(MAVLinkLogManagerLog) << QString("Log Upload Error: %1 status: %2").arg(reply->errorString(), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()); emit failed(); } reply->deleteLater(); //-- Next (if any) uploadLog(); } //----------------------------------------------------------------------------- void MAVLinkLogManager::_uploadProgress(qint64 bytesSent, qint64 bytesTotal) { if(bytesTotal) { qreal progress = static_cast(bytesSent) / static_cast(bytesTotal); if(_currentLogfile) { _currentLogfile->setProgress(progress); } } qCDebug(MAVLinkLogManagerLog) << bytesSent << "of" << bytesTotal; } //----------------------------------------------------------------------------- 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. // For now, we only handle one log download at a time. The proper way is to have // each vehicle with their own instance of this "log manager". // Disconnect the previous one (if any) if(_vehicle && _vehicle->px4Firmware()) { disconnect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); disconnect(_vehicle, &Vehicle::mavCommandResult, this, &MAVLinkLogManager::_mavCommandResult); _vehicle = nullptr; //-- Stop logging (if that's the case) stopLogging(); emit canStartLogChanged(); } // Connect new system if(vehicle && vehicle->px4Firmware()) { _vehicle = vehicle; //-- Reset logging denied flag as well _logginDenied = false; connect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); connect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); connect(_vehicle, &Vehicle::mavCommandResult, this, &MAVLinkLogManager::_mavCommandResult); emit canStartLogChanged(); } } //----------------------------------------------------------------------------- void MAVLinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t first_message, QByteArray data, bool /*acked*/) { if(_logProcessor && _logProcessor->valid()) { if(!_logProcessor->processStreamData(sequence, first_message, data)) { qCWarning(MAVLinkLogManagerLog) << "Error writing MAVLink log file:" << _logProcessor->fileName(); delete _logProcessor; _logProcessor = nullptr; _logRunning = false; _vehicle->stopMavlinkLog(); emit logRunningChanged(); } } else { qCWarning(MAVLinkLogManagerLog) << "MAVLink log data received when not expected."; } } //----------------------------------------------------------------------------- void MAVLinkLogManager::_mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle) { Q_UNUSED(vehicleId); Q_UNUSED(component); Q_UNUSED(noReponseFromVehicle) if(command == MAV_CMD_LOGGING_START || command == MAV_CMD_LOGGING_STOP) { //-- Did it fail? if(result != MAV_RESULT_ACCEPTED) { if(command == MAV_CMD_LOGGING_STOP) { //-- Not that it could happen but... qCWarning(MAVLinkLogManagerLog) << "Stop MAVLink log command failed."; } else { //-- Could not start logging for some reason. if(result == MAV_RESULT_DENIED) { _logginDenied = true; qCWarning(MAVLinkLogManagerLog) << "Start MAVLink log command denied."; } else { qCWarning(MAVLinkLogManagerLog) << "Start MAVLink log command failed."; } _discardLog(); } } } } //----------------------------------------------------------------------------- void MAVLinkLogManager::_discardLog() { //-- Delete (empty) log file (and record) if(_logProcessor) { _logProcessor->close(); if(_logProcessor->record()) { _deleteLog(_logProcessor->record()); } delete _logProcessor; _logProcessor = nullptr; } _logRunning = false; emit logRunningChanged(); } //----------------------------------------------------------------------------- bool MAVLinkLogManager::_createNewLog() { delete _logProcessor; _logProcessor = new MAVLinkLogProcessor; if(_logProcessor->create(this, _logPath, static_cast(_vehicle->id()))) { _insertNewLog(_logProcessor->record()); emit logFilesChanged(); } else { qCWarning(MAVLinkLogManagerLog) << "Could not create MAVLink log file:" << _logProcessor->fileName(); delete _logProcessor; _logProcessor = nullptr; } return _logProcessor != nullptr; } //----------------------------------------------------------------------------- void MAVLinkLogManager::_armedChanged(bool armed) { if(_vehicle && _vehicle->px4Firmware()) { if(armed) { if(_enableAutoStart) { startLogging(); } } else { if(_logRunning && _enableAutoStart) { stopLogging(); } } } } //----------------------------------------------------------------------------- QString MAVLinkLogManager::_makeFilename(const QString& baseName) { QString filePath = _logPath; filePath += "/"; filePath += baseName; filePath += _ulogExtension; return filePath; }