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 | |
28 | namespace OCC { |
29 | |
30 | /** |
31 | * @brief The ProgressInfo class |
32 | * @ingroup libsync |
33 | */ |
34 | class OWNCLOUDSYNC_EXPORT ProgressInfo : public QObject |
35 | { |
36 | Q_OBJECT |
37 | public: |
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 | |
217 | private slots: |
218 | /** |
219 | * Called every second once started, this function updates the |
220 | * estimates. |
221 | */ |
222 | void updateEstimates(); |
223 | |
224 | private: |
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 | |
243 | namespace 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 | */ |
257 | enum 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 | */ |
271 | class OWNCLOUDSYNC_EXPORT ProgressDispatcher : public QObject |
272 | { |
273 | Q_OBJECT |
274 | |
275 | friend class Folder; // only allow Folder class to access the setting slots. |
276 | public: |
277 | static ProgressDispatcher *instance(); |
278 | ~ProgressDispatcher(); |
279 | |
280 | signals: |
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 | |
304 | protected: |
305 | void setProgressInfo(const QString &folder, const ProgressInfo &progress); |
306 | |
307 | private: |
308 | ProgressDispatcher(QObject *parent = 0); |
309 | |
310 | QElapsedTimer _timer; |
311 | static ProgressDispatcher *_instance; |
312 | }; |
313 | } |
314 | #endif // PROGRESSDISPATCHER_H |
315 | |