1/*
2 * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15#ifndef PROGRESSDISPATCHER_H
16#define PROGRESSDISPATCHER_H
17
18#include "owncloudlib.h"
19#include <QObject>
20#include <QHash>
21#include <QTime>
22#include <QQueue>
23#include <QElapsedTimer>
24#include <QTimer>
25
26#include "syncfileitem.h"
27
28namespace OCC {
29
30/**
31 * @brief The ProgressInfo class
32 * @ingroup libsync
33 */
34class OWNCLOUDSYNC_EXPORT ProgressInfo : public QObject
35{
36 Q_OBJECT
37public:
38 ProgressInfo();
39
40 /** Resets for a new sync run.
41 */
42 void reset();
43
44 /** Records the status of the sync run
45 */
46 enum Status {
47 /// Emitted once at start
48 Starting,
49
50 /**
51 * Emitted once without _currentDiscoveredFolder when it starts,
52 * then for each folder.
53 */
54 Discovery,
55
56 /// Emitted once when reconcile starts
57 Reconcile,
58
59 /// Emitted during propagation, with progress data
60 Propagation,
61
62 /**
63 * Emitted once when done
64 *
65 * Except when SyncEngine jumps directly to finalize() without going
66 * through slotFinished().
67 */
68 Done
69 };
70
71 Status status() const;
72
73 /**
74 * Called when propagation starts.
75 *
76 * isUpdatingEstimates() will return true afterwards.
77 */
78 void startEstimateUpdates();
79
80 /**
81 * Returns true when startEstimateUpdates() was called.
82 *
83 * This is used when the SyncEngine wants to indicate a new sync
84 * is about to start via the transmissionProgress() signal. The
85 * first ProgressInfo will have isUpdatingEstimates() == false.
86 */
87 bool isUpdatingEstimates() const;
88
89 /**
90 * Increase the file and size totals by the amount indicated in item.
91 */
92 void adjustTotalsForFile(const SyncFileItem &item);
93
94 quint64 totalFiles() const;
95 quint64 completedFiles() const;
96
97 quint64 totalSize() const;
98 quint64 completedSize() const;
99
100 /** Number of a file that is currently in progress. */
101 quint64 currentFile() const;
102
103 /** Return true if the size needs to be taken in account in the total amount of time */
104 static inline bool isSizeDependent(const SyncFileItem &item)
105 {
106 return !item.isDirectory() && (item._instruction == CSYNC_INSTRUCTION_CONFLICT
107 || item._instruction == CSYNC_INSTRUCTION_SYNC
108 || item._instruction == CSYNC_INSTRUCTION_NEW
109 || item._instruction == CSYNC_INSTRUCTION_TYPE_CHANGE);
110 }
111
112 /**
113 * Holds estimates about progress, returned to the user.
114 */
115 struct Estimates
116 {
117 /// Estimated completion amount per second. (of bytes or files)
118 quint64 estimatedBandwidth;
119
120 /// Estimated time remaining in milliseconds.
121 quint64 estimatedEta;
122 };
123
124 /**
125 * Holds the current state of something making progress and maintains an
126 * estimate of the current progress per second.
127 */
128 struct OWNCLOUDSYNC_EXPORT Progress
129 {
130 Progress()
131 : _progressPerSec(0)
132 , _prevCompleted(0)
133 , _initialSmoothing(1.0)
134 , _completed(0)
135 , _total(0)
136 {
137 }
138
139 /** Returns the estimates about progress per second and eta. */
140 Estimates estimates() const;
141
142 quint64 completed() const;
143 quint64 remaining() const;
144
145 private:
146 /**
147 * Update the exponential moving average estimate of _progressPerSec.
148 */
149 void update();
150
151 /**
152 * Changes the _completed value and does sanity checks on
153 * _prevCompleted and _total.
154 */
155 void setCompleted(quint64 completed);
156
157 // Updated by update()
158 double _progressPerSec;
159 quint64 _prevCompleted;
160
161 // Used to get to a good value faster when
162 // progress measurement stats. See update().
163 double _initialSmoothing;
164
165 // Set and updated by ProgressInfo
166 quint64 _completed;
167 quint64 _total;
168
169 friend class ProgressInfo;
170 };
171
172 Status _status;
173
174 struct OWNCLOUDSYNC_EXPORT ProgressItem
175 {
176 SyncFileItem _item;
177 Progress _progress;
178 };
179 QHash<QString, ProgressItem> _currentItems;
180
181 SyncFileItem _lastCompletedItem;
182
183 // Used during local and remote update phase
184 QString _currentDiscoveredRemoteFolder;
185 QString _currentDiscoveredLocalFolder;
186
187 void setProgressComplete(const SyncFileItem &item);
188
189 void setProgressItem(const SyncFileItem &item, quint64 completed);
190
191 /**
192 * Get the total completion estimate
193 */
194 Estimates totalProgress() const;
195
196 /**
197 * Get the optimistic eta.
198 *
199 * This value is based on the highest observed transfer bandwidth
200 * and files-per-second speed.
201 */
202 quint64 optimisticEta() const;
203
204 /**
205 * Whether the remaining-time estimate is trusted.
206 *
207 * We don't trust it if it is hugely above the optimistic estimate.
208 * See #5046.
209 */
210 bool trustEta() const;
211
212 /**
213 * Get the current file completion estimate structure
214 */
215 Estimates fileProgress(const SyncFileItem &item) const;
216
217private slots:
218 /**
219 * Called every second once started, this function updates the
220 * estimates.
221 */
222 void updateEstimates();
223
224private:
225 // Sets the completed size by summing finished jobs with the progress
226 // of active ones.
227 void recomputeCompletedSize();
228
229 // Triggers the update() slot every second once propagation started.
230 QTimer _updateEstimatesTimer;
231
232 Progress _sizeProgress;
233 Progress _fileProgress;
234
235 // All size from completed jobs only.
236 quint64 _totalSizeOfCompletedJobs;
237
238 // The fastest observed rate of files per second in this sync.
239 double _maxFilesPerSecond;
240 double _maxBytesPerSecond;
241};
242
243namespace Progress {
244
245 OWNCLOUDSYNC_EXPORT QString asActionString(const SyncFileItem &item);
246 OWNCLOUDSYNC_EXPORT QString asResultString(const SyncFileItem &item);
247
248 OWNCLOUDSYNC_EXPORT bool isWarningKind(SyncFileItem::Status);
249 OWNCLOUDSYNC_EXPORT bool isIgnoredKind(SyncFileItem::Status);
250}
251
252/** Type of error
253 *
254 * Used for ProgressDispatcher::syncError. May trigger error interactivity
255 * in IssuesWidget.
256 */
257enum class ErrorCategory {
258 Normal,
259 InsufficientRemoteStorage,
260};
261
262/**
263 * @file progressdispatcher.h
264 * @brief A singleton class to provide sync progress information to other gui classes.
265 *
266 * How to use the ProgressDispatcher:
267 * Just connect to the two signals either to progress for every individual file
268 * or the overall sync progress.
269 *
270 */
271class OWNCLOUDSYNC_EXPORT ProgressDispatcher : public QObject
272{
273 Q_OBJECT
274
275 friend class Folder; // only allow Folder class to access the setting slots.
276public:
277 static ProgressDispatcher *instance();
278 ~ProgressDispatcher();
279
280signals:
281 /**
282 @brief Signals the progress of data transmission.
283
284 @param[out] folder The folder which is being processed
285 @param[out] progress A struct with all progress info.
286
287 */
288 void progressInfo(const QString &folder, const ProgressInfo &progress);
289 /**
290 * @brief: the item was completed by a job
291 */
292 void itemCompleted(const QString &folder, const SyncFileItemPtr &item);
293
294 /**
295 * @brief A new folder-wide sync error was seen.
296 */
297 void syncError(const QString &folder, const QString &message, ErrorCategory category);
298
299 /**
300 * @brief Emitted for a folder when a sync is done, listing all pending conflicts
301 */
302 void folderConflicts(const QString &folder, const QStringList &conflictPaths);
303
304protected:
305 void setProgressInfo(const QString &folder, const ProgressInfo &progress);
306
307private:
308 ProgressDispatcher(QObject *parent = 0);
309
310 QElapsedTimer _timer;
311 static ProgressDispatcher *_instance;
312};
313}
314#endif // PROGRESSDISPATCHER_H
315