diff 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
line wrap: on
line diff
--- a/ServerMonitor/Objects/ServerMonitor.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/ServerMonitor.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -1,11 +1,14 @@
-using System;
+using ServerMonitorApp.Properties;
+using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
+using System.Net.NetworkInformation;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using System.Windows.Forms;
 using System.Xml.Serialization;
 
 namespace ServerMonitorApp
@@ -15,20 +18,24 @@
         private readonly string configFileDir;
         private readonly Logger logger;
         private readonly Dictionary<int, CancellationTokenSource> tokens = new Dictionary<int, CancellationTokenSource>();
-        private bool running;
+        private readonly List<int> pausedChecks = new List<int>();
+        private bool running, networkAvailable;
         private Dictionary<Task<CheckResult>, int> tasks;
+        private ServerSummaryForm mainForm;
+
         //private List<Task<CheckResult>> tasks;
 
         public event EventHandler<CheckStatusChangedEventArgs> CheckStatusChanged;
 
         public List<Server> Servers { get; private set; } = new List<Server>();
 
-        public IEnumerable<Check> Checks { get { return Servers.SelectMany(s => s.Checks); } }
+        public IEnumerable<Check> Checks => Servers.SelectMany(s => s.Checks);
 
         public string ConfigFile { get; private set; }
 
-        public ServerMonitor()
+        public ServerMonitor(ServerSummaryForm mainForm)
         {
+            this.mainForm = mainForm;
             configFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ServerMonitor");
             ConfigFile = Path.Combine(configFileDir, "servers.xml");
             logger = new Logger(Path.Combine(configFileDir, "monitor.log"));
@@ -37,6 +44,13 @@
         public void AddServer(Server server)
         {
             Servers.Add(server);
+            SaveServers();
+        }
+
+        public void DeleteServer(Server server)
+        {
+            Servers.Remove(server);
+            SaveServers();
         }
 
         public void LoadServers()
@@ -56,8 +70,11 @@
                     foreach (Check check in server.Checks)
                     {
                         check.Server = server;
+                        if (check.Status == CheckStatus.Running)
+                            check.Status = check.LastRunStatus;
                     }
                     server.CheckModified += Server_CheckModified;
+                    server.EnabledChanged += Server_EnabledChanged;
                 }
             }
             // If the file doesn't exist, no special handling is needed. It will be created later.
@@ -72,6 +89,7 @@
             {
                 reader?.Close();
             }
+            NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
             Run();
         }
 
@@ -109,6 +127,15 @@
             if (running)
                 return;
             running = true;
+            networkAvailable = Helpers.IsNetworkAvailable();
+            if (networkAvailable)
+            {
+                foreach (int id in pausedChecks)
+                {
+                    await ExecuteCheckAsync(Checks.FirstOrDefault(c => c.Id == id));
+                }
+                pausedChecks.Clear();
+            }
             //TODO subscribe to power events. Find any check's NextExecutionTime is in the past. Cancel waiting task and run immediately (or after short delay).
             //tasks = Checks.Select(c => ScheduleExecuteCheckAsync(c)).ToList();
             tasks = Checks.ToDictionary(c => ScheduleExecuteCheckAsync(c), c => c.Id);
@@ -137,8 +164,17 @@
             OnCheckStatusChanged(check);
             CheckResult result = await check.ExecuteAsync(token);
             OnCheckStatusChanged(check, result);
+            HandleResultAsync(result);
+            return result;
+        }
+
+        private void HandleResultAsync(CheckResult result)
+        {
             logger.Log(result);
-            return result;
+            if (result.FailAction == FailAction.FlashTaskbar)
+                mainForm.AlertServerForm(result.Check);
+            if (result.FailAction.In(FailAction.FlashTaskbar, FailAction.NotificationBalloon))
+                mainForm.ShowBalloon(result);
         }
 
         public IList<CheckResult> GetLog(Server server)
@@ -166,9 +202,7 @@
                 else
                 {
                     // Check was modified or deleted, so remove any waiting tasks
-                    tasks.Remove(task);
-                    if (tokens.TryGetValue(check.Id, out CancellationTokenSource cts))
-                        cts.Cancel();
+                    CancelCheck(check);
                     if (check.Server != null)
                     {
                         // If the check was not deleted, schedule the new check.
@@ -183,9 +217,34 @@
             Run();
         }
 
+        private void Server_EnabledChanged(object sender, EventArgs e)
+        {
+            Server server = (Server)sender;
+            if (server.Enabled)
+            {
+                Run();
+            }
+            else
+            {
+                foreach (Check check in server.Checks)
+                {
+                    CancelCheck(check);
+                }
+            }
+        }
+
+        private void CancelCheck(Check check)
+        {
+            Task<CheckResult> task = tasks.FirstOrDefault(kvp => kvp.Value == check.Id).Key;
+            tasks.Remove(task);
+            pausedChecks.RemoveAll(id => id == check.Id);
+            if (tokens.TryGetValue(check.Id, out CancellationTokenSource cts))
+                cts.Cancel();
+        }
+
         private async Task<CheckResult> ScheduleExecuteCheckAsync(Check check)
         {
-            if (!check.Enabled)
+            if (!check.Enabled || !check.Server.Enabled)
                 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null));
 
             CancellationTokenSource cts = new CancellationTokenSource();
@@ -193,7 +252,21 @@
             check.NextRunTime = check.Schedule.GetNextTime(check.LastScheduledRunTime);
             await Task.Delay(check.NextRunTime - DateTime.Now, cts.Token);
             check.LastScheduledRunTime = check.NextRunTime;
-            return await ExecuteCheckAsync(check, cts.Token);
+            if (networkAvailable)
+                return await ExecuteCheckAsync(check, cts.Token);
+            else
+            {
+                if (!pausedChecks.Contains(check.Id))
+                    pausedChecks.Add(check.Id);
+                return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null));
+            }
+        }
+
+        private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
+        {
+            networkAvailable = Helpers.IsNetworkAvailable();
+            if (networkAvailable)
+                mainForm.Invoke((MethodInvoker)(() => Run()));
         }
 
         private void GenerateIds()
@@ -208,15 +281,16 @@
                 }
             }
 
-            //TODO if a check is deleted, there might be old results in the log file that share an ID with a new one
             if (Checks.Any())
             {
-                int id = Checks.Max(c => c.Id);
+                int id = Math.Max(Settings.Default.MaxCheckId, Checks.Max(c => c.Id));
                 foreach (Check check in Checks)
                 {
                     if (check.Id == 0)
                         check.Id = ++id;
                 }
+                Settings.Default.MaxCheckId = id;
+                Settings.Default.Save();
             }
         }
 
@@ -237,6 +311,7 @@
             Check = check;
             CheckResult = result;
         }
+    }
 
-    }
+    public enum FailAction { FlashTaskbar = 0, NotificationBalloon = 1, None = 10 }
 }