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 }