18#include <QtCore/QApplicationStatic>
20#include <QtCore/QTimeZone>
21#include <QtCore/QTimer>
34 , _timer(new QTimer(this))
37 qCDebug(OnboardLogControllerLog) <<
this;
40 (void) connect(_timer, &QTimer::timeout,
this, &OnboardLogController::_processDownload);
42 _timer->setSingleShot(
false);
49 qCDebug(OnboardLogControllerLog) <<
this;
55 if (_transport == Transport::Ftp) {
56 _ftpDownloadToDirectory(dir);
58 _downloadToDirectory(dir);
62void OnboardLogController::_downloadToDirectory(
const QString &dir)
64 _receivedAllEntries();
66 _downloadData.reset();
69 if (_downloadPath.isEmpty()) {
73 if (!_downloadPath.endsWith(QDir::separator())) {
74 _downloadPath += QDir::separator();
82 _setDownloading(
true);
86void OnboardLogController::_processDownload()
88 if (_requestingLogEntries) {
89 _findMissingEntries();
90 }
else if (_downloadingLogs) {
95void OnboardLogController::_findMissingEntries()
97 const int num_logs = _logEntriesModel->
count();
100 for (
int i = 0; i < num_logs; i++) {
112 }
else if (start >= 0) {
118 _receivedAllEntries();
122 if (_retries++ > 2) {
123 for (
int i = 0; i < num_logs; i++) {
130 _receivedAllEntries();
131 qCWarning(OnboardLogControllerLog) <<
"Too many errors retreiving log list. Giving up.";
142 _requestLogList(
static_cast<uint32_t
>(start),
static_cast<uint32_t
>(end));
145void OnboardLogController::_setActiveVehicle(
Vehicle *vehicle)
147 if (vehicle == _vehicle) {
156 if (_downloadData->file.exists()) {
157 (void) _downloadData->file.remove();
159 _downloadData.reset();
161 _setDownloading(
false);
165 (void) disconnect(_vehicle, &
Vehicle::logEntry,
this, &OnboardLogController::_logEntry);
166 (void) disconnect(_vehicle, &
Vehicle::logData,
this, &OnboardLogController::_logData);
174 _ftpListState = FtpListState::Idle;
175 _ftpDirsToList.clear();
176 _ftpLogIdCounter = 0;
177 _ftpDownloadQueue.clear();
178 _ftpDeleteQueue.clear();
179 _ftpDeleting =
false;
180 _ftpCurrentDownloadEntry =
nullptr;
181 _ftpDisabled =
false;
182 _ftpDownloadHadError =
false;
183 _setTransport(Transport::Messages);
189 (void) connect(_vehicle, &
Vehicle::logEntry,
this, &OnboardLogController::_logEntry);
190 (void) connect(_vehicle, &
Vehicle::logData,
this, &OnboardLogController::_logData);
200void OnboardLogController::_logEntry(uint32_t time_utc, uint32_t size, uint16_t
id, uint16_t num_logs, uint16_t last_log_num)
202 Q_UNUSED(last_log_num);
204 if (!_requestingLogEntries || (_transport != Transport::Messages)) {
208 if ((_logEntriesModel->
count() == 0) && (num_logs > 0)) {
209 if (_vehicle->
firmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) {
214 for (
int i = 0; i < num_logs; i++) {
217 _logEntriesModel->
append(entry);
222 if ((size > 0) || (_vehicle->
firmwareType() != MAV_AUTOPILOT_ARDUPILOTMEGA)) {
224 if (id < _logEntriesModel->count()) {
227 entry->
setTime(QDateTime::fromSecsSinceEpoch(time_utc));
231 qCWarning(OnboardLogControllerLog) <<
"Received onboard log entry for out-of-bound index:" << id;
235 _receivedAllEntries();
240 if (_entriesComplete()) {
241 _receivedAllEntries();
243 _timer->start(kTimeOutMs);
247void OnboardLogController::_receivedAllEntries()
253bool OnboardLogController::_entriesComplete()
const
255 const int num_logs = _logEntriesModel->
count();
256 for (
int i = 0; i < num_logs; i++) {
270void OnboardLogController::_logData(uint32_t ofs, uint16_t
id, uint8_t count,
const uint8_t *data)
272 if (!_downloadingLogs || !_downloadData || (_transport != Transport::Messages)) {
277 if (_downloadData->ID !=
id) {
278 qCWarning(OnboardLogControllerLog) <<
"Received log data for wrong log";
282 if ((ofs % MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN) != 0) {
283 qCWarning(OnboardLogControllerLog) <<
"Ignored misaligned incoming packet @" << ofs;
288 if (ofs <= _downloadData->entry->
size()) {
291 if (chunk != _downloadData->current_chunk) {
292 qCWarning(OnboardLogControllerLog) <<
"Ignored packet for out of order chunk actual:expected" << chunk << _downloadData->current_chunk;
297 if (bin >= _downloadData->chunk_table.size()) {
298 qCWarning(OnboardLogControllerLog) <<
"Out of range bin received";
300 _downloadData->chunk_table.setBit(bin);
303 if (_downloadData->file.pos() != ofs) {
304 if (!_downloadData->file.seek(ofs)) {
305 qCWarning(OnboardLogControllerLog) <<
"Error while seeking log file offset";
310 if (_downloadData->file.write(
reinterpret_cast<const char*
>(data), count)) {
311 _downloadData->written += count;
312 _downloadData->rate_bytes += count;
318 _timer->start(kTimeOutMs);
319 if (_logComplete()) {
320 _downloadData->entry->setStatus(tr(
"Downloaded"));
322 }
else if (_chunkComplete()) {
323 _downloadData->advanceChunk();
324 _requestLogData(_downloadData->ID,
326 _downloadData->chunk_table.size() * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN);
327 }
else if ((bin < (_downloadData->chunk_table.size() - 1)) && _downloadData->chunk_table.at(bin + 1)) {
332 qCWarning(OnboardLogControllerLog) <<
"Error while writing log file chunk";
335 qCWarning(OnboardLogControllerLog) <<
"Received log offset greater than expected";
339 _downloadData->entry->setStatus(tr(
"Error"));
343void OnboardLogController::_findMissingData()
345 if (_logComplete()) {
350 if (_chunkComplete()) {
351 _downloadData->advanceChunk();
358 uint16_t start = 0, end = 0;
359 const int size = _downloadData->chunk_table.size();
360 for (; start < size; start++) {
361 if (!_downloadData->chunk_table.testBit(start)) {
366 for (end = start; end < size; end++) {
367 if (_downloadData->chunk_table.testBit(end)) {
373 const uint32_t len = (end - start) * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN;
374 _requestLogData(_downloadData->ID, pos, len, _retries);
377void OnboardLogController::_updateDataRate()
379 constexpr uint kSizeUpdateThreshold = 102400;
380 const bool timeThresholdMet = _downloadData->elapsed.elapsed() >= kGUIRateMs;
381 const bool sizeThresholdMet = (_downloadData->written - _downloadData->last_status_written) >= kSizeUpdateThreshold;
383 if (!timeThresholdMet && !sizeThresholdMet) {
388 if (timeThresholdMet) {
390 const qreal rate = _downloadData->rate_bytes / (_downloadData->elapsed.elapsed() / 1000.0);
391 _downloadData->rate_avg = (_downloadData->rate_avg * 0.95) + (rate * 0.05);
392 _downloadData->rate_bytes = 0;
396 _downloadData->elapsed.start();
403 _downloadData->entry->setStatus(status);
404 _downloadData->last_status_written = _downloadData->written;
407bool OnboardLogController::_chunkComplete()
const
409 return _downloadData->chunkEquals(
true);
412bool OnboardLogController::_logComplete()
const
414 return (_chunkComplete() && ((_downloadData->current_chunk + 1) == _downloadData->numChunks()));
417void OnboardLogController::_receivedAllData()
420 while (_prepareLogDownload()) {
421 if (_downloadData->chunk_table.isEmpty()) {
425 _downloadData->entry->setStatus(tr(
"Downloaded"));
426 _downloadData.reset();
430 _requestLogData(_downloadData->ID, 0, _downloadData->chunk_table.size() * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN);
431 _timer->start(kTimeOutMs);
436 _setDownloading(
false);
439bool OnboardLogController::_prepareLogDownload()
441 _downloadData.reset();
451 const QString ftime = (entry->
time().date().year() >= 2010) ? entry->
time().toString(QStringLiteral(
"yyyy-M-d-hh-mm-ss")) : QStringLiteral(
"UnknownDate");
453 _downloadData = std::make_unique<OnboardLogDownloadData>(entry);
454 _downloadData->filename = QStringLiteral(
"log_") + QString::number(entry->
id()) +
"_" + ftime;
457 const QString loggerParam = QStringLiteral(
"SYS_LOGGER");
460 if (loggerFact && (loggerFact->
rawValue().toInt() == 0)) {
461 _downloadData->filename +=
".px4log";
463 _downloadData->filename +=
".ulg";
466 _downloadData->filename +=
".bin";
469 _downloadData->file.setFileName(_downloadPath + _downloadData->filename);
471 if (_downloadData->file.exists()) {
472 uint32_t numDups = 0;
473 const QStringList filename_spl = _downloadData->filename.split(
'.');
476 const QString filename = filename_spl[0] +
'_' + QString::number(numDups) +
'.' + filename_spl[1];
477 _downloadData->file.setFileName(_downloadPath + filename);
478 }
while ( _downloadData->file.exists());
482 if (!_downloadData->file.open(QIODevice::WriteOnly)) {
483 qCWarning(OnboardLogControllerLog) <<
"Failed to create log file:" << _downloadData->filename;
484 }
else if (!_downloadData->file.resize(entry->
size())) {
485 qCWarning(OnboardLogControllerLog) <<
"Failed to allocate space for log file:" << _downloadData->filename;
487 _downloadData->current_chunk = 0;
488 _downloadData->chunk_table = QBitArray(_downloadData->chunkBins(),
false);
489 _downloadData->elapsed.start();
494 if (_downloadData->file.exists()) {
495 (void) _downloadData->file.remove();
498 _downloadData->entry->setStatus(tr(
"Error"));
499 _downloadData.reset();
507 if (_downloadingLogs || _requestingLogEntries || _ftpDeleting) {
510 qCDebug(OnboardLogControllerLog) <<
"refresh: ignored - transfer in progress";
518 qCDebug(OnboardLogControllerLog) <<
"refresh: using ftp transport";
519 _setTransport(Transport::Ftp);
522 qCDebug(OnboardLogControllerLog) <<
"refresh: using message transport - ftpDisabled:" << _ftpDisabled
524 <<
"ftpCapable:" <<
bool(_vehicle && (_vehicle->
capabilityBits() & MAV_PROTOCOL_CAPABILITY_FTP));
525 _setTransport(Transport::Messages);
526 _requestLogList(0, 0xffff);
532 const int numLogs = _logEntriesModel->
count();
533 for (
int i = 0; i < numLogs; i++) {
549 if (_transport == Transport::Ftp) {
551 if (_requestingLogEntries) {
554 _ftpListState = FtpListState::Idle;
555 _ftpDirsToList.clear();
563 _ftpDeleteQueue.clear();
564 _resetSelection(
true);
568 if (_downloadingLogs) {
570 if (_ftpCurrentDownloadEntry) {
571 _ftpCurrentDownloadEntry->
setStatus(tr(
"Canceled"));
572 _ftpCurrentDownloadEntry =
nullptr;
574 _ftpDownloadQueue.clear();
580 _receivedAllEntries();
583 _downloadData->entry->setStatus(tr(
"Canceled"));
584 if (_downloadData->file.exists()) {
585 (void) _downloadData->file.remove();
588 _downloadData.reset();
592 _resetSelection(
true);
593 _setDownloading(
false);
598 const int count = _logEntriesModel->
count();
599 for (
int i = 0; i < count; i++) {
620 const int count = _logEntriesModel->
count();
621 for (
int i = 0; i < count; i++) {
635 const int count = _logEntriesModel->
count();
636 for (
int i = 0; i < count; i++) {
646 return (selectable > 0) && (selected == selectable);
656 if (_sortAscending == ascending) {
660 _sortAscending = ascending;
661 _sortEntriesByTimestamp();
665void OnboardLogController::_resetSelection(
bool canceled)
667 const int num_logs = _logEntriesModel->
count();
668 for (
int i = 0; i < num_logs; i++) {
685void OnboardLogController::_sortEntriesByTimestamp()
687 QObjectList sortedEntries = *_logEntriesModel->
objectList();
688 std::stable_sort(sortedEntries.begin(), sortedEntries.end(), [
this](
const QObject *lhsObj,
const QObject *rhsObj) {
689 const QGCOnboardLogEntry *const lhs = qobject_cast<const QGCOnboardLogEntry*>(lhsObj);
690 const QGCOnboardLogEntry *const rhs = qobject_cast<const QGCOnboardLogEntry*>(rhsObj);
701 const bool lhsHasTime = lhs->received() && (lhs->time().toSecsSinceEpoch() > 0);
702 const bool rhsHasTime = rhs->received() && (rhs->time().toSecsSinceEpoch() > 0);
703 if (lhsHasTime != rhsHasTime) {
708 if (lhsHasTime && rhsHasTime) {
709 if (lhs->time() == rhs->time()) {
710 return _sortAscending ? (lhs->id() < rhs->id()) : (lhs->id() > rhs->id());
713 return _sortAscending ? (lhs->time() < rhs->time()) : (lhs->time() > rhs->time());
717 return _sortAscending ? (lhs->id() < rhs->id()) : (lhs->id() > rhs->id());
720 (void) _logEntriesModel->swapObjectList(sortedEntries);
726 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
732 qCWarning(OnboardLogControllerLog) <<
"Link Unavailable";
737 (void) mavlink_msg_log_erase_pack_chan(
740 sharedLink->mavlinkChannel(),
747 qCWarning(OnboardLogControllerLog) <<
"Failed to send";
757 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
761 if (_transport != Transport::Ftp) {
762 qCWarning(OnboardLogControllerLog) <<
"ftp: selective erase requires the FTP transport";
766 _ftpDeleteQueue.clear();
767 const int numLogs = _logEntriesModel->
count();
768 for (
int i = 0; i < numLogs; i++) {
771 _ftpDeleteQueue.enqueue(entry);
775 if (_ftpDeleteQueue.isEmpty()) {
776 qCWarning(OnboardLogControllerLog) <<
"ftp: no selected logs have FTP paths for erase";
780 qCDebug(OnboardLogControllerLog) <<
"ftp: erasing" << _ftpDeleteQueue.size() <<
"selected logs";
782 _setDownloading(
true);
786void OnboardLogController::_ftpDeleteNext()
788 if (_ftpDeleteQueue.isEmpty()) {
789 _ftpDeleting =
false;
790 _setDownloading(
false);
799 qCDebug(OnboardLogControllerLog) <<
"ftp: deleting" << entry->
ftpPath();
802 qCWarning(OnboardLogControllerLog) <<
"ftp: failed to start delete for" << entry->
ftpPath();
808void OnboardLogController::_ftpDeleteComplete(
const QString &file,
const QString &errorMsg)
814 if (!errorMsg.isEmpty()) {
815 qCWarning(OnboardLogControllerLog) <<
"ftp: delete error:" << file << errorMsg;
821void OnboardLogController::_requestLogList(uint32_t start, uint32_t end)
824 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
830 qCWarning(OnboardLogControllerLog) <<
"Link Unavailable";
835 (void) mavlink_msg_log_request_list_pack_chan(
838 sharedLink->mavlinkChannel(),
847 qCWarning(OnboardLogControllerLog) <<
"Failed to send";
851 qCDebug(OnboardLogControllerLog) <<
"Request onboard log entry list (" << start <<
"through" << end <<
")";
853 _timer->start(kRequestLogListTimeoutMs);
856void OnboardLogController::_requestLogData(uint16_t
id, uint32_t offset, uint32_t count,
int retryCount)
859 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
865 qCWarning(OnboardLogControllerLog) <<
"Link Unavailable";
870 qCDebug(OnboardLogControllerLog) <<
"Request log data (id:" <<
id <<
"offset:" << offset <<
"size:" << count <<
"retryCount" << retryCount <<
")";
873 (void) mavlink_msg_log_request_data_pack_chan(
876 sharedLink->mavlinkChannel(),
886 qCWarning(OnboardLogControllerLog) <<
"Failed to send";
890void OnboardLogController::_requestLogEnd()
893 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
899 qCWarning(OnboardLogControllerLog) <<
"Link Unavailable";
904 (void) mavlink_msg_log_request_end_pack_chan(
907 sharedLink->mavlinkChannel(),
914 qCWarning(OnboardLogControllerLog) <<
"Failed to send";
918void OnboardLogController::_setDownloading(
bool active)
920 if (_downloadingLogs != active) {
921 _downloadingLogs = active;
929void OnboardLogController::_setListing(
bool active)
931 if (_requestingLogEntries != active) {
932 _requestingLogEntries = active;
937 _sortEntriesByTimestamp();
943void OnboardLogController::_setTransport(
Transport transport)
951void OnboardLogController::_ftpStartListing()
953 _ftpDirsToList.clear();
954 _ftpLogIdCounter = 0;
956 _ftpTriedFallbackRoot =
false;
962void OnboardLogController::_ftpListRoot()
964 _ftpListState = FtpListState::ListingRoot;
966 qCDebug(OnboardLogControllerLog) <<
"ftp: listing root" << _ftpLogRoot;
969 qCWarning(OnboardLogControllerLog) <<
"ftp: failed to start root listing for" << _ftpLogRoot;
970 _ftpFallbackToMessages();
974void OnboardLogController::_ftpListDirComplete(
const QStringList &dirList,
const QString &errorMsg)
976 if (_ftpListState == FtpListState::Idle) {
980 if (!errorMsg.isEmpty()) {
981 if ((_ftpListState == FtpListState::ListingRoot) && !_ftpTriedFallbackRoot && _vehicle) {
982 const char *fallback =
nullptr;
990 qCDebug(OnboardLogControllerLog) <<
"ftp: root listing of" << _ftpLogRoot <<
"failed (" << errorMsg
991 <<
"), falling back to" << fallback;
992 _ftpTriedFallbackRoot =
true;
993 _ftpLogRoot = QString::fromLatin1(fallback);
999 qCWarning(OnboardLogControllerLog) <<
"ftp: listing error:" << errorMsg;
1000 _ftpFallbackToMessages();
1005 qCDebug(OnboardLogControllerLog) <<
"ftp: raw entries for"
1006 << ((_ftpListState == FtpListState::ListingRoot) ? _ftpLogRoot : (_ftpDirsToList.isEmpty() ? QString() : _ftpDirsToList.first()))
1009 if (_ftpListState == FtpListState::ListingRoot) {
1012 const uint flatLogs = _ftpProcessFileEntries(dirList, QString());
1021 for (
const QString &entry : dirList) {
1022 if (entry.startsWith(QLatin1Char(
'D'))) {
1023 QString dirName = entry.mid(1);
1025 dirName = dirName.section(QLatin1Char(
'\t'), 0, 0);
1030 if (!dirName.isEmpty() &&
1031 (dirName != QStringLiteral(
".")) && (dirName != QStringLiteral(
".."))) {
1032 _ftpDirsToList.append(dirName);
1037 _ftpDirsToList.sort();
1038 qCDebug(OnboardLogControllerLog) <<
"ftp: root listing of" << _ftpLogRoot
1039 <<
"found" << flatLogs <<
"flat logs and" << _ftpDirsToList.size() <<
"subdirectories";
1041 _ftpListState = FtpListState::ListingSubdir;
1042 _ftpListNextSubdir();
1046 const QString currentDir = _ftpDirsToList.isEmpty() ? QString() : _ftpDirsToList.first();
1047 const uint logsFoundInDir = _ftpProcessFileEntries(dirList, currentDir);
1049 qCDebug(OnboardLogControllerLog) <<
"ftp:" << currentDir <<
"->" << logsFoundInDir <<
"logs";
1051 if (!_ftpDirsToList.isEmpty()) {
1052 _ftpDirsToList.removeFirst();
1055 _ftpListNextSubdir();
1058uint OnboardLogController::_ftpProcessFileEntries(
const QStringList &dirList,
const QString &subdir)
1060 const QDate dirDate = subdir.isEmpty() ? QDate() : QDate::fromString(subdir, QStringLiteral(
"yyyy-MM-dd"));
1063 for (
const QString &entry : dirList) {
1064 if (!entry.startsWith(QLatin1Char(
'F'))) {
1070 const QString fileInfo = entry.mid(1);
1071 const int tabIdx = fileInfo.indexOf(QLatin1Char(
'\t'));
1076 const QString fileName = fileInfo.left(tabIdx);
1077 const QString sizeStr = fileInfo.section(QLatin1Char(
'\t'), 1, 1);
1078 const QString mtimeStr = fileInfo.section(QLatin1Char(
'\t'), 2, 2);
1080 if (!fileName.endsWith(QStringLiteral(
".ulg"), Qt::CaseInsensitive) &&
1081 !fileName.endsWith(QStringLiteral(
".bin"), Qt::CaseInsensitive)) {
1085 bool sizeOk =
false;
1086 const uint fileSize = sizeStr.toUInt(&sizeOk);
1094 bool mtimeOk =
false;
1095 const qint64 mtimeSecs = mtimeStr.toLongLong(&mtimeOk);
1096 if (mtimeOk && (mtimeSecs > 0)) {
1097 dateTime = QDateTime::fromSecsSinceEpoch(mtimeSecs, QTimeZone::UTC);
1101 if (!dateTime.isValid() && dirDate.isValid()) {
1102 const QString baseName = fileName.left(fileName.lastIndexOf(QLatin1Char(
'.')));
1103 const QTime fileTime = QTime::fromString(baseName, QStringLiteral(
"HH_mm_ss"));
1104 if (fileTime.isValid()) {
1105 dateTime = QDateTime(dirDate, fileTime, QTimeZone::UTC);
1107 dateTime = QDateTime(dirDate, QTime(), QTimeZone::UTC);
1111 const QString ftpPath = subdir.isEmpty()
1112 ? (_ftpLogRoot + QStringLiteral(
"/") + fileName)
1113 : (_ftpLogRoot + QStringLiteral(
"/") + subdir + QStringLiteral(
"/") + fileName);
1119 _logEntriesModel->
append(logEntry);
1126void OnboardLogController::_ftpListNextSubdir()
1128 if (_ftpDirsToList.isEmpty()) {
1129 qCDebug(OnboardLogControllerLog) <<
"ftp: listing complete, found" << _logEntriesModel->
count() <<
"logs";
1130 _ftpFinishListing();
1134 const QString subdir = _ftpDirsToList.first();
1135 const QString path = _ftpLogRoot + QStringLiteral(
"/") + subdir;
1137 qCDebug(OnboardLogControllerLog) <<
"ftp: listing subdir" << path;
1140 qCWarning(OnboardLogControllerLog) <<
"ftp: failed to list subdir" << path;
1141 _ftpDirsToList.removeFirst();
1142 _ftpListNextSubdir();
1146void OnboardLogController::_ftpFinishListing()
1148 _ftpListState = FtpListState::Idle;
1153 _ftpFallbackToMessages();
1160void OnboardLogController::_ftpFallbackToMessages()
1162 qCDebug(OnboardLogControllerLog) <<
"ftp: transport failed, falling back to message based log download";
1164 _ftpListState = FtpListState::Idle;
1165 _ftpDirsToList.clear();
1166 _ftpDisabled =
true;
1167 _setTransport(Transport::Messages);
1173 _requestLogList(0, 0xffff);
1176void OnboardLogController::_ftpDownloadToDirectory(
const QString &dir)
1178 _downloadPath = dir;
1179 if (_downloadPath.isEmpty()) {
1183 if (!_downloadPath.endsWith(QDir::separator())) {
1184 _downloadPath += QDir::separator();
1187 _ftpDownloadQueue.clear();
1188 _ftpDownloadHadError =
false;
1189 const int numLogs = _logEntriesModel->
count();
1190 for (
int i = 0; i < numLogs; i++) {
1194 _ftpDownloadQueue.enqueue(entry);
1198 if (_ftpDownloadQueue.isEmpty()) {
1199 qCWarning(OnboardLogControllerLog) <<
"ftp: no selected logs have FTP paths for download";
1203 qCDebug(OnboardLogControllerLog) <<
"ftp: queued" << _ftpDownloadQueue.size() <<
"logs for download to" << _downloadPath;
1204 _setDownloading(
true);
1206 _ftpDownloadEntry(_ftpDownloadQueue.dequeue());
1211 if (!entry || !_vehicle) {
1217 _ftpCurrentDownloadEntry = entry;
1218 _ftpDownloadBytesAtLastUpdate = 0;
1219 _ftpDownloadRateAvg = 0.;
1220 _ftpDownloadElapsed.start();
1225 QString localFilename = entry->
ftpPath().section(QLatin1Char(
'/'), -1);
1226 if (localFilename.isEmpty()) {
1227 localFilename = QStringLiteral(
"log_") + QString::number(entry->
id()) + QStringLiteral(
".ulg");
1230 if (QFile::exists(_downloadPath + localFilename)) {
1231 const int dotIdx = localFilename.lastIndexOf(QLatin1Char(
'.'));
1232 const QString
base = (dotIdx > 0) ? localFilename.left(dotIdx) : localFilename;
1233 const QString ext = (dotIdx > 0) ? localFilename.mid(dotIdx) : QString();
1237 localFilename =
base + QStringLiteral(
"_") + QString::number(numDups) + ext;
1238 }
while (QFile::exists(_downloadPath + localFilename));
1241 qCDebug(OnboardLogControllerLog) <<
"ftp: downloading" << entry->
ftpPath() <<
"to" << _downloadPath + localFilename;
1244 qCWarning(OnboardLogControllerLog) <<
"ftp: failed to start download for" << entry->
ftpPath();
1246 _ftpCurrentDownloadEntry =
nullptr;
1247 _ftpDownloadHadError =
true;
1248 _ftpDownloadQueueNext();
1252void OnboardLogController::_ftpDownloadQueueNext()
1254 if (!_ftpDownloadQueue.isEmpty()) {
1255 _ftpDownloadEntry(_ftpDownloadQueue.dequeue());
1259 if (_ftpDownloadHadError) {
1260 qCDebug(OnboardLogControllerLog) <<
"ftp: download errors occurred, using message based transport for subsequent refreshes";
1261 _ftpDisabled =
true;
1264 _setDownloading(
false);
1267void OnboardLogController::_ftpDownloadComplete(
const QString &file,
const QString &errorMsg)
1269 if (!_ftpCurrentDownloadEntry) {
1273 if (errorMsg.isEmpty()) {
1274 _ftpCurrentDownloadEntry->
setStatus(tr(
"Downloaded"));
1275 qCDebug(OnboardLogControllerLog) <<
"ftp: download complete" << file;
1277 _ftpCurrentDownloadEntry->
setStatus(tr(
"Error"));
1278 _ftpDownloadHadError =
true;
1279 qCWarning(OnboardLogControllerLog) <<
"ftp: download error:" << errorMsg;
1282 _ftpCurrentDownloadEntry =
nullptr;
1283 _ftpDownloadQueueNext();
1286void OnboardLogController::_ftpDownloadProgress(
float value)
1288 if (!_ftpCurrentDownloadEntry) {
1292 if (_ftpDownloadElapsed.elapsed() < kGUIRateMs) {
1296 const size_t totalBytes =
static_cast<size_t>(
static_cast<qreal
>(_ftpCurrentDownloadEntry->
size()) *
static_cast<qreal
>(value));
1297 if (totalBytes < _ftpDownloadBytesAtLastUpdate) {
1299 _ftpDownloadBytesAtLastUpdate = totalBytes;
1303 const size_t bytesSinceLastUpdate = totalBytes - _ftpDownloadBytesAtLastUpdate;
1304 const qreal elapsedSec = _ftpDownloadElapsed.elapsed() / 1000.0;
1305 const qreal rate = (elapsedSec > 0) ? (bytesSinceLastUpdate / elapsedSec) : 0;
1306 _ftpDownloadRateAvg = (_ftpDownloadRateAvg * 0.95) + (rate * 0.05);
1307 _ftpDownloadBytesAtLastUpdate = totalBytes;
1308 _ftpDownloadElapsed.start();
1310 const QString status = QStringLiteral(
"%1 (%2/s)").arg(
1314 _ftpCurrentDownloadEntry->
setStatus(status);
1319 if (_compressLogs != compress) {
1320 _compressLogs = compress;
1328 qCWarning(OnboardLogControllerLog) <<
"Log compression not yet implemented (decompression-only API)";
1337void OnboardLogController::_handleCompressionProgress(qreal progress)
1343void OnboardLogController::_handleCompressionFinished(
bool success)
@ Transport
The link could not read, write, or change its baud rate.
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
static constexpr const char * kApmLogRootFallback
static constexpr const char * kPx4LogRootFallback
static constexpr const char * kMavlinkLogRoot
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool listDirectoryWithTimeUnsupported() const
true when the vehicle NAK'ed kCmdListDirectoryWithTime, i.e. directory listings carry no modification...
bool listDirectory(uint8_t fromCompId, const QString &fromURI)
bool deleteFile(uint8_t fromCompId, const QString &fromURI)
void commandProgress(float value)
void downloadComplete(const QString &file, const QString &errorMsg)
void cancelListDirectory()
void deleteComplete(const QString &file, const QString &errorMsg)
bool download(uint8_t fromCompId, const QString &fromURI, const QString &toDir, const QString &fileName="", bool checksize=true)
void listDirectoryComplete(const QStringList &dirList, const QString &errorMsg)
A Fact is used to hold a single value within the system.
QVariant rawValue() const
static int getComponentId()
static MAVLinkProtocol * instance()
static MultiVehicleManager * instance()
void activeVehicleChanged(Vehicle *activeVehicle)
int selectedCount() const
void downloadingLogsChanged()
void requestingListChanged()
QString transport() const
Transport currently used to list/download logs: "messages" (LOG_* messages) or "ftp" (MAVLink FTP)
Q_INVOKABLE void selectAll(bool select)
Q_INVOKABLE void eraseAll()
Q_INVOKABLE void eraseSelected()
Q_INVOKABLE void download(const QString &path=QString())
Q_INVOKABLE void toggleSortByDate()
Q_INVOKABLE void refresh()
bool allLogsSelected() const
void setCompressLogs(bool compress)
void sortAscendingChanged()
void setSortAscending(bool ascending)
void compressLogsChanged()
Q_INVOKABLE void cancel()
Q_INVOKABLE bool compressLogFile(const QString &logPath)
Compress a single log file.
Q_INVOKABLE void cancelCompression()
Cancel compression.
bool parameterExists(int componentId, const QString ¶mName) const
Fact * getParameter(int componentId, const QString ¶mName)
static constexpr int defaultComponentId
void setStatus(const QString &stat)
void setFtpPath(const QString &path)
void setSelected(bool sel)
void setTime(const QDateTime &date)
void setReceived(bool rec)
QString ftpPath() const
Remote path when the entry was listed via MAVLink FTP; empty for message-based entries.
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
QList< QObject * > * objectList()
static SettingsManager * instance()
AppSettings * appSettings() const
WeakLinkInterfacePtr primaryLink() const
void setCommunicationLostEnabled(bool communicationLostEnabled)
uint64_t capabilityBits() const
VehicleLinkManager * vehicleLinkManager()
void logEntry(uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t last_log_num)
MAV_AUTOPILOT firmwareType() const
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
int defaultComponentId() const
ParameterManager * parameterManager()
FTPManager * ftpManager()
bool capabilitiesKnown() const
void logData(uint32_t ofs, uint16_t id, uint8_t count, const uint8_t *data)
QString bigSizeToString(quint64 size)
Byte size with unit: B, KB, MB, GB, TB. 1 fractional digit above 1 KB.
GPSCommandSequence base(QByteArrayView port, const GPSBaseStationConfig &base)
Streams RTCM3 corrections and the PVTGeodetic survey status over port in the mode base selects.
static const uint32_t kChunkSize