13#include <QtCore/QDateTime>
15#include <QtCore/QFile>
16#include <QtCore/QTimeZone>
31 qCDebug(OnboardLogFtpControllerLog) <<
this;
39 qCDebug(OnboardLogFtpControllerLog) <<
this;
42void OnboardLogFtpController::_setActiveVehicle(
Vehicle *vehicle)
44 if (vehicle == _vehicle) {
58 _downloadQueue.clear();
59 _currentDownloadEntry =
nullptr;
70 qCWarning(OnboardLogFtpControllerLog) <<
"refresh: no active vehicle";
75 qCWarning(OnboardLogFtpControllerLog) <<
"refresh: vehicle does not advertise MAV_PROTOCOL_CAPABILITY_FTP"
83void OnboardLogFtpController::_startListing()
88 _triedFallbackRoot =
false;
98void OnboardLogFtpController::_listRoot()
100 _listState = ListingRoot;
102 qCDebug(OnboardLogFtpControllerLog) <<
"listing root" << _logRoot;
105 qCWarning(OnboardLogFtpControllerLog) <<
"failed to start root listing for" << _logRoot;
110void OnboardLogFtpController::_listDirComplete(
const QStringList &dirList,
const QString &errorMsg)
112 if (!errorMsg.isEmpty()) {
113 if (_listState == ListingRoot && !_triedFallbackRoot && _vehicle) {
114 const char *fallback =
nullptr;
122 qCDebug(OnboardLogFtpControllerLog) <<
"root listing of" << _logRoot <<
"failed (" << errorMsg
123 <<
"), falling back to" << fallback;
124 _triedFallbackRoot =
true;
125 _logRoot = QString::fromLatin1(fallback);
131 qCWarning(OnboardLogFtpControllerLog) <<
"listing error:" << errorMsg;
136 if (_listState == ListingRoot) {
139 const uint flatLogs = _processFileEntries(dirList, QString());
141 for (
const QString &entry : dirList) {
142 if (entry.startsWith(QLatin1Char(
'D'))) {
143 const QString dirName = entry.mid(1);
144 if (!dirName.isEmpty()) {
145 _dirsToList.append(dirName);
151 qCDebug(OnboardLogFtpControllerLog) <<
"root listing of" << _logRoot
152 <<
"found" << flatLogs <<
"flat logs and" << _dirsToList.size() <<
"subdirectories";
154 _listState = ListingSubdir;
159 const QString currentDir = _dirsToList.isEmpty() ? QString() : _dirsToList.first();
160 const uint logsFoundInDir = _processFileEntries(dirList, currentDir);
162 qCDebug(OnboardLogFtpControllerLog) << currentDir <<
"->" << logsFoundInDir <<
"logs";
164 if (!_dirsToList.isEmpty()) {
165 _dirsToList.removeFirst();
171uint OnboardLogFtpController::_processFileEntries(
const QStringList &dirList,
const QString &subdir)
173 const QDate dirDate = subdir.isEmpty() ? QDate() : QDate::fromString(subdir, QStringLiteral(
"yyyy-MM-dd"));
176 for (
const QString &entry : dirList) {
177 if (!entry.startsWith(QLatin1Char(
'F'))) {
181 const QString fileInfo = entry.mid(1);
182 const int tabIdx = fileInfo.indexOf(QLatin1Char(
'\t'));
187 const QString fileName = fileInfo.left(tabIdx);
188 const QString sizeStr = fileInfo.mid(tabIdx + 1);
190 if (!fileName.endsWith(QStringLiteral(
".ulg"), Qt::CaseInsensitive) &&
191 !fileName.endsWith(QStringLiteral(
".bin"), Qt::CaseInsensitive)) {
196 const uint fileSize = sizeStr.toUInt(&sizeOk);
202 if (dirDate.isValid()) {
203 const QString baseName = fileName.left(fileName.lastIndexOf(QLatin1Char(
'.')));
204 const QTime fileTime = QTime::fromString(baseName, QStringLiteral(
"HH_mm_ss"));
205 if (fileTime.isValid()) {
206 dateTime = QDateTime(dirDate, fileTime, QTimeZone::UTC);
208 dateTime = QDateTime(dirDate, QTime(), QTimeZone::UTC);
212 const QString ftpPath = subdir.isEmpty()
213 ? (_logRoot + QStringLiteral(
"/") + fileName)
214 : (_logRoot + QStringLiteral(
"/") + subdir + QStringLiteral(
"/") + fileName);
219 _logEntriesModel->
append(logEntry);
226void OnboardLogFtpController::_listNextSubdir()
228 if (_dirsToList.isEmpty()) {
229 qCDebug(OnboardLogFtpControllerLog) <<
"listing complete, found" << _logEntriesModel->
count() <<
"logs";
234 const QString subdir = _dirsToList.first();
235 const QString path = _logRoot + QStringLiteral(
"/") + subdir;
237 qCDebug(OnboardLogFtpControllerLog) <<
"listing subdir" << path;
240 qCWarning(OnboardLogFtpControllerLog) <<
"failed to list subdir" << path;
241 _dirsToList.removeFirst();
246void OnboardLogFtpController::_finishListing()
255 _downloadToDirectory(dir);
258void OnboardLogFtpController::_downloadToDirectory(
const QString &dir)
261 if (_downloadPath.isEmpty()) {
265 if (!_downloadPath.endsWith(QDir::separator())) {
266 _downloadPath += QDir::separator();
269 _downloadQueue.clear();
270 const int numLogs = _logEntriesModel->
count();
271 for (
int i = 0; i < numLogs; i++) {
275 _downloadQueue.enqueue(entry);
279 if (_downloadQueue.isEmpty()) {
280 qCWarning(OnboardLogFtpControllerLog) <<
"no selected logs have FTP paths for download";
284 qCDebug(OnboardLogFtpControllerLog) <<
"queued" << _downloadQueue.size() <<
"logs for download to" << _downloadPath;
285 _setDownloading(
true);
287 _downloadEntry(_downloadQueue.dequeue());
292 if (!entry || !_vehicle) {
299 _currentDownloadEntry = entry;
300 _downloadBytesAtLastUpdate = 0;
301 _downloadRateAvg = 0.;
302 _downloadElapsed.start();
306 QString localFilename;
307 if (entry->
time().isValid() && entry->
time().date().year() >= 2010) {
308 localFilename = entry->
time().toString(QStringLiteral(
"yyyy-M-d-hh-mm-ss")) + QStringLiteral(
".ulg");
310 localFilename = QStringLiteral(
"log_") + QString::number(entry->
id()) + QStringLiteral(
".ulg");
313 if (QFile::exists(_downloadPath + localFilename)) {
314 const QStringList parts = localFilename.split(QLatin1Char(
'.'));
318 localFilename = parts[0] + QStringLiteral(
"_") + QString::number(numDups) + QStringLiteral(
".") + parts[1];
319 }
while (QFile::exists(_downloadPath + localFilename));
328 qCDebug(OnboardLogFtpControllerLog) <<
"downloading" << entry->
ftpPath() <<
"to" << _downloadPath + localFilename;
330 if (!ftp->
download(MAV_COMP_ID_AUTOPILOT1, entry->
ftpPath(), _downloadPath, localFilename,
true)) {
331 qCWarning(OnboardLogFtpControllerLog) <<
"failed to start download for" << entry->
ftpPath();
333 _currentDownloadEntry =
nullptr;
335 if (!_downloadQueue.isEmpty()) {
336 _downloadEntry(_downloadQueue.dequeue());
338 _setDownloading(
false);
343void OnboardLogFtpController::_downloadComplete(
const QString &file,
const QString &errorMsg)
345 if (!_currentDownloadEntry) {
349 if (errorMsg.isEmpty()) {
350 _currentDownloadEntry->
setStatus(tr(
"Downloaded"));
351 qCDebug(OnboardLogFtpControllerLog) <<
"download complete" << file;
353 _currentDownloadEntry->
setStatus(tr(
"Error"));
354 qCWarning(OnboardLogFtpControllerLog) <<
"download error:" << errorMsg;
357 _currentDownloadEntry =
nullptr;
359 if (!_downloadQueue.isEmpty()) {
360 _downloadEntry(_downloadQueue.dequeue());
362 _setDownloading(
false);
366void OnboardLogFtpController::_downloadProgress(
float value)
368 if (!_currentDownloadEntry) {
372 if (_downloadElapsed.elapsed() < kGUIRateMs) {
376 const size_t totalBytes =
static_cast<size_t>(
static_cast<qreal
>(_currentDownloadEntry->
size()) *
static_cast<qreal
>(value));
377 const size_t bytesSinceLastUpdate = totalBytes - _downloadBytesAtLastUpdate;
378 const qreal elapsedSec = _downloadElapsed.elapsed() / 1000.0;
379 const qreal rate = (elapsedSec > 0) ? (bytesSinceLastUpdate / elapsedSec) : 0;
380 _downloadRateAvg = (_downloadRateAvg * 0.95) + (rate * 0.05);
381 _downloadBytesAtLastUpdate = totalBytes;
382 _downloadElapsed.start();
384 const QString status = QStringLiteral(
"%1 (%2/s)").arg(
388 _currentDownloadEntry->
setStatus(status);
397 if (_requestingLogEntries) {
403 if (_downloadingLogs) {
405 if (_currentDownloadEntry) {
406 _currentDownloadEntry->
setStatus(tr(
"Canceled"));
407 _currentDownloadEntry =
nullptr;
409 _downloadQueue.clear();
412 _resetSelection(
true);
413 _setDownloading(
false);
416void OnboardLogFtpController::_resetSelection(
bool canceled)
418 const int numLogs = _logEntriesModel->
count();
419 for (
int i = 0; i < numLogs; i++) {
436void OnboardLogFtpController::_setDownloading(
bool active)
438 if (_downloadingLogs != active) {
439 _downloadingLogs = active;
447void OnboardLogFtpController::_setListing(
bool active)
449 if (_requestingLogEntries != active) {
450 _requestingLogEntries = active;
static constexpr const char * kApmLogRootFallback
static constexpr const char * kPx4LogRootFallback
static constexpr const char * kMavlinkLogRoot
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool listDirectory(uint8_t fromCompId, const QString &fromURI)
void commandProgress(float value)
void downloadComplete(const QString &file, const QString &errorMsg)
void cancelListDirectory()
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)
static MultiVehicleManager * instance()
void activeVehicleChanged(Vehicle *activeVehicle)
void requestingListChanged()
Q_INVOKABLE void cancel()
Q_INVOKABLE void download(const QString &path=QString())
void downloadingLogsChanged()
~OnboardLogFtpController()
Q_INVOKABLE void refresh()
void setFtpPath(const QString &path)
void setStatus(const QString &stat)
void setSelected(bool sel)
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.
static SettingsManager * instance()
AppSettings * appSettings() const
void setCommunicationLostEnabled(bool communicationLostEnabled)
uint64_t capabilityBits() const
VehicleLinkManager * vehicleLinkManager()
FTPManager * ftpManager()
bool capabilitiesKnown() const
QString bigSizeToString(quint64 size)
Byte size with unit: B, KB, MB, GB, TB. 1 fractional digit above 1 KB.