From dbd8cd879e1770e92b01e3bc1cbeb107460a51f2 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 15 May 2024 15:47:17 +0200 Subject: Show progress of upload by reading status.txt from dnbd3-fuse --- src/gui.cpp | 163 +++++++++++++++++++++++++++++++++++++++-------------------- src/gui.h | 7 ++- src/main.cpp | 6 ++- 3 files changed, 118 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/gui.cpp b/src/gui.cpp index 361d802..aad42da 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -25,7 +26,7 @@ static QString STATE_PROCESSING("PROCESSING"); // Hashing, renaming, creating DB static QString STATE_ERROR("ERROR"); static QString STATE_COMPLETELY_DONE("COMPLETELY_DONE"); -Gui::Gui(const QString &urlbase, const QString &uuid, int dnbd3pid, QWidget *parent) +Gui::Gui(const QString &urlbase, const QString &uuid, int dnbd3pid, const QString &statusFile, QWidget *parent) : QDialog(parent) , _nam(new QNetworkAccessManager(this)) , _urlStatus(urlbase + QLatin1String("status/") + uuid) @@ -36,6 +37,8 @@ Gui::Gui(const QString &urlbase, const QString &uuid, int dnbd3pid, QWidget *par , _tmrStatus(new QTimer(this)) , _status(nullptr) , _dnbd3pid(dnbd3pid) + , _statusFile(statusFile) + , _totalClusters(0) { _nam->setAutoDeleteReplies(true); _nam->setTransferTimeout(5000); @@ -44,68 +47,116 @@ Gui::Gui(const QString &urlbase, const QString &uuid, int dnbd3pid, QWidget *par setWindowFlags(flags & ~(Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint)); _tmrStatus->start(2500); QObject::connect(_tmrStatus, &QTimer::timeout, [this]() { - QNetworkReply *reply = _nam->get(QNetworkRequest(_urlStatus)); - QObject::connect(reply, &QNetworkReply::finished, [reply, this]() { - // JSON - auto repData = reply->readAll(); - auto doc = QJsonDocument::fromJson(repData); - if (doc.isEmpty() || !doc.isObject()) { - QString str = QString::fromUtf8(repData); - int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (code == 404) { - _remoteState = STATE_ERROR; - _status->setText(tr("Sitzung dem Server nicht bekannt")); - updateButtons(); - } else if (str.length() > 0 && str.length() < 160) { - _status->setText(QLatin1String("Server: ") + str); - } else { - _status->setText(tr("Fehler beim Statusabruf")); - } - return; + Gui::queryRemoteStatus(); + if (_remoteState == STATE_WAITING_FOR_UPLOAD_DONE || _remoteState == STATE_PROCESSING) { + Gui::readDnbd3Status(); + } + }); +} + +void Gui::queryRemoteStatus() +{ + QNetworkReply *reply = _nam->get(QNetworkRequest(_urlStatus)); + QObject::connect(reply, &QNetworkReply::finished, [reply, this]() { + // JSON + auto repData = reply->readAll(); + auto doc = QJsonDocument::fromJson(repData); + if (doc.isEmpty() || !doc.isObject()) { + QString str = QString::fromUtf8(repData); + int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (code == 404) { + _remoteState = STATE_ERROR; + _status->setText(tr("Sitzung dem Server nicht bekannt")); + updateButtons(); + } else if (str.length() > 0 && str.length() < 160) { + _status->setText(QLatin1String("Server: ") + str); + } else { + _status->setText(tr("Fehler beim Statusabruf")); } - // Got JSON - auto outer = doc.object(); - QJsonValue state = outer[QLatin1String("state")]; - if (state.isString()) { - auto ns = state.toString(); - if (ns != _remoteState) { - _status->setText(ns); - _remoteState = ns; - updateButtons(); - } + return; + } + // Got JSON + auto outer = doc.object(); + QJsonValue state = outer[QLatin1String("state")]; + if (state.isString()) { + auto ns = state.toString(); + if (ns != _remoteState) { + _status->setText(ns); + _remoteState = ns; + updateButtons(); } - QJsonValue tasks = outer[QLatin1String("tasks")]; - if (tasks.isArray()) { - auto arr = tasks.toArray(); - for (auto it : arr) { - if (!it.isObject()) - continue; - auto o = it.toObject(); - auto jt = o.value(QLatin1String("title")); - if (!jt.isString()) - continue; - auto jp = o.value(QLatin1String("percent")); - auto je = o.value(QLatin1String("error")); - QString title = jt.toString(); - QString err = je.isString() ? je.toString() : QLatin1String(""); - int percent = err.isEmpty() && jp.isDouble() ? (int)jp.toDouble() : -1; - Progress *item = _items.value(title); - if (item == nullptr) { - if (_items.size() > 10) - continue; - item = new Progress(title, this); - _itemBox->addWidget(item); - _items.insert(title, item); - item->show(); - } - item->setProgress(percent); - item->setCaption(title + QLatin1String(" ") + err); + } + QJsonValue tasks = outer[QLatin1String("tasks")]; + if (tasks.isArray()) { + auto arr = tasks.toArray(); + for (auto it : arr) { + if (!it.isObject()) + continue; + auto o = it.toObject(); + auto jt = o.value(QLatin1String("title")); + if (!jt.isString()) + continue; + auto jp = o.value(QLatin1String("percent")); + auto je = o.value(QLatin1String("error")); + QString title = jt.toString(); + QString err = je.isString() ? je.toString() : QLatin1String(""); + int percent = err.isEmpty() && jp.isDouble() ? (int)jp.toDouble() : -1; + setProgressState(title, percent, err); + if (!err.isEmpty()) { + _status->setText(_remoteState + QLatin1String(": ") + err); } } - }); + } }); } +void Gui::setProgressState(const QString &title, int percent, const QString &err) +{ + Progress *item = _items.value(title); + if (item == nullptr) { + if (_items.size() > 10) + return; + item = new Progress(title, this); + _itemBox->addWidget(item); + _items.insert(title, item); + item->show(); + } + item->setProgress(percent); + if (err.isEmpty()) { + item->setCaption(title); + } else { + item->setCaption(title + QLatin1String(" ") + err); + } +} + +void Gui::readDnbd3Status() +{ + if (_statusFile.isEmpty()) + return; + /* + [General] + uuid=43dd730b-56e0-4581-9480-4fbafdfa772d + state=backgroundUpload + inQueue=0 + modifiedClusters=8 + idleClusters=134 + totalClustersUploaded=211 + activeUploads=0 + avgSpeedKb=0.00 + */ + int remaining = 0; + QSettings setting(_statusFile, QSettings::IniFormat); + remaining += setting.value(QLatin1String("inQueue")).toInt(); + remaining += setting.value(QLatin1String("modifiedClusters")).toInt(); + if (remaining > _totalClusters) { + _totalClusters = remaining; + } + if (_totalClusters == 0) + return; + int percent = ((_totalClusters - remaining) * 100) / _totalClusters; + setProgressState(tr("Ă„nderungen hochladen"), percent, QString()); +} + Gui::~Gui() { _nam->blockSignals(true); diff --git a/src/gui.h b/src/gui.h index 24b4478..e6706ae 100644 --- a/src/gui.h +++ b/src/gui.h @@ -16,7 +16,7 @@ class Gui : public QDialog { Q_OBJECT public: - explicit Gui(const QString &urlbase, const QString &uuid, int dnbd3pid, QWidget *parent = nullptr); + explicit Gui(const QString &urlbase, const QString &uuid, int dnbd3pid, const QString &statusFile, QWidget *parent = nullptr); ~Gui(); protected: @@ -29,6 +29,9 @@ private slots: private: void setupUi(); void updateButtons(); + void readDnbd3Status(); + void queryRemoteStatus(); + void setProgressState(const QString &title, int percent, const QString &err); QNetworkAccessManager *_nam; QVBoxLayout *_itemBox; @@ -41,6 +44,8 @@ private: QHash _items; QLabel *_status; int _dnbd3pid; + QString _statusFile; + int _totalClusters; }; #endif diff --git a/src/main.cpp b/src/main.cpp index bbb70a5..134f3a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,9 +20,13 @@ int main(int argc, char **argv) QCommandLineOption pidOption(QStringList() << QLatin1String("pid") << QLatin1String("p"), QObject::tr("PID of dnbd3-fuse, so we can cancel the upload via SIGQUIT on abort"), QLatin1String("pid")); + QCommandLineOption statusOption(QStringList() << QLatin1String("status") << QLatin1String("f"), + QObject::tr("Path to dnbd3-fuse cow status file"), + QLatin1String("path")); parser.addOption(sessionOption); parser.addOption(urlOption); parser.addOption(pidOption); + parser.addOption(statusOption); parser.process(app); if (!parser.isSet(sessionOption) || !parser.isSet(urlOption) || !parser.isSet(pidOption)) { @@ -31,7 +35,7 @@ int main(int argc, char **argv) return 1; } - Gui window(parser.value(urlOption), parser.value(sessionOption), parser.value(pidOption).toInt()); + Gui window(parser.value(urlOption), parser.value(sessionOption), parser.value(pidOption).toInt(), parser.value(statusOption)); window.show(); return QGuiApplication::exec(); -- cgit v1.2.3-55-g7522