diff ServerMonitor/Objects/ServerMonitor.cs @ 8:052aa62cb42a

Single instance. Add autorun option. Add icons. General enhancements.
author Brad Greco <brad@bgreco.net>
date Sat, 09 Mar 2019 20:14:03 -0500
parents c1dffaac66fa
children 68d7834dc28e
line wrap: on
line diff
--- a/ServerMonitor/Objects/ServerMonitor.cs	Fri Mar 01 21:39:22 2019 -0500
+++ b/ServerMonitor/Objects/ServerMonitor.cs	Sat Mar 09 20:14:03 2019 -0500
@@ -1,4 +1,5 @@
-using Renci.SshNet;
+using Microsoft.Win32;
+using Renci.SshNet;
 using Renci.SshNet.Common;
 using ServerMonitorApp.Properties;
 using System;
@@ -22,7 +23,7 @@
         private readonly Dictionary<int, CancellationTokenSource> tokens = new Dictionary<int, CancellationTokenSource>();
         private readonly Dictionary<string, PrivateKeyFile> privateKeys = new Dictionary<string, PrivateKeyFile>();
         private readonly List<int> pausedChecks = new List<int>();
-        private bool running, networkAvailable;
+        private bool running, networkAvailable, suspend;
         private Dictionary<Task<CheckResult>, int> tasks;
         private ServerSummaryForm mainForm;
 
@@ -60,6 +61,7 @@
 
         public void LoadServers()
         {
+            Read:
             TextReader reader = null;
             try
             {
@@ -89,14 +91,18 @@
             catch (DirectoryNotFoundException) { }
             catch (InvalidOperationException)
             {
-                //TODO log
-                throw;
+                reader?.Close();
+                File.Copy(ConfigFile, ConfigFile + ".error", true);
+                File.Copy(ConfigFile + ".bak", ConfigFile, true);
+                goto Read;
             }
             finally
             {
                 reader?.Close();
             }
+            Application.ApplicationExit += Application_ApplicationExit;
             NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
+            SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
             logger.TrimLog();
             Run();
         }
@@ -108,6 +114,11 @@
             XmlSerializer serializer = null;
             try
             {
+                File.Copy(ConfigFile, ConfigFile + ".bak", true);
+            }
+            catch { }
+            try
+            {
                 writer = new StreamWriter(ConfigFile);
                 serializer = CreateXmlSerializer();
                 serializer.Serialize(writer, Servers);
@@ -119,11 +130,6 @@
                 serializer = CreateXmlSerializer();
                 serializer.Serialize(writer, Servers);
             }
-            catch (Exception)
-            {
-                //TODO log
-                throw;
-            }
             finally
             {
                 writer?.Close();
@@ -132,7 +138,7 @@
 
         private async void Run()
         {
-            if (running)
+            if (running || suspend)
                 return;
             running = true;
             networkAvailable = Helpers.IsNetworkAvailable();
@@ -144,8 +150,6 @@
                 }
                 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);
             while (tasks.Count > 0)
             {
@@ -171,6 +175,8 @@
             check.Status = CheckStatus.Running;
             OnCheckStatusChanged(check);
             CheckResult result = await check.ExecuteAsync(token);
+            if (result.Failed)
+                check.ConsecutiveFailures++;
             OnCheckStatusChanged(check, result);
             HandleResultAsync(result);
             return result;
@@ -179,10 +185,13 @@
         private void HandleResultAsync(CheckResult result)
         {
             logger.Log(result);
-            if (result.FailAction == FailAction.FlashTaskbar)
-                mainForm.AlertServerForm(result.Check);
-            if (result.FailAction.In(FailAction.FlashTaskbar, FailAction.NotificationBalloon))
-                mainForm.ShowBalloon(result);
+            if (result.Check.ConsecutiveFailures >= result.Check.MaxConsecutiveFailures)
+            {
+                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)
@@ -261,10 +270,13 @@
             CancellationTokenSource cts = new CancellationTokenSource();
             tokens[check.Id] = cts;
             check.NextRunTime = check.Schedule.GetNextTime(check.LastScheduledRunTime);
-            await Task.Delay(check.NextRunTime - DateTime.Now, cts.Token);
+            int delay = Math.Max(0, (int)(check.NextRunTime - DateTime.Now).TotalMilliseconds);
+            await Task.Delay(delay, cts.Token);
             check.LastScheduledRunTime = check.NextRunTime;
-            if (networkAvailable)
+            if (networkAvailable && !cts.IsCancellationRequested)
+            {
                 return await ExecuteCheckAsync(check, cts.Token);
+            }
             else
             {
                 if (!pausedChecks.Contains(check.Id))
@@ -280,25 +292,61 @@
                 mainForm.Invoke((MethodInvoker)(() => Run()));
         }
 
+        private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
+        {
+            if (e.Mode == PowerModes.Suspend)
+            {
+                foreach (Check check in Checks)
+                {
+                    CancelCheck(check);
+                }
+                suspend = true;
+            }
+            else if (e.Mode == PowerModes.Resume)
+            {
+                pausedChecks.Clear();
+                foreach (Check check in Checks)
+                {
+                    //CancelCheck(check);
+                    if (check.Enabled && check.Server.Enabled && check.NextRunTime < DateTime.Now)
+                    {
+                        pausedChecks.Add(check.Id);
+                    }
+                }
+                await Task.Delay(10000);
+                suspend = false;
+                Run();
+            }
+        }
+
+        private void Application_ApplicationExit(object sender, EventArgs e)
+        {
+            NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged;
+            SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
+        }
+
         public KeyStatus OpenPrivateKey(string path, string password = null)
         {
             KeyStatus keyStatus;
             if (path == null)
-                return KeyStatus.NotAccessible;
+                keyStatus = KeyStatus.NotAccessible;
             if (privateKeys.TryGetValue(path, out PrivateKeyFile key) && key != null)
-                return KeyStatus.Open;
-            try
-            {
-                key = new PrivateKeyFile(path, password);
                 keyStatus = KeyStatus.Open;
-            }
-            catch (Exception e) when (e is SshPassPhraseNullOrEmptyException || e is InvalidOperationException)
+            else
             {
-                keyStatus = KeyStatus.NeedPassword;
-            }
-            catch (Exception)
-            {
-                keyStatus = KeyStatus.NotAccessible;
+                try
+                {
+                    key = new PrivateKeyFile(path, password);
+                    keyStatus = KeyStatus.Open;
+                }
+                catch (Exception e) when (e is SshPassPhraseNullOrEmptyException || e is InvalidOperationException)
+                {
+                    keyStatus = KeyStatus.NeedPassword;
+                }
+                catch (Exception)
+                {
+                    keyStatus = KeyStatus.NotAccessible;
+                }
             }
             foreach (Server server in Servers)
             {