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

Revision 434, 12.1 KB checked in by lowjoel, 6 years ago (diff)

Implemented Weekly scheduling.

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