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 if (_prepareLogDownload()) {
421 _requestLogData(_downloadData->ID, 0, _downloadData->chunk_table.size() * MAVLINK_MSG_LOG_DATA_FIELD_DATA_LEN);
422 _timer->start(kTimeOutMs);
425 _setDownloading(
false);
429bool OnboardLogController::_prepareLogDownload()
431 _downloadData.reset();
441 const QString ftime = (entry->
time().date().year() >= 2010) ? entry->
time().toString(QStringLiteral(
"yyyy-M-d-hh-mm-ss")) : QStringLiteral(
"UnknownDate");
443 _downloadData = std::make_unique<OnboardLogDownloadData>(entry);
444 _downloadData->filename = QStringLiteral(
"log_") + QString::number(entry->
id()) +
"_" + ftime;
447 const QString loggerParam = QStringLiteral(
"SYS_LOGGER");
450 if (loggerFact && (loggerFact->
rawValue().toInt() == 0)) {
451 _downloadData->filename +=
".px4log";
453 _downloadData->filename +=
".ulg";
456 _downloadData->filename +=
".bin";
459 _downloadData->file.setFileName(_downloadPath + _downloadData->filename);
461 if (_downloadData->file.exists()) {
462 uint32_t numDups = 0;
463 const QStringList filename_spl = _downloadData->filename.split(
'.');
466 const QString filename = filename_spl[0] +
'_' + QString::number(numDups) +
'.' + filename_spl[1];
467 _downloadData->file.setFileName(_downloadPath + filename);
468 }
while ( _downloadData->file.exists());
472 if (!_downloadData->file.open(QIODevice::WriteOnly)) {
473 qCWarning(OnboardLogControllerLog) <<
"Failed to create log file:" << _downloadData->filename;
474 }
else if (!_downloadData->file.resize(entry->
size())) {
475 qCWarning(OnboardLogControllerLog) <<
"Failed to allocate space for log file:" << _downloadData->filename;
477 _downloadData->current_chunk = 0;
478 _downloadData->chunk_table = QBitArray(_downloadData->chunkBins(),
false);
479 _downloadData->elapsed.start();
484 if (_downloadData->file.exists()) {
485 (void) _downloadData->file.remove();
488 _downloadData->entry->setStatus(tr(
"Error"));
489 _downloadData.reset();
501 qCDebug(OnboardLogControllerLog) <<
"refresh: using ftp transport";
502 _setTransport(Transport::Ftp);
505 qCDebug(OnboardLogControllerLog) <<
"refresh: using message transport - ftpDisabled:" << _ftpDisabled
507 <<
"ftpCapable:" <<
bool(_vehicle && (_vehicle->
capabilityBits() & MAV_PROTOCOL_CAPABILITY_FTP));
508 _setTransport(Transport::Messages);
509 _requestLogList(0, 0xffff);
515 const int numLogs = _logEntriesModel->
count();
516 for (
int i = 0; i < numLogs; i++) {
532 if (_transport == Transport::Ftp) {
534 if (_requestingLogEntries) {
537 _ftpListState = FtpListState::Idle;
538 _ftpDirsToList.clear();
546 _ftpDeleteQueue.clear();
547 _resetSelection(
true);
551 if (_downloadingLogs) {
553 if (_ftpCurrentDownloadEntry) {
554 _ftpCurrentDownloadEntry->
setStatus(tr(
"Canceled"));
555 _ftpCurrentDownloadEntry =
nullptr;
557 _ftpDownloadQueue.clear();
562 _receivedAllEntries();
565 _downloadData->entry->setStatus(tr(
"Canceled"));
566 if (_downloadData->file.exists()) {
567 (void) _downloadData->file.remove();
570 _downloadData.reset();
574 _resetSelection(
true);
575 _setDownloading(
false);
580 const int count = _logEntriesModel->
count();
581 for (
int i = 0; i < count; i++) {
602 const int count = _logEntriesModel->
count();
603 for (
int i = 0; i < count; i++) {
617 const int count = _logEntriesModel->
count();
618 for (
int i = 0; i < count; i++) {
628 return (selectable > 0) && (selected == selectable);
638 if (_sortAscending == ascending) {
642 _sortAscending = ascending;
643 _sortEntriesByTimestamp();
647void OnboardLogController::_resetSelection(
bool canceled)
649 const int num_logs = _logEntriesModel->
count();
650 for (
int i = 0; i < num_logs; i++) {
667void OnboardLogController::_sortEntriesByTimestamp()
669 QObjectList sortedEntries = *_logEntriesModel->
objectList();
670 std::stable_sort(sortedEntries.begin(), sortedEntries.end(), [
this](
const QObject *lhsObj,
const QObject *rhsObj) {
671 const QGCOnboardLogEntry *const lhs = qobject_cast<const QGCOnboardLogEntry*>(lhsObj);
672 const QGCOnboardLogEntry *const rhs = qobject_cast<const QGCOnboardLogEntry*>(rhsObj);
683 const bool lhsHasTime = lhs->received() && (lhs->time().toSecsSinceEpoch() > 0);
684 const bool rhsHasTime = rhs->received() && (rhs->time().toSecsSinceEpoch() > 0);
685 if (lhsHasTime != rhsHasTime) {
690 if (lhsHasTime && rhsHasTime) {
691 if (lhs->time() == rhs->time()) {
692 return _sortAscending ? (lhs->id() < rhs->id()) : (lhs->id() > rhs->id());
695 return _sortAscending ? (lhs->time() < rhs->time()) : (lhs->time() > rhs->time());
699 return _sortAscending ? (lhs->id() < rhs->id()) : (lhs->id() > rhs->id());
702 (void) _logEntriesModel->swapObjectList(sortedEntries);
708 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
714 qCWarning(OnboardLogControllerLog) <<
"Link Unavailable";
719 (void) mavlink_msg_log_erase_pack_chan(
722 sharedLink->mavlinkChannel(),
729 qCWarning(OnboardLogControllerLog) <<
"Failed to send";
739 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
743 if (_transport != Transport::Ftp) {
744 qCWarning(OnboardLogControllerLog) <<
"ftp: selective erase requires the FTP transport";
748 _ftpDeleteQueue.clear();
749 const int numLogs = _logEntriesModel->
count();
750 for (
int i = 0; i < numLogs; i++) {
753 _ftpDeleteQueue.enqueue(entry);
757 if (_ftpDeleteQueue.isEmpty()) {
758 qCWarning(OnboardLogControllerLog) <<
"ftp: no selected logs have FTP paths for erase";
762 qCDebug(OnboardLogControllerLog) <<
"ftp: erasing" << _ftpDeleteQueue.size() <<
"selected logs";
764 _setDownloading(
true);
768void OnboardLogController::_ftpDeleteNext()
770 if (_ftpDeleteQueue.isEmpty()) {
771 _ftpDeleting =
false;
772 _setDownloading(
false);
781 qCDebug(OnboardLogControllerLog) <<
"ftp: deleting" << entry->
ftpPath();
784 qCWarning(OnboardLogControllerLog) <<
"ftp: failed to start delete for" << entry->
ftpPath();
790void OnboardLogController::_ftpDeleteComplete(
const QString &file,
const QString &errorMsg)
796 if (!errorMsg.isEmpty()) {
797 qCWarning(OnboardLogControllerLog) <<
"ftp: delete error:" << file << errorMsg;
803void OnboardLogController::_requestLogList(uint32_t start, uint32_t end)
806 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
812 qCWarning(OnboardLogControllerLog) <<
"Link Unavailable";
817 (void) mavlink_msg_log_request_list_pack_chan(
820 sharedLink->mavlinkChannel(),
829 qCWarning(OnboardLogControllerLog) <<
"Failed to send";
833 qCDebug(OnboardLogControllerLog) <<
"Request onboard log entry list (" << start <<
"through" << end <<
")";
835 _timer->start(kRequestLogListTimeoutMs);
838void OnboardLogController::_requestLogData(uint16_t
id, uint32_t offset, uint32_t count,
int retryCount)
841 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
847 qCWarning(OnboardLogControllerLog) <<
"Link Unavailable";
852 qCDebug(OnboardLogControllerLog) <<
"Request log data (id:" <<
id <<
"offset:" << offset <<
"size:" << count <<
"retryCount" << retryCount <<
")";
855 (void) mavlink_msg_log_request_data_pack_chan(
858 sharedLink->mavlinkChannel(),
868 qCWarning(OnboardLogControllerLog) <<
"Failed to send";
872void OnboardLogController::_requestLogEnd()
875 qCWarning(OnboardLogControllerLog) <<
"Vehicle Unavailable";
881 qCWarning(OnboardLogControllerLog) <<
"Link Unavailable";
886 (void) mavlink_msg_log_request_end_pack_chan(
889 sharedLink->mavlinkChannel(),
896 qCWarning(OnboardLogControllerLog) <<
"Failed to send";
900void OnboardLogController::_setDownloading(
bool active)
902 if (_downloadingLogs != active) {
903 _downloadingLogs = active;
911void OnboardLogController::_setListing(
bool active)
913 if (_requestingLogEntries != active) {
914 _requestingLogEntries = active;
919 _sortEntriesByTimestamp();
925void OnboardLogController::_setTransport(Transport transport)
933void OnboardLogController::_ftpStartListing()
935 _ftpDirsToList.clear();
936 _ftpLogIdCounter = 0;
938 _ftpTriedFallbackRoot =
false;
944void OnboardLogController::_ftpListRoot()
946 _ftpListState = FtpListState::ListingRoot;
948 qCDebug(OnboardLogControllerLog) <<
"ftp: listing root" << _ftpLogRoot;
951 qCWarning(OnboardLogControllerLog) <<
"ftp: failed to start root listing for" << _ftpLogRoot;
952 _ftpFallbackToMessages();
956void OnboardLogController::_ftpListDirComplete(
const QStringList &dirList,
const QString &errorMsg)
958 if (_ftpListState == FtpListState::Idle) {
962 if (!errorMsg.isEmpty()) {
963 if ((_ftpListState == FtpListState::ListingRoot) && !_ftpTriedFallbackRoot && _vehicle) {
964 const char *fallback =
nullptr;
972 qCDebug(OnboardLogControllerLog) <<
"ftp: root listing of" << _ftpLogRoot <<
"failed (" << errorMsg
973 <<
"), falling back to" << fallback;
974 _ftpTriedFallbackRoot =
true;
975 _ftpLogRoot = QString::fromLatin1(fallback);
981 qCWarning(OnboardLogControllerLog) <<
"ftp: listing error:" << errorMsg;
982 _ftpFallbackToMessages();
987 qCDebug(OnboardLogControllerLog) <<
"ftp: raw entries for"
988 << ((_ftpListState == FtpListState::ListingRoot) ? _ftpLogRoot : (_ftpDirsToList.isEmpty() ? QString() : _ftpDirsToList.first()))
991 if (_ftpListState == FtpListState::ListingRoot) {
994 const uint flatLogs = _ftpProcessFileEntries(dirList, QString());
996 for (
const QString &entry : dirList) {
997 if (entry.startsWith(QLatin1Char(
'D'))) {
998 const QString dirName = entry.mid(1);
999 if (!dirName.isEmpty()) {
1000 _ftpDirsToList.append(dirName);
1005 _ftpDirsToList.sort();
1006 qCDebug(OnboardLogControllerLog) <<
"ftp: root listing of" << _ftpLogRoot
1007 <<
"found" << flatLogs <<
"flat logs and" << _ftpDirsToList.size() <<
"subdirectories";
1009 _ftpListState = FtpListState::ListingSubdir;
1010 _ftpListNextSubdir();
1014 const QString currentDir = _ftpDirsToList.isEmpty() ? QString() : _ftpDirsToList.first();
1015 const uint logsFoundInDir = _ftpProcessFileEntries(dirList, currentDir);
1017 qCDebug(OnboardLogControllerLog) <<
"ftp:" << currentDir <<
"->" << logsFoundInDir <<
"logs";
1019 if (!_ftpDirsToList.isEmpty()) {
1020 _ftpDirsToList.removeFirst();
1023 _ftpListNextSubdir();
1026uint OnboardLogController::_ftpProcessFileEntries(
const QStringList &dirList,
const QString &subdir)
1028 const QDate dirDate = subdir.isEmpty() ? QDate() : QDate::fromString(subdir, QStringLiteral(
"yyyy-MM-dd"));
1031 for (
const QString &entry : dirList) {
1032 if (!entry.startsWith(QLatin1Char(
'F'))) {
1038 const QString fileInfo = entry.mid(1);
1039 const int tabIdx = fileInfo.indexOf(QLatin1Char(
'\t'));
1044 const QString fileName = fileInfo.left(tabIdx);
1045 const QString sizeStr = fileInfo.section(QLatin1Char(
'\t'), 1, 1);
1046 const QString mtimeStr = fileInfo.section(QLatin1Char(
'\t'), 2, 2);
1048 if (!fileName.endsWith(QStringLiteral(
".ulg"), Qt::CaseInsensitive) &&
1049 !fileName.endsWith(QStringLiteral(
".bin"), Qt::CaseInsensitive)) {
1053 bool sizeOk =
false;
1054 const uint fileSize = sizeStr.toUInt(&sizeOk);
1062 bool mtimeOk =
false;
1063 const qint64 mtimeSecs = mtimeStr.toLongLong(&mtimeOk);
1064 if (mtimeOk && (mtimeSecs > 0)) {
1065 dateTime = QDateTime::fromSecsSinceEpoch(mtimeSecs, QTimeZone::UTC);
1069 if (!dateTime.isValid() && dirDate.isValid()) {
1070 const QString baseName = fileName.left(fileName.lastIndexOf(QLatin1Char(
'.')));
1071 const QTime fileTime = QTime::fromString(baseName, QStringLiteral(
"HH_mm_ss"));
1072 if (fileTime.isValid()) {
1073 dateTime = QDateTime(dirDate, fileTime, QTimeZone::UTC);
1075 dateTime = QDateTime(dirDate, QTime(), QTimeZone::UTC);
1079 const QString ftpPath = subdir.isEmpty()
1080 ? (_ftpLogRoot + QStringLiteral(
"/") + fileName)
1081 : (_ftpLogRoot + QStringLiteral(
"/") + subdir + QStringLiteral(
"/") + fileName);
1087 _logEntriesModel->
append(logEntry);
1094void OnboardLogController::_ftpListNextSubdir()
1096 if (_ftpDirsToList.isEmpty()) {
1097 qCDebug(OnboardLogControllerLog) <<
"ftp: listing complete, found" << _logEntriesModel->
count() <<
"logs";
1098 _ftpFinishListing();
1102 const QString subdir = _ftpDirsToList.first();
1103 const QString path = _ftpLogRoot + QStringLiteral(
"/") + subdir;
1105 qCDebug(OnboardLogControllerLog) <<
"ftp: listing subdir" << path;
1108 qCWarning(OnboardLogControllerLog) <<
"ftp: failed to list subdir" << path;
1109 _ftpDirsToList.removeFirst();
1110 _ftpListNextSubdir();
1114void OnboardLogController::_ftpFinishListing()
1116 _ftpListState = FtpListState::Idle;
1121 _ftpFallbackToMessages();
1128void OnboardLogController::_ftpFallbackToMessages()
1130 qCDebug(OnboardLogControllerLog) <<
"ftp: transport failed, falling back to message based log download";
1132 _ftpListState = FtpListState::Idle;
1133 _ftpDirsToList.clear();
1134 _ftpDisabled =
true;
1135 _setTransport(Transport::Messages);
1141 _requestLogList(0, 0xffff);
1144void OnboardLogController::_ftpDownloadToDirectory(
const QString &dir)
1146 _downloadPath = dir;
1147 if (_downloadPath.isEmpty()) {
1151 if (!_downloadPath.endsWith(QDir::separator())) {
1152 _downloadPath += QDir::separator();
1155 _ftpDownloadQueue.clear();
1156 _ftpDownloadHadError =
false;
1157 const int numLogs = _logEntriesModel->
count();
1158 for (
int i = 0; i < numLogs; i++) {
1162 _ftpDownloadQueue.enqueue(entry);
1166 if (_ftpDownloadQueue.isEmpty()) {
1167 qCWarning(OnboardLogControllerLog) <<
"ftp: no selected logs have FTP paths for download";
1171 qCDebug(OnboardLogControllerLog) <<
"ftp: queued" << _ftpDownloadQueue.size() <<
"logs for download to" << _downloadPath;
1172 _setDownloading(
true);
1174 _ftpDownloadEntry(_ftpDownloadQueue.dequeue());
1179 if (!entry || !_vehicle) {
1185 _ftpCurrentDownloadEntry = entry;
1186 _ftpDownloadBytesAtLastUpdate = 0;
1187 _ftpDownloadRateAvg = 0.;
1188 _ftpDownloadElapsed.start();
1193 QString localFilename = entry->
ftpPath().section(QLatin1Char(
'/'), -1);
1194 if (localFilename.isEmpty()) {
1195 localFilename = QStringLiteral(
"log_") + QString::number(entry->
id()) + QStringLiteral(
".ulg");
1198 if (QFile::exists(_downloadPath + localFilename)) {
1199 const int dotIdx = localFilename.lastIndexOf(QLatin1Char(
'.'));
1200 const QString base = (dotIdx > 0) ? localFilename.left(dotIdx) : localFilename;
1201 const QString ext = (dotIdx > 0) ? localFilename.mid(dotIdx) : QString();
1205 localFilename = base + QStringLiteral(
"_") + QString::number(numDups) + ext;
1206 }
while (QFile::exists(_downloadPath + localFilename));
1209 qCDebug(OnboardLogControllerLog) <<
"ftp: downloading" << entry->
ftpPath() <<
"to" << _downloadPath + localFilename;
1212 qCWarning(OnboardLogControllerLog) <<
"ftp: failed to start download for" << entry->
ftpPath();
1214 _ftpCurrentDownloadEntry =
nullptr;
1215 _ftpDownloadHadError =
true;
1216 _ftpDownloadQueueNext();
1220void OnboardLogController::_ftpDownloadQueueNext()
1222 if (!_ftpDownloadQueue.isEmpty()) {
1223 _ftpDownloadEntry(_ftpDownloadQueue.dequeue());
1227 if (_ftpDownloadHadError) {
1228 qCDebug(OnboardLogControllerLog) <<
"ftp: download errors occurred, using message based transport for subsequent refreshes";
1229 _ftpDisabled =
true;
1232 _setDownloading(
false);
1235void OnboardLogController::_ftpDownloadComplete(
const QString &file,
const QString &errorMsg)
1237 if (!_ftpCurrentDownloadEntry) {
1241 if (errorMsg.isEmpty()) {
1242 _ftpCurrentDownloadEntry->
setStatus(tr(
"Downloaded"));
1243 qCDebug(OnboardLogControllerLog) <<
"ftp: download complete" << file;
1245 _ftpCurrentDownloadEntry->
setStatus(tr(
"Error"));
1246 _ftpDownloadHadError =
true;
1247 qCWarning(OnboardLogControllerLog) <<
"ftp: download error:" << errorMsg;
1250 _ftpCurrentDownloadEntry =
nullptr;
1251 _ftpDownloadQueueNext();
1254void OnboardLogController::_ftpDownloadProgress(
float value)
1256 if (!_ftpCurrentDownloadEntry) {
1260 if (_ftpDownloadElapsed.elapsed() < kGUIRateMs) {
1264 const size_t totalBytes =
static_cast<size_t>(
static_cast<qreal
>(_ftpCurrentDownloadEntry->
size()) *
static_cast<qreal
>(value));
1265 if (totalBytes < _ftpDownloadBytesAtLastUpdate) {
1267 _ftpDownloadBytesAtLastUpdate = totalBytes;
1271 const size_t bytesSinceLastUpdate = totalBytes - _ftpDownloadBytesAtLastUpdate;
1272 const qreal elapsedSec = _ftpDownloadElapsed.elapsed() / 1000.0;
1273 const qreal rate = (elapsedSec > 0) ? (bytesSinceLastUpdate / elapsedSec) : 0;
1274 _ftpDownloadRateAvg = (_ftpDownloadRateAvg * 0.95) + (rate * 0.05);
1275 _ftpDownloadBytesAtLastUpdate = totalBytes;
1276 _ftpDownloadElapsed.start();
1278 const QString status = QStringLiteral(
"%1 (%2/s)").arg(
1282 _ftpCurrentDownloadEntry->
setStatus(status);
1287 if (_compressLogs != compress) {
1288 _compressLogs = compress;
1296 qCWarning(OnboardLogControllerLog) <<
"Log compression not yet implemented (decompression-only API)";
1305void OnboardLogController::_handleCompressionProgress(qreal progress)
1311void OnboardLogController::_handleCompressionFinished(
bool success)
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.
static const uint32_t kChunkSize