From 064d990feadc5a59c8668fc626e7cf21a16fb978 Mon Sep 17 00:00:00 2001 From: Holden Date: Tue, 27 Aug 2024 18:33:08 -0400 Subject: [PATCH] AnalyzeView: Download Logs in Separate Thread --- src/AnalyzeView/CMakeLists.txt | 3 +- src/AnalyzeView/LogDownloadController.cc | 771 ++++++++++++----------- src/AnalyzeView/LogDownloadController.h | 134 ++-- src/AnalyzeView/LogEntry.cc | 48 +- src/AnalyzeView/LogEntry.h | 123 ++-- test/AnalyzeView/LogDownloadTest.cc | 29 +- test/AnalyzeView/LogDownloadTest.h | 29 +- test/CMakeLists.txt | 2 +- test/UnitTestList.cc | 4 +- 9 files changed, 607 insertions(+), 536 deletions(-) diff --git a/src/AnalyzeView/CMakeLists.txt b/src/AnalyzeView/CMakeLists.txt index 9cd572d2113..c14060f003b 100644 --- a/src/AnalyzeView/CMakeLists.txt +++ b/src/AnalyzeView/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(Qt6 REQUIRED COMPONENTS Core Charts Gui Qml QmlIntegration) +find_package(Qt6 REQUIRED COMPONENTS Concurrent Core Charts Gui Qml QmlIntegration) qt_add_library(AnalyzeView STATIC GeoTagController.cc @@ -27,6 +27,7 @@ qt_add_library(AnalyzeView STATIC target_link_libraries(AnalyzeView PRIVATE + Qt6::Concurrent Qt6::Charts Qt6::Gui Qt6::Qml diff --git a/src/AnalyzeView/LogDownloadController.cc b/src/AnalyzeView/LogDownloadController.cc index 1070cffd449..4d7138bcf2d 100644 --- a/src/AnalyzeView/LogDownloadController.cc +++ b/src/AnalyzeView/LogDownloadController.cc @@ -8,238 +8,197 @@ ****************************************************************************/ #include "LogDownloadController.h" +#include "LogEntry.h" +#include "MAVLinkProtocol.h" #include "MultiVehicleManager.h" +#include "ParameterManager.h" #include "QGCApplication.h" +#include "QGCLoggingCategory.h" #include "QGCToolbox.h" -#include "ParameterManager.h" -#include "Vehicle.h" +#include "QmlObjectListModel.h" #include "SettingsManager.h" -#include "MAVLinkProtocol.h" -#include "LogEntry.h" -#include "QGCLoggingCategory.h" +#include "Vehicle.h" -#define kTimeOutMilliseconds 500 -#define kGUIRateMilliseconds 17 -#define kTableBins 512 -#define kChunkSize (kTableBins * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN) +#include +#include QGC_LOGGING_CATEGORY(LogDownloadControllerLog, "qgc.analyzeview.logdownloadcontroller") -//---------------------------------------------------------------------------------------- -LogDownloadController::LogDownloadController(void) - : _downloadData(nullptr) - , _vehicle(nullptr) - , _requestingLogEntries(false) - , _downloadingLogs(false) - , _retries(0) - , _apmOneBased(0) +/*===========================================================================*/ + +LogDownloadWorker::LogDownloadWorker(const QString &dir, QObject *parent) + : QObject(parent) + , _dir(dir) +{ + // qCDebug(LogDownloadControllerLog) << Q_FUNC_INFO << this; +} + +LogDownloadWorker::~LogDownloadWorker() { - MultiVehicleManager *manager = qgcApp()->toolbox()->multiVehicleManager(); - connect(manager, &MultiVehicleManager::activeVehicleChanged, this, &LogDownloadController::_setActiveVehicle); - connect(&_timer, &QTimer::timeout, this, &LogDownloadController::_processDownload); + // qCDebug(LogDownloadControllerLog) << Q_FUNC_INFO << this; +} + +void LogDownloadWorker::process() +{ + downloadToDirectory(_dir); +} + +void LogDownloadWorker::downloadToDirectory(const QString &dir) +{ + emit downloadCompleted(); +} + +/*===========================================================================*/ + +LogDownloadController::LogDownloadController(QObject *parent) + : QObject(parent) + , _timer(new QTimer(this)) + , _logEntriesModel(new QmlObjectListModel(this)) + , _workerThread(new QThread(this)) +{ + // qCDebug(LogDownloadControllerLog) << Q_FUNC_INFO << this; + + MultiVehicleManager *const manager = qgcApp()->toolbox()->multiVehicleManager(); + (void) connect(manager, &MultiVehicleManager::activeVehicleChanged, this, &LogDownloadController::_setActiveVehicle); + (void) connect(_timer, &QTimer::timeout, this, &LogDownloadController::_processDownload); + + _timer->setSingleShot(false); + _setActiveVehicle(manager->activeVehicle()); } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_processDownload() +LogDownloadController::~LogDownloadController() { - if(_requestingLogEntries) { - _findMissingEntries(); - } else if(_downloadingLogs) { - _findMissingData(); + if (_workerThread) { + _workerThread->quit(); + _workerThread->wait(); + delete _workerThread; } + + // qCDebug(LogDownloadControllerLog) << Q_FUNC_INFO << this; } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_setActiveVehicle(Vehicle* vehicle) +void LogDownloadController::download(const QString &path) { - if(_vehicle) { - _logEntriesModel.clearAndDeleteContents(); - disconnect(_vehicle, &Vehicle::logEntry, this, &LogDownloadController::_logEntry); - disconnect(_vehicle, &Vehicle::logData, this, &LogDownloadController::_logData); - } - _vehicle = vehicle; - if(_vehicle) { - connect(_vehicle, &Vehicle::logEntry, this, &LogDownloadController::_logEntry); - connect(_vehicle, &Vehicle::logData, this, &LogDownloadController::_logData); + QString dir = path; + if (dir.isEmpty()) { + dir = qgcApp()->toolbox()->settingsManager()->appSettings()->logSavePath(); } + + LogDownloadWorker *const worker = new LogDownloadWorker(dir); + + worker->moveToThread(_workerThread); + + (void) connect(_workerThread, &QThread::started, worker, &LogDownloadWorker::process); + (void) connect(worker, &LogDownloadWorker::downloadCompleted, this, &LogDownloadController::_onDownloadCompleted); + (void) connect(worker, &LogDownloadWorker::downloadCompleted, _workerThread, &QThread::quit); + (void) connect(_workerThread, &QThread::finished, worker, &QObject::deleteLater); + + _workerThread->start(); + + // (void) QtConcurrent::run([this, dir]() { + // downloadToDirectory(dir); + // }); } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_logEntry(uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t /*last_log_num*/) +void LogDownloadController::downloadToDirectory(const QString &dir) { - //-- Do we care? - if(!_requestingLogEntries) { + _receivedAllEntries(); + + delete _downloadData; + _downloadData = nullptr; + + _downloadPath = dir; + if (_downloadPath.isEmpty()) { return; } - //-- If this is the first, pre-fill it - if(!_logEntriesModel.count() && num_logs > 0) { - //-- Is this APM? They send a first entry with bogus ID and only the - // count is valid. From now on, all entries are 1-based. - if(_vehicle->firmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { - _apmOneBased = 1; - } - for(int i = 0; i < num_logs; i++) { - QGCLogEntry *entry = new QGCLogEntry(i); - _logEntriesModel.append(entry); - } - } - //-- Update this log record - if(num_logs > 0) { - //-- Skip if empty (APM first packet) - if(size || _vehicle->firmwareType() != MAV_AUTOPILOT_ARDUPILOTMEGA) { - id -= _apmOneBased; - if(id < _logEntriesModel.count()) { - QGCLogEntry* entry = _logEntriesModel.value(id); - entry->setSize(size); - entry->setTime(QDateTime::fromSecsSinceEpoch(time_utc)); - entry->setReceived(true); - entry->setStatus(tr("Available")); - } else { - qCWarning(LogDownloadControllerLog) << "Received log entry for out-of-bound index:" << id; - } - } - } else { - //-- No logs to list - _receivedAllEntries(); + + if (!_downloadPath.endsWith(QDir::separator())) { + _downloadPath += QDir::separator(); } - //-- Reset retry count - _retries = 0; - //-- Do we have it all? - if(_entriesComplete()) { - _receivedAllEntries(); - } else { - //-- Reset timer - _timer.start(kTimeOutMilliseconds); + + QGCLogEntry *const log = _getNextSelected(); + if (log) { + log->setStatus(tr("Waiting")); } + + _setDownloading(true); + _receivedAllData(); } -//---------------------------------------------------------------------------------------- -bool -LogDownloadController::_entriesComplete() +void LogDownloadController::_setActiveVehicle(Vehicle *vehicle) { - //-- Iterate entries and look for a gap - int num_logs = _logEntriesModel.count(); - for(int i = 0; i < num_logs; i++) { - QGCLogEntry* entry = _logEntriesModel.value(i); - if(entry) { - if(!entry->received()) { - return false; - } - } + if (_vehicle) { + _logEntriesModel->clearAndDeleteContents(); + (void) disconnect(_vehicle, &Vehicle::logEntry, this, &LogDownloadController::_logEntry); + (void) disconnect(_vehicle, &Vehicle::logData, this, &LogDownloadController::_logData); } - return true; -} -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_resetSelection(bool canceled) -{ - int num_logs = _logEntriesModel.count(); - for(int i = 0; i < num_logs; i++) { - QGCLogEntry* entry = _logEntriesModel.value(i); - if(entry) { - if(entry->selected()) { - if(canceled) { - entry->setStatus(tr("Canceled")); - } - entry->setSelected(false); - } - } + _vehicle = vehicle; + + if (_vehicle) { + (void) connect(_vehicle, &Vehicle::logEntry, this, &LogDownloadController::_logEntry); + (void) connect(_vehicle, &Vehicle::logData, this, &LogDownloadController::_logData); } - emit selectionChanged(); } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_receivedAllEntries() +void LogDownloadController::_logEntry(uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t last_log_num) { - _timer.stop(); - _setListing(false); -} + Q_UNUSED(last_log_num); -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_findMissingEntries() -{ - int start = -1; - int end = -1; - int num_logs = _logEntriesModel.count(); - //-- Iterate entries and look for a gap - for(int i = 0; i < num_logs; i++) { - QGCLogEntry* entry = _logEntriesModel.value(i); - if(entry) { - if(!entry->received()) { - if(start < 0) - start = i; - else - end = i; - } else { - if(start >= 0) { - break; - } - } + if (!_requestingLogEntries) { + return; + } + + // If this is the first, pre-fill it + if ((_logEntriesModel->count() == 0) && (num_logs > 0)) { + // Is this APM? They send a first entry with bogus ID and only the + // count is valid. From now on, all entries are 1-based. + if (_vehicle->firmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { + _apmOneBased = 1; + } + + for (int i = 0; i < num_logs; i++) { + QGCLogEntry *const entry = new QGCLogEntry(i); + (void) _logEntriesModel->append(entry); } } - //-- Is there something missing? - if(start >= 0) { - //-- Have we tried too many times? - if(_retries++ > 2) { - for(int i = 0; i < num_logs; i++) { - QGCLogEntry* entry = _logEntriesModel.value(i); - if(entry && !entry->received()) { - entry->setStatus(tr("Error")); - } + + if (num_logs > 0) { + if ((size > 0) || (_vehicle->firmwareType() != MAV_AUTOPILOT_ARDUPILOTMEGA)) { + id -= _apmOneBased; + if (id < _logEntriesModel->count()) { + QGCLogEntry *const entry = _logEntriesModel->value(id); + entry->setSize(size); + entry->setTime(QDateTime::fromSecsSinceEpoch(time_utc)); + entry->setReceived(true); + entry->setStatus(tr("Available")); + } else { + qCWarning(LogDownloadControllerLog) << "Received log entry for out-of-bound index:" << id; } - //-- Give up - _receivedAllEntries(); - qCWarning(LogDownloadControllerLog) << "Too many errors retreiving log list. Giving up."; - return; } - //-- Is it a sequence or just one entry? - if(end < 0) { - end = start; - } - //-- APM "Fix" - start += _apmOneBased; - end += _apmOneBased; - //-- Request these entries again - _requestLogList((uint32_t)start, (uint32_t) end); } else { _receivedAllEntries(); } -} - -void LogDownloadController::_updateDataRate(void) -{ - if (_downloadData->elapsed.elapsed() >= kGUIRateMilliseconds) { - //-- Update download rate - qreal rrate = _downloadData->rate_bytes / (_downloadData->elapsed.elapsed() / 1000.0); - _downloadData->rate_avg = (_downloadData->rate_avg * 0.95) + (rrate * 0.05); - _downloadData->rate_bytes = 0; - //-- Update status - const QString status = QString("%1 (%2/s)").arg(qgcApp()->bigSizeToString(_downloadData->written), - qgcApp()->bigSizeToString(_downloadData->rate_avg)); + _retries = 0; - _downloadData->entry->setStatus(status); - _downloadData->elapsed.start(); + if (_entriesComplete()) { + _receivedAllEntries(); + } else { + _timer->start(kTimeOutMilliseconds); } } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_logData(uint32_t ofs, uint16_t id, uint8_t count, const uint8_t* data) +void LogDownloadController::_logData(uint32_t ofs, uint16_t id, uint8_t count, const uint8_t* data) { - if(!_downloadData) { + if (!_downloadData) { return; } - //-- APM "Fix" + + //-- APM Offset id -= _apmOneBased; - if(_downloadData->ID != id) { + if (_downloadData->ID != id) { qCWarning(LogDownloadControllerLog) << "Received log data for wrong log"; return; } @@ -250,47 +209,45 @@ LogDownloadController::_logData(uint32_t ofs, uint16_t id, uint8_t count, const } bool result = false; - uint32_t timeout_time = kTimeOutMilliseconds; - if(ofs <= _downloadData->entry->size()) { - const uint32_t chunk = ofs / kChunkSize; + if (ofs <= _downloadData->entry->size()) { + const uint32_t chunk = ofs / LogDownloadData::kChunkSize; if (chunk != _downloadData->current_chunk) { qCWarning(LogDownloadControllerLog) << "Ignored packet for out of order chunk actual:expected" << chunk << _downloadData->current_chunk; return; } - const uint16_t bin = (ofs - chunk*kChunkSize) / MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN; + + const uint16_t bin = (ofs - (chunk * LogDownloadData::kChunkSize)) / MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN; if (bin >= _downloadData->chunk_table.size()) { qCWarning(LogDownloadControllerLog) << "Out of range bin received"; - } else + } else { _downloadData->chunk_table.setBit(bin); + } + if (_downloadData->file.pos() != ofs) { - // Seek to correct position if (!_downloadData->file.seek(ofs)) { qCWarning(LogDownloadControllerLog) << "Error while seeking log file offset"; return; } } - //-- Write chunk to file - if(_downloadData->file.write((const char*)data, count)) { + if (_downloadData->file.write(reinterpret_cast(data), count)) { _downloadData->written += count; _downloadData->rate_bytes += count; _updateDataRate(); + result = true; - //-- reset retries _retries = 0; - //-- Reset timer - _timer.start(timeout_time); - //-- Do we have it all? - if(_logComplete()) { + + _timer->start(kTimeOutMilliseconds); + if (_logComplete()) { _downloadData->entry->setStatus(tr("Downloaded")); - //-- Check for more _receivedAllData(); } else if (_chunkComplete()) { _downloadData->advanceChunk(); _requestLogData(_downloadData->ID, - _downloadData->current_chunk*kChunkSize, - _downloadData->chunk_table.size()*MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN); - } else if (bin < _downloadData->chunk_table.size() - 1 && _downloadData->chunk_table.at(bin+1)) { + _downloadData->current_chunk * LogDownloadData::kChunkSize, + _downloadData->chunk_table.size() * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN); + } else if ((bin < (_downloadData->chunk_table.size() - 1)) && _downloadData->chunk_table.at(bin + 1)) { // Likely to be grabbing fragments and got to the end of a gap _findMissingData(); } @@ -300,58 +257,176 @@ LogDownloadController::_logData(uint32_t ofs, uint16_t id, uint8_t count, const } else { qCWarning(LogDownloadControllerLog) << "Received log offset greater than expected"; } - if(!result) { + + if (!result) { _downloadData->entry->setStatus(tr("Error")); } } +void LogDownloadController::_processDownload() +{ + if (_requestingLogEntries) { + _findMissingEntries(); + } else if (_downloadingLogs) { + _findMissingData(); + } +} + +void LogDownloadController::_onDownloadCompleted() +{ + +} + +bool LogDownloadController::_entriesComplete() const +{ + const int num_logs = _logEntriesModel->count(); + for (int i = 0; i < num_logs; i++) { + const QGCLogEntry *const entry = _logEntriesModel->value(i); + if (!entry) { + continue; + } + + if (!entry->received()) { + return false; + } + } + + return true; +} + +void LogDownloadController::_resetSelection(bool canceled) +{ + const int num_logs = _logEntriesModel->count(); + for (int i = 0; i < num_logs; i++) { + QGCLogEntry *const entry = _logEntriesModel->value(i); + if (!entry) { + continue; + } + + if (entry->selected()) { + if (canceled) { + entry->setStatus(tr("Canceled")); + } + entry->setSelected(false); + } + } + + emit selectionChanged(); +} + +void LogDownloadController::_receivedAllEntries() +{ + _timer->stop(); + _setListing(false); +} + +void LogDownloadController::_findMissingEntries() +{ + int start = -1; + int end = -1; + int num_logs = _logEntriesModel->count(); + for (int i = 0; i < num_logs; i++) { + const QGCLogEntry *const entry = _logEntriesModel->value(i); + if (!entry) { + continue; + } + + if (!entry->received()) { + if (start < 0) { + start = i; + } else { + end = i; + } + } else if (start >= 0) { + break; + } + } + + if (start >= 0) { + if (_retries++ > 2) { + for (int i = 0; i < num_logs; i++) { + QGCLogEntry *const entry = _logEntriesModel->value(i); + if (entry && !entry->received()) { + entry->setStatus(tr("Error")); + } + } + + _receivedAllEntries(); + qCWarning(LogDownloadControllerLog) << "Too many errors retreiving log list. Giving up."; + return; + } + + //-- Is it a sequence or just one entry? + if (end < 0) { + end = start; + } + + //-- APM Offset + start += _apmOneBased; + end += _apmOneBased; + + _requestLogList(static_cast(start), static_cast(end)); + } else { + _receivedAllEntries(); + } +} + +void LogDownloadController::_updateDataRate() +{ + if (_downloadData->elapsed.elapsed() < kGUIRateMilliseconds) { + return; + } + + const qreal rrate = _downloadData->rate_bytes / (_downloadData->elapsed.elapsed() / 1000.0); + _downloadData->rate_avg = (_downloadData->rate_avg * 0.95) + (rrate * 0.05); + _downloadData->rate_bytes = 0; -//---------------------------------------------------------------------------------------- -bool -LogDownloadController::_chunkComplete() const + const QString status = QStringLiteral("%1 (%2/s)").arg(qgcApp()->bigSizeToString(_downloadData->written), + qgcApp()->bigSizeToString(_downloadData->rate_avg)); + + _downloadData->entry->setStatus(status); + _downloadData->elapsed.start(); +} + +bool LogDownloadController::_chunkComplete() const { return _downloadData->chunkEquals(true); } -//---------------------------------------------------------------------------------------- -bool -LogDownloadController::_logComplete() const +bool LogDownloadController::_logComplete() const { - return _chunkComplete() && (_downloadData->current_chunk+1) == _downloadData->numChunks(); + return (_chunkComplete() && ((_downloadData->current_chunk + 1) == _downloadData->numChunks())); } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_receivedAllData() +void LogDownloadController::_receivedAllData() { - _timer.stop(); - //-- Anything queued up for download? - if(_prepareLogDownload()) { - //-- Request Log - _requestLogData(_downloadData->ID, 0, _downloadData->chunk_table.size()*MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN); - _timer.start(kTimeOutMilliseconds); + _timer->stop(); + if (_prepareLogDownload()) { + _requestLogData(_downloadData->ID, 0, _downloadData->chunk_table.size() * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN); + _timer->start(kTimeOutMilliseconds); } else { _resetSelection(); _setDownloading(false); } } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_findMissingData() +void LogDownloadController::_findMissingData() { if (_logComplete()) { - _receivedAllData(); - return; - } else if (_chunkComplete()) { + _receivedAllData(); + return; + } + + if (_chunkComplete()) { _downloadData->advanceChunk(); } _retries++; + #if 0 // Trying the change to infinite log download. This way if retries hit 100% failure the data rate will // slowly fall to 0 and the user can Cancel. This should work better on really crappy links. - if(_retries > 5) { + if (_retries > 5) { _downloadData->entry->setStatus(tr("Timed Out")); //-- Give up qCWarning(LogDownloadControllerLog) << "Too many errors retreiving log data. Giving up."; @@ -376,153 +451,118 @@ LogDownloadController::_findMissingData() } } - const uint32_t pos = _downloadData->current_chunk*kChunkSize + start*MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN, - len = (end - start)*MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN; + const uint32_t pos = (_downloadData->current_chunk * LogDownloadData::kChunkSize) + (start * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN); + const uint32_t len = (end - start) * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN; _requestLogData(_downloadData->ID, pos, len, _retries); } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_requestLogData(uint16_t id, uint32_t offset, uint32_t count, int retryCount) +void LogDownloadController::_requestLogData(uint16_t id, uint32_t offset, uint32_t count, int retryCount) { - if (_vehicle) { - SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock(); - if (sharedLink) { - - //-- APM "Fix" - id += _apmOneBased; - qCDebug(LogDownloadControllerLog) << "Request log data (id:" << id << "offset:" << offset << "size:" << count << "retryCount" << retryCount << ")"; - mavlink_message_t msg; - mavlink_msg_log_request_data_pack_chan( - qgcApp()->toolbox()->mavlinkProtocol()->getSystemId(), - qgcApp()->toolbox()->mavlinkProtocol()->getComponentId(), - sharedLink->mavlinkChannel(), - &msg, - _vehicle->id(), _vehicle->defaultComponentId(), - id, offset, count); - _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg); - } + if (!_vehicle) { + return; + } + + SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock(); + if (sharedLink) { + //-- APM Offset + id += _apmOneBased; + qCDebug(LogDownloadControllerLog) << "Request log data (id:" << id << "offset:" << offset << "size:" << count << "retryCount" << retryCount << ")"; + + const MAVLinkProtocol* const mavlinkProtocol = qgcApp()->toolbox()->mavlinkProtocol(); + mavlink_message_t msg; + (void) mavlink_msg_log_request_data_pack_chan( + mavlinkProtocol->getSystemId(), + mavlinkProtocol->getComponentId(), + sharedLink->mavlinkChannel(), + &msg, + _vehicle->id(), + _vehicle->defaultComponentId(), + id, + offset, + count + ); + _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg); } } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::refresh(void) +void LogDownloadController::refresh() { - _logEntriesModel.clearAndDeleteContents(); - //-- Get first 50 entries + _logEntriesModel->clearAndDeleteContents(); _requestLogList(0, 49); } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_requestLogList(uint32_t start, uint32_t end) +void LogDownloadController::_requestLogList(uint32_t start, uint32_t end) { - if(_vehicle) { - qCDebug(LogDownloadControllerLog) << "Request log entry list (" << start << "through" << end << ")"; - _setListing(true); - SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock(); - if (sharedLink) { - mavlink_message_t msg; - mavlink_msg_log_request_list_pack_chan( - qgcApp()->toolbox()->mavlinkProtocol()->getSystemId(), - qgcApp()->toolbox()->mavlinkProtocol()->getComponentId(), - sharedLink->mavlinkChannel(), - &msg, - _vehicle->id(), - _vehicle->defaultComponentId(), - start, - end); - _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg); - } - //-- Wait 5 seconds before bitching about not getting anything - _timer.start(5000); + if (!_vehicle) { + return; } -} -//---------------------------------------------------------------------------------------- -void -LogDownloadController::download(QString path) -{ - QString dir = path; - if (dir.isEmpty()) { - dir = qgcApp()->toolbox()->settingsManager()->appSettings()->logSavePath(); + qCDebug(LogDownloadControllerLog) << "Request log entry list (" << start << "through" << end << ")"; + _setListing(true); + + SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock(); + if (sharedLink) { + const MAVLinkProtocol* const mavlinkProtocol = qgcApp()->toolbox()->mavlinkProtocol(); + mavlink_message_t msg; + (void) mavlink_msg_log_request_list_pack_chan( + mavlinkProtocol->getSystemId(), + mavlinkProtocol->getComponentId(), + sharedLink->mavlinkChannel(), + &msg, + _vehicle->id(), + _vehicle->defaultComponentId(), + start, + end + ); + _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg); } - downloadToDirectory(dir); + + _timer->start(5000); } -void LogDownloadController::downloadToDirectory(const QString& dir) +QGCLogEntry *LogDownloadController::_getNextSelected() { - //-- Stop listing just in case - _receivedAllEntries(); - //-- Reset downloads, again just in case - delete _downloadData; - _downloadData = nullptr; - - _downloadPath = dir; - if(!_downloadPath.isEmpty()) { - if(!_downloadPath.endsWith(QDir::separator())) - _downloadPath += QDir::separator(); - //-- Iterate selected entries and shown them as waiting - int num_logs = _logEntriesModel.count(); - for(int i = 0; i < num_logs; i++) { - QGCLogEntry* entry = _logEntriesModel.value(i); - if(entry) { - if(entry->selected()) { - entry->setStatus(tr("Waiting")); - } - } + const int num_logs = _logEntriesModel->count(); + for (int i = 0; i < num_logs; i++) { + QGCLogEntry *const entry = _logEntriesModel->value(i); + if (!entry) { + continue; } - //-- Start download process - _setDownloading(true); - _receivedAllData(); - } -} -//---------------------------------------------------------------------------------------- -QGCLogEntry* -LogDownloadController::_getNextSelected() -{ - //-- Iterate entries and look for a selected file - int num_logs = _logEntriesModel.count(); - for(int i = 0; i < num_logs; i++) { - QGCLogEntry* entry = _logEntriesModel.value(i); - if(entry) { - if(entry->selected()) { - return entry; - } + if (entry->selected()) { + return entry; } } + return nullptr; } -//---------------------------------------------------------------------------------------- -bool -LogDownloadController::_prepareLogDownload() +bool LogDownloadController::_prepareLogDownload() { delete _downloadData; _downloadData = nullptr; - QGCLogEntry* entry = _getNextSelected(); - if(!entry) { + QGCLogEntry *const entry = _getNextSelected(); + if (!entry) { return false; } - //-- Deselect file entry->setSelected(false); emit selectionChanged(); + bool result = false; QString ftime; - if(entry->time().date().year() < 2010) { - ftime = tr("UnknownDate"); + if (entry->time().date().year() < 2010) { + ftime = QStringLiteral("UnknownDate"); } else { ftime = entry->time().toString(QStringLiteral("yyyy-M-d-hh-mm-ss")); } + _downloadData = new LogDownloadData(entry); - _downloadData->filename = QString("log_") + QString::number(entry->id()) + "_" + ftime; + _downloadData->filename = QStringLiteral("log_") + QString::number(entry->id()) + "_" + ftime; if (_vehicle->firmwareType() == MAV_AUTOPILOT_PX4) { - QString loggerParam = QStringLiteral("SYS_LOGGER"); - if (_vehicle->parameterManager()->parameterExists(ParameterManager::defaultComponentId, loggerParam) && - _vehicle->parameterManager()->getParameter(ParameterManager::defaultComponentId, loggerParam)->rawValue().toInt() == 0) { + const QString loggerParam = QStringLiteral("SYS_LOGGER"); + ParameterManager *const parameterManager = _vehicle->parameterManager(); + if (parameterManager->parameterExists(ParameterManager::defaultComponentId, loggerParam) && parameterManager->getParameter(ParameterManager::defaultComponentId, loggerParam)->rawValue().toInt() == 0) { _downloadData->filename += ".px4log"; } else { _downloadData->filename += ".ulg"; @@ -531,21 +571,21 @@ LogDownloadController::_prepareLogDownload() _downloadData->filename += ".bin"; } _downloadData->file.setFileName(_downloadPath + _downloadData->filename); + //-- Append a number to the end if the filename already exists - if (_downloadData->file.exists()){ + if (_downloadData->file.exists()) { uint num_dups = 0; - QStringList filename_spl = _downloadData->filename.split('.'); + const QStringList filename_spl = _downloadData->filename.split('.'); do { num_dups +=1; _downloadData->file.setFileName(filename_spl[0] + '_' + QString::number(num_dups) + '.' + filename_spl[1]); } while( _downloadData->file.exists()); } - //-- Create file + if (!_downloadData->file.open(QIODevice::WriteOnly)) { qCWarning(LogDownloadControllerLog) << "Failed to create log file:" << _downloadData->filename; } else { - //-- Preallocate file - if(!_downloadData->file.resize(entry->size())) { + if (!_downloadData->file.resize(entry->size())) { qCWarning(LogDownloadControllerLog) << "Failed to allocate space for log file:" << _downloadData->filename; } else { _downloadData->current_chunk = 0; @@ -554,20 +594,20 @@ LogDownloadController::_prepareLogDownload() result = true; } } - if(!result) { + + if (!result) { if (_downloadData->file.exists()) { _downloadData->file.remove(); } - _downloadData->entry->setStatus(tr("Error")); + _downloadData->entry->setStatus(QStringLiteral("Error")); delete _downloadData; _downloadData = nullptr; } + return result; } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_setDownloading(bool active) +void LogDownloadController::_setDownloading(bool active) { if (_downloadingLogs != active) { _downloadingLogs = active; @@ -576,9 +616,7 @@ LogDownloadController::_setDownloading(bool active) } } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::_setListing(bool active) +void LogDownloadController::_setListing(bool active) { if (_requestingLogEntries != active) { _requestingLogEntries = active; @@ -587,39 +625,44 @@ LogDownloadController::_setListing(bool active) } } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::eraseAll(void) +void LogDownloadController::eraseAll() { - if(_vehicle) { - SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock(); - if (sharedLink) { - mavlink_message_t msg; - mavlink_msg_log_erase_pack_chan( - qgcApp()->toolbox()->mavlinkProtocol()->getSystemId(), - qgcApp()->toolbox()->mavlinkProtocol()->getComponentId(), - sharedLink->mavlinkChannel(), - &msg, - qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()->id(), qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()->defaultComponentId()); - _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg); - } - refresh(); + if (!_vehicle) { + return; + } + + SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock(); + if (sharedLink) { + const MAVLinkProtocol *const mavlinkProtocol = qgcApp()->toolbox()->mavlinkProtocol(); + MultiVehicleManager *const multiVehicleManager = qgcApp()->toolbox()->multiVehicleManager(); + mavlink_message_t msg; + (void) mavlink_msg_log_erase_pack_chan( + mavlinkProtocol->getSystemId(), + mavlinkProtocol->getComponentId(), + sharedLink->mavlinkChannel(), + &msg, + multiVehicleManager->activeVehicle()->id(), + multiVehicleManager->activeVehicle()->defaultComponentId() + ); + _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg); } + + refresh(); } -//---------------------------------------------------------------------------------------- -void -LogDownloadController::cancel(void) +void LogDownloadController::cancel() { _receivedAllEntries(); - if(_downloadData) { - _downloadData->entry->setStatus(tr("Canceled")); + + if (_downloadData) { + _downloadData->entry->setStatus(QStringLiteral("Canceled")); if (_downloadData->file.exists()) { _downloadData->file.remove(); } delete _downloadData; - _downloadData = 0; + _downloadData = nullptr; } + _resetSelection(true); _setDownloading(false); } diff --git a/src/AnalyzeView/LogDownloadController.h b/src/AnalyzeView/LogDownloadController.h index 228b036a630..b5c38b1c749 100644 --- a/src/AnalyzeView/LogDownloadController.h +++ b/src/AnalyzeView/LogDownloadController.h @@ -9,81 +9,111 @@ #pragma once -#include -#include #include +#include #include -#include "QmlObjectListModel.h" - Q_DECLARE_LOGGING_CATEGORY(LogDownloadControllerLog) -class Vehicle; -class QGCLogEntry; struct LogDownloadData; +class QGCLogEntry; +class QmlObjectListModel; +class QTimer; +class QThread; +class Vehicle; + +/*===========================================================================*/ + +class LogDownloadWorker : public QObject +{ + Q_OBJECT + +public: + explicit LogDownloadWorker(const QString &dir, QObject *parent = nullptr); + ~LogDownloadWorker(); + +signals: + void downloadCompleted(); + +public slots: + void process(); + +private: + void downloadToDirectory(const QString &dir); + + QString _dir; +}; + +/*===========================================================================*/ -//----------------------------------------------------------------------------- class LogDownloadController : public QObject { Q_OBJECT QML_ELEMENT Q_MOC_INCLUDE("Vehicle.h") + Q_MOC_INCLUDE("QmlObjectListModel.h") + Q_PROPERTY(QmlObjectListModel *model READ model NOTIFY modelChanged) + Q_PROPERTY(bool requestingList READ requestingList NOTIFY requestingListChanged) + Q_PROPERTY(bool downloadingLogs READ downloadingLogs NOTIFY downloadingLogsChanged) public: - LogDownloadController(void); + explicit LogDownloadController(QObject *parent = nullptr); + ~LogDownloadController(); - Q_PROPERTY(QmlObjectListModel* model READ model NOTIFY modelChanged) - Q_PROPERTY(bool requestingList READ requestingList NOTIFY requestingListChanged) - Q_PROPERTY(bool downloadingLogs READ downloadingLogs NOTIFY downloadingLogsChanged) + QmlObjectListModel *model() { return _logEntriesModel; } + bool requestingList() const { return _requestingLogEntries; } + bool downloadingLogs() const { return _downloadingLogs; } - QmlObjectListModel* model () { return &_logEntriesModel; } - bool requestingList () const{ return _requestingLogEntries; } - bool downloadingLogs () const{ return _downloadingLogs; } + Q_INVOKABLE void refresh(); + Q_INVOKABLE void download(const QString &path = QString()); + Q_INVOKABLE void eraseAll(); + Q_INVOKABLE void cancel(); - Q_INVOKABLE void refresh (); - Q_INVOKABLE void download (QString path = QString()); - Q_INVOKABLE void eraseAll (); - Q_INVOKABLE void cancel (); - - void downloadToDirectory(const QString& dir); + void downloadToDirectory(const QString &dir); signals: - void requestingListChanged (); - void downloadingLogsChanged (); - void modelChanged (); - void selectionChanged (); + void requestingListChanged(); + void downloadingLogsChanged(); + void modelChanged(); + void selectionChanged(); private slots: - void _setActiveVehicle (Vehicle* vehicle); - void _logEntry (uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t last_log_num); - void _logData (uint32_t ofs, uint16_t id, uint8_t count, const uint8_t *data); - void _processDownload (); + void _setActiveVehicle(Vehicle *vehicle); + void _logEntry(uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t last_log_num); + void _logData(uint32_t ofs, uint16_t id, uint8_t count, const uint8_t *data); + void _processDownload(); + void _onDownloadCompleted(); private: - bool _entriesComplete (); - bool _chunkComplete () const; - bool _logComplete () const; + bool _chunkComplete() const; + bool _entriesComplete() const; + bool _logComplete() const; + bool _prepareLogDownload(); + void _findMissingData(); void _findMissingEntries(); + void _receivedAllData(); void _receivedAllEntries(); - void _receivedAllData (); - void _resetSelection (bool canceled = false); - void _findMissingData (); - void _requestLogList (uint32_t start, uint32_t end); - void _requestLogData (uint16_t id, uint32_t offset, uint32_t count, int retryCount = 0); - bool _prepareLogDownload(); - void _setDownloading (bool active); - void _setListing (bool active); - void _updateDataRate (); - - QGCLogEntry* _getNextSelected(); - - LogDownloadData* _downloadData; - QTimer _timer; - QmlObjectListModel _logEntriesModel; - Vehicle* _vehicle; - bool _requestingLogEntries; - bool _downloadingLogs; - int _retries; - int _apmOneBased; - QString _downloadPath; + void _requestLogData(uint16_t id, uint32_t offset, uint32_t count, int retryCount = 0); + void _requestLogList(uint32_t start, uint32_t end); + void _resetSelection(bool canceled = false); + void _setDownloading(bool active); + void _setListing(bool active); + void _updateDataRate(); + + QGCLogEntry *_getNextSelected(); + + QTimer *_timer = nullptr; + QmlObjectListModel *_logEntriesModel = nullptr; + QThread *_workerThread = nullptr; + + bool _downloadingLogs = false; + bool _requestingLogEntries = false; + int _apmOneBased = 0; + int _retries = 0; + LogDownloadData *_downloadData = nullptr; + QString _downloadPath; + Vehicle *_vehicle = nullptr; + + static constexpr uint32_t kTimeOutMilliseconds = 500; + static constexpr uint32_t kGUIRateMilliseconds = 17; }; diff --git a/src/AnalyzeView/LogEntry.cc b/src/AnalyzeView/LogEntry.cc index 7e76e1cb0db..d910a6b3392 100644 --- a/src/AnalyzeView/LogEntry.cc +++ b/src/AnalyzeView/LogEntry.cc @@ -9,64 +9,62 @@ #include "LogEntry.h" #include "QGCApplication.h" -#include "MAVLinkLib.h" #include "QGCLoggingCategory.h" #include -#define kTableBins 512 -#define kChunkSize (kTableBins * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN) - QGC_LOGGING_CATEGORY(LogEntryLog, "qgc.analyzeview.logentry") -//----------------------------------------------------------------------------- -LogDownloadData::LogDownloadData(QGCLogEntry* entry_) - : ID(entry_->id()) - , entry(entry_) - , written(0) - , rate_bytes(0) - , rate_avg(0) +LogDownloadData::LogDownloadData(QGCLogEntry *entry) + : ID(entry->id()) + , entry(entry) { + // qCDebug(LogEntryLog) << Q_FUNC_INFO << this; +} +LogDownloadData::~LogDownloadData() +{ + // qCDebug(LogEntryLog) << Q_FUNC_INFO << this; } void LogDownloadData::advanceChunk() { - current_chunk++; - chunk_table = QBitArray(chunkBins(), false); + current_chunk++; + chunk_table = QBitArray(chunkBins(), false); } -// The number of MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN bins in the current chunk uint32_t LogDownloadData::chunkBins() const { - return qMin(qCeil((entry->size() - current_chunk*kChunkSize)/static_cast(MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN)), - kTableBins); + return qMin(static_cast(qCeil(static_cast((entry->size() - (current_chunk * kChunkSize))) / static_cast(MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN))), kTableBins); } -// The number of kChunkSize chunks in the file uint32_t LogDownloadData::numChunks() const { return qCeil(entry->size() / static_cast(kChunkSize)); } -// True if all bins in the chunk have been set to val bool LogDownloadData::chunkEquals(const bool val) const { - return chunk_table == QBitArray(chunk_table.size(), val); + return (chunk_table == QBitArray(chunk_table.size(), val)); } -//---------------------------------------------------------------------------------------- -QGCLogEntry::QGCLogEntry(uint logId, const QDateTime& dateTime, uint logSize, bool received) - : _logID(logId) +/*===========================================================================*/ + +QGCLogEntry::QGCLogEntry(uint logId, const QDateTime &dateTime, uint logSize, bool received, QObject *parent) + : QObject(parent) + , _logID(logId) , _logSize(logSize) , _logTimeUTC(dateTime) , _received(received) - , _selected(false) { - _status = tr("Pending"); + // qCDebug(LogEntryLog) << Q_FUNC_INFO << this; +} + +QGCLogEntry::~QGCLogEntry() +{ + // qCDebug(LogEntryLog) << Q_FUNC_INFO << this; } -//---------------------------------------------------------------------------------------- QString QGCLogEntry::sizeStr() const { return qgcApp()->bigSizeToString(_logSize); diff --git a/src/AnalyzeView/LogEntry.h b/src/AnalyzeView/LogEntry.h index 34dc46d137f..d04c631b366 100644 --- a/src/AnalyzeView/LogEntry.h +++ b/src/AnalyzeView/LogEntry.h @@ -9,23 +9,60 @@ #pragma once -#include -#include -#include #include +#include #include #include +#include +#include #include +#include "MAVLinkLib.h" + +class QGCLogEntry; + Q_DECLARE_LOGGING_CATEGORY(LogEntryLog) -//----------------------------------------------------------------------------- +struct LogDownloadData +{ + explicit LogDownloadData(QGCLogEntry *entry); + ~LogDownloadData(); + + void advanceChunk(); + + /// The number of MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN bins in the current chunk + uint32_t chunkBins() const; + + /// The number of kChunkSize chunks in the file + uint32_t numChunks() const; + + /// True if all bins in the chunk have been set to val + bool chunkEquals(const bool val) const; + + uint ID = 0; + QGCLogEntry *entry = nullptr; + + QBitArray chunk_table; + uint32_t current_chunk = 0; + QFile file; + QString filename; + uint written = 0; + size_t rate_bytes = 0; + qreal rate_avg = 0.; + QElapsedTimer elapsed; + + static constexpr uint32_t kTableBins = 512; + static constexpr uint32_t kChunkSize = (kTableBins * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN); +}; + +/*===========================================================================*/ + class QGCLogEntry : public QObject { Q_OBJECT QML_ELEMENT - Q_PROPERTY(uint id READ id CONSTANT) + Q_PROPERTY(uint id READ id NOTIFY idChanged) Q_PROPERTY(QDateTime time READ time NOTIFY timeChanged) Q_PROPERTY(uint size READ size NOTIFY sizeChanged) Q_PROPERTY(QString sizeStr READ sizeStr NOTIFY sizeChanged) @@ -34,56 +71,38 @@ class QGCLogEntry : public QObject Q_PROPERTY(QString status READ status NOTIFY statusChanged) public: - QGCLogEntry(uint logId, const QDateTime& dateTime = QDateTime(), uint logSize = 0, bool received = false); - - uint id () const { return _logID; } - uint size () const { return _logSize; } - QString sizeStr () const; - QDateTime time () const { return _logTimeUTC; } - bool received () const { return _received; } - bool selected () const { return _selected; } - QString status () const { return _status; } - - void setId (uint id_) { _logID = id_; } - void setSize (uint size_) { _logSize = size_; emit sizeChanged(); } - void setTime (QDateTime date_) { _logTimeUTC = date_; emit timeChanged(); } - void setReceived (bool rec_) { _received = rec_; emit receivedChanged(); } - void setSelected (bool sel_) { _selected = sel_; emit selectedChanged(); } - void setStatus (QString stat_) { _status = stat_; emit statusChanged(); } + explicit QGCLogEntry(uint logId, const QDateTime &dateTime = QDateTime(), uint logSize = 0, bool received = false, QObject *parent = nullptr); + ~QGCLogEntry(); + + uint id() const { return _logID; } + uint size() const { return _logSize; } + QString sizeStr() const; + QDateTime time() const { return _logTimeUTC; } + bool received() const { return _received; } + bool selected() const { return _selected; } + QString status() const { return _status; } + + void setId(uint id) { if (id != _logID) { _logID = id; emit idChanged(); } } + void setSize(uint size) { if (size != _logSize) { _logSize = size; emit sizeChanged(); } } + void setTime(const QDateTime &date) { if (date != _logTimeUTC) {_logTimeUTC = date; emit timeChanged(); } } + void setReceived(bool rec) { if (rec != _received) { _received = rec; emit receivedChanged(); } } + void setSelected(bool sel) { if (sel != _selected) { _selected = sel; emit selectedChanged(); } } + void setStatus(const QString &stat) { if (stat != _status) { _status = stat; emit statusChanged(); } } signals: - void idChanged (); - void timeChanged (); - void sizeChanged (); - void receivedChanged (); - void selectedChanged (); - void statusChanged (); + void idChanged(); + void timeChanged(); + void sizeChanged(); + void receivedChanged(); + void selectedChanged(); + void statusChanged(); private: - uint _logID; - uint _logSize; - QDateTime _logTimeUTC; - bool _received; - bool _selected; - QString _status; -}; - -struct LogDownloadData { - LogDownloadData(QGCLogEntry* entry); - - QBitArray chunk_table; - uint32_t current_chunk; - QFile file; - QString filename; - uint ID; - QGCLogEntry* entry; - uint written; - size_t rate_bytes; - qreal rate_avg; - QElapsedTimer elapsed; + uint _logID = 0; + uint _logSize = 0; + QDateTime _logTimeUTC; + bool _received = false; - void advanceChunk(); - uint32_t chunkBins() const; - uint32_t numChunks() const; - bool chunkEquals(const bool val) const; + bool _selected = false; + QString _status = QStringLiteral("Pending"); }; diff --git a/test/AnalyzeView/LogDownloadTest.cc b/test/AnalyzeView/LogDownloadTest.cc index 6acbe18a3e9..e967ea39833 100644 --- a/test/AnalyzeView/LogDownloadTest.cc +++ b/test/AnalyzeView/LogDownloadTest.cc @@ -12,26 +12,21 @@ #include "LogEntry.h" #include "MockLink.h" #include "MultiSignalSpy.h" +#include "QmlObjectListModel.h" #include -LogDownloadTest::LogDownloadTest(void) +void LogDownloadTest::_downloadTest() { - -} - -void LogDownloadTest::downloadTest(void) -{ - _connectMockLink(MAV_AUTOPILOT_PX4); - LogDownloadController* controller = new LogDownloadController(); + LogDownloadController* const controller = new LogDownloadController(this); - _rgLogDownloadControllerSignals[requestingListChangedSignalIndex] = SIGNAL(requestingListChanged()); - _rgLogDownloadControllerSignals[downloadingLogsChangedSignalIndex] = SIGNAL(downloadingLogsChanged()); - _rgLogDownloadControllerSignals[modelChangedSignalIndex] = SIGNAL(modelChanged()); + _rgLogDownloadControllerSignals[requestingListChangedSignalIndex] = SIGNAL(requestingListChanged()); + _rgLogDownloadControllerSignals[downloadingLogsChangedSignalIndex] = SIGNAL(downloadingLogsChanged()); + _rgLogDownloadControllerSignals[modelChangedSignalIndex] = SIGNAL(modelChanged()); - _multiSpyLogDownloadController = new MultiSignalSpy(); + _multiSpyLogDownloadController = new MultiSignalSpy(this); QVERIFY(_multiSpyLogDownloadController->init(controller, _rgLogDownloadControllerSignals, _cLogDownloadControllerSignals)); controller->refresh(); @@ -43,13 +38,11 @@ void LogDownloadTest::downloadTest(void) } _multiSpyLogDownloadController->clearAllSignals(); - auto model = controller->model(); + QmlObjectListModel* const model = controller->model(); QVERIFY(model); - qDebug() << model->count(); model->value(0)->setSelected(true); - QString downloadTo = QDir::currentPath(); - qDebug() << "download to:" << downloadTo; + const QString downloadTo = QDir::currentPath(); controller->downloadToDirectory(downloadTo); QVERIFY(_multiSpyLogDownloadController->waitForSignalByIndex(downloadingLogsChangedSignalIndex, 10000)); _multiSpyLogDownloadController->clearAllSignals(); @@ -59,10 +52,8 @@ void LogDownloadTest::downloadTest(void) } _multiSpyLogDownloadController->clearAllSignals(); - QString downloadFile = QDir(downloadTo).filePath("log_0_UnknownDate.ulg"); + const QString downloadFile = QDir(downloadTo).filePath("log_0_UnknownDate.ulg"); QVERIFY(UnitTest::fileCompare(downloadFile, _mockLink->logDownloadFile())); QFile::remove(downloadFile); - - delete controller; } diff --git a/test/AnalyzeView/LogDownloadTest.h b/test/AnalyzeView/LogDownloadTest.h index dc524d74809..0c3d80e78a2 100644 --- a/test/AnalyzeView/LogDownloadTest.h +++ b/test/AnalyzeView/LogDownloadTest.h @@ -7,8 +7,7 @@ * ****************************************************************************/ -#ifndef LogDownloadTest_H -#define LogDownloadTest_H +#pragma once #include "UnitTest.h" @@ -17,19 +16,11 @@ class MultiSignalSpy; class LogDownloadTest : public UnitTest { Q_OBJECT - -public: - LogDownloadTest(void); - -private slots: - //void init(void); - //void cleanup(void) { _cleanup(); } - void downloadTest(void); +private slots: + void _downloadTest(); private: - // LogDownloadController signals - enum { requestingListChangedSignalIndex = 0, downloadingLogsChangedSignalIndex, @@ -38,15 +29,13 @@ private slots: }; enum { - requestingListChangedSignalMask = 1 << requestingListChangedSignalIndex, - downloadingLogsChangedSignalMask = 1 << downloadingLogsChangedSignalIndex, - modelChangedSignalIndexMask = 1 << modelChangedSignalIndex, + requestingListChangedSignalMask = 1 << requestingListChangedSignalIndex, + downloadingLogsChangedSignalMask = 1 << downloadingLogsChangedSignalIndex, + modelChangedSignalIndexMask = 1 << modelChangedSignalIndex, }; - MultiSignalSpy* _multiSpyLogDownloadController; - static const size_t _cLogDownloadControllerSignals = logDownloadControllerMaxSignalIndex; - const char* _rgLogDownloadControllerSignals[_cLogDownloadControllerSignals]; + MultiSignalSpy *_multiSpyLogDownloadController = nullptr; + static constexpr size_t _cLogDownloadControllerSignals = logDownloadControllerMaxSignalIndex; + const char *_rgLogDownloadControllerSignals[_cLogDownloadControllerSignals] = {0}; }; - -#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 24f743bcd45..f8ed8d48192 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,7 +29,7 @@ add_qgc_test(ADSBTest) add_subdirectory(AnalyzeView) add_qgc_test(ExifParserTest) -# add_qgc_test(LogDownloadTest) +add_qgc_test(LogDownloadTest) # add_qgc_test(MavlinkLogTest) add_qgc_test(PX4LogParserTest) add_qgc_test(ULogParserTest) diff --git a/test/UnitTestList.cc b/test/UnitTestList.cc index 62476b9e8f9..20b271bcca6 100644 --- a/test/UnitTestList.cc +++ b/test/UnitTestList.cc @@ -18,7 +18,7 @@ // AnalyzeView #include "ExifParserTest.h" // #include "MavlinkLogTest.h" -// #include "LogDownloadTest.h" +#include "LogDownloadTest.h" #include "PX4LogParserTest.h" #include "ULogParserTest.h" @@ -114,7 +114,7 @@ int runTests(bool stress, QStringView unitTestOptions) // AnalyzeView UT_REGISTER_TEST(ExifParserTest) // UT_REGISTER_TEST(MavlinkLogTest) - // UT_REGISTER_TEST(LogDownloadTest) + UT_REGISTER_TEST(LogDownloadTest) UT_REGISTER_TEST(PX4LogParserTest) UT_REGISTER_TEST(ULogParserTest)