source: branches/eraser6/Manager/Schedule.cs @ 241

Revision 241, 10.5 KB checked in by lowjoel, 7 years ago (diff)

-Immediately-run tasks should have no UIText - their NextRun? state depends on the Queued property, not visible to the Schedule object
-Refactor the DisplayTask? function even more
-Update the list view when the user clicks OK to a task edit request

Line 
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Diagnostics;
5using System.Runtime.Serialization;
6
7namespace Eraser.Manager
8{
9    /// <summary>
10    /// Base class for all schedule types.
11    /// </summary>
12    [Serializable]
13    public abstract class Schedule : ISerializable
14    {
15        #region Default values
16        [Serializable]
17        private class RunNowSchedule : Schedule
18        {
19            #region Object serialization
20            public RunNowSchedule(SerializationInfo info, StreamingContext context)
21            {
22            }
23
24            public override void GetObjectData(SerializationInfo info,
25                StreamingContext context)
26            {
27            }
28            #endregion
29
30            public RunNowSchedule()
31            {
32            }
33
34            public override string UIText
35            {
36                get { return string.Empty; }
37            }
38        }
39
40        [Serializable]
41        private class RunOnRestartSchedule : Schedule
42        {
43            #region Object serialization
44            public RunOnRestartSchedule(SerializationInfo info, StreamingContext context)
45            {
46            }
47
48            public override void GetObjectData(SerializationInfo info,
49                StreamingContext context)
50            {
51            }
52            #endregion
53
54            public RunOnRestartSchedule()
55            {
56            }
57
58            public override string UIText
59            {
60                get { return "Running on restart"; }
61            }
62        }
63        #endregion
64
65        /// <summary>
66        /// Retrieves the text that should be displayed detailing the nature of
67        /// the schedule for use in user interface elements.
68        /// </summary>
69        public abstract string UIText
70        {
71            get;
72        }
73
74        /// <summary>
75        /// Populates a SerializationInfo with the data needed to serialize the
76        /// target object.
77        /// </summary>
78        /// <param name="info">The SerializationInfo to populate with data.</param>
79        /// <param name="context">The destination for this serialization.</param>
80        public abstract void GetObjectData(SerializationInfo info, StreamingContext context);
81
82        /// <summary>
83        /// The global value for tasks which should be run immediately.
84        /// </summary>
85        public static readonly Schedule RunNow = new RunNowSchedule();
86
87        /// <summary>
88        /// The global value for tasks which should be run when the computer is
89        /// restarted
90        /// </summary>
91        public static readonly Schedule RunOnRestart = new RunOnRestartSchedule();
92    }
93
94    /// <summary>
95    /// Recurring runs schedule type.
96    /// </summary>
97    [Serializable]
98    public class RecurringSchedule : Schedule
99    {
100        #region Overridden members
101        public override string UIText
102        {
103            get
104            {
105                string result = string.Empty;
106                switch (type)
107                {
108                    case ScheduleUnit.DAILY:
109                        result = "Once every ";
110                        if (frequency != 1)
111                            result += string.Format("{0} days", frequency);
112                        else
113                            result += "day";
114                        break;
115                    case ScheduleUnit.WEEKDAYS:
116                        result = "Every weekday";
117                        break;
118                    case ScheduleUnit.WEEKLY:
119                        result = "Every ";
120                        if ((weeklySchedule & DaysOfWeek.MONDAY) != 0)
121                            result += "Monday, ";
122                        if ((weeklySchedule & DaysOfWeek.TUESDAY) != 0)
123                            result += "Tuesday, ";
124                        if ((weeklySchedule & DaysOfWeek.WEDNESDAY) != 0)
125                            result += "Wednesday, ";
126                        if ((weeklySchedule & DaysOfWeek.THURSDAY) != 0)
127                            result += "Thursday, ";
128                        if ((weeklySchedule & DaysOfWeek.FRIDAY) != 0)
129                            result += "Friday, ";
130                        if ((weeklySchedule & DaysOfWeek.SATURDAY) != 0)
131                            result += "Saturday, ";
132                        if ((weeklySchedule & DaysOfWeek.SUNDAY) != 0)
133                            result += "Sunday, ";
134
135                        result += string.Format("once every {0} week{1}.", frequency,
136                            frequency == 1 ? "s" : "");
137                        break;
138                    case ScheduleUnit.MONTHLY:
139                        result = string.Format("On day {0} of every {1} month{2}",
140                            monthlySchedule, frequency, frequency != 1 ? "s" : "");
141                        break;
142                }
143
144                return result + string.Format(", at {0}", executionTime.TimeOfDay.ToString());
145            }
146        }
147        #endregion
148
149        #region Object serialization
150        public RecurringSchedule(SerializationInfo info, StreamingContext context)
151        {
152            type = (ScheduleUnit)info.GetValue("Type", typeof(ScheduleUnit));
153            frequency = (int)info.GetValue("Frequency", typeof(int));
154            executionTime = (DateTime)info.GetValue("ExecutionTime", typeof(DateTime));
155            weeklySchedule = (DaysOfWeek)info.GetValue("WeeklySchedule", typeof(DaysOfWeek));
156            monthlySchedule = (int)info.GetValue("MonthlySchedule", typeof(int));
157        }
158
159        public override void GetObjectData(SerializationInfo info,
160            StreamingContext context)
161        {
162            info.AddValue("Type", type);
163            info.AddValue("Frequency", frequency);
164            info.AddValue("ExecutionTime", executionTime);
165            info.AddValue("WeeklySchedule", weeklySchedule);
166            info.AddValue("MonthlySchedule", monthlySchedule);
167        }
168        #endregion
169
170        /// <summary>
171        /// Constructor.
172        /// </summary>
173        public RecurringSchedule()
174        {
175        }
176
177        /// <summary>
178        /// The types of schedule
179        /// </summary>
180        public enum ScheduleUnit
181        {
182            /// <summary>
183            /// Daily schedule type
184            /// </summary>
185            DAILY,
186
187            /// <summary>
188            /// Weekdays-only schedule type
189            /// </summary>
190            WEEKDAYS,
191
192            /// <summary>
193            /// Weekly schedule type
194            /// </summary>
195            WEEKLY,
196
197            /// <summary>
198            /// Monthly schedule type
199            /// </summary>
200            MONTHLY
201        }
202
203        /// <summary>
204        /// The days of the week, with values usable in a bitfield.
205        /// </summary>
206        public enum DaysOfWeek
207        {
208            SUNDAY = 1,
209            MONDAY = 1 << 1,
210            TUESDAY = 1 << 2,
211            WEDNESDAY = 1 << 3,
212            THURSDAY = 1 << 4,
213            FRIDAY = 1 << 5,
214            SATURDAY = 1 << 6
215        }
216
217        /// <summary>
218        /// The type of schedule.
219        /// </summary>
220        public ScheduleUnit Type
221        {
222            get { return type; }
223            set { type = value; }
224        }
225
226        /// <summary>
227        /// The frequency of the event. This value is valid only with Daily,
228        /// Weekly and Monthly schedules.
229        /// </summary>
230        public int Frequency
231        {
232            get
233            {
234                if (Type != ScheduleUnit.DAILY && Type != ScheduleUnit.WEEKLY &&
235                    Type != ScheduleUnit.MONTHLY)
236                    throw new ArgumentException("The ScheduleUnit of the schedule does " +
237                        "not require a frequency value, this field would contain garbage.");
238
239                return frequency;
240            }
241            set
242            {
243                if (value == 0)
244                    throw new ArgumentException("The frequency of the recurrance should " +
245                        "be greater than one");
246
247                frequency = value;
248            }
249        }
250
251        /// <summary>
252        /// The time of day where the task will be executed.
253        /// </summary>
254        public DateTime ExecutionTime
255        {
256            get { return executionTime; }
257            set { executionTime = value; }
258        }
259
260        /// <summary>
261        /// The days of the week which this task should be run. This is valid only
262        /// with Weekly schedules. This field is the DaysOfWeek enumerations
263        /// ORed together.
264        /// </summary>
265        public DaysOfWeek WeeklySchedule
266        {
267            get
268            {
269                if (Type != ScheduleUnit.WEEKLY)
270                    throw new ArgumentException("The ScheduleUnit of the schedule does " +
271                        "not require the WeeklySchedule value, this field would contain garbage");
272
273                return weeklySchedule;
274            }
275            set
276            {
277                if (value == 0)
278                    throw new ArgumentException("The WeeklySchedule should have at " +
279                        "least one day where the task should be run.");
280
281                weeklySchedule = value;
282            }
283        }
284
285        /// <summary>
286        /// The nth day of the month on which this task will run. This is valid
287        /// only with Monthly schedules
288        /// </summary>
289        public int MonthlySchedule
290        {
291            get
292            {
293                if (Type != ScheduleUnit.MONTHLY)
294                    throw new ArgumentException("The ScheduleUnit of the schedule does " +
295                        "not require the MonthlySchedule value, this field would contain garbage");
296
297                return monthlySchedule;
298            }
299            set { monthlySchedule = value; }
300        }
301
302        /// <summary>
303        /// The last time this task was executed. This value is used for computing
304        /// the next time the task should be run.
305        /// </summary>
306        public DateTime LastRun
307        {
308            get { return lastRun; }
309        }
310
311        /// <summary>
312        /// Based on the last run time and the current schedule, the next run time
313        /// will be computed.
314        /// </summary>
315        public DateTime NextRun
316        {
317            get
318            {
319                //Get a good starting point, either now, or the last time the task
320                //was run.
321                DateTime nextRun = LastRun;
322                if (nextRun == DateTime.MinValue)
323                    nextRun = DateTime.Now;
324
325                switch (Type)
326                {
327                    case ScheduleUnit.DAILY:
328                        //First assume that it is today that we are running the schedule
329                        long daysToAdd = (DateTime.Now - nextRun).Days;
330                        if (daysToAdd % frequency != 0)
331                            daysToAdd += frequency - (daysToAdd % frequency);
332                        nextRun = nextRun.AddDays(daysToAdd);
333                        nextRun = nextRun.AddHours(executionTime.Hour);
334                        nextRun = nextRun.AddMinutes(executionTime.Minute);
335
336                        //If we have passed today's run time, schedule it after the next
337                        //frequency
338                        if (nextRun < DateTime.Now)
339                            nextRun = nextRun.AddDays(1);
340                        break;
341                    case ScheduleUnit.WEEKDAYS:
342                        while (nextRun < DateTime.Now ||
343                            lastRun.DayOfWeek == DayOfWeek.Saturday ||
344                            lastRun.DayOfWeek == DayOfWeek.Sunday)
345                            nextRun = nextRun.AddDays(1);
346                        break;
347                    case ScheduleUnit.WEEKLY:
348                        //First find the next day of the week within this week.
349                        for (DayOfWeek day = lastRun.DayOfWeek; day <= DayOfWeek.Sunday; ++day)
350                            if (((int)weeklySchedule & (1 << (int)day)) != 0)
351                                //Bullseye! Run the task next day in the week.
352                                nextRun = nextRun.AddDays(day - lastRun.DayOfWeek);
353
354                        //Ok, we now need to find the earliest day of the week where
355                        //the task will run.
356                        throw new NotImplementedException("Weekly schedule calculation not implemented");
357                    case ScheduleUnit.MONTHLY:
358                        //Increment the month until we are past our current date.
359                        nextRun = nextRun.AddMinutes(executionTime.Minute - nextRun.Minute);
360                        nextRun = nextRun.AddHours(executionTime.Hour - nextRun.Hour);
361                        nextRun = nextRun.AddDays(-((int)monthlySchedule - nextRun.Day));
362                        while (nextRun < DateTime.Now)
363                            nextRun = nextRun.AddMonths((int)frequency);
364                        break;
365                }
366
367                Debug.WriteLine(string.Format("Next scheduled run time, for object " +
368                    "with last run time {0} and schedule {1}, is at {2}",
369                    lastRun.ToString(), UIText, nextRun.ToString()));
370                return nextRun;
371            }
372        }
373
374        /// <summary>
375        /// Reschedules the task.
376        /// </summary>
377        /// <param name="lastRun">The last time the task was run.</param>
378        internal void Reschedule(DateTime lastRun)
379        {
380            this.lastRun = lastRun;
381        }
382
383        private ScheduleUnit type;
384        private int frequency;
385        private DateTime executionTime;
386        private DaysOfWeek weeklySchedule;
387        private int monthlySchedule;
388
389        private DateTime lastRun;
390    }
391}
Note: See TracBrowser for help on using the repository browser.