source: branches/eraser6/SpeedMeter/Eraser.Manager/ProgressManager.cs @ 1506

Revision 1506, 13.2 KB checked in by lowjoel, 4 years ago (diff)

Tolerate some margin of error when checking the total weights in the SteppedProgressManager?

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2009 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26
27namespace Eraser.Manager
28{
29    /// <summary>
30    /// Manages the progress for any operation.
31    /// </summary>
32    public abstract class ProgressManagerBase
33    {
34        /// <summary>
35        /// Constructor.
36        ///
37        /// This sets the starting time of this task to allow the computation of
38        /// the estimated end time by extrapolating collected data based on the
39        /// amount of time already elapsed.
40        /// </summary>
41        public ProgressManagerBase()
42        {
43            StartTime = DateTime.Now;
44        }
45
46        /// <summary>
47        /// Resets the starting time of the task. The speed measurement is
48        /// automatically started when the ProgressManagerBase object is created.
49        /// </summary>
50        public void Restart()
51        {
52            StartTime = DateTime.Now;
53        }
54
55        /// <summary>
56        /// Gets the percentage of the operation completed.
57        /// </summary>
58        public abstract float Progress
59        {
60            get;
61        }
62
63        /// <summary>
64        /// Computes the speed of the erase, in units of completion per second,
65        /// based on the information collected in the previous 15 seconds.
66        /// </summary>
67        public abstract int Speed
68        {
69            get;
70        }
71
72        /// <summary>
73        /// Calculates the estimated amount of time left based on the total
74        /// amount of information to erase and the current speed of the erase
75        /// </summary>
76        public abstract TimeSpan TimeLeft
77        {
78            get;
79        }
80
81        /// <summary>
82        /// The starting time of this task.
83        /// </summary>
84        public DateTime StartTime
85        {
86            get;
87            private set;
88        }
89    }
90
91    /// <summary>
92    /// Manages progress based only on one input, set through the Completed and Total
93    /// properties.
94    /// </summary>
95    public class ProgressManager : ProgressManagerBase
96    {
97        /// <summary>
98        /// Gets or sets the number of work units already completed.
99        /// </summary>
100        public long Completed
101        {
102            get;
103            set;
104        }
105
106        /// <summary>
107        /// Gets or sets the total number of work units that this task has.
108        /// </summary>
109        public long Total
110        {
111            get;
112            set;
113        }
114
115        public override float Progress
116        {
117            get
118            {
119                if (Total == 0)
120                    return 0.0f;
121
122                return (float)((double)Completed / Total);
123            }
124        }
125
126        public override int Speed
127        {
128            get
129            {
130                if (DateTime.Now == StartTime)
131                    return 0;
132
133                if ((DateTime.Now - lastSpeedCalc).Seconds < 5 && lastSpeed != 0)
134                    return lastSpeed;
135
136                //Calculate how much time has passed
137                double timeElapsed = (DateTime.Now - lastSpeedCalc).TotalSeconds;
138                if (timeElapsed == 0.0)
139                    return 0;
140
141                //Then compute the speed of the calculation
142                lastSpeed = (int)((Completed - lastCompleted) / timeElapsed);
143                lastSpeedCalc = DateTime.Now;
144                lastCompleted = Completed;
145                return lastSpeed;
146            }
147        }
148
149        public override TimeSpan TimeLeft
150        {
151            get
152            {
153                if (Speed == 0)
154                    return TimeSpan.Zero;
155                return new TimeSpan(0, 0, (int)((Total - Completed) / Speed));
156            }
157        }
158
159        /// <summary>
160        /// The last time a speed calculation was computed so that speed is not
161        /// computed too often.
162        /// </summary>
163        private DateTime lastSpeedCalc;
164
165        /// <summary>
166        /// The amount of the operation completed at the last speed computation.
167        /// </summary>
168        private long lastCompleted;
169
170        /// <summary>
171        /// The last calculated speed of the operation.
172        /// </summary>
173        private int lastSpeed;
174    }
175
176    /// <summary>
177    /// Manages progress based on sub-tasks.
178    /// </summary>
179    public abstract class ChainedProgressManager : ProgressManagerBase
180    {
181    }
182
183    /// <summary>
184    /// Manages progress based on sub-tasks, taking each sub-task to be a step
185    /// in which the next step will not be executed until the current step is
186    /// complete. Each step is also assign weights so that certain steps which
187    /// take more time are given a larger amount of progress-bar space for finer
188    /// grained progress reporting.
189    /// </summary>
190    public class SteppedProgressManager : ChainedProgressManager
191    {
192        /// <summary>
193        /// Represents one step in the list of steps to complete.
194        /// </summary>
195        public class Step
196        {
197            /// <summary>
198            /// Constructor.
199            /// </summary>
200            /// <param name="progress">The <see cref="ProgressManagerBase"/> instance
201            /// which measures the progress of this step.</param>
202            /// <param name="weight">The weight of this step. The weight is a decimal
203            /// number in the range [0.0, 1.0] which represents the percentage of the
204            /// entire process this particular step is.</param>
205            public Step(ProgressManagerBase progress, float weight)
206                : this(progress, weight, null)
207            {
208            }
209
210            /// <summary>
211            /// Constructor.
212            /// </summary>
213            /// <param name="progress">The <see cref="ProgressManagerBase"/> instance
214            /// which measures the progress of this step.</param>
215            /// <param name="weight">The weight of this step. The weight is a decimal
216            /// number in the range [0.0, 1.0] which represents the percentage of the
217            /// entire process this particular step is.</param>
218            /// <param name="name">A user-specified value of the name of this step.
219            /// This value is not used by the class at all.</param>
220            public Step(ProgressManagerBase progress, float weight, string name)
221            {
222                Progress = progress;
223                Weight = weight;
224                Name = name;
225            }
226
227            /// <summary>
228            /// The <see cref="ProgressManagerBase"/> instance which measures the
229            /// progress of the step.
230            /// </summary>
231            public ProgressManagerBase Progress
232            {
233                get;
234                set;
235            }
236
237            /// <summary>
238            /// The weight associated with this step.
239            /// </summary>
240            public float Weight
241            {
242                get;
243                private set;
244            }
245
246            /// <summary>
247            /// The name of this step.
248            /// </summary>
249            public string Name
250            {
251                get;
252                set;
253            }
254        }
255
256        /// <summary>
257        /// The class which manages the steps which comprise the overall progress.
258        /// </summary>
259        public class StepsList : IList<Step>
260        {
261            public StepsList(SteppedProgressManager manager)
262            {
263                List = new List<Step>();
264                Manager = manager;
265            }
266
267            #region IList<Step> Members
268
269            public int IndexOf(Step item)
270            {
271                return List.IndexOf(item);
272            }
273
274            public void Insert(int index, Step item)
275            {
276                List.Insert(index, item);
277                TotalWeights += item.Weight;
278            }
279
280            public void RemoveAt(int index)
281            {
282                TotalWeights -= List[index].Weight;
283                List.RemoveAt(index);
284            }
285
286            public Step this[int index]
287            {
288                get
289                {
290                    return List[index];
291                }
292                set
293                {
294                    TotalWeights -= List[index].Weight;
295                    List[index] = value;
296                    TotalWeights += value.Weight;
297                }
298            }
299
300            #endregion
301
302            #region ICollection<Step> Members
303
304            public void Add(Step item)
305            {
306                List.Add(item);
307                TotalWeights += item.Weight;
308            }
309
310            public void Clear()
311            {
312                List.Clear();
313                TotalWeights = 0;
314            }
315
316            public bool Contains(Step item)
317            {
318                return List.Contains(item);
319            }
320
321            public void CopyTo(Step[] array, int arrayIndex)
322            {
323                List.CopyTo(array, arrayIndex);
324            }
325
326            public int Count
327            {
328                get { return List.Count; }
329            }
330
331            public bool IsReadOnly
332            {
333                get { return false; }
334            }
335
336            public bool Remove(Step item)
337            {
338                int index = List.IndexOf(item);
339                if (index != -1)
340                    TotalWeights -= List[index].Weight;
341
342                return List.Remove(item);
343            }
344
345            #endregion
346
347            #region IEnumerable<Step> Members
348
349            public IEnumerator<Step> GetEnumerator()
350            {
351                return List.GetEnumerator();
352            }
353
354            #endregion
355
356            #region IEnumerable Members
357
358            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
359            {
360                return List.GetEnumerator();
361            }
362
363            #endregion
364
365            /// <summary>
366            /// The total weights of all the steps.
367            /// </summary>
368            public float TotalWeights
369            {
370                get
371                {
372                    return totalWeights;
373                }
374                private set
375                {
376                    if (value >= 1.1f || value < 0.0f)
377                        throw new ArgumentOutOfRangeException("The total weights of all steps in " +
378                            "the task must be within the range [0.0, 1.0]");
379
380                    totalWeights = value;
381                }
382            }
383
384            /// <summary>
385            /// The list storing the steps for this instance.
386            /// </summary>
387            private List<Step> List;
388
389            /// <summary>
390            /// The <see cref="SteppedProgressManager"/> instance which owns this list.
391            /// </summary>
392            private SteppedProgressManager Manager;
393
394            /// <summary>
395            /// The backing variable for the total weights of all the steps.
396            /// </summary>
397            private float totalWeights;
398        }
399
400        /// <summary>
401        /// Constructor.
402        /// </summary>
403        public SteppedProgressManager()
404        {
405            Steps = new StepsList(this);
406        }
407
408        public override float Progress
409        {
410            get
411            {
412                float result = 0.0f;
413                foreach (Step step in Steps)
414                    result += step.Progress.Progress * step.Weight;
415
416                return result;
417            }
418        }
419
420        public override int Speed
421        {
422            get
423            {
424                if (CurrentStep == null)
425                    return 0;
426
427                return CurrentStep.Progress.Speed;
428            }
429        }
430
431        public override TimeSpan TimeLeft
432        {
433            get
434            {
435                long ticksElapsed = (DateTime.Now - StartTime).Ticks;
436                float progressRemaining = 1.0f - Progress;
437                return new TimeSpan((long)
438                    (progressRemaining * (ticksElapsed / (double)Progress)));
439            }
440        }
441
442        /// <summary>
443        /// The list of steps involved in completion of the task.
444        /// </summary>
445        public StepsList Steps
446        {
447            get;
448            private set;
449        }
450
451        /// <summary>
452        /// Gets the current step which is executing. This property is null if
453        /// no steps are executing (also when the task is complete)
454        /// </summary>
455        public Step CurrentStep
456        {
457            get
458            {
459                if (Steps.Count == 0)
460                    return null;
461
462                foreach (Step step in Steps)
463                    if (step.Progress.Progress < 1.0f)
464                        return step;
465
466                //Return the last step since we don't have any
467                return Steps[Steps.Count - 1];
468            }
469        }
470    }
471
472    /// <summary>
473    /// Manages progress based on sub-tasks, assuming each sub-task to be independent
474    /// of the rest.
475    /// </summary>
476    public class ParallelProgressManager : ChainedProgressManager
477    {
478        /// <summary>
479        /// The class which manages the progress of each dependent task.
480        /// </summary>
481        public class SubTasksList : IList<ProgressManagerBase>
482        {
483            public SubTasksList()
484            {
485                List = new List<ProgressManagerBase>();
486            }
487
488            #region IList<SubTasksList> Members
489
490            public int IndexOf(ProgressManagerBase item)
491            {
492                return List.IndexOf(item);
493            }
494
495            public void Insert(int index, ProgressManagerBase item)
496            {
497                List.Insert(index, item);
498            }
499
500            public void RemoveAt(int index)
501            {
502                List.RemoveAt(index);
503            }
504
505            public ProgressManagerBase this[int index]
506            {
507                get
508                {
509                    return List[index];
510                }
511                set
512                {
513                    List[index] = value;
514                }
515            }
516
517            #endregion
518
519            #region ICollection<SteppedProgressManagerStep> Members
520
521            public void Add(ProgressManagerBase item)
522            {
523                List.Add(item);
524            }
525
526            public void Clear()
527            {
528                List.Clear();
529            }
530
531            public bool Contains(ProgressManagerBase item)
532            {
533                return List.Contains(item);
534            }
535
536            public void CopyTo(ProgressManagerBase[] array, int arrayIndex)
537            {
538                List.CopyTo(array, arrayIndex);
539            }
540
541            public int Count
542            {
543                get { return List.Count; }
544            }
545
546            public bool IsReadOnly
547            {
548                get { return false; }
549            }
550
551            public bool Remove(ProgressManagerBase item)
552            {
553                return List.Remove(item);
554            }
555
556            #endregion
557
558            #region IEnumerable<ProgressManagerBase> Members
559
560            public IEnumerator<ProgressManagerBase> GetEnumerator()
561            {
562                return List.GetEnumerator();
563            }
564
565            #endregion
566
567            #region IEnumerable Members
568
569            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
570            {
571                return List.GetEnumerator();
572            }
573
574            #endregion
575
576            /// <summary>
577            /// The list storing the steps for this instance.
578            /// </summary>
579            private List<ProgressManagerBase> List;
580
581            /// <summary>
582            /// The total weights of all the steps.
583            /// </summary>
584            public int TotalWeights
585            {
586                get;
587                private set;
588            }
589        }
590
591        /// <summary>
592        /// Constructor.
593        /// </summary>
594        public ParallelProgressManager()
595        {
596            Tasks = new SubTasksList();
597        }
598
599        public override float Progress
600        {
601            get
602            {
603                float result = 0.0f;
604                foreach (ProgressManagerBase subTask in Tasks)
605                    result += subTask.Progress * (1.0f / Tasks.Count);
606
607                return result;
608            }
609        }
610
611        public override int Speed
612        {
613            get
614            {
615                int maxSpeed = 0;
616                foreach (ProgressManagerBase subTask in Tasks)
617                    maxSpeed = Math.Max(subTask.Speed, maxSpeed);
618
619                return maxSpeed;
620            }
621        }
622
623        public override TimeSpan TimeLeft
624        {
625            get
626            {
627                TimeSpan maxTime = TimeSpan.MinValue;
628                foreach (ProgressManagerBase subTask in Tasks)
629                    if (maxTime < subTask.TimeLeft)
630                        maxTime = subTask.TimeLeft;
631
632                return maxTime;
633            }
634        }
635
636        /// <summary>
637        /// Gets the list of tasks which must complete execution before the task
638        /// is completed.
639        /// </summary>
640        public SubTasksList Tasks
641        {
642            get;
643            private set;
644        }
645    }
646}
Note: See TracBrowser for help on using the repository browser.