comparison ServerMonitor/Objects/Schedule.cs @ 17:68d7834dc28e

More comments.
author Brad Greco <brad@bgreco.net>
date Sat, 25 May 2019 15:14:26 -0400
parents d92176c5398a
children 2342e9459444
comparison
equal deleted inserted replaced
16:7626b099aefd 17:68d7834dc28e
1 using System; 1 using System;
2 2
3 namespace ServerMonitorApp 3 namespace ServerMonitorApp
4 { 4 {
5 /// <summary>Schedule to control when a check is automatically executed.<summary>
5 public class Schedule 6 public class Schedule
6 { 7 {
8 // Required empty constructor for XML serialization.
7 public Schedule() { } 9 public Schedule() { }
8 10
11 /// <summary>Schedule constructor</summary>
12 /// <param name="units">The time units to use.</param>
13 /// <param name="frequency">How frequently to run the check.</param>
14 /// <param name="startTime">Time of day the check should begin running.</param>
15 /// <param name="endTime">Time of day the check should stop running.</param>
16 /// <remarks>
17 /// If endTime is before startTime, then the endTime is interpreted as being on the next day.
18 /// For example, if startTime = 17:00 and endTime is 8:00, then the check will start running
19 /// at 17:00 and stop running at 8:00 the next day.
20 /// </remarks>
9 public Schedule(FrequencyUnits units, int frequency, TimeSpan startTime, TimeSpan endTime) 21 public Schedule(FrequencyUnits units, int frequency, TimeSpan startTime, TimeSpan endTime)
10 { 22 {
11 Units = units; 23 Units = units;
12 Frequency = frequency; 24 Frequency = frequency;
13 StartTime = startTime; 25 StartTime = startTime;
14 EndTime = endTime; 26 EndTime = endTime;
15 } 27 }
16 28
29 /// <summary>How often the check should be executed.</summary>
17 public int Frequency { get; set; } 30 public int Frequency { get; set; }
18 31
32 /// <summary>The time units used to interpret the frequency.</summary>
19 public FrequencyUnits Units { get; set; } 33 public FrequencyUnits Units { get; set; }
20 34
35 /// <summary>Time of day the check should begin running.</summary>
21 public TimeSpan StartTime { get; set; } 36 public TimeSpan StartTime { get; set; }
22 37
38 /// <summary>Time of day the check should stop running.</summary>
23 public TimeSpan EndTime { get; set; } 39 public TimeSpan EndTime { get; set; }
24 40
41 /// <summary>Given the last time a check was scheduled to run, calculates the next time in the future it should run.</summary>
42 /// <param name="lastScheduledTime">The last time a check was scheduled to run.</param>
25 public DateTime GetNextTime(DateTime lastScheduledTime) 43 public DateTime GetNextTime(DateTime lastScheduledTime)
26 { 44 {
27 return GetNextTime(lastScheduledTime, DateTime.Now); 45 return GetNextTime(lastScheduledTime, DateTime.Now);
28 } 46 }
29 47
48 /// <summary>Given the last time a check was scheduled to run, calculates the next time after the given date it should run.</summary>
49 /// <param name="lastScheduledTime">The last time a check was scheduled to run.</param>
50 /// <param name="minStartTime">The earliest allowed time to return.</param>
51 /// <remarks>
52 /// The next execution time of a check cannot necessarily be determined by taking the
53 /// last execution time and adding the configured number of time units. The computer might
54 /// have been asleep or the program might have not been running, so the next date might
55 /// be in the past.
56 ///
57 /// To best follow the schedule, we take the last execution time and fast-forward time
58 /// by adding the configured time interval until we get a resulting time that is in the future.
59 /// For example, suppose a check is scheduled to run every 5 minutes starting at 7:00.
60 /// The check last ran at 7:40, and the computer was suspended shortly thereafter and resumed
61 /// at 8:28. The next execution time is determined by adding 5 minutes to 7:40 until we obtain
62 /// a time after 8:28, in this case, 8:30.
63 /// </remarks>
30 public DateTime GetNextTime(DateTime lastScheduledTime, DateTime minStartTime) 64 public DateTime GetNextTime(DateTime lastScheduledTime, DateTime minStartTime)
31 { 65 {
66 // Start by setting the next time to the last time the check was run.
32 DateTime nextTime = lastScheduledTime; 67 DateTime nextTime = lastScheduledTime;
33 if (Units == FrequencyUnits.Day) 68 if (Units == FrequencyUnits.Day)
34 { 69 {
70 // If the check is scheduled only once a day, simply add days until we find a time in the future.
35 while (nextTime < minStartTime) 71 while (nextTime < minStartTime)
36 nextTime = nextTime.AddDays(Frequency).Date.Add(StartTime); 72 nextTime = nextTime.AddDays(Frequency).Date.Add(StartTime);
37 } 73 }
38 else 74 else
39 { 75 {
40 // If the last run time was more than a day ago, fast-forward to reduce the number of loops 76 // If the last run time was more than a day ago, fast-forward a day at a time to reduce the number of loops.
41 if (nextTime < minStartTime.AddHours(-24)) 77 if (nextTime < minStartTime.AddHours(-24))
42 nextTime = minStartTime.Date.Add(StartTime).AddHours(-24); 78 nextTime = minStartTime.Date.Add(StartTime).AddHours(-24);
79 // Add the configured time interval to the last run time until we obtain a time in the future.
43 while (nextTime < minStartTime) 80 while (nextTime < minStartTime)
44 { 81 {
45 switch (Units) 82 switch (Units)
46 { 83 {
47 case FrequencyUnits.Second: nextTime = nextTime.AddSeconds(Frequency); break; 84 case FrequencyUnits.Second: nextTime = nextTime.AddSeconds(Frequency); break;
48 case FrequencyUnits.Minute: nextTime = nextTime.AddMinutes(Frequency); break; 85 case FrequencyUnits.Minute: nextTime = nextTime.AddMinutes(Frequency); break;
49 case FrequencyUnits.Hour: nextTime = nextTime.AddHours(Frequency); break; 86 case FrequencyUnits.Hour: nextTime = nextTime.AddHours(Frequency); break;
50 default: throw new InvalidOperationException("Unexpected frequency units: " + Units); 87 default: throw new InvalidOperationException("Unexpected frequency units: " + Units);
51 } 88 }
52 } 89 }
90 // Now we have the next date and time, but we don't know yet if it is within
91 // the active times of day the check is allowed to run between.
53 if (StartTime < EndTime) 92 if (StartTime < EndTime)
54 { 93 {
94 // The allowed start and end times are on the same day.
95 // If the next scheduled time is too early (before the allowed start time),
96 // wait to run it until the start time happens.
55 if (nextTime.TimeOfDay < StartTime) 97 if (nextTime.TimeOfDay < StartTime)
56 nextTime = nextTime.Date + StartTime; 98 nextTime = nextTime.Date + StartTime;
99 // If the next scheduled time is too late (after the allowed end time),
100 // wait to run it until the start time happens on the following day.
57 else if (nextTime.TimeOfDay > EndTime) 101 else if (nextTime.TimeOfDay > EndTime)
58 nextTime = nextTime.Date.AddDays(1) + StartTime; 102 nextTime = nextTime.Date.AddDays(1) + StartTime;
59 } 103 }
60 else if (nextTime.TimeOfDay > EndTime && nextTime.TimeOfDay < StartTime) 104 else if (nextTime.TimeOfDay > EndTime && nextTime.TimeOfDay < StartTime)
61 { 105 {
106 // The allowed start time is on the day after the allowed end time,
107 // and the next scheduled time is too early (before the allowed start time).
108 // Wait to run the check until the allowed start time happens.
62 nextTime = nextTime.Date + StartTime; 109 nextTime = nextTime.Date + StartTime;
63 } 110 }
64 } 111 }
65 return nextTime; 112 return nextTime;
66 } 113 }
69 { 116 {
70 return string.Format("Every {0} {1}", Frequency, Units.ToString().ToLower() + (Frequency == 1 ? "" : "s")); 117 return string.Format("Every {0} {1}", Frequency, Units.ToString().ToLower() + (Frequency == 1 ? "" : "s"));
71 } 118 }
72 } 119 }
73 120
121 /// <summary>Units of time that a check can be scheduled to run in intervals of.</summary>
74 public enum FrequencyUnits { Second, Minute, Hour, Day } 122 public enum FrequencyUnits { Second, Minute, Hour, Day }
75 } 123 }