From 1509c4ffba1d1a2f4ed22d004383a2d7c3bcd46d Mon Sep 17 00:00:00 2001 From: Eran Date: Wed, 14 May 2014 10:55:04 +0300 Subject: [PATCH] made the time description more malleable ,added support for none size update estimation (ie.. deletion), made the estimation more responsive at start and less jittery after some time --- src/mirall/owncloudgui.cpp | 8 ++---- src/mirall/progressdispatcher.h | 30 ++++++++++++++------ src/mirall/utility.cpp | 49 +++++++++++++++++++++------------ src/mirall/utility.h | 15 ++++++---- 4 files changed, 64 insertions(+), 38 deletions(-) diff --git a/src/mirall/owncloudgui.cpp b/src/mirall/owncloudgui.cpp index 8d2561583..9429b5bef 100644 --- a/src/mirall/owncloudgui.cpp +++ b/src/mirall/owncloudgui.cpp @@ -405,12 +405,10 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const Progress::Info QString totalSizeStr = Utility::octetsToString( progress._totalSize ); if(progress._totalSize == 0 ) { - quint64 completedSize = progress.completedSize(); - quint64 currentFile = progress._completedFileCount + progress._currentItems.count(); - QString completedSizeStr = Utility::octetsToString( completedSize ); - _actionStatus->setText( tr("Syncing %1 of %2 ( %3 of %4 )") + quint64 currentFile = progress._completedFileCount + progress._currentItems.count(); + _actionStatus->setText( tr("Syncing %1 of %2 ( %3 left )") .arg( currentFile ).arg( progress._totalFileCount ) - .arg( completedSizeStr, totalSizeStr ) ); + .arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate()) ) ); } else { _actionStatus->setText( tr("Syncing %1 ( %2 left )") .arg( totalSizeStr ) diff --git a/src/mirall/progressdispatcher.h b/src/mirall/progressdispatcher.h index 30132bdf9..198b03910 100644 --- a/src/mirall/progressdispatcher.h +++ b/src/mirall/progressdispatcher.h @@ -45,20 +45,22 @@ namespace Progress // Should this be in a separate file? struct EtaEstimate { - EtaEstimate() : _startedTime(QDateTime::currentMSecsSinceEpoch()), _agvEtaMSecs(0),_effectivProgressPerSec(0) {} + EtaEstimate() : _startedTime(QDateTime::currentMSecsSinceEpoch()), _agvEtaMSecs(0),_effectivProgressPerSec(0),_sampleCount(1) {} - static const int AVG_DIVIDER=10; + static const int MAX_AVG_DIVIDER=120; static const int INITAL_WAIT_TIME=5; - quint64 _startedTime ; - quint64 _agvEtaMSecs; - quint64 _effectivProgressPerSec; + quint64 _startedTime ; + quint64 _agvEtaMSecs; + quint64 _effectivProgressPerSec; + quint16 _sampleCount; /** * reset the estiamte. */ void reset() { _startedTime = QDateTime::currentMSecsSinceEpoch(); + _sampleCount =1; _effectivProgressPerSec = _agvEtaMSecs = 0; } @@ -70,7 +72,8 @@ namespace Progress void updateTime(quint64 completed, quint64 total) { quint64 elapsedTime = QDateTime::currentMSecsSinceEpoch() - this->_startedTime ; //don't start until you have some good data to process, prevents jittring estiamtes at the start of the syncing process - if(total != 0 && completed != 0 && elapsedTime > INITAL_WAIT_TIME ) { + if(total != 0 && completed != 0 && elapsedTime > INITAL_WAIT_TIME ) { + if(_sampleCount < MAX_AVG_DIVIDER) { _sampleCount++; } // (elapsedTime-1) is an hack to avoid float "rounding" issue (ie. 0.99999999999999999999....) _agvEtaMSecs = _agvEtaMSecs + (((static_cast(total) / completed) * elapsedTime) - (elapsedTime-1)) - this->getEtaEstimate(); _effectivProgressPerSec = ( total - completed ) / (1+this->getEtaEstimate()/1000); @@ -82,7 +85,7 @@ namespace Progress * @return quint64 the estimate amount of milliseconds to end the process. */ quint64 getEtaEstimate() const { - return _agvEtaMSecs / AVG_DIVIDER; + return _agvEtaMSecs / _sampleCount; } /** @@ -113,16 +116,25 @@ namespace Progress } _completedFileCount++; _lastCompletedItem = item; + this->updateEstimation(); } void setProgressItem(const SyncFileItem &item, quint64 size) { _currentItems[item._file]._item = item; _currentItems[item._file]._completedSize = size; _lastCompletedItem = SyncFileItem(); - - _totalEtaEstimate.updateTime(this->completedSize(),this->_totalSize); + + this->updateEstimation(); _currentItems[item._file]._etaEstimate.updateTime(size,item._size); } + + void updateEstimation() { + if(this->_totalSize > 0) { + _totalEtaEstimate.updateTime(this->completedSize(),this->_totalSize); + } else { + _totalEtaEstimate.updateTime(this->_completedFileCount,this->_totalFileCount); + } + } quint64 completedSize() const { quint64 r = _completedSize; diff --git a/src/mirall/utility.cpp b/src/mirall/utility.cpp index 4f2f00ff1..f4a58e073 100644 --- a/src/mirall/utility.cpp +++ b/src/mirall/utility.cpp @@ -452,35 +452,48 @@ qint64 Utility::qDateTimeToTime_t(const QDateTime& t) return t.toMSecsSinceEpoch() / 1000; } -QString Utility::timeToDescriptiveString(quint64 msecs) +QString Utility::timeToDescriptiveString(quint64 msecs) { QList > timeMapping = QList >(); - //timeMapping.append(QPair("years",86400*365)); - //timeMapping.append(QPair("months",86400*30)); - //timeMapping.append(QPair("days",86400)); + timeMapping.append(QPair("years",86400*365)); + timeMapping.append(QPair("months",86400*30)); + timeMapping.append(QPair("days",86400)); timeMapping.append(QPair("hours",3600)); timeMapping.append(QPair("minutes",60)); timeMapping.append(QPair("seconds",1)); + return timeToDescriptiveString(timeMapping, msecs, 1); +} + +QString Utility::timeToDescriptiveString(QList > &timeMapping, quint64 msecs, quint8 precision) +{ quint64 secs = msecs / 1000; - QString units = "seconds"; - qint64 minor =-1, major = -1; + QString retStr = "0 seconds"; // default value in case theres no actual time in msecs. + qint64 values[6]; + int idx = 0; + + for(QList >::Iterator itr = timeMapping.begin(); itr != timeMapping.end() && idx <= precision; itr++) { + quint64 result = secs / itr->second; + if(idx == 0) { + if(result == 0 ) { + continue; + } else { + retStr = itr->first; + retStr.prepend(" "); + } + } + secs -= result * itr->second; + values[idx++] = result; + } - QList >::Iterator itr = timeMapping.begin(); - for(; itr != timeMapping.end(); itr++) { - major = secs / itr->second; - secs -= (major * itr->second); - if(major > 0 ) { - units = itr->first; - break; + for(idx--; idx >= 0; idx--) { + retStr = retStr.prepend("%1").arg(values[idx], 2, 10, QChar('0')); + if(0 < idx) { + retStr.prepend(":"); } - - } - if(itr < timeMapping.end() ) { - minor = secs / (++itr)->second; } - return (QString("%1").arg(major)).append(minor > -1 ? QString(":%1").arg(minor, 2, 10, QChar('0')) : "" ).append(" ").append(units); + return retStr; } diff --git a/src/mirall/utility.h b/src/mirall/utility.h index 18164c534..0ef07f7f2 100644 --- a/src/mirall/utility.h +++ b/src/mirall/utility.h @@ -61,12 +61,15 @@ namespace Utility QDateTime qDateTimeFromTime_t(qint64 t); qint64 qDateTimeToTime_t(const QDateTime &t); - /** - * Convert milliseconds to HMS string. - * @param quint64 msecs the milliseconds to convert to string - * @return an HMS representation of the milliseconds value. - */ - QString timeToDescriptiveString(quint64 msecs); + + QString timeToDescriptiveString(quint64 msecs); + /** + * @brief Convert milliseconds to HMS string. + * @param quint64 msecs the milliseconds to convert to string. + * @param uint precision the amount of sub dviving scale to include in the result. + * @return an HMS representation of the milliseconds value. + */ + QString timeToDescriptiveString(QList > &timeMapping, quint64 msecs, quint8 precision); // convinience OS detection methods bool isWindows();