Mercurial > servermonitor
comparison ServerMonitor/Objects/ServerMonitor.cs @ 4:3142e52cbe69
Lots more progress
author | Brad Greco <brad@bgreco.net> |
---|---|
date | Sun, 10 Feb 2019 20:51:26 -0500 |
parents | 3e1a2131f897 |
children | b6fe203af9d5 |
comparison
equal
deleted
inserted
replaced
3:96f0b028176d | 4:3142e52cbe69 |
---|---|
1 using System; | 1 using ServerMonitorApp.Properties; |
2 using System; | |
2 using System.Collections.Generic; | 3 using System.Collections.Generic; |
3 using System.Diagnostics; | 4 using System.Diagnostics; |
4 using System.IO; | 5 using System.IO; |
5 using System.Linq; | 6 using System.Linq; |
7 using System.Net.NetworkInformation; | |
6 using System.Text; | 8 using System.Text; |
7 using System.Threading; | 9 using System.Threading; |
8 using System.Threading.Tasks; | 10 using System.Threading.Tasks; |
11 using System.Windows.Forms; | |
9 using System.Xml.Serialization; | 12 using System.Xml.Serialization; |
10 | 13 |
11 namespace ServerMonitorApp | 14 namespace ServerMonitorApp |
12 { | 15 { |
13 public class ServerMonitor | 16 public class ServerMonitor |
14 { | 17 { |
15 private readonly string configFileDir; | 18 private readonly string configFileDir; |
16 private readonly Logger logger; | 19 private readonly Logger logger; |
17 private readonly Dictionary<int, CancellationTokenSource> tokens = new Dictionary<int, CancellationTokenSource>(); | 20 private readonly Dictionary<int, CancellationTokenSource> tokens = new Dictionary<int, CancellationTokenSource>(); |
18 private bool running; | 21 private readonly List<int> pausedChecks = new List<int>(); |
22 private bool running, networkAvailable; | |
19 private Dictionary<Task<CheckResult>, int> tasks; | 23 private Dictionary<Task<CheckResult>, int> tasks; |
24 private ServerSummaryForm mainForm; | |
25 | |
20 //private List<Task<CheckResult>> tasks; | 26 //private List<Task<CheckResult>> tasks; |
21 | 27 |
22 public event EventHandler<CheckStatusChangedEventArgs> CheckStatusChanged; | 28 public event EventHandler<CheckStatusChangedEventArgs> CheckStatusChanged; |
23 | 29 |
24 public List<Server> Servers { get; private set; } = new List<Server>(); | 30 public List<Server> Servers { get; private set; } = new List<Server>(); |
25 | 31 |
26 public IEnumerable<Check> Checks { get { return Servers.SelectMany(s => s.Checks); } } | 32 public IEnumerable<Check> Checks => Servers.SelectMany(s => s.Checks); |
27 | 33 |
28 public string ConfigFile { get; private set; } | 34 public string ConfigFile { get; private set; } |
29 | 35 |
30 public ServerMonitor() | 36 public ServerMonitor(ServerSummaryForm mainForm) |
31 { | 37 { |
38 this.mainForm = mainForm; | |
32 configFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ServerMonitor"); | 39 configFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ServerMonitor"); |
33 ConfigFile = Path.Combine(configFileDir, "servers.xml"); | 40 ConfigFile = Path.Combine(configFileDir, "servers.xml"); |
34 logger = new Logger(Path.Combine(configFileDir, "monitor.log")); | 41 logger = new Logger(Path.Combine(configFileDir, "monitor.log")); |
35 } | 42 } |
36 | 43 |
37 public void AddServer(Server server) | 44 public void AddServer(Server server) |
38 { | 45 { |
39 Servers.Add(server); | 46 Servers.Add(server); |
47 SaveServers(); | |
48 } | |
49 | |
50 public void DeleteServer(Server server) | |
51 { | |
52 Servers.Remove(server); | |
53 SaveServers(); | |
40 } | 54 } |
41 | 55 |
42 public void LoadServers() | 56 public void LoadServers() |
43 { | 57 { |
44 TextReader reader = null; | 58 TextReader reader = null; |
54 foreach (Server server in Servers) | 68 foreach (Server server in Servers) |
55 { | 69 { |
56 foreach (Check check in server.Checks) | 70 foreach (Check check in server.Checks) |
57 { | 71 { |
58 check.Server = server; | 72 check.Server = server; |
73 if (check.Status == CheckStatus.Running) | |
74 check.Status = check.LastRunStatus; | |
59 } | 75 } |
60 server.CheckModified += Server_CheckModified; | 76 server.CheckModified += Server_CheckModified; |
77 server.EnabledChanged += Server_EnabledChanged; | |
61 } | 78 } |
62 } | 79 } |
63 // If the file doesn't exist, no special handling is needed. It will be created later. | 80 // If the file doesn't exist, no special handling is needed. It will be created later. |
64 catch (FileNotFoundException) { } | 81 catch (FileNotFoundException) { } |
65 catch (DirectoryNotFoundException) { } | 82 catch (DirectoryNotFoundException) { } |
70 } | 87 } |
71 finally | 88 finally |
72 { | 89 { |
73 reader?.Close(); | 90 reader?.Close(); |
74 } | 91 } |
92 NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; | |
75 Run(); | 93 Run(); |
76 } | 94 } |
77 | 95 |
78 public void SaveServers() | 96 public void SaveServers() |
79 { | 97 { |
107 private async void Run() | 125 private async void Run() |
108 { | 126 { |
109 if (running) | 127 if (running) |
110 return; | 128 return; |
111 running = true; | 129 running = true; |
130 networkAvailable = Helpers.IsNetworkAvailable(); | |
131 if (networkAvailable) | |
132 { | |
133 foreach (int id in pausedChecks) | |
134 { | |
135 await ExecuteCheckAsync(Checks.FirstOrDefault(c => c.Id == id)); | |
136 } | |
137 pausedChecks.Clear(); | |
138 } | |
112 //TODO subscribe to power events. Find any check's NextExecutionTime is in the past. Cancel waiting task and run immediately (or after short delay). | 139 //TODO subscribe to power events. Find any check's NextExecutionTime is in the past. Cancel waiting task and run immediately (or after short delay). |
113 //tasks = Checks.Select(c => ScheduleExecuteCheckAsync(c)).ToList(); | 140 //tasks = Checks.Select(c => ScheduleExecuteCheckAsync(c)).ToList(); |
114 tasks = Checks.ToDictionary(c => ScheduleExecuteCheckAsync(c), c => c.Id); | 141 tasks = Checks.ToDictionary(c => ScheduleExecuteCheckAsync(c), c => c.Id); |
115 while (tasks.Count > 0) | 142 while (tasks.Count > 0) |
116 { | 143 { |
135 { | 162 { |
136 check.Status = CheckStatus.Running; | 163 check.Status = CheckStatus.Running; |
137 OnCheckStatusChanged(check); | 164 OnCheckStatusChanged(check); |
138 CheckResult result = await check.ExecuteAsync(token); | 165 CheckResult result = await check.ExecuteAsync(token); |
139 OnCheckStatusChanged(check, result); | 166 OnCheckStatusChanged(check, result); |
167 HandleResultAsync(result); | |
168 return result; | |
169 } | |
170 | |
171 private void HandleResultAsync(CheckResult result) | |
172 { | |
140 logger.Log(result); | 173 logger.Log(result); |
141 return result; | 174 if (result.FailAction == FailAction.FlashTaskbar) |
175 mainForm.AlertServerForm(result.Check); | |
176 if (result.FailAction.In(FailAction.FlashTaskbar, FailAction.NotificationBalloon)) | |
177 mainForm.ShowBalloon(result); | |
142 } | 178 } |
143 | 179 |
144 public IList<CheckResult> GetLog(Server server) | 180 public IList<CheckResult> GetLog(Server server) |
145 { | 181 { |
146 return logger.Read(server); | 182 return logger.Read(server); |
164 tasks.Add(ScheduleExecuteCheckAsync(check), check.Id); | 200 tasks.Add(ScheduleExecuteCheckAsync(check), check.Id); |
165 } | 201 } |
166 else | 202 else |
167 { | 203 { |
168 // Check was modified or deleted, so remove any waiting tasks | 204 // Check was modified or deleted, so remove any waiting tasks |
169 tasks.Remove(task); | 205 CancelCheck(check); |
170 if (tokens.TryGetValue(check.Id, out CancellationTokenSource cts)) | |
171 cts.Cancel(); | |
172 if (check.Server != null) | 206 if (check.Server != null) |
173 { | 207 { |
174 // If the check was not deleted, schedule the new check. | 208 // If the check was not deleted, schedule the new check. |
175 // But only if it's still running, otherwise restarting the monitor below | 209 // But only if it's still running, otherwise restarting the monitor below |
176 // will create a duplicate run. | 210 // will create a duplicate run. |
181 } | 215 } |
182 // Run again in case removing a task above caused it to stop | 216 // Run again in case removing a task above caused it to stop |
183 Run(); | 217 Run(); |
184 } | 218 } |
185 | 219 |
220 private void Server_EnabledChanged(object sender, EventArgs e) | |
221 { | |
222 Server server = (Server)sender; | |
223 if (server.Enabled) | |
224 { | |
225 Run(); | |
226 } | |
227 else | |
228 { | |
229 foreach (Check check in server.Checks) | |
230 { | |
231 CancelCheck(check); | |
232 } | |
233 } | |
234 } | |
235 | |
236 private void CancelCheck(Check check) | |
237 { | |
238 Task<CheckResult> task = tasks.FirstOrDefault(kvp => kvp.Value == check.Id).Key; | |
239 tasks.Remove(task); | |
240 pausedChecks.RemoveAll(id => id == check.Id); | |
241 if (tokens.TryGetValue(check.Id, out CancellationTokenSource cts)) | |
242 cts.Cancel(); | |
243 } | |
244 | |
186 private async Task<CheckResult> ScheduleExecuteCheckAsync(Check check) | 245 private async Task<CheckResult> ScheduleExecuteCheckAsync(Check check) |
187 { | 246 { |
188 if (!check.Enabled) | 247 if (!check.Enabled || !check.Server.Enabled) |
189 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null)); | 248 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null)); |
190 | 249 |
191 CancellationTokenSource cts = new CancellationTokenSource(); | 250 CancellationTokenSource cts = new CancellationTokenSource(); |
192 tokens[check.Id] = cts; | 251 tokens[check.Id] = cts; |
193 check.NextRunTime = check.Schedule.GetNextTime(check.LastScheduledRunTime); | 252 check.NextRunTime = check.Schedule.GetNextTime(check.LastScheduledRunTime); |
194 await Task.Delay(check.NextRunTime - DateTime.Now, cts.Token); | 253 await Task.Delay(check.NextRunTime - DateTime.Now, cts.Token); |
195 check.LastScheduledRunTime = check.NextRunTime; | 254 check.LastScheduledRunTime = check.NextRunTime; |
196 return await ExecuteCheckAsync(check, cts.Token); | 255 if (networkAvailable) |
256 return await ExecuteCheckAsync(check, cts.Token); | |
257 else | |
258 { | |
259 if (!pausedChecks.Contains(check.Id)) | |
260 pausedChecks.Add(check.Id); | |
261 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null)); | |
262 } | |
263 } | |
264 | |
265 private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) | |
266 { | |
267 networkAvailable = Helpers.IsNetworkAvailable(); | |
268 if (networkAvailable) | |
269 mainForm.Invoke((MethodInvoker)(() => Run())); | |
197 } | 270 } |
198 | 271 |
199 private void GenerateIds() | 272 private void GenerateIds() |
200 { | 273 { |
201 if (Servers.Any()) | 274 if (Servers.Any()) |
206 if (server.Id == 0) | 279 if (server.Id == 0) |
207 server.Id = ++id; | 280 server.Id = ++id; |
208 } | 281 } |
209 } | 282 } |
210 | 283 |
211 //TODO if a check is deleted, there might be old results in the log file that share an ID with a new one | |
212 if (Checks.Any()) | 284 if (Checks.Any()) |
213 { | 285 { |
214 int id = Checks.Max(c => c.Id); | 286 int id = Math.Max(Settings.Default.MaxCheckId, Checks.Max(c => c.Id)); |
215 foreach (Check check in Checks) | 287 foreach (Check check in Checks) |
216 { | 288 { |
217 if (check.Id == 0) | 289 if (check.Id == 0) |
218 check.Id = ++id; | 290 check.Id = ++id; |
219 } | 291 } |
292 Settings.Default.MaxCheckId = id; | |
293 Settings.Default.Save(); | |
220 } | 294 } |
221 } | 295 } |
222 | 296 |
223 private XmlSerializer CreateXmlSerializer() | 297 private XmlSerializer CreateXmlSerializer() |
224 { | 298 { |
235 public CheckStatusChangedEventArgs(Check check, CheckResult result) | 309 public CheckStatusChangedEventArgs(Check check, CheckResult result) |
236 { | 310 { |
237 Check = check; | 311 Check = check; |
238 CheckResult = result; | 312 CheckResult = result; |
239 } | 313 } |
240 | |
241 } | 314 } |
315 | |
316 public enum FailAction { FlashTaskbar = 0, NotificationBalloon = 1, None = 10 } | |
242 } | 317 } |