changeset 0:3e1a2131f897

Initial commit. Ping check, scheduling, UI working. SSH check mostly working.
author Brad Greco <brad@bgreco.net>
date Mon, 31 Dec 2018 18:32:14 -0500
parents
children 9e92780ebc0f
files .hgignore ServerMonitor.sln ServerMonitor/App.config ServerMonitor/Attributes.cs ServerMonitor/Controls/CheckControl.Designer.cs ServerMonitor/Controls/CheckControl.cs ServerMonitor/Controls/CheckControl.resx ServerMonitor/Controls/HttpCheckControl.Designer.cs ServerMonitor/Controls/HttpCheckControl.cs ServerMonitor/Controls/HttpCheckControl.resx ServerMonitor/Controls/IncludesComboBox.cs ServerMonitor/Controls/ServerSummaryControl.Designer.cs ServerMonitor/Controls/ServerSummaryControl.cs ServerMonitor/Controls/ServerSummaryControl.resx ServerMonitor/Controls/SshCheckControl.Designer.cs ServerMonitor/Controls/SshCheckControl.cs ServerMonitor/Controls/SshCheckControl.resx ServerMonitor/Forms/CheckBoxDialog.Designer.cs ServerMonitor/Forms/CheckBoxDialog.cs ServerMonitor/Forms/CheckBoxDialog.resx ServerMonitor/Forms/CheckForm.Designer.cs ServerMonitor/Forms/CheckForm.cs ServerMonitor/Forms/CheckForm.resx ServerMonitor/Forms/QuickHelpForm.Designer.cs ServerMonitor/Forms/QuickHelpForm.cs ServerMonitor/Forms/QuickHelpForm.resx ServerMonitor/Forms/ServerForm.Designer.cs ServerMonitor/Forms/ServerForm.cs ServerMonitor/Forms/ServerForm.resx ServerMonitor/Forms/ServerSummaryForm.Designer.cs ServerMonitor/Forms/ServerSummaryForm.cs ServerMonitor/Forms/ServerSummaryForm.resx ServerMonitor/Helpers.cs ServerMonitor/Objects/Check.cs ServerMonitor/Objects/CheckResult.cs ServerMonitor/Objects/HttpCheck.cs ServerMonitor/Objects/Logger.cs ServerMonitor/Objects/PingCheck.cs ServerMonitor/Objects/Schedule.cs ServerMonitor/Objects/Server.cs ServerMonitor/Objects/ServerMonitor.cs ServerMonitor/Objects/SshCheck.cs ServerMonitor/Program.cs ServerMonitor/Properties/AssemblyInfo.cs ServerMonitor/Properties/DataSources/CheckResult.datasource ServerMonitor/Properties/Resources.Designer.cs ServerMonitor/Properties/Resources.resx ServerMonitor/Properties/Settings.Designer.cs ServerMonitor/Properties/Settings.settings ServerMonitor/Resources/Icon1.ico ServerMonitor/Resources/Image1.png ServerMonitor/Resources/add.png ServerMonitor/Resources/delete.png ServerMonitor/Resources/edit.png ServerMonitor/Resources/error.png ServerMonitor/Resources/help.png ServerMonitor/Resources/info.png ServerMonitor/Resources/pass.png ServerMonitor/Resources/run.png ServerMonitor/Resources/runall.png ServerMonitor/Resources/server.png ServerMonitor/Resources/warning.png ServerMonitor/ServerMonitor.csproj ServerMonitor/packages.config ServerMonitorTest/Properties/AssemblyInfo.cs ServerMonitorTest/ScheduleTest.cs ServerMonitorTest/ServerMonitorTest.csproj ServerMonitorTest/packages.config
diffstat 68 files changed, 6474 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,6 @@
+syntax: glob
+bin
+obj
+*.suo
+packages
+.vs
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor.sln	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27703.2035
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerMonitor", "ServerMonitor\ServerMonitor.csproj", "{68E905D9-18FD-4ADC-9CF7-B5984C3E2158}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerMonitorTest", "ServerMonitorTest\ServerMonitorTest.csproj", "{BD66790B-EBA2-4C11-A770-8901A8196BCD}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{68E905D9-18FD-4ADC-9CF7-B5984C3E2158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{68E905D9-18FD-4ADC-9CF7-B5984C3E2158}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{68E905D9-18FD-4ADC-9CF7-B5984C3E2158}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{68E905D9-18FD-4ADC-9CF7-B5984C3E2158}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BD66790B-EBA2-4C11-A770-8901A8196BCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BD66790B-EBA2-4C11-A770-8901A8196BCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BD66790B-EBA2-4C11-A770-8901A8196BCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BD66790B-EBA2-4C11-A770-8901A8196BCD}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {E5AB43B5-6016-4C4B-A846-4AC2A3DFEE62}
+	EndGlobalSection
+EndGlobal
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/App.config	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+    <configSections>
+        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+            <section name="ServerMonitor.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
+        </sectionGroup>
+    </configSections>
+    <startup> 
+        
+    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup>
+    <userSettings>
+        <ServerMonitor.Properties.Settings>
+            <setting name="ConfirmDeleteCheck" serializeAs="String">
+                <value>True</value>
+            </setting>
+        </ServerMonitor.Properties.Settings>
+    </userSettings>
+</configuration>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Attributes.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ServerMonitorApp
+{
+    internal class CheckTypeAttribute : Attribute
+    {
+        public Type CheckType { get; private set; }
+
+        public CheckTypeAttribute(Type checkType) { CheckType = checkType; }
+    }
+
+    internal class DisplayWeightAttribute : Attribute
+    {
+        public int DisplayWeight { get; private set; }
+        
+        public DisplayWeightAttribute(int displayWeight) { DisplayWeight = displayWeight; }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/CheckControl.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,60 @@
+namespace ServerMonitorApp
+{
+    partial class CheckControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.CheckGroupBox = new System.Windows.Forms.GroupBox();
+            this.SuspendLayout();
+            // 
+            // CheckGroupBox
+            // 
+            this.CheckGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.CheckGroupBox.Location = new System.Drawing.Point(0, 0);
+            this.CheckGroupBox.Name = "CheckGroupBox";
+            this.CheckGroupBox.Size = new System.Drawing.Size(526, 217);
+            this.CheckGroupBox.TabIndex = 0;
+            this.CheckGroupBox.TabStop = false;
+            this.CheckGroupBox.Text = "Check";
+            // 
+            // CheckControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.CheckGroupBox);
+            this.Name = "CheckControl";
+            this.Size = new System.Drawing.Size(526, 217);
+            this.Load += new System.EventHandler(this.CheckControl_Load);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        protected System.Windows.Forms.GroupBox CheckGroupBox;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/CheckControl.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    /// <summary>Base class for check controls</summary>
+    /// <remarks>This control should never be used directly, but marking it abstract causes problems with the designer.</remarks>
+    public partial class CheckControl : UserControl
+    {
+        public Type CheckType { get { return GetCheckType(GetType()); } }
+
+        public CheckControl()
+        {
+            InitializeComponent();
+        }
+
+        private void CheckControl_Load(object sender, EventArgs e)
+        {
+            CheckGroupBox.Text = Helpers.GetDisplayName(CheckType);
+
+            IEnumerable<Panel> panels = CheckGroupBox.Controls.OfType<Panel>();
+            foreach (Panel panel in panels)
+            {
+                CheckBox mainCheckBox = panel.Controls.OfType<CheckBox>().OrderBy(c => c.Left).First();
+                mainCheckBox.CheckedChanged += CheckControl_CheckedChanged;
+                DisablePanelByCheckBox(mainCheckBox);
+            }
+        }
+
+        private void CheckControl_CheckedChanged(object sender, EventArgs e)
+        {
+            DisablePanelByCheckBox((CheckBox)sender);
+        }
+
+        private void DisablePanelByCheckBox(CheckBox checkBox)
+        {
+            foreach (Control control in checkBox.Parent.Controls)
+            {
+                if (control != checkBox)
+                    control.Enabled = checkBox.Checked;
+            }
+        }
+
+        public virtual void LoadCheck(Check check) { }
+
+        public virtual void UpdateCheck(Check check) { }
+
+        public static CheckControl Create(Type CheckType)
+        {
+            Type checkControlType = typeof(CheckControl).Assembly.GetTypes().FirstOrDefault(t => GetCheckType(t) == CheckType);
+            return checkControlType == null ? null : (CheckControl)Activator.CreateInstance(checkControlType);
+        }
+
+        private static Type GetCheckType(Type type)
+        {
+            return type.GetAttribute<CheckTypeAttribute>()?.CheckType;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/CheckControl.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/HttpCheckControl.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,260 @@
+namespace ServerMonitorApp
+{
+    partial class HttpCheckControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.ResponseBodyPanel = new System.Windows.Forms.Panel();
+            this.ResponseBodyRegexCheckBox = new System.Windows.Forms.CheckBox();
+            this.ResponseBodyComboBox = new ServerMonitorApp.MatchComboBox();
+            this.ResponseBodyCheckBox = new System.Windows.Forms.CheckBox();
+            this.ResponseBodyTextBox = new System.Windows.Forms.TextBox();
+            this.ResponseLengthPanel = new System.Windows.Forms.Panel();
+            this.ResponseLengthCheckbox = new System.Windows.Forms.CheckBox();
+            this.ResponseLengthMaxTextBox = new System.Windows.Forms.TextBox();
+            this.ResponseLengthKbLabel = new System.Windows.Forms.Label();
+            this.ResponseLengthMinTextBox = new System.Windows.Forms.TextBox();
+            this.ResponseLengthAndLabel = new System.Windows.Forms.Label();
+            this.ResponseCodePanel = new System.Windows.Forms.Panel();
+            this.ResponseCodeCheckBox = new System.Windows.Forms.CheckBox();
+            this.ResponseCodeTextBox = new System.Windows.Forms.TextBox();
+            this.HttpUrlLabel = new System.Windows.Forms.Label();
+            this.UrlTextBox = new System.Windows.Forms.TextBox();
+            this.CheckGroupBox.SuspendLayout();
+            this.ResponseBodyPanel.SuspendLayout();
+            this.ResponseLengthPanel.SuspendLayout();
+            this.ResponseCodePanel.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // CheckGroupBox
+            // 
+            this.CheckGroupBox.Controls.Add(this.ResponseBodyPanel);
+            this.CheckGroupBox.Controls.Add(this.ResponseLengthPanel);
+            this.CheckGroupBox.Controls.Add(this.ResponseCodePanel);
+            this.CheckGroupBox.Controls.Add(this.HttpUrlLabel);
+            this.CheckGroupBox.Controls.Add(this.UrlTextBox);
+            this.CheckGroupBox.Size = new System.Drawing.Size(526, 142);
+            this.CheckGroupBox.Text = "null";
+            // 
+            // ResponseBodyPanel
+            // 
+            this.ResponseBodyPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.ResponseBodyPanel.Controls.Add(this.ResponseBodyRegexCheckBox);
+            this.ResponseBodyPanel.Controls.Add(this.ResponseBodyComboBox);
+            this.ResponseBodyPanel.Controls.Add(this.ResponseBodyCheckBox);
+            this.ResponseBodyPanel.Controls.Add(this.ResponseBodyTextBox);
+            this.ResponseBodyPanel.Location = new System.Drawing.Point(9, 102);
+            this.ResponseBodyPanel.Name = "ResponseBodyPanel";
+            this.ResponseBodyPanel.Size = new System.Drawing.Size(511, 28);
+            this.ResponseBodyPanel.TabIndex = 21;
+            // 
+            // ResponseBodyRegexCheckBox
+            // 
+            this.ResponseBodyRegexCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.ResponseBodyRegexCheckBox.AutoSize = true;
+            this.ResponseBodyRegexCheckBox.Location = new System.Drawing.Point(395, 6);
+            this.ResponseBodyRegexCheckBox.Name = "ResponseBodyRegexCheckBox";
+            this.ResponseBodyRegexCheckBox.Size = new System.Drawing.Size(116, 17);
+            this.ResponseBodyRegexCheckBox.TabIndex = 9;
+            this.ResponseBodyRegexCheckBox.Text = "Regular expression";
+            this.ResponseBodyRegexCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // ResponseBodyComboBox
+            // 
+            this.ResponseBodyComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.ResponseBodyComboBox.FormattingEnabled = true;
+            this.ResponseBodyComboBox.Items.AddRange(new object[] {
+            "contains",
+            "does not contain",
+            "contains",
+            "does not contain"});
+            this.ResponseBodyComboBox.Location = new System.Drawing.Point(98, 4);
+            this.ResponseBodyComboBox.Name = "ResponseBodyComboBox";
+            this.ResponseBodyComboBox.Size = new System.Drawing.Size(110, 21);
+            this.ResponseBodyComboBox.TabIndex = 8;
+            // 
+            // ResponseBodyCheckBox
+            // 
+            this.ResponseBodyCheckBox.AutoSize = true;
+            this.ResponseBodyCheckBox.Location = new System.Drawing.Point(0, 6);
+            this.ResponseBodyCheckBox.Name = "ResponseBodyCheckBox";
+            this.ResponseBodyCheckBox.Size = new System.Drawing.Size(100, 17);
+            this.ResponseBodyCheckBox.TabIndex = 4;
+            this.ResponseBodyCheckBox.Text = "Response body";
+            this.ResponseBodyCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // ResponseBodyTextBox
+            // 
+            this.ResponseBodyTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.ResponseBodyTextBox.Location = new System.Drawing.Point(214, 4);
+            this.ResponseBodyTextBox.Name = "ResponseBodyTextBox";
+            this.ResponseBodyTextBox.Size = new System.Drawing.Size(175, 20);
+            this.ResponseBodyTextBox.TabIndex = 7;
+            // 
+            // ResponseLengthPanel
+            // 
+            this.ResponseLengthPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.ResponseLengthPanel.Controls.Add(this.ResponseLengthCheckbox);
+            this.ResponseLengthPanel.Controls.Add(this.ResponseLengthMaxTextBox);
+            this.ResponseLengthPanel.Controls.Add(this.ResponseLengthKbLabel);
+            this.ResponseLengthPanel.Controls.Add(this.ResponseLengthMinTextBox);
+            this.ResponseLengthPanel.Controls.Add(this.ResponseLengthAndLabel);
+            this.ResponseLengthPanel.Location = new System.Drawing.Point(9, 75);
+            this.ResponseLengthPanel.Name = "ResponseLengthPanel";
+            this.ResponseLengthPanel.Size = new System.Drawing.Size(511, 28);
+            this.ResponseLengthPanel.TabIndex = 20;
+            // 
+            // ResponseLengthCheckbox
+            // 
+            this.ResponseLengthCheckbox.AutoSize = true;
+            this.ResponseLengthCheckbox.Location = new System.Drawing.Point(0, 6);
+            this.ResponseLengthCheckbox.Name = "ResponseLengthCheckbox";
+            this.ResponseLengthCheckbox.Size = new System.Drawing.Size(150, 17);
+            this.ResponseLengthCheckbox.TabIndex = 5;
+            this.ResponseLengthCheckbox.Text = "Response length between";
+            this.ResponseLengthCheckbox.UseVisualStyleBackColor = true;
+            // 
+            // ResponseLengthMaxTextBox
+            // 
+            this.ResponseLengthMaxTextBox.Location = new System.Drawing.Point(228, 4);
+            this.ResponseLengthMaxTextBox.Name = "ResponseLengthMaxTextBox";
+            this.ResponseLengthMaxTextBox.Size = new System.Drawing.Size(44, 20);
+            this.ResponseLengthMaxTextBox.TabIndex = 8;
+            this.ResponseLengthMaxTextBox.Text = "200";
+            // 
+            // ResponseLengthKbLabel
+            // 
+            this.ResponseLengthKbLabel.AutoSize = true;
+            this.ResponseLengthKbLabel.Location = new System.Drawing.Point(278, 7);
+            this.ResponseLengthKbLabel.Name = "ResponseLengthKbLabel";
+            this.ResponseLengthKbLabel.Size = new System.Drawing.Size(21, 13);
+            this.ResponseLengthKbLabel.TabIndex = 11;
+            this.ResponseLengthKbLabel.Text = "KB";
+            // 
+            // ResponseLengthMinTextBox
+            // 
+            this.ResponseLengthMinTextBox.Location = new System.Drawing.Point(151, 4);
+            this.ResponseLengthMinTextBox.Name = "ResponseLengthMinTextBox";
+            this.ResponseLengthMinTextBox.Size = new System.Drawing.Size(44, 20);
+            this.ResponseLengthMinTextBox.TabIndex = 9;
+            this.ResponseLengthMinTextBox.Text = "100";
+            // 
+            // ResponseLengthAndLabel
+            // 
+            this.ResponseLengthAndLabel.AutoSize = true;
+            this.ResponseLengthAndLabel.Location = new System.Drawing.Point(200, 7);
+            this.ResponseLengthAndLabel.Name = "ResponseLengthAndLabel";
+            this.ResponseLengthAndLabel.Size = new System.Drawing.Size(25, 13);
+            this.ResponseLengthAndLabel.TabIndex = 10;
+            this.ResponseLengthAndLabel.Text = "and";
+            // 
+            // ResponseCodePanel
+            // 
+            this.ResponseCodePanel.Controls.Add(this.ResponseCodeCheckBox);
+            this.ResponseCodePanel.Controls.Add(this.ResponseCodeTextBox);
+            this.ResponseCodePanel.Location = new System.Drawing.Point(9, 48);
+            this.ResponseCodePanel.Name = "ResponseCodePanel";
+            this.ResponseCodePanel.Size = new System.Drawing.Size(511, 28);
+            this.ResponseCodePanel.TabIndex = 19;
+            // 
+            // ResponseCodeCheckBox
+            // 
+            this.ResponseCodeCheckBox.AutoSize = true;
+            this.ResponseCodeCheckBox.Location = new System.Drawing.Point(0, 6);
+            this.ResponseCodeCheckBox.Name = "ResponseCodeCheckBox";
+            this.ResponseCodeCheckBox.Size = new System.Drawing.Size(135, 17);
+            this.ResponseCodeCheckBox.TabIndex = 4;
+            this.ResponseCodeCheckBox.Text = "Response code equals";
+            this.ResponseCodeCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // ResponseCodeTextBox
+            // 
+            this.ResponseCodeTextBox.Location = new System.Drawing.Point(136, 4);
+            this.ResponseCodeTextBox.Name = "ResponseCodeTextBox";
+            this.ResponseCodeTextBox.Size = new System.Drawing.Size(39, 20);
+            this.ResponseCodeTextBox.TabIndex = 7;
+            this.ResponseCodeTextBox.Text = "200";
+            // 
+            // HttpUrlLabel
+            // 
+            this.HttpUrlLabel.AutoSize = true;
+            this.HttpUrlLabel.Location = new System.Drawing.Point(6, 25);
+            this.HttpUrlLabel.Name = "HttpUrlLabel";
+            this.HttpUrlLabel.Size = new System.Drawing.Size(32, 13);
+            this.HttpUrlLabel.TabIndex = 18;
+            this.HttpUrlLabel.Text = "URL:";
+            // 
+            // UrlTextBox
+            // 
+            this.UrlTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.UrlTextBox.Location = new System.Drawing.Point(44, 22);
+            this.UrlTextBox.Name = "UrlTextBox";
+            this.UrlTextBox.Size = new System.Drawing.Size(476, 20);
+            this.UrlTextBox.TabIndex = 17;
+            // 
+            // HttpCheckControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Name = "HttpCheckControl";
+            this.Size = new System.Drawing.Size(526, 142);
+            this.CheckGroupBox.ResumeLayout(false);
+            this.CheckGroupBox.PerformLayout();
+            this.ResponseBodyPanel.ResumeLayout(false);
+            this.ResponseBodyPanel.PerformLayout();
+            this.ResponseLengthPanel.ResumeLayout(false);
+            this.ResponseLengthPanel.PerformLayout();
+            this.ResponseCodePanel.ResumeLayout(false);
+            this.ResponseCodePanel.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Panel ResponseBodyPanel;
+        private System.Windows.Forms.CheckBox ResponseBodyRegexCheckBox;
+        private MatchComboBox ResponseBodyComboBox;
+        private System.Windows.Forms.CheckBox ResponseBodyCheckBox;
+        private System.Windows.Forms.TextBox ResponseBodyTextBox;
+        private System.Windows.Forms.Panel ResponseLengthPanel;
+        private System.Windows.Forms.CheckBox ResponseLengthCheckbox;
+        private System.Windows.Forms.TextBox ResponseLengthMaxTextBox;
+        private System.Windows.Forms.Label ResponseLengthKbLabel;
+        private System.Windows.Forms.TextBox ResponseLengthMinTextBox;
+        private System.Windows.Forms.Label ResponseLengthAndLabel;
+        private System.Windows.Forms.Panel ResponseCodePanel;
+        private System.Windows.Forms.CheckBox ResponseCodeCheckBox;
+        private System.Windows.Forms.TextBox ResponseCodeTextBox;
+        private System.Windows.Forms.Label HttpUrlLabel;
+        private System.Windows.Forms.TextBox UrlTextBox;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/HttpCheckControl.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    [CheckType(typeof(HttpCheck))]
+    public partial class HttpCheckControl : CheckControl
+    {
+        public HttpCheckControl()
+        {
+            InitializeComponent();
+        }
+
+        public override void LoadCheck(Check check1)
+        {
+            HttpCheck check = (HttpCheck)check1;
+            UrlTextBox.Text = check.Url;
+            ResponseCodeCheckBox.Checked = check.CheckResponseCode;
+            ResponseCodeTextBox.Text = check.ResponseCode.ToString();
+            ResponseLengthCheckbox.Checked = check.CheckResponseLength;
+            ResponseLengthMinTextBox.Text = check.ResponseLengthMin;
+            ResponseLengthMaxTextBox.Text = check.ResponseLengthMax;
+            ResponseBodyCheckBox.Checked = check.CheckResponseBody;
+            ResponseBodyComboBox.SelectedIndex = (int)check.ResponseBodyMatchType;
+            ResponseBodyTextBox.Text = check.ResponseBodyPattern;
+            ResponseBodyRegexCheckBox.Checked = check.ResponseBodyUseRegex;
+        }
+
+        public override void UpdateCheck(Check check1)
+        {
+            HttpCheck check = (HttpCheck)check1;
+            check.Url = UrlTextBox.Text.Trim();
+            check.CheckResponseCode = ResponseCodeCheckBox.Checked;
+            check.ResponseCode = int.Parse(ResponseCodeTextBox.Text);
+            check.CheckResponseLength = ResponseLengthCheckbox.Checked;
+            check.ResponseLengthMin = ResponseLengthMinTextBox.Text;
+            check.ResponseLengthMax = ResponseLengthMaxTextBox.Text;
+            check.CheckResponseBody = ResponseBodyCheckBox.Checked;
+            check.ResponseBodyMatchType = (MatchType)ResponseBodyComboBox.SelectedIndex;
+            check.ResponseBodyPattern = ResponseBodyTextBox.Text;
+            check.ResponseBodyUseRegex = ResponseBodyRegexCheckBox.Checked;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/HttpCheckControl.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/IncludesComboBox.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    class MatchComboBox : ComboBox
+    {
+        protected override void OnCreateControl()
+        {
+            base.OnCreateControl();
+            Items.Clear();
+            Items.Add("equals");
+            Items.Add("does not equal");
+            Items.Add("contains");
+            Items.Add("does not contain");
+        }
+    }
+
+    public enum MatchType { Equals = 0, NotEquals = 1, Contains = 2, NotContains = 3 }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/ServerSummaryControl.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,76 @@
+namespace ServerMonitorApp
+{
+    partial class ServerSummaryControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.ServerNameLabel = new System.Windows.Forms.Label();
+            this.ServerPictureBox = new System.Windows.Forms.PictureBox();
+            ((System.ComponentModel.ISupportInitialize)(this.ServerPictureBox)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // ServerNameLabel
+            // 
+            this.ServerNameLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.ServerNameLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+            this.ServerNameLabel.Location = new System.Drawing.Point(0, 127);
+            this.ServerNameLabel.Name = "ServerNameLabel";
+            this.ServerNameLabel.Size = new System.Drawing.Size(192, 21);
+            this.ServerNameLabel.TabIndex = 1;
+            this.ServerNameLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+            // 
+            // ServerPictureBox
+            // 
+            this.ServerPictureBox.Image = global::ServerMonitorApp.Properties.Resources.server;
+            this.ServerPictureBox.Location = new System.Drawing.Point(0, 0);
+            this.ServerPictureBox.Name = "ServerPictureBox";
+            this.ServerPictureBox.Size = new System.Drawing.Size(192, 124);
+            this.ServerPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.ServerPictureBox.TabIndex = 0;
+            this.ServerPictureBox.TabStop = false;
+            // 
+            // ServerSummaryControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.BackColor = System.Drawing.SystemColors.Control;
+            this.Controls.Add(this.ServerNameLabel);
+            this.Controls.Add(this.ServerPictureBox);
+            this.Name = "ServerSummaryControl";
+            this.Size = new System.Drawing.Size(192, 192);
+            ((System.ComponentModel.ISupportInitialize)(this.ServerPictureBox)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.PictureBox ServerPictureBox;
+        private System.Windows.Forms.Label ServerNameLabel;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/ServerSummaryControl.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    public partial class ServerSummaryControl : UserControl
+    {
+        public new event EventHandler Click;
+
+        public Server Server { get; private set; }
+
+        public ServerSummaryControl(Server server)
+        {
+            InitializeComponent();
+
+            foreach (Control control in new Control[] { this, ServerPictureBox, ServerNameLabel })
+            {
+                control.Cursor = Cursors.Hand;
+                control.Click += Control_Click;
+            }
+            Server = server;
+            ServerNameLabel.Text = Server.Name;
+        }
+
+        private void Control_Click(object sender, EventArgs e)
+        {
+            Click?.Invoke(this, e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/ServerSummaryControl.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/SshCheckControl.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,181 @@
+namespace ServerMonitorApp
+{
+    partial class SshCheckControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.ResponseBodyPanel = new System.Windows.Forms.Panel();
+            this.CommandOutputRegexCheckBox = new System.Windows.Forms.CheckBox();
+            this.CommandOutputComboBox = new ServerMonitorApp.MatchComboBox();
+            this.CommandOutputCheckBox = new System.Windows.Forms.CheckBox();
+            this.CommandOutputTextBox = new System.Windows.Forms.TextBox();
+            this.ResponseCodePanel = new System.Windows.Forms.Panel();
+            this.ExitCodeCheckBox = new System.Windows.Forms.CheckBox();
+            this.ExitCodeTextBox = new System.Windows.Forms.TextBox();
+            this.CommandLabel = new System.Windows.Forms.Label();
+            this.CommandTextBox = new System.Windows.Forms.TextBox();
+            this.CheckGroupBox.SuspendLayout();
+            this.ResponseBodyPanel.SuspendLayout();
+            this.ResponseCodePanel.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // CheckGroupBox
+            // 
+            this.CheckGroupBox.Controls.Add(this.ResponseBodyPanel);
+            this.CheckGroupBox.Controls.Add(this.ResponseCodePanel);
+            this.CheckGroupBox.Controls.Add(this.CommandLabel);
+            this.CheckGroupBox.Controls.Add(this.CommandTextBox);
+            this.CheckGroupBox.Size = new System.Drawing.Size(526, 114);
+            this.CheckGroupBox.Text = "null";
+            // 
+            // ResponseBodyPanel
+            // 
+            this.ResponseBodyPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.ResponseBodyPanel.Controls.Add(this.CommandOutputRegexCheckBox);
+            this.ResponseBodyPanel.Controls.Add(this.CommandOutputComboBox);
+            this.ResponseBodyPanel.Controls.Add(this.CommandOutputCheckBox);
+            this.ResponseBodyPanel.Controls.Add(this.CommandOutputTextBox);
+            this.ResponseBodyPanel.Location = new System.Drawing.Point(9, 75);
+            this.ResponseBodyPanel.Name = "ResponseBodyPanel";
+            this.ResponseBodyPanel.Size = new System.Drawing.Size(511, 28);
+            this.ResponseBodyPanel.TabIndex = 21;
+            // 
+            // CommandOutputRegexCheckBox
+            // 
+            this.CommandOutputRegexCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.CommandOutputRegexCheckBox.AutoSize = true;
+            this.CommandOutputRegexCheckBox.Location = new System.Drawing.Point(395, 6);
+            this.CommandOutputRegexCheckBox.Name = "CommandOutputRegexCheckBox";
+            this.CommandOutputRegexCheckBox.Size = new System.Drawing.Size(116, 17);
+            this.CommandOutputRegexCheckBox.TabIndex = 9;
+            this.CommandOutputRegexCheckBox.Text = "Regular expression";
+            this.CommandOutputRegexCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // CommandOutputComboBox
+            // 
+            this.CommandOutputComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.CommandOutputComboBox.FormattingEnabled = true;
+            this.CommandOutputComboBox.Location = new System.Drawing.Point(106, 4);
+            this.CommandOutputComboBox.Name = "CommandOutputComboBox";
+            this.CommandOutputComboBox.Size = new System.Drawing.Size(110, 21);
+            this.CommandOutputComboBox.TabIndex = 8;
+            // 
+            // CommandOutputCheckBox
+            // 
+            this.CommandOutputCheckBox.AutoSize = true;
+            this.CommandOutputCheckBox.Location = new System.Drawing.Point(0, 6);
+            this.CommandOutputCheckBox.Name = "CommandOutputCheckBox";
+            this.CommandOutputCheckBox.Size = new System.Drawing.Size(106, 17);
+            this.CommandOutputCheckBox.TabIndex = 4;
+            this.CommandOutputCheckBox.Text = "Command output";
+            this.CommandOutputCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // CommandOutputTextBox
+            // 
+            this.CommandOutputTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.CommandOutputTextBox.Location = new System.Drawing.Point(222, 4);
+            this.CommandOutputTextBox.Name = "CommandOutputTextBox";
+            this.CommandOutputTextBox.Size = new System.Drawing.Size(167, 20);
+            this.CommandOutputTextBox.TabIndex = 7;
+            // 
+            // ResponseCodePanel
+            // 
+            this.ResponseCodePanel.Controls.Add(this.ExitCodeCheckBox);
+            this.ResponseCodePanel.Controls.Add(this.ExitCodeTextBox);
+            this.ResponseCodePanel.Location = new System.Drawing.Point(9, 48);
+            this.ResponseCodePanel.Name = "ResponseCodePanel";
+            this.ResponseCodePanel.Size = new System.Drawing.Size(511, 28);
+            this.ResponseCodePanel.TabIndex = 19;
+            // 
+            // ExitCodeCheckBox
+            // 
+            this.ExitCodeCheckBox.AutoSize = true;
+            this.ExitCodeCheckBox.Location = new System.Drawing.Point(0, 6);
+            this.ExitCodeCheckBox.Name = "ExitCodeCheckBox";
+            this.ExitCodeCheckBox.Size = new System.Drawing.Size(104, 17);
+            this.ExitCodeCheckBox.TabIndex = 4;
+            this.ExitCodeCheckBox.Text = "Exit code equals";
+            this.ExitCodeCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // ExitCodeTextBox
+            // 
+            this.ExitCodeTextBox.Location = new System.Drawing.Point(104, 4);
+            this.ExitCodeTextBox.Name = "ExitCodeTextBox";
+            this.ExitCodeTextBox.Size = new System.Drawing.Size(39, 20);
+            this.ExitCodeTextBox.TabIndex = 7;
+            this.ExitCodeTextBox.Text = "0";
+            // 
+            // CommandLabel
+            // 
+            this.CommandLabel.AutoSize = true;
+            this.CommandLabel.Location = new System.Drawing.Point(6, 25);
+            this.CommandLabel.Name = "CommandLabel";
+            this.CommandLabel.Size = new System.Drawing.Size(57, 13);
+            this.CommandLabel.TabIndex = 18;
+            this.CommandLabel.Text = "Command:";
+            // 
+            // CommandTextBox
+            // 
+            this.CommandTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.CommandTextBox.Location = new System.Drawing.Point(68, 22);
+            this.CommandTextBox.Name = "CommandTextBox";
+            this.CommandTextBox.Size = new System.Drawing.Size(452, 20);
+            this.CommandTextBox.TabIndex = 17;
+            // 
+            // SshCheckControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Name = "SshCheckControl";
+            this.Size = new System.Drawing.Size(526, 114);
+            this.CheckGroupBox.ResumeLayout(false);
+            this.CheckGroupBox.PerformLayout();
+            this.ResponseBodyPanel.ResumeLayout(false);
+            this.ResponseBodyPanel.PerformLayout();
+            this.ResponseCodePanel.ResumeLayout(false);
+            this.ResponseCodePanel.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Panel ResponseBodyPanel;
+        private System.Windows.Forms.CheckBox CommandOutputRegexCheckBox;
+        private MatchComboBox CommandOutputComboBox;
+        private System.Windows.Forms.CheckBox CommandOutputCheckBox;
+        private System.Windows.Forms.TextBox CommandOutputTextBox;
+        private System.Windows.Forms.Panel ResponseCodePanel;
+        private System.Windows.Forms.CheckBox ExitCodeCheckBox;
+        private System.Windows.Forms.TextBox ExitCodeTextBox;
+        private System.Windows.Forms.Label CommandLabel;
+        private System.Windows.Forms.TextBox CommandTextBox;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/SshCheckControl.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    [CheckType(typeof(SshCheck))]
+    public partial class SshCheckControl : CheckControl
+    {
+        public SshCheckControl()
+        {
+            InitializeComponent();
+        }
+
+        public override void LoadCheck(Check check1)
+        {
+            SshCheck check = (SshCheck)check1;
+            CommandTextBox.Text = check.Command;
+            ExitCodeCheckBox.Checked = check.CheckExitCode;
+            ExitCodeTextBox.Text = check.ExitCode.ToString();
+            CommandOutputCheckBox.Checked = check.CheckCommandOutput;
+            CommandOutputComboBox.SelectedIndex = (int)check.CommandOutputMatchType;
+            CommandOutputTextBox.Text = check.CommandOutputPattern;
+            CommandOutputRegexCheckBox.Checked = check.CommandOutputUseRegex;
+        }
+
+        public override void UpdateCheck(Check check1)
+        {
+            SshCheck check = (SshCheck)check1;
+            check.Command = CommandTextBox.Text.Trim();
+            check.CheckExitCode = ExitCodeCheckBox.Checked;
+            check.ExitCode = int.Parse(ExitCodeTextBox.Text);
+            check.CheckCommandOutput = CommandOutputCheckBox.Checked;
+            check.CommandOutputMatchType = (MatchType)CommandOutputComboBox.SelectedIndex;
+            check.CommandOutputPattern = CommandOutputTextBox.Text;
+            check.CommandOutputUseRegex = CommandOutputRegexCheckBox.Checked;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Controls/SshCheckControl.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/CheckBoxDialog.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,138 @@
+namespace ServerMonitorApp
+{
+    partial class CheckBoxDialog
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.PromptCheckBox = new System.Windows.Forms.CheckBox();
+            this.YesButton = new System.Windows.Forms.Button();
+            this.NoButton = new System.Windows.Forms.Button();
+            this.MessageLabel = new System.Windows.Forms.Label();
+            this.MessageIcon = new System.Windows.Forms.PictureBox();
+            this.panel1 = new System.Windows.Forms.Panel();
+            ((System.ComponentModel.ISupportInitialize)(this.MessageIcon)).BeginInit();
+            this.panel1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // PromptCheckBox
+            // 
+            this.PromptCheckBox.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.PromptCheckBox.AutoSize = true;
+            this.PromptCheckBox.Location = new System.Drawing.Point(12, 17);
+            this.PromptCheckBox.Name = "PromptCheckBox";
+            this.PromptCheckBox.Size = new System.Drawing.Size(107, 17);
+            this.PromptCheckBox.TabIndex = 3;
+            this.PromptCheckBox.Text = "&Do not ask again";
+            this.PromptCheckBox.UseVisualStyleBackColor = true;
+            this.PromptCheckBox.CheckedChanged += new System.EventHandler(this.PromptCheckBox_CheckedChanged);
+            // 
+            // YesButton
+            // 
+            this.YesButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.YesButton.DialogResult = System.Windows.Forms.DialogResult.OK;
+            this.YesButton.Location = new System.Drawing.Point(165, 13);
+            this.YesButton.Name = "YesButton";
+            this.YesButton.Size = new System.Drawing.Size(75, 23);
+            this.YesButton.TabIndex = 1;
+            this.YesButton.Text = "&Yes";
+            this.YesButton.UseVisualStyleBackColor = true;
+            // 
+            // NoButton
+            // 
+            this.NoButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.NoButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.NoButton.Location = new System.Drawing.Point(246, 13);
+            this.NoButton.Name = "NoButton";
+            this.NoButton.Size = new System.Drawing.Size(75, 23);
+            this.NoButton.TabIndex = 2;
+            this.NoButton.Text = "&No";
+            this.NoButton.UseVisualStyleBackColor = true;
+            // 
+            // MessageLabel
+            // 
+            this.MessageLabel.AutoSize = true;
+            this.MessageLabel.Location = new System.Drawing.Point(70, 36);
+            this.MessageLabel.Name = "MessageLabel";
+            this.MessageLabel.Size = new System.Drawing.Size(0, 13);
+            this.MessageLabel.TabIndex = 3;
+            // 
+            // MessageIcon
+            // 
+            this.MessageIcon.Location = new System.Drawing.Point(25, 26);
+            this.MessageIcon.Name = "MessageIcon";
+            this.MessageIcon.Size = new System.Drawing.Size(32, 32);
+            this.MessageIcon.TabIndex = 4;
+            this.MessageIcon.TabStop = false;
+            // 
+            // panel1
+            // 
+            this.panel1.BackColor = System.Drawing.SystemColors.Control;
+            this.panel1.Controls.Add(this.YesButton);
+            this.panel1.Controls.Add(this.NoButton);
+            this.panel1.Controls.Add(this.PromptCheckBox);
+            this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.panel1.Location = new System.Drawing.Point(0, 84);
+            this.panel1.Name = "panel1";
+            this.panel1.Size = new System.Drawing.Size(333, 49);
+            this.panel1.TabIndex = 5;
+            // 
+            // CheckBoxDialog
+            // 
+            this.AcceptButton = this.YesButton;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.BackColor = System.Drawing.Color.White;
+            this.CancelButton = this.NoButton;
+            this.ClientSize = new System.Drawing.Size(333, 133);
+            this.Controls.Add(this.panel1);
+            this.Controls.Add(this.MessageIcon);
+            this.Controls.Add(this.MessageLabel);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+            this.MaximizeBox = false;
+            this.MinimizeBox = false;
+            this.Name = "CheckBoxDialog";
+            this.ShowInTaskbar = false;
+            this.Text = "Delete Check";
+            this.Load += new System.EventHandler(this.CheckBoxDialog_Load);
+            ((System.ComponentModel.ISupportInitialize)(this.MessageIcon)).EndInit();
+            this.panel1.ResumeLayout(false);
+            this.panel1.PerformLayout();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.CheckBox PromptCheckBox;
+        private System.Windows.Forms.Button YesButton;
+        private System.Windows.Forms.Button NoButton;
+        private System.Windows.Forms.Label MessageLabel;
+        private System.Windows.Forms.PictureBox MessageIcon;
+        private System.Windows.Forms.Panel panel1;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/CheckBoxDialog.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    public partial class CheckBoxDialog : Form
+    {
+        public string Message { get; set; }
+
+        public bool Checked { get; private set; }
+
+        public CheckBoxDialog()
+        {
+            InitializeComponent();
+        }
+
+        private void CheckBoxDialog_Load(object sender, EventArgs e)
+        {
+            MessageIcon.Image = SystemIcons.Question.ToBitmap();
+            MessageLabel.Text = Message;
+        }
+
+        private void PromptCheckBox_CheckedChanged(object sender, EventArgs e)
+        {
+            Checked = PromptCheckBox.Checked;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/CheckBoxDialog.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/CheckForm.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,476 @@
+namespace ServerMonitorApp
+{
+    partial class CheckForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.CheckTypeLabel = new System.Windows.Forms.Label();
+            this.CheckTypeComboBox = new System.Windows.Forms.ComboBox();
+            this.CheckTypePanel = new System.Windows.Forms.Panel();
+            this.TypeHelpPictureBox = new System.Windows.Forms.PictureBox();
+            this.GeneralGroupBox = new System.Windows.Forms.GroupBox();
+            this.ScheduleAtPanel = new System.Windows.Forms.Panel();
+            this.AtTimePicker = new System.Windows.Forms.DateTimePicker();
+            this.ScheduleAtLabel = new System.Windows.Forms.Label();
+            this.ScheduleBetweenPanel = new System.Windows.Forms.Panel();
+            this.ScheduleBetweenAndLabel = new System.Windows.Forms.Label();
+            this.EndTimePicker = new System.Windows.Forms.DateTimePicker();
+            this.StartTimePicker = new System.Windows.Forms.DateTimePicker();
+            this.ScheduleBetweenLabel = new System.Windows.Forms.Label();
+            this.FrequencyUnitsComboBox = new System.Windows.Forms.ComboBox();
+            this.ScheduleEveryLabel = new System.Windows.Forms.Label();
+            this.FrequencyUpDown = new System.Windows.Forms.NumericUpDown();
+            this.TimeoutInput = new System.Windows.Forms.NumericUpDown();
+            this.PingTimeoutLabel = new System.Windows.Forms.Label();
+            this.ScheduleLabel = new System.Windows.Forms.Label();
+            this.NameLabel = new System.Windows.Forms.Label();
+            this.NameTextBox = new System.Windows.Forms.TextBox();
+            this.EnabledCheckBox = new System.Windows.Forms.CheckBox();
+            this.OkButton = new System.Windows.Forms.Button();
+            this.CancelCheckButton = new System.Windows.Forms.Button();
+            this.CheckSettingsPanel = new System.Windows.Forms.Panel();
+            this.ResultLabel = new System.Windows.Forms.Label();
+            this.ResultIconPictureBox = new System.Windows.Forms.PictureBox();
+            this.CancelRunButton = new System.Windows.Forms.Button();
+            this.RunButton = new System.Windows.Forms.Button();
+            this.CheckTypePanel.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.TypeHelpPictureBox)).BeginInit();
+            this.GeneralGroupBox.SuspendLayout();
+            this.ScheduleAtPanel.SuspendLayout();
+            this.ScheduleBetweenPanel.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.FrequencyUpDown)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.TimeoutInput)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.ResultIconPictureBox)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // CheckTypeLabel
+            // 
+            this.CheckTypeLabel.AutoSize = true;
+            this.CheckTypeLabel.Location = new System.Drawing.Point(3, 3);
+            this.CheckTypeLabel.Name = "CheckTypeLabel";
+            this.CheckTypeLabel.Size = new System.Drawing.Size(64, 13);
+            this.CheckTypeLabel.TabIndex = 0;
+            this.CheckTypeLabel.Text = "Check type:";
+            // 
+            // CheckTypeComboBox
+            // 
+            this.CheckTypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.CheckTypeComboBox.FormattingEnabled = true;
+            this.CheckTypeComboBox.Location = new System.Drawing.Point(73, 0);
+            this.CheckTypeComboBox.Name = "CheckTypeComboBox";
+            this.CheckTypeComboBox.Size = new System.Drawing.Size(121, 21);
+            this.CheckTypeComboBox.TabIndex = 1;
+            this.CheckTypeComboBox.SelectedIndexChanged += new System.EventHandler(this.CheckTypeComboBox_SelectedIndexChanged);
+            this.CheckTypeComboBox.Format += new System.Windows.Forms.ListControlConvertEventHandler(this.CheckTypeComboBox_Format);
+            // 
+            // CheckTypePanel
+            // 
+            this.CheckTypePanel.Anchor = System.Windows.Forms.AnchorStyles.Top;
+            this.CheckTypePanel.Controls.Add(this.CheckTypeLabel);
+            this.CheckTypePanel.Controls.Add(this.TypeHelpPictureBox);
+            this.CheckTypePanel.Controls.Add(this.CheckTypeComboBox);
+            this.CheckTypePanel.Location = new System.Drawing.Point(162, 12);
+            this.CheckTypePanel.Name = "CheckTypePanel";
+            this.CheckTypePanel.Size = new System.Drawing.Size(227, 26);
+            this.CheckTypePanel.TabIndex = 1;
+            // 
+            // TypeHelpPictureBox
+            // 
+            this.TypeHelpPictureBox.Cursor = System.Windows.Forms.Cursors.Hand;
+            this.TypeHelpPictureBox.Image = global::ServerMonitorApp.Properties.Resources.help;
+            this.TypeHelpPictureBox.Location = new System.Drawing.Point(200, 0);
+            this.TypeHelpPictureBox.Name = "TypeHelpPictureBox";
+            this.TypeHelpPictureBox.Size = new System.Drawing.Size(20, 20);
+            this.TypeHelpPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.TypeHelpPictureBox.TabIndex = 1;
+            this.TypeHelpPictureBox.TabStop = false;
+            this.TypeHelpPictureBox.Click += new System.EventHandler(this.TypeHelpPictureBox_Click);
+            // 
+            // GeneralGroupBox
+            // 
+            this.GeneralGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.GeneralGroupBox.Controls.Add(this.ScheduleAtPanel);
+            this.GeneralGroupBox.Controls.Add(this.ScheduleBetweenPanel);
+            this.GeneralGroupBox.Controls.Add(this.FrequencyUnitsComboBox);
+            this.GeneralGroupBox.Controls.Add(this.ScheduleEveryLabel);
+            this.GeneralGroupBox.Controls.Add(this.FrequencyUpDown);
+            this.GeneralGroupBox.Controls.Add(this.TimeoutInput);
+            this.GeneralGroupBox.Controls.Add(this.PingTimeoutLabel);
+            this.GeneralGroupBox.Controls.Add(this.ScheduleLabel);
+            this.GeneralGroupBox.Controls.Add(this.NameLabel);
+            this.GeneralGroupBox.Controls.Add(this.NameTextBox);
+            this.GeneralGroupBox.Controls.Add(this.EnabledCheckBox);
+            this.GeneralGroupBox.Location = new System.Drawing.Point(12, 39);
+            this.GeneralGroupBox.Name = "GeneralGroupBox";
+            this.GeneralGroupBox.Size = new System.Drawing.Size(526, 115);
+            this.GeneralGroupBox.TabIndex = 2;
+            this.GeneralGroupBox.TabStop = false;
+            this.GeneralGroupBox.Text = "General settings";
+            // 
+            // ScheduleAtPanel
+            // 
+            this.ScheduleAtPanel.Controls.Add(this.AtTimePicker);
+            this.ScheduleAtPanel.Controls.Add(this.ScheduleAtLabel);
+            this.ScheduleAtPanel.Location = new System.Drawing.Point(247, 76);
+            this.ScheduleAtPanel.Name = "ScheduleAtPanel";
+            this.ScheduleAtPanel.Size = new System.Drawing.Size(270, 29);
+            this.ScheduleAtPanel.TabIndex = 32;
+            this.ScheduleAtPanel.Visible = false;
+            // 
+            // AtTimePicker
+            // 
+            this.AtTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Time;
+            this.AtTimePicker.Location = new System.Drawing.Point(21, 4);
+            this.AtTimePicker.Name = "AtTimePicker";
+            this.AtTimePicker.ShowUpDown = true;
+            this.AtTimePicker.Size = new System.Drawing.Size(85, 20);
+            this.AtTimePicker.TabIndex = 1;
+            this.AtTimePicker.Value = new System.DateTime(1970, 1, 1, 0, 0, 0, 0);
+            // 
+            // ScheduleAtLabel
+            // 
+            this.ScheduleAtLabel.AutoSize = true;
+            this.ScheduleAtLabel.Location = new System.Drawing.Point(3, 6);
+            this.ScheduleAtLabel.Name = "ScheduleAtLabel";
+            this.ScheduleAtLabel.Size = new System.Drawing.Size(16, 13);
+            this.ScheduleAtLabel.TabIndex = 0;
+            this.ScheduleAtLabel.Text = "at";
+            // 
+            // ScheduleBetweenPanel
+            // 
+            this.ScheduleBetweenPanel.Controls.Add(this.ScheduleBetweenAndLabel);
+            this.ScheduleBetweenPanel.Controls.Add(this.EndTimePicker);
+            this.ScheduleBetweenPanel.Controls.Add(this.StartTimePicker);
+            this.ScheduleBetweenPanel.Controls.Add(this.ScheduleBetweenLabel);
+            this.ScheduleBetweenPanel.Location = new System.Drawing.Point(247, 76);
+            this.ScheduleBetweenPanel.Name = "ScheduleBetweenPanel";
+            this.ScheduleBetweenPanel.Size = new System.Drawing.Size(270, 29);
+            this.ScheduleBetweenPanel.TabIndex = 31;
+            // 
+            // ScheduleBetweenAndLabel
+            // 
+            this.ScheduleBetweenAndLabel.AutoSize = true;
+            this.ScheduleBetweenAndLabel.Location = new System.Drawing.Point(138, 6);
+            this.ScheduleBetweenAndLabel.Name = "ScheduleBetweenAndLabel";
+            this.ScheduleBetweenAndLabel.Size = new System.Drawing.Size(25, 13);
+            this.ScheduleBetweenAndLabel.TabIndex = 3;
+            this.ScheduleBetweenAndLabel.Text = "and";
+            // 
+            // EndTimePicker
+            // 
+            this.EndTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Time;
+            this.EndTimePicker.Location = new System.Drawing.Point(167, 4);
+            this.EndTimePicker.Name = "EndTimePicker";
+            this.EndTimePicker.ShowUpDown = true;
+            this.EndTimePicker.Size = new System.Drawing.Size(85, 20);
+            this.EndTimePicker.TabIndex = 2;
+            this.EndTimePicker.Value = new System.DateTime(1970, 1, 1, 23, 59, 59, 0);
+            // 
+            // StartTimePicker
+            // 
+            this.StartTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Time;
+            this.StartTimePicker.Location = new System.Drawing.Point(51, 4);
+            this.StartTimePicker.Name = "StartTimePicker";
+            this.StartTimePicker.ShowUpDown = true;
+            this.StartTimePicker.Size = new System.Drawing.Size(85, 20);
+            this.StartTimePicker.TabIndex = 1;
+            this.StartTimePicker.Value = new System.DateTime(1970, 1, 1, 0, 0, 0, 0);
+            // 
+            // ScheduleBetweenLabel
+            // 
+            this.ScheduleBetweenLabel.AutoSize = true;
+            this.ScheduleBetweenLabel.Location = new System.Drawing.Point(3, 6);
+            this.ScheduleBetweenLabel.Name = "ScheduleBetweenLabel";
+            this.ScheduleBetweenLabel.Size = new System.Drawing.Size(48, 13);
+            this.ScheduleBetweenLabel.TabIndex = 0;
+            this.ScheduleBetweenLabel.Text = "between";
+            // 
+            // FrequencyUnitsComboBox
+            // 
+            this.FrequencyUnitsComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.FrequencyUnitsComboBox.FormattingEnabled = true;
+            this.FrequencyUnitsComboBox.Location = new System.Drawing.Point(169, 79);
+            this.FrequencyUnitsComboBox.Name = "FrequencyUnitsComboBox";
+            this.FrequencyUnitsComboBox.Size = new System.Drawing.Size(75, 21);
+            this.FrequencyUnitsComboBox.TabIndex = 30;
+            this.FrequencyUnitsComboBox.SelectedIndexChanged += new System.EventHandler(this.FrequencyUnitsComboBox_SelectedIndexChanged);
+            this.FrequencyUnitsComboBox.Format += new System.Windows.Forms.ListControlConvertEventHandler(this.FrequencyUnitsComboBox_Format);
+            // 
+            // ScheduleEveryLabel
+            // 
+            this.ScheduleEveryLabel.AutoSize = true;
+            this.ScheduleEveryLabel.Location = new System.Drawing.Point(78, 82);
+            this.ScheduleEveryLabel.Name = "ScheduleEveryLabel";
+            this.ScheduleEveryLabel.Size = new System.Drawing.Size(34, 13);
+            this.ScheduleEveryLabel.TabIndex = 29;
+            this.ScheduleEveryLabel.Text = "Every";
+            // 
+            // FrequencyUpDown
+            // 
+            this.FrequencyUpDown.Location = new System.Drawing.Point(114, 80);
+            this.FrequencyUpDown.Maximum = new decimal(new int[] {
+            59,
+            0,
+            0,
+            0});
+            this.FrequencyUpDown.Name = "FrequencyUpDown";
+            this.FrequencyUpDown.Size = new System.Drawing.Size(49, 20);
+            this.FrequencyUpDown.TabIndex = 28;
+            this.FrequencyUpDown.Value = new decimal(new int[] {
+            5,
+            0,
+            0,
+            0});
+            // 
+            // TimeoutInput
+            // 
+            this.TimeoutInput.Increment = new decimal(new int[] {
+            100,
+            0,
+            0,
+            0});
+            this.TimeoutInput.Location = new System.Drawing.Point(81, 54);
+            this.TimeoutInput.Maximum = new decimal(new int[] {
+            60000,
+            0,
+            0,
+            0});
+            this.TimeoutInput.Minimum = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            this.TimeoutInput.Name = "TimeoutInput";
+            this.TimeoutInput.Size = new System.Drawing.Size(58, 20);
+            this.TimeoutInput.TabIndex = 27;
+            this.TimeoutInput.Value = new decimal(new int[] {
+            5000,
+            0,
+            0,
+            0});
+            // 
+            // PingTimeoutLabel
+            // 
+            this.PingTimeoutLabel.AutoSize = true;
+            this.PingTimeoutLabel.Location = new System.Drawing.Point(5, 56);
+            this.PingTimeoutLabel.Name = "PingTimeoutLabel";
+            this.PingTimeoutLabel.Size = new System.Drawing.Size(70, 13);
+            this.PingTimeoutLabel.TabIndex = 26;
+            this.PingTimeoutLabel.Text = "Timeout (ms):";
+            // 
+            // ScheduleLabel
+            // 
+            this.ScheduleLabel.AutoSize = true;
+            this.ScheduleLabel.Location = new System.Drawing.Point(6, 82);
+            this.ScheduleLabel.Name = "ScheduleLabel";
+            this.ScheduleLabel.Size = new System.Drawing.Size(55, 13);
+            this.ScheduleLabel.TabIndex = 25;
+            this.ScheduleLabel.Text = "Schedule:";
+            // 
+            // NameLabel
+            // 
+            this.NameLabel.AutoSize = true;
+            this.NameLabel.Location = new System.Drawing.Point(6, 31);
+            this.NameLabel.Name = "NameLabel";
+            this.NameLabel.Size = new System.Drawing.Size(38, 13);
+            this.NameLabel.TabIndex = 21;
+            this.NameLabel.Text = "Name:";
+            // 
+            // NameTextBox
+            // 
+            this.NameTextBox.Location = new System.Drawing.Point(81, 28);
+            this.NameTextBox.Name = "NameTextBox";
+            this.NameTextBox.Size = new System.Drawing.Size(181, 20);
+            this.NameTextBox.TabIndex = 22;
+            // 
+            // EnabledCheckBox
+            // 
+            this.EnabledCheckBox.AutoSize = true;
+            this.EnabledCheckBox.Checked = true;
+            this.EnabledCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.EnabledCheckBox.Location = new System.Drawing.Point(268, 31);
+            this.EnabledCheckBox.Name = "EnabledCheckBox";
+            this.EnabledCheckBox.Size = new System.Drawing.Size(65, 17);
+            this.EnabledCheckBox.TabIndex = 23;
+            this.EnabledCheckBox.Text = "Enabled";
+            this.EnabledCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // OkButton
+            // 
+            this.OkButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.OkButton.DialogResult = System.Windows.Forms.DialogResult.OK;
+            this.OkButton.Location = new System.Drawing.Point(382, 397);
+            this.OkButton.Name = "OkButton";
+            this.OkButton.Size = new System.Drawing.Size(75, 23);
+            this.OkButton.TabIndex = 20;
+            this.OkButton.Text = "OK";
+            this.OkButton.UseVisualStyleBackColor = true;
+            this.OkButton.Click += new System.EventHandler(this.OkButton_Click);
+            // 
+            // CancelCheckButton
+            // 
+            this.CancelCheckButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.CancelCheckButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.CancelCheckButton.Location = new System.Drawing.Point(463, 397);
+            this.CancelCheckButton.Name = "CancelCheckButton";
+            this.CancelCheckButton.Size = new System.Drawing.Size(75, 23);
+            this.CancelCheckButton.TabIndex = 19;
+            this.CancelCheckButton.Text = "Cancel";
+            this.CancelCheckButton.UseVisualStyleBackColor = true;
+            this.CancelCheckButton.Click += new System.EventHandler(this.CancelCheckButton_Click);
+            // 
+            // CheckSettingsPanel
+            // 
+            this.CheckSettingsPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.CheckSettingsPanel.Location = new System.Drawing.Point(12, 160);
+            this.CheckSettingsPanel.Name = "CheckSettingsPanel";
+            this.CheckSettingsPanel.Size = new System.Drawing.Size(526, 231);
+            this.CheckSettingsPanel.TabIndex = 27;
+            // 
+            // ResultLabel
+            // 
+            this.ResultLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.ResultLabel.AutoEllipsis = true;
+            this.ResultLabel.Location = new System.Drawing.Point(115, 394);
+            this.ResultLabel.Name = "ResultLabel";
+            this.ResultLabel.Size = new System.Drawing.Size(261, 29);
+            this.ResultLabel.TabIndex = 29;
+            this.ResultLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.ResultLabel.Visible = false;
+            // 
+            // ResultIconPictureBox
+            // 
+            this.ResultIconPictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+            this.ResultIconPictureBox.Location = new System.Drawing.Point(93, 399);
+            this.ResultIconPictureBox.Name = "ResultIconPictureBox";
+            this.ResultIconPictureBox.Size = new System.Drawing.Size(20, 20);
+            this.ResultIconPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.ResultIconPictureBox.TabIndex = 30;
+            this.ResultIconPictureBox.TabStop = false;
+            this.ResultIconPictureBox.Visible = false;
+            // 
+            // CancelRunButton
+            // 
+            this.CancelRunButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+            this.CancelRunButton.Image = global::ServerMonitorApp.Properties.Resources.delete;
+            this.CancelRunButton.Location = new System.Drawing.Point(93, 397);
+            this.CancelRunButton.Name = "CancelRunButton";
+            this.CancelRunButton.Size = new System.Drawing.Size(75, 23);
+            this.CancelRunButton.TabIndex = 28;
+            this.CancelRunButton.Text = "Cancel";
+            this.CancelRunButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.CancelRunButton.UseVisualStyleBackColor = true;
+            this.CancelRunButton.Visible = false;
+            this.CancelRunButton.Click += new System.EventHandler(this.CancelRunButton_Click);
+            // 
+            // RunButton
+            // 
+            this.RunButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+            this.RunButton.Image = global::ServerMonitorApp.Properties.Resources.run;
+            this.RunButton.Location = new System.Drawing.Point(12, 397);
+            this.RunButton.Name = "RunButton";
+            this.RunButton.Size = new System.Drawing.Size(75, 23);
+            this.RunButton.TabIndex = 26;
+            this.RunButton.Text = "Run";
+            this.RunButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.RunButton.UseVisualStyleBackColor = true;
+            this.RunButton.Click += new System.EventHandler(this.RunButton_Click);
+            // 
+            // CheckForm
+            // 
+            this.AcceptButton = this.OkButton;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.CancelButton = this.CancelCheckButton;
+            this.ClientSize = new System.Drawing.Size(550, 432);
+            this.Controls.Add(this.ResultIconPictureBox);
+            this.Controls.Add(this.ResultLabel);
+            this.Controls.Add(this.CancelRunButton);
+            this.Controls.Add(this.CheckSettingsPanel);
+            this.Controls.Add(this.RunButton);
+            this.Controls.Add(this.OkButton);
+            this.Controls.Add(this.CancelCheckButton);
+            this.Controls.Add(this.GeneralGroupBox);
+            this.Controls.Add(this.CheckTypePanel);
+            this.MinimumSize = new System.Drawing.Size(550, 38);
+            this.Name = "CheckForm";
+            this.Text = "CheckForm";
+            this.Load += new System.EventHandler(this.CheckForm_Load);
+            this.Click += new System.EventHandler(this.Control_Click);
+            this.CheckTypePanel.ResumeLayout(false);
+            this.CheckTypePanel.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.TypeHelpPictureBox)).EndInit();
+            this.GeneralGroupBox.ResumeLayout(false);
+            this.GeneralGroupBox.PerformLayout();
+            this.ScheduleAtPanel.ResumeLayout(false);
+            this.ScheduleAtPanel.PerformLayout();
+            this.ScheduleBetweenPanel.ResumeLayout(false);
+            this.ScheduleBetweenPanel.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.FrequencyUpDown)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.TimeoutInput)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.ResultIconPictureBox)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label CheckTypeLabel;
+        private System.Windows.Forms.PictureBox TypeHelpPictureBox;
+        private System.Windows.Forms.ComboBox CheckTypeComboBox;
+        private System.Windows.Forms.Panel CheckTypePanel;
+        private System.Windows.Forms.GroupBox GeneralGroupBox;
+        private System.Windows.Forms.Button OkButton;
+        private System.Windows.Forms.Button CancelCheckButton;
+        private System.Windows.Forms.Label NameLabel;
+        private System.Windows.Forms.TextBox NameTextBox;
+        private System.Windows.Forms.CheckBox EnabledCheckBox;
+        private System.Windows.Forms.Label ScheduleLabel;
+        private System.Windows.Forms.Button RunButton;
+        private System.Windows.Forms.Panel CheckSettingsPanel;
+        private System.Windows.Forms.Button CancelRunButton;
+        private System.Windows.Forms.Label ResultLabel;
+        private System.Windows.Forms.PictureBox ResultIconPictureBox;
+        private System.Windows.Forms.NumericUpDown TimeoutInput;
+        private System.Windows.Forms.Label PingTimeoutLabel;
+        private System.Windows.Forms.ComboBox FrequencyUnitsComboBox;
+        private System.Windows.Forms.Label ScheduleEveryLabel;
+        private System.Windows.Forms.NumericUpDown FrequencyUpDown;
+        private System.Windows.Forms.Panel ScheduleBetweenPanel;
+        private System.Windows.Forms.DateTimePicker StartTimePicker;
+        private System.Windows.Forms.Label ScheduleBetweenLabel;
+        private System.Windows.Forms.Label ScheduleBetweenAndLabel;
+        private System.Windows.Forms.DateTimePicker EndTimePicker;
+        private System.Windows.Forms.Panel ScheduleAtPanel;
+        private System.Windows.Forms.DateTimePicker AtTimePicker;
+        private System.Windows.Forms.Label ScheduleAtLabel;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/CheckForm.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,336 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    public partial class CheckForm : Form
+    {
+        //private static readonly Dictionary<Type, CheckInfo> checkInfo = new Dictionary<Type, CheckInfo>();
+        private readonly List<CheckControl> checkControls = new List<CheckControl>();
+        private bool helpShown;
+        private CancellationTokenSource cancellationTokenSource;
+        private Check workCheck;
+        private CheckControl checkControl;
+        private QuickHelpForm helpForm;
+        private Server server;
+        private ServerMonitor monitor;
+
+        public event EventHandler<HelpLocationChangedEventArgs> HelpLocationChanged;
+
+        public Check Check { get; private set; }
+
+        public int CheckId { get; private set; }
+
+        public Point HelpLocation { get { return TypeHelpPictureBox.PointToScreen(Point.Empty); } }
+
+        public CheckForm(ServerMonitor monitor, Check check)
+        {
+            InitializeComponent();
+
+            Check = check;
+            CheckId = Check.Id;
+            server = Check.Server;
+            this.monitor = monitor;
+        }
+
+        public CheckForm(ServerMonitor monitor, Server server)
+        {
+            InitializeComponent();
+            this.server = server;
+            this.monitor = monitor;
+        }
+
+        private void CheckForm_Load(object sender, EventArgs e)
+        {
+            CheckTypeComboBox.Items.AddRange(Check.CheckTypes);
+            FrequencyUnitsComboBox.DataSource = Enum.GetValues(typeof(FrequencyUnits));
+            Helpers.FormatImageButton(RunButton);
+            Helpers.FormatImageButton(CancelRunButton);
+
+            Move += CheckForm_Move;
+            CheckTypePanel.LocationChanged += CheckTypePanel_LocationChanged;
+            GeneralGroupBox.Click += Control_Click;
+            CheckSettingsPanel.Click += Control_Click;
+
+            CheckTypeComboBox.SelectedItem = Check?.GetType();
+            if (Check != null)
+                LoadCheck(Check);
+        }
+
+        private void CheckTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            ShowCheckControl();
+            workCheck = (Check)Activator.CreateInstance((Type)CheckTypeComboBox.SelectedItem);
+        }
+
+        private void CheckTypeComboBox_Format(object sender, ListControlConvertEventArgs e)
+        {
+            e.Value = Helpers.GetDisplayName((Type)e.ListItem);
+        }
+
+        /*private void showGroupBox(GroupBox groupBox)
+        {
+
+        }*/
+
+        /*private Type GetCheckType()
+        {
+            return (Type)CheckTypeComboBox.SelectedItem;
+        }*/
+
+        private void ShowCheckControl()
+        {
+            IEnumerable<CheckControl> checkControls = CheckSettingsPanel.Controls.OfType<CheckControl>();
+            foreach (CheckControl control in checkControls)
+                control.Hide();
+            Type type = (Type)CheckTypeComboBox.SelectedItem;
+            checkControl = checkControls.FirstOrDefault(c => c.CheckType == type);
+            if (checkControl == null)
+            {
+                checkControl = CheckControl.Create(type);
+                if (checkControl != null)
+                {
+                    checkControl.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
+                    checkControl.Click += Control_Click;
+                    CheckSettingsPanel.Controls.Add(checkControl);
+                }
+            }
+            if (checkControl != null)
+            {
+                CheckSettingsPanel.Height = checkControl.Height;
+                //Height = 230 + checkControl.Height;
+                checkControl.Show();
+            }
+        }
+
+        private void LoadCheck(Check check)
+        {
+            NameTextBox.Text = Check.Name;
+            EnabledCheckBox.Checked = check.Enabled;
+            TimeoutInput.Value = check.Timeout;
+            FrequencyUnitsComboBox.SelectedItem = check.Schedule.Units;
+            FrequencyUpDown.Value = check.Schedule.Frequency;
+            StartTimePicker.Value = new DateTime(1970, 1, 1) + check.Schedule.StartTime;
+            EndTimePicker.Value = new DateTime(1970, 1, 1) + check.Schedule.EndTime;
+            checkControl?.LoadCheck(check);
+        }
+
+        private bool UpdateCheck(Check check, bool saving = true)
+        {
+            string result;
+            if (CheckTypeComboBox.SelectedIndex == -1)
+            {
+                result = "Check type cannot be blank.";
+            }
+            else
+            {
+                check.Id = CheckId;
+                check.Server = server;
+                check.Name = NameTextBox.Text;
+                check.Enabled = EnabledCheckBox.Checked;
+                check.Timeout = (int)TimeoutInput.Value;
+                check.Schedule = new Schedule((FrequencyUnits)FrequencyUnitsComboBox.SelectedItem, (int)FrequencyUpDown.Value, StartTimePicker.Value.TimeOfDay, EndTimePicker.Value.TimeOfDay);
+                checkControl?.UpdateCheck(check);
+                result = check.Validate(saving);
+            }
+            if (!result.IsNullOrEmpty())
+            {
+                MessageBox.Show(result, "Error validating check", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                return false;
+            }
+            return true;
+        }
+
+        private void OkButton_Click(object sender, EventArgs e)
+        {
+            if (!UpdateCheck(workCheck))
+                return;
+            Check = workCheck;
+            server.UpdateCheck(Check);
+            monitor.SaveServers();
+            Close();
+        }
+
+        private void CancelCheckButton_Click(object sender, EventArgs e)
+        {
+            Close();
+        }
+
+        private async void RunButton_Click(object sender, EventArgs e)
+        {
+            if (!UpdateCheck(workCheck, false))
+                return;
+
+            RunButton.Enabled = false;
+            RunButton.Text = "Running";
+            ResultLabel.Visible = ResultIconPictureBox.Visible = false;
+            CancelRunButton.Visible = true;
+
+            // Create a CancellationTokenSource for this execution, and set it to both variables.
+            // localCancellationTokenSource will mantain a reference to the token for this execution,
+            // while cancellationTokenSource might end up referencing a different token if the Cancel
+            // button is clicked (but the Task itself is unable to be cancelled), then Run is clicked
+            // again.
+            CancellationTokenSource localCancellationTokenSource = new CancellationTokenSource();
+            cancellationTokenSource = localCancellationTokenSource;
+            CheckResult result = await workCheck.ExecuteAsync(cancellationTokenSource.Token, false);
+            if (!localCancellationTokenSource.IsCancellationRequested)
+                OnRunFinished(result);
+            localCancellationTokenSource.Dispose();
+            localCancellationTokenSource = null;
+        }
+
+        //private void RunButton_Click(object sender, EventArgs e)
+        //{
+        //    if (!Check.Server.SshClient.IsConnected)
+        //        Check.Server.SshClient.Connect();
+        //    using (Renci.SshNet.SshCommand command = Check.Server.SshClient.CreateCommand("ls"))
+        //    {
+        //        //token.Register(command.CancelAsync);
+        //        //command.BeginExecute(asyncResult =>
+        //        //{
+        //        //    string result = command.EndExecute(asyncResult);
+        //        //    //tcs.SetResult(new CheckResult(this, CheckStatus.Success, result));
+        //        //});
+        //        IAsyncResult iar = command.BeginExecute(z);
+        //        command.EndExecute(iar);
+        //        //string result = command.Execute();
+        //        //System.Console.Write(result);
+        //    }
+        //}
+
+        //private void z(IAsyncResult ar)
+        //{
+        //    throw new NotImplementedException();
+        //}
+
+        private void CancelRunButton_Click(object sender, EventArgs e)
+        {
+            cancellationTokenSource.Cancel();
+            OnRunFinished();
+        }
+
+        /// <summary>Updates the UI after a check is finished running.</summary>
+        /// <param name="result">Result of the check execution. If null, the check was cancelled before it completed.</param>
+        private void OnRunFinished(CheckResult result = null)
+        {
+            RunButton.Enabled = true;
+            RunButton.Text = "Run";
+            CancelRunButton.Visible = false;
+            if (result != null)
+            {
+                ResultLabel.Text = result.Message;
+                //ResultLabel.ForeColor = result.CheckStatus == CheckStatus.Success ? Color.Green : Color.Red;
+                ResultIconPictureBox.Image = result.CheckStatus.GetIcon();
+                ResultLabel.Visible = ResultIconPictureBox.Visible = true;
+            }
+        }
+
+        /*private class CheckInfo
+        {
+            public GroupBox GroupBox { get; private set; }
+
+            public Func<string> ValidateFunction { get; private set; }
+
+            public CheckInfo(GroupBox groupBox, Func<string> validateFunction)
+            {
+                GroupBox = groupBox;
+                ValidateFunction = validateFunction;
+            }
+        }*/
+
+        private string BuildHelpText()
+        {
+            StringBuilder rtf = new StringBuilder(@"{\rtf1\ansi ");
+            foreach (Type checkType in Check.CheckTypes)
+            {
+                // Arguments to AppendLine() must end with a \ for the RichTextBox to render the newline character
+                rtf.Append(@"\b ").Append(Helpers.GetDisplayName(checkType)).AppendLine(@"\b0 \")
+                    .Append(checkType.GetAttribute<DescriptionAttribute>()?.Description ?? "No description provided.").AppendLine(@"\").AppendLine(@"\");
+            }
+            return rtf.ToString().TrimEnd(' ', '\r', '\n', '\\');
+        }
+
+        private void TypeHelpPictureBox_Click(object sender, EventArgs e)
+        {
+            if (helpShown)
+            {
+                helpForm.Close();
+            }
+            else
+            {
+                helpForm = new QuickHelpForm(BuildHelpText());
+                helpForm.FormClosed += QuickHelpForm_FormClosed;
+                helpForm.Show(this);
+                Activate();
+                helpShown = true;
+                OnHelpLocationChanged();
+            }
+        }
+
+        private void QuickHelpForm_FormClosed(object sender, FormClosedEventArgs e)
+        {
+            helpShown = false;
+            helpForm.FormClosed -= QuickHelpForm_FormClosed;
+            helpForm.Dispose();
+        }
+
+        private void OnHelpLocationChanged()
+        {
+            HelpLocationChanged?.Invoke(this, new HelpLocationChangedEventArgs(HelpLocation));
+        }
+
+        private void CheckTypePanel_LocationChanged(object sender, EventArgs e)
+        {
+            OnHelpLocationChanged();
+        }
+
+        private void CheckForm_Move(object sender, EventArgs e)
+        {
+            OnHelpLocationChanged();
+        }
+
+        private void Control_Click(object sender, EventArgs e)
+        {
+            if (helpShown)
+                helpForm.Close();
+        }
+
+        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+        {
+            if (keyData == Keys.Escape && helpShown)
+            {
+                helpForm.Close();
+                return true;
+            }
+            return base.ProcessCmdKey(ref msg, keyData);
+        }
+
+        private void FrequencyUnitsComboBox_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            ScheduleBetweenPanel.Visible = !(ScheduleAtPanel.Visible = FrequencyUnitsComboBox.SelectedIndex == 3);
+        }
+
+        private void FrequencyUnitsComboBox_Format(object sender, ListControlConvertEventArgs e)
+        {
+            e.Value = e.Value.ToString().ToLower() + 's';
+        }
+    }
+
+    public class HelpLocationChangedEventArgs : EventArgs
+    {
+        public Point HelpLocation { get; private set; }
+
+        public HelpLocationChangedEventArgs(Point helpLocation)
+        {
+            HelpLocation = helpLocation;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/CheckForm.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/QuickHelpForm.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,89 @@
+namespace ServerMonitorApp
+{
+    partial class QuickHelpForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.HelpTextBox = new System.Windows.Forms.RichTextBox();
+            this.panel1 = new System.Windows.Forms.Panel();
+            this.panel1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // HelpTextBox
+            // 
+            this.HelpTextBox.BackColor = System.Drawing.SystemColors.Info;
+            this.HelpTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
+            this.HelpTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
+            this.HelpTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.HelpTextBox.Location = new System.Drawing.Point(5, 5);
+            this.HelpTextBox.Name = "HelpTextBox";
+            this.HelpTextBox.ReadOnly = true;
+            this.HelpTextBox.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None;
+            this.HelpTextBox.Size = new System.Drawing.Size(261, 438);
+            this.HelpTextBox.TabIndex = 0;
+            this.HelpTextBox.TabStop = false;
+            this.HelpTextBox.Text = "";
+            this.HelpTextBox.Click += new System.EventHandler(this.Control_Click);
+            this.HelpTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Control_KeyDown);
+            // 
+            // panel1
+            // 
+            this.panel1.BackColor = System.Drawing.SystemColors.Info;
+            this.panel1.Controls.Add(this.HelpTextBox);
+            this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.panel1.Location = new System.Drawing.Point(1, 1);
+            this.panel1.Name = "panel1";
+            this.panel1.Padding = new System.Windows.Forms.Padding(5);
+            this.panel1.Size = new System.Drawing.Size(271, 448);
+            this.panel1.TabIndex = 1;
+            // 
+            // QuickHelpForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.BackColor = System.Drawing.Color.Black;
+            this.ClientSize = new System.Drawing.Size(273, 450);
+            this.Controls.Add(this.panel1);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
+            this.Name = "QuickHelpForm";
+            this.Padding = new System.Windows.Forms.Padding(1);
+            this.ShowInTaskbar = false;
+            this.Text = "QuickHelpForm";
+            this.Load += new System.EventHandler(this.QuickHelpForm_Load);
+            this.Click += new System.EventHandler(this.Control_Click);
+            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Control_KeyDown);
+            this.panel1.ResumeLayout(false);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.RichTextBox HelpTextBox;
+        private System.Windows.Forms.Panel panel1;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/QuickHelpForm.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    public partial class QuickHelpForm : Form
+    {
+        private string Rtf;
+
+        public QuickHelpForm()
+        {
+            InitializeComponent();
+        }
+
+        public QuickHelpForm(string rtf)
+        {
+            InitializeComponent();
+            Rtf = rtf;
+        }
+
+        private void QuickHelpForm_Load(object sender, EventArgs e)
+        {
+            (Owner as CheckForm).HelpLocationChanged += Owner_HelpPositionChanged;
+            HelpTextBox.ContentsResized += HelpTextBox_ContentsResized; // Causes form to resize after Rtf is assigned
+            HelpTextBox.Rtf = Rtf;
+        }
+
+        private void HelpTextBox_ContentsResized(object sender, ContentsResizedEventArgs e)
+        {
+            Height = e.NewRectangle.Height + 12;
+        }
+
+        private void Owner_HelpPositionChanged(object sender, HelpLocationChangedEventArgs e)
+        {
+            Point location = e.HelpLocation;
+            location.Offset(24, 0);
+            Location = location;
+        }
+
+        private void Control_KeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.KeyCode == Keys.Escape)
+                Close();
+        }
+
+        private void Control_Click(object sender, EventArgs e)
+        {
+            Close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/QuickHelpForm.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/ServerForm.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,755 @@
+namespace ServerMonitorApp
+{
+    partial class ServerForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.components = new System.ComponentModel.Container();
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ServerForm));
+            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+            this.TitleLabel = new System.Windows.Forms.Label();
+            this.NameTextBox = new System.Windows.Forms.TextBox();
+            this.NameLabel = new System.Windows.Forms.Label();
+            this.HostTextBox = new System.Windows.Forms.TextBox();
+            this.PortTextBox = new System.Windows.Forms.TextBox();
+            this.HostLabel = new System.Windows.Forms.Label();
+            this.PortLabel = new System.Windows.Forms.Label();
+            this.LoginComboBox = new System.Windows.Forms.ComboBox();
+            this.UsernameLabel = new System.Windows.Forms.Label();
+            this.UsernameTextBox = new System.Windows.Forms.TextBox();
+            this.KeyTextBox = new System.Windows.Forms.TextBox();
+            this.LoginLabel = new System.Windows.Forms.Label();
+            this.KeyBrowseButton = new System.Windows.Forms.Button();
+            this.PasswordTextBox = new System.Windows.Forms.TextBox();
+            this.CheckGrid = new System.Windows.Forms.DataGridView();
+            this.CheckActionsDividerLabel = new System.Windows.Forms.Label();
+            this.RunAllButton = new System.Windows.Forms.Button();
+            this.RunButton = new System.Windows.Forms.Button();
+            this.EditCheckButton = new System.Windows.Forms.Button();
+            this.DeleteCheckButton = new System.Windows.Forms.Button();
+            this.NewCheckButton = new System.Windows.Forms.Button();
+            this.CheckTabControl = new System.Windows.Forms.TabControl();
+            this.CheckTabPage = new System.Windows.Forms.TabPage();
+            this.LogTabPage = new System.Windows.Forms.TabPage();
+            this.LogWarningCheckBox = new System.Windows.Forms.CheckBox();
+            this.LogErrorCheckBox = new System.Windows.Forms.CheckBox();
+            this.LogInformationCheckBox = new System.Windows.Forms.CheckBox();
+            this.LogSuccessCheckBox = new System.Windows.Forms.CheckBox();
+            this.LogCheckLabel = new System.Windows.Forms.Label();
+            this.LogCheckComboBox = new System.Windows.Forms.ComboBox();
+            this.LogGrid = new System.Windows.Forms.DataGridView();
+            this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn();
+            this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.ServerInfoPanel = new System.Windows.Forms.Panel();
+            this.dataGridViewImageColumn2 = new System.Windows.Forms.DataGridViewImageColumn();
+            this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.LastRunTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.EnabledColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn();
+            this.dataGridViewImageColumn3 = new System.Windows.Forms.DataGridViewImageColumn();
+            this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.StatusColumn = new System.Windows.Forms.DataGridViewImageColumn();
+            this.NameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.ScheduleColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.CheckBindingSource = new System.Windows.Forms.BindingSource(this.components);
+            this.LogStatusColumn = new System.Windows.Forms.DataGridViewImageColumn();
+            this.LogNameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.LogMessageColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.LogStartTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.LogEndTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.CheckResultBindingSource = new System.Windows.Forms.BindingSource(this.components);
+            ((System.ComponentModel.ISupportInitialize)(this.CheckGrid)).BeginInit();
+            this.CheckTabControl.SuspendLayout();
+            this.CheckTabPage.SuspendLayout();
+            this.LogTabPage.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.LogGrid)).BeginInit();
+            this.ServerInfoPanel.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.CheckBindingSource)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.CheckResultBindingSource)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // TitleLabel
+            // 
+            this.TitleLabel.Dock = System.Windows.Forms.DockStyle.Top;
+            this.TitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+            this.TitleLabel.Location = new System.Drawing.Point(0, 0);
+            this.TitleLabel.Name = "TitleLabel";
+            this.TitleLabel.Padding = new System.Windows.Forms.Padding(0, 20, 0, 0);
+            this.TitleLabel.Size = new System.Drawing.Size(728, 65);
+            this.TitleLabel.TabIndex = 0;
+            this.TitleLabel.Text = "New Server";
+            this.TitleLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+            // 
+            // NameTextBox
+            // 
+            this.NameTextBox.Location = new System.Drawing.Point(61, 4);
+            this.NameTextBox.Name = "NameTextBox";
+            this.NameTextBox.Size = new System.Drawing.Size(354, 20);
+            this.NameTextBox.TabIndex = 3;
+            this.NameTextBox.TextChanged += new System.EventHandler(this.NameTextBox_TextChanged);
+            // 
+            // NameLabel
+            // 
+            this.NameLabel.AutoSize = true;
+            this.NameLabel.Location = new System.Drawing.Point(6, 7);
+            this.NameLabel.Name = "NameLabel";
+            this.NameLabel.Size = new System.Drawing.Size(35, 13);
+            this.NameLabel.TabIndex = 2;
+            this.NameLabel.Text = "Name";
+            this.NameLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            // 
+            // HostTextBox
+            // 
+            this.HostTextBox.Location = new System.Drawing.Point(61, 30);
+            this.HostTextBox.Name = "HostTextBox";
+            this.HostTextBox.Size = new System.Drawing.Size(354, 20);
+            this.HostTextBox.TabIndex = 6;
+            // 
+            // PortTextBox
+            // 
+            this.PortTextBox.Location = new System.Drawing.Point(61, 56);
+            this.PortTextBox.Name = "PortTextBox";
+            this.PortTextBox.Size = new System.Drawing.Size(57, 20);
+            this.PortTextBox.TabIndex = 7;
+            // 
+            // HostLabel
+            // 
+            this.HostLabel.AutoSize = true;
+            this.HostLabel.Location = new System.Drawing.Point(6, 33);
+            this.HostLabel.Name = "HostLabel";
+            this.HostLabel.Size = new System.Drawing.Size(29, 13);
+            this.HostLabel.TabIndex = 8;
+            this.HostLabel.Text = "Host";
+            this.HostLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            // 
+            // PortLabel
+            // 
+            this.PortLabel.AutoSize = true;
+            this.PortLabel.Location = new System.Drawing.Point(6, 59);
+            this.PortLabel.Name = "PortLabel";
+            this.PortLabel.Size = new System.Drawing.Size(50, 13);
+            this.PortLabel.TabIndex = 9;
+            this.PortLabel.Text = "SSH port";
+            this.PortLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            // 
+            // LoginComboBox
+            // 
+            this.LoginComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.LoginComboBox.FormattingEnabled = true;
+            this.LoginComboBox.Items.AddRange(new object[] {
+            "Private key",
+            "Password"});
+            this.LoginComboBox.Location = new System.Drawing.Point(61, 82);
+            this.LoginComboBox.Name = "LoginComboBox";
+            this.LoginComboBox.Size = new System.Drawing.Size(80, 21);
+            this.LoginComboBox.TabIndex = 10;
+            this.LoginComboBox.SelectedIndexChanged += new System.EventHandler(this.LoginComboBox_SelectedIndexChanged);
+            // 
+            // UsernameLabel
+            // 
+            this.UsernameLabel.AutoSize = true;
+            this.UsernameLabel.Location = new System.Drawing.Point(144, 59);
+            this.UsernameLabel.Name = "UsernameLabel";
+            this.UsernameLabel.Size = new System.Drawing.Size(78, 13);
+            this.UsernameLabel.TabIndex = 11;
+            this.UsernameLabel.Text = "SSH username";
+            this.UsernameLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            // 
+            // UsernameTextBox
+            // 
+            this.UsernameTextBox.Location = new System.Drawing.Point(228, 56);
+            this.UsernameTextBox.Name = "UsernameTextBox";
+            this.UsernameTextBox.Size = new System.Drawing.Size(187, 20);
+            this.UsernameTextBox.TabIndex = 12;
+            // 
+            // KeyTextBox
+            // 
+            this.KeyTextBox.Location = new System.Drawing.Point(143, 82);
+            this.KeyTextBox.Name = "KeyTextBox";
+            this.KeyTextBox.Size = new System.Drawing.Size(202, 20);
+            this.KeyTextBox.TabIndex = 13;
+            // 
+            // LoginLabel
+            // 
+            this.LoginLabel.AutoSize = true;
+            this.LoginLabel.Location = new System.Drawing.Point(6, 85);
+            this.LoginLabel.Name = "LoginLabel";
+            this.LoginLabel.Size = new System.Drawing.Size(54, 13);
+            this.LoginLabel.TabIndex = 14;
+            this.LoginLabel.Text = "SSH login";
+            this.LoginLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            // 
+            // KeyBrowseButton
+            // 
+            this.KeyBrowseButton.Location = new System.Drawing.Point(355, 80);
+            this.KeyBrowseButton.Name = "KeyBrowseButton";
+            this.KeyBrowseButton.Size = new System.Drawing.Size(60, 23);
+            this.KeyBrowseButton.TabIndex = 15;
+            this.KeyBrowseButton.Text = "Browse...";
+            this.KeyBrowseButton.UseVisualStyleBackColor = true;
+            // 
+            // PasswordTextBox
+            // 
+            this.PasswordTextBox.Location = new System.Drawing.Point(147, 82);
+            this.PasswordTextBox.Name = "PasswordTextBox";
+            this.PasswordTextBox.Size = new System.Drawing.Size(268, 20);
+            this.PasswordTextBox.TabIndex = 16;
+            this.PasswordTextBox.UseSystemPasswordChar = true;
+            this.PasswordTextBox.Visible = false;
+            this.PasswordTextBox.TextChanged += new System.EventHandler(this.PasswordTextBox_TextChanged);
+            // 
+            // CheckGrid
+            // 
+            this.CheckGrid.AllowUserToAddRows = false;
+            this.CheckGrid.AllowUserToDeleteRows = false;
+            this.CheckGrid.AllowUserToOrderColumns = true;
+            this.CheckGrid.AllowUserToResizeRows = false;
+            this.CheckGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.CheckGrid.AutoGenerateColumns = false;
+            this.CheckGrid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
+            this.CheckGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+            this.CheckGrid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.StatusColumn,
+            this.NameColumn,
+            this.ScheduleColumn,
+            this.LastRunTimeColumn,
+            this.EnabledColumn});
+            this.CheckGrid.DataSource = this.CheckBindingSource;
+            this.CheckGrid.Location = new System.Drawing.Point(0, 0);
+            this.CheckGrid.Name = "CheckGrid";
+            this.CheckGrid.RowHeadersVisible = false;
+            this.CheckGrid.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
+            this.CheckGrid.ShowEditingIcon = false;
+            this.CheckGrid.Size = new System.Drawing.Size(611, 256);
+            this.CheckGrid.TabIndex = 19;
+            this.CheckGrid.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.CheckGrid_CellClick);
+            this.CheckGrid.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.CheckGrid_CellContentClick);
+            this.CheckGrid.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.CheckGrid_CellDoubleClick);
+            this.CheckGrid.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.CheckGrid_CellFormatting);
+            this.CheckGrid.CellMouseEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.CheckGrid_CellMouseEnter);
+            this.CheckGrid.CellMouseLeave += new System.Windows.Forms.DataGridViewCellEventHandler(this.CheckGrid_CellMouseLeave);
+            this.CheckGrid.SelectionChanged += new System.EventHandler(this.CheckGrid_SelectionChanged);
+            this.CheckGrid.KeyDown += new System.Windows.Forms.KeyEventHandler(this.CheckGrid_KeyDown);
+            // 
+            // CheckActionsDividerLabel
+            // 
+            this.CheckActionsDividerLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.CheckActionsDividerLabel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
+            this.CheckActionsDividerLabel.Location = new System.Drawing.Point(617, 79);
+            this.CheckActionsDividerLabel.Name = "CheckActionsDividerLabel";
+            this.CheckActionsDividerLabel.Size = new System.Drawing.Size(75, 2);
+            this.CheckActionsDividerLabel.TabIndex = 26;
+            // 
+            // RunAllButton
+            // 
+            this.RunAllButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.RunAllButton.Image = ((System.Drawing.Image)(resources.GetObject("RunAllButton.Image")));
+            this.RunAllButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.RunAllButton.Location = new System.Drawing.Point(617, 52);
+            this.RunAllButton.Name = "RunAllButton";
+            this.RunAllButton.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
+            this.RunAllButton.Size = new System.Drawing.Size(75, 23);
+            this.RunAllButton.TabIndex = 25;
+            this.RunAllButton.Text = "Run All";
+            this.RunAllButton.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.RunAllButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.RunAllButton.UseVisualStyleBackColor = true;
+            this.RunAllButton.Click += new System.EventHandler(this.RunAllButton_Click);
+            // 
+            // RunButton
+            // 
+            this.RunButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.RunButton.Image = global::ServerMonitorApp.Properties.Resources.run;
+            this.RunButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.RunButton.Location = new System.Drawing.Point(617, 84);
+            this.RunButton.Name = "RunButton";
+            this.RunButton.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
+            this.RunButton.Size = new System.Drawing.Size(75, 23);
+            this.RunButton.TabIndex = 24;
+            this.RunButton.Text = "Run";
+            this.RunButton.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.RunButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.RunButton.UseVisualStyleBackColor = true;
+            this.RunButton.Click += new System.EventHandler(this.RunButton_Click);
+            // 
+            // EditCheckButton
+            // 
+            this.EditCheckButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.EditCheckButton.Enabled = false;
+            this.EditCheckButton.Image = global::ServerMonitorApp.Properties.Resources.edit;
+            this.EditCheckButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.EditCheckButton.Location = new System.Drawing.Point(617, 113);
+            this.EditCheckButton.Name = "EditCheckButton";
+            this.EditCheckButton.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
+            this.EditCheckButton.Size = new System.Drawing.Size(75, 23);
+            this.EditCheckButton.TabIndex = 23;
+            this.EditCheckButton.Text = "Edit";
+            this.EditCheckButton.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.EditCheckButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.EditCheckButton.UseVisualStyleBackColor = true;
+            this.EditCheckButton.Click += new System.EventHandler(this.EditCheckButton_Click);
+            // 
+            // DeleteCheckButton
+            // 
+            this.DeleteCheckButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.DeleteCheckButton.Enabled = false;
+            this.DeleteCheckButton.Image = global::ServerMonitorApp.Properties.Resources.delete;
+            this.DeleteCheckButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.DeleteCheckButton.Location = new System.Drawing.Point(617, 142);
+            this.DeleteCheckButton.Name = "DeleteCheckButton";
+            this.DeleteCheckButton.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
+            this.DeleteCheckButton.Size = new System.Drawing.Size(75, 23);
+            this.DeleteCheckButton.TabIndex = 21;
+            this.DeleteCheckButton.Text = "Delete";
+            this.DeleteCheckButton.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.DeleteCheckButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.DeleteCheckButton.UseVisualStyleBackColor = true;
+            this.DeleteCheckButton.Click += new System.EventHandler(this.DeleteCheckButton_Click);
+            // 
+            // NewCheckButton
+            // 
+            this.NewCheckButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.NewCheckButton.Image = ((System.Drawing.Image)(resources.GetObject("NewCheckButton.Image")));
+            this.NewCheckButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.NewCheckButton.Location = new System.Drawing.Point(617, 23);
+            this.NewCheckButton.Name = "NewCheckButton";
+            this.NewCheckButton.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
+            this.NewCheckButton.Size = new System.Drawing.Size(75, 23);
+            this.NewCheckButton.TabIndex = 20;
+            this.NewCheckButton.Text = "New";
+            this.NewCheckButton.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            this.NewCheckButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.NewCheckButton.UseVisualStyleBackColor = true;
+            this.NewCheckButton.Click += new System.EventHandler(this.NewCheckButton_Click);
+            // 
+            // CheckTabControl
+            // 
+            this.CheckTabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.CheckTabControl.Controls.Add(this.CheckTabPage);
+            this.CheckTabControl.Controls.Add(this.LogTabPage);
+            this.CheckTabControl.Location = new System.Drawing.Point(15, 173);
+            this.CheckTabControl.Name = "CheckTabControl";
+            this.CheckTabControl.SelectedIndex = 0;
+            this.CheckTabControl.Size = new System.Drawing.Size(701, 282);
+            this.CheckTabControl.TabIndex = 21;
+            this.CheckTabControl.SelectedIndexChanged += new System.EventHandler(this.CheckTabControl_SelectedIndexChanged);
+            // 
+            // CheckTabPage
+            // 
+            this.CheckTabPage.BackColor = System.Drawing.SystemColors.Control;
+            this.CheckTabPage.Controls.Add(this.CheckActionsDividerLabel);
+            this.CheckTabPage.Controls.Add(this.CheckGrid);
+            this.CheckTabPage.Controls.Add(this.RunAllButton);
+            this.CheckTabPage.Controls.Add(this.NewCheckButton);
+            this.CheckTabPage.Controls.Add(this.RunButton);
+            this.CheckTabPage.Controls.Add(this.DeleteCheckButton);
+            this.CheckTabPage.Controls.Add(this.EditCheckButton);
+            this.CheckTabPage.Location = new System.Drawing.Point(4, 22);
+            this.CheckTabPage.Name = "CheckTabPage";
+            this.CheckTabPage.Padding = new System.Windows.Forms.Padding(3);
+            this.CheckTabPage.Size = new System.Drawing.Size(693, 256);
+            this.CheckTabPage.TabIndex = 0;
+            this.CheckTabPage.Text = "Checks";
+            // 
+            // LogTabPage
+            // 
+            this.LogTabPage.BackColor = System.Drawing.SystemColors.Control;
+            this.LogTabPage.Controls.Add(this.LogWarningCheckBox);
+            this.LogTabPage.Controls.Add(this.LogErrorCheckBox);
+            this.LogTabPage.Controls.Add(this.LogInformationCheckBox);
+            this.LogTabPage.Controls.Add(this.LogSuccessCheckBox);
+            this.LogTabPage.Controls.Add(this.LogCheckLabel);
+            this.LogTabPage.Controls.Add(this.LogCheckComboBox);
+            this.LogTabPage.Controls.Add(this.LogGrid);
+            this.LogTabPage.Location = new System.Drawing.Point(4, 22);
+            this.LogTabPage.Name = "LogTabPage";
+            this.LogTabPage.Padding = new System.Windows.Forms.Padding(3);
+            this.LogTabPage.Size = new System.Drawing.Size(693, 256);
+            this.LogTabPage.TabIndex = 1;
+            this.LogTabPage.Text = "Log";
+            // 
+            // LogWarningCheckBox
+            // 
+            this.LogWarningCheckBox.AutoSize = true;
+            this.LogWarningCheckBox.Checked = true;
+            this.LogWarningCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.LogWarningCheckBox.Location = new System.Drawing.Point(342, 8);
+            this.LogWarningCheckBox.Name = "LogWarningCheckBox";
+            this.LogWarningCheckBox.Size = new System.Drawing.Size(66, 17);
+            this.LogWarningCheckBox.TabIndex = 34;
+            this.LogWarningCheckBox.Text = "Warning";
+            this.LogWarningCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // LogErrorCheckBox
+            // 
+            this.LogErrorCheckBox.AutoSize = true;
+            this.LogErrorCheckBox.Checked = true;
+            this.LogErrorCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.LogErrorCheckBox.Location = new System.Drawing.Point(414, 8);
+            this.LogErrorCheckBox.Name = "LogErrorCheckBox";
+            this.LogErrorCheckBox.Size = new System.Drawing.Size(48, 17);
+            this.LogErrorCheckBox.TabIndex = 33;
+            this.LogErrorCheckBox.Text = "Error";
+            this.LogErrorCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // LogInformationCheckBox
+            // 
+            this.LogInformationCheckBox.AutoSize = true;
+            this.LogInformationCheckBox.Checked = true;
+            this.LogInformationCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.LogInformationCheckBox.Location = new System.Drawing.Point(258, 8);
+            this.LogInformationCheckBox.Name = "LogInformationCheckBox";
+            this.LogInformationCheckBox.Size = new System.Drawing.Size(78, 17);
+            this.LogInformationCheckBox.TabIndex = 32;
+            this.LogInformationCheckBox.Text = "Information";
+            this.LogInformationCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // LogSuccessCheckBox
+            // 
+            this.LogSuccessCheckBox.AutoSize = true;
+            this.LogSuccessCheckBox.Checked = true;
+            this.LogSuccessCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.LogSuccessCheckBox.Location = new System.Drawing.Point(185, 8);
+            this.LogSuccessCheckBox.Name = "LogSuccessCheckBox";
+            this.LogSuccessCheckBox.Size = new System.Drawing.Size(67, 17);
+            this.LogSuccessCheckBox.TabIndex = 31;
+            this.LogSuccessCheckBox.Text = "Success";
+            this.LogSuccessCheckBox.UseVisualStyleBackColor = true;
+            // 
+            // LogCheckLabel
+            // 
+            this.LogCheckLabel.AutoSize = true;
+            this.LogCheckLabel.Location = new System.Drawing.Point(6, 9);
+            this.LogCheckLabel.Name = "LogCheckLabel";
+            this.LogCheckLabel.Size = new System.Drawing.Size(38, 13);
+            this.LogCheckLabel.TabIndex = 30;
+            this.LogCheckLabel.Text = "Check";
+            // 
+            // LogCheckComboBox
+            // 
+            this.LogCheckComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.LogCheckComboBox.FormattingEnabled = true;
+            this.LogCheckComboBox.Location = new System.Drawing.Point(50, 6);
+            this.LogCheckComboBox.Name = "LogCheckComboBox";
+            this.LogCheckComboBox.Size = new System.Drawing.Size(121, 21);
+            this.LogCheckComboBox.TabIndex = 29;
+            this.LogCheckComboBox.SelectedIndexChanged += new System.EventHandler(this.FilterChanged);
+            // 
+            // LogGrid
+            // 
+            this.LogGrid.AllowUserToAddRows = false;
+            this.LogGrid.AllowUserToDeleteRows = false;
+            this.LogGrid.AllowUserToOrderColumns = true;
+            this.LogGrid.AllowUserToResizeRows = false;
+            this.LogGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.LogGrid.AutoGenerateColumns = false;
+            this.LogGrid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
+            this.LogGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+            this.LogGrid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.LogStatusColumn,
+            this.LogNameColumn,
+            this.LogMessageColumn,
+            this.LogStartTimeColumn,
+            this.LogEndTimeColumn});
+            this.LogGrid.DataSource = this.CheckResultBindingSource;
+            this.LogGrid.Location = new System.Drawing.Point(0, 33);
+            this.LogGrid.Name = "LogGrid";
+            this.LogGrid.ReadOnly = true;
+            this.LogGrid.RowHeadersVisible = false;
+            this.LogGrid.Size = new System.Drawing.Size(693, 223);
+            this.LogGrid.TabIndex = 28;
+            this.LogGrid.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.LogGrid_CellFormatting);
+            // 
+            // dataGridViewImageColumn1
+            // 
+            this.dataGridViewImageColumn1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.dataGridViewImageColumn1.DataPropertyName = "Status";
+            this.dataGridViewImageColumn1.HeaderText = "";
+            this.dataGridViewImageColumn1.Name = "dataGridViewImageColumn1";
+            this.dataGridViewImageColumn1.ReadOnly = true;
+            this.dataGridViewImageColumn1.ToolTipText = "Last Run Status";
+            this.dataGridViewImageColumn1.Width = 25;
+            // 
+            // dataGridViewTextBoxColumn1
+            // 
+            this.dataGridViewTextBoxColumn1.DataPropertyName = "Schedule";
+            this.dataGridViewTextBoxColumn1.HeaderText = "Schedule";
+            this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
+            this.dataGridViewTextBoxColumn1.ReadOnly = true;
+            this.dataGridViewTextBoxColumn1.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            this.dataGridViewTextBoxColumn1.Width = 192;
+            // 
+            // ServerInfoPanel
+            // 
+            this.ServerInfoPanel.Anchor = System.Windows.Forms.AnchorStyles.Top;
+            this.ServerInfoPanel.Controls.Add(this.NameLabel);
+            this.ServerInfoPanel.Controls.Add(this.NameTextBox);
+            this.ServerInfoPanel.Controls.Add(this.KeyBrowseButton);
+            this.ServerInfoPanel.Controls.Add(this.PasswordTextBox);
+            this.ServerInfoPanel.Controls.Add(this.LoginLabel);
+            this.ServerInfoPanel.Controls.Add(this.PortTextBox);
+            this.ServerInfoPanel.Controls.Add(this.KeyTextBox);
+            this.ServerInfoPanel.Controls.Add(this.HostTextBox);
+            this.ServerInfoPanel.Controls.Add(this.UsernameTextBox);
+            this.ServerInfoPanel.Controls.Add(this.HostLabel);
+            this.ServerInfoPanel.Controls.Add(this.UsernameLabel);
+            this.ServerInfoPanel.Controls.Add(this.PortLabel);
+            this.ServerInfoPanel.Controls.Add(this.LoginComboBox);
+            this.ServerInfoPanel.Location = new System.Drawing.Point(148, 54);
+            this.ServerInfoPanel.Name = "ServerInfoPanel";
+            this.ServerInfoPanel.Size = new System.Drawing.Size(428, 113);
+            this.ServerInfoPanel.TabIndex = 27;
+            // 
+            // dataGridViewImageColumn2
+            // 
+            this.dataGridViewImageColumn2.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.dataGridViewImageColumn2.DataPropertyName = "Status";
+            this.dataGridViewImageColumn2.HeaderText = "";
+            this.dataGridViewImageColumn2.ImageLayout = System.Windows.Forms.DataGridViewImageCellLayout.Zoom;
+            this.dataGridViewImageColumn2.Name = "dataGridViewImageColumn2";
+            this.dataGridViewImageColumn2.ReadOnly = true;
+            this.dataGridViewImageColumn2.ToolTipText = "Last Run Status";
+            this.dataGridViewImageColumn2.Width = 25;
+            // 
+            // dataGridViewTextBoxColumn2
+            // 
+            this.dataGridViewTextBoxColumn2.DataPropertyName = "Schedule";
+            this.dataGridViewTextBoxColumn2.HeaderText = "Schedule";
+            this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2";
+            this.dataGridViewTextBoxColumn2.ReadOnly = true;
+            this.dataGridViewTextBoxColumn2.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            this.dataGridViewTextBoxColumn2.Width = 267;
+            // 
+            // LastRunTimeColumn
+            // 
+            this.LastRunTimeColumn.DataPropertyName = "LastRunTime";
+            this.LastRunTimeColumn.HeaderText = "LastRunTime";
+            this.LastRunTimeColumn.Name = "LastRunTimeColumn";
+            this.LastRunTimeColumn.ReadOnly = true;
+            this.LastRunTimeColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            // 
+            // EnabledColumn
+            // 
+            this.EnabledColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.EnabledColumn.DataPropertyName = "Enabled";
+            this.EnabledColumn.HeaderText = "Enabled";
+            this.EnabledColumn.Name = "EnabledColumn";
+            this.EnabledColumn.ReadOnly = true;
+            this.EnabledColumn.Width = 50;
+            // 
+            // dataGridViewImageColumn3
+            // 
+            this.dataGridViewImageColumn3.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.dataGridViewImageColumn3.DataPropertyName = "Status";
+            this.dataGridViewImageColumn3.HeaderText = "";
+            this.dataGridViewImageColumn3.ImageLayout = System.Windows.Forms.DataGridViewImageCellLayout.Zoom;
+            this.dataGridViewImageColumn3.Name = "dataGridViewImageColumn3";
+            this.dataGridViewImageColumn3.ReadOnly = true;
+            this.dataGridViewImageColumn3.ToolTipText = "Last Run Status";
+            this.dataGridViewImageColumn3.Width = 25;
+            // 
+            // dataGridViewTextBoxColumn3
+            // 
+            this.dataGridViewTextBoxColumn3.DataPropertyName = "Schedule";
+            this.dataGridViewTextBoxColumn3.HeaderText = "Schedule";
+            this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
+            this.dataGridViewTextBoxColumn3.ReadOnly = true;
+            this.dataGridViewTextBoxColumn3.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            this.dataGridViewTextBoxColumn3.Width = 267;
+            // 
+            // StatusColumn
+            // 
+            this.StatusColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.StatusColumn.DataPropertyName = "Status";
+            this.StatusColumn.HeaderText = "";
+            this.StatusColumn.ImageLayout = System.Windows.Forms.DataGridViewImageCellLayout.Zoom;
+            this.StatusColumn.Name = "StatusColumn";
+            this.StatusColumn.ReadOnly = true;
+            this.StatusColumn.ToolTipText = "Last Run Status";
+            this.StatusColumn.Width = 25;
+            // 
+            // NameColumn
+            // 
+            this.NameColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
+            this.NameColumn.DataPropertyName = "Name";
+            this.NameColumn.HeaderText = "Name";
+            this.NameColumn.Name = "NameColumn";
+            this.NameColumn.ReadOnly = true;
+            this.NameColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            this.NameColumn.Width = 41;
+            // 
+            // ScheduleColumn
+            // 
+            this.ScheduleColumn.DataPropertyName = "Schedule";
+            this.ScheduleColumn.HeaderText = "Schedule";
+            this.ScheduleColumn.Name = "ScheduleColumn";
+            this.ScheduleColumn.ReadOnly = true;
+            this.ScheduleColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            // 
+            // CheckBindingSource
+            // 
+            this.CheckBindingSource.DataSource = typeof(ServerMonitorApp.Check);
+            this.CheckBindingSource.ListChanged += new System.ComponentModel.ListChangedEventHandler(this.CheckBindingSource_ListChanged);
+            // 
+            // LogStatusColumn
+            // 
+            this.LogStatusColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.LogStatusColumn.DataPropertyName = "CheckStatus";
+            this.LogStatusColumn.HeaderText = "";
+            this.LogStatusColumn.ImageLayout = System.Windows.Forms.DataGridViewImageCellLayout.Zoom;
+            this.LogStatusColumn.Name = "LogStatusColumn";
+            this.LogStatusColumn.ReadOnly = true;
+            this.LogStatusColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+            this.LogStatusColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
+            this.LogStatusColumn.ToolTipText = "Status";
+            this.LogStatusColumn.Width = 25;
+            // 
+            // LogNameColumn
+            // 
+            this.LogNameColumn.DataPropertyName = "Check";
+            this.LogNameColumn.HeaderText = "Check";
+            this.LogNameColumn.Name = "LogNameColumn";
+            this.LogNameColumn.ReadOnly = true;
+            // 
+            // LogMessageColumn
+            // 
+            this.LogMessageColumn.DataPropertyName = "Message";
+            this.LogMessageColumn.HeaderText = "Message";
+            this.LogMessageColumn.Name = "LogMessageColumn";
+            this.LogMessageColumn.ReadOnly = true;
+            // 
+            // LogStartTimeColumn
+            // 
+            this.LogStartTimeColumn.DataPropertyName = "StartTime";
+            dataGridViewCellStyle1.Format = "G";
+            dataGridViewCellStyle1.NullValue = null;
+            this.LogStartTimeColumn.DefaultCellStyle = dataGridViewCellStyle1;
+            this.LogStartTimeColumn.HeaderText = "StartTime";
+            this.LogStartTimeColumn.Name = "LogStartTimeColumn";
+            this.LogStartTimeColumn.ReadOnly = true;
+            // 
+            // LogEndTimeColumn
+            // 
+            this.LogEndTimeColumn.DataPropertyName = "EndTime";
+            dataGridViewCellStyle2.Format = "G";
+            dataGridViewCellStyle2.NullValue = null;
+            this.LogEndTimeColumn.DefaultCellStyle = dataGridViewCellStyle2;
+            this.LogEndTimeColumn.HeaderText = "EndTime";
+            this.LogEndTimeColumn.Name = "LogEndTimeColumn";
+            this.LogEndTimeColumn.ReadOnly = true;
+            // 
+            // CheckResultBindingSource
+            // 
+            this.CheckResultBindingSource.DataSource = typeof(ServerMonitorApp.CheckResult);
+            // 
+            // ServerForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(728, 467);
+            this.Controls.Add(this.ServerInfoPanel);
+            this.Controls.Add(this.CheckTabControl);
+            this.Controls.Add(this.TitleLabel);
+            this.MinimumSize = new System.Drawing.Size(515, 38);
+            this.Name = "ServerForm";
+            this.Text = "New Server";
+            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ServerForm_FormClosing);
+            this.Load += new System.EventHandler(this.ServerForm_Load);
+            ((System.ComponentModel.ISupportInitialize)(this.CheckGrid)).EndInit();
+            this.CheckTabControl.ResumeLayout(false);
+            this.CheckTabPage.ResumeLayout(false);
+            this.LogTabPage.ResumeLayout(false);
+            this.LogTabPage.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.LogGrid)).EndInit();
+            this.ServerInfoPanel.ResumeLayout(false);
+            this.ServerInfoPanel.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.CheckBindingSource)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.CheckResultBindingSource)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label TitleLabel;
+        private System.Windows.Forms.TextBox NameTextBox;
+        private System.Windows.Forms.Label NameLabel;
+        private System.Windows.Forms.TextBox HostTextBox;
+        private System.Windows.Forms.TextBox PortTextBox;
+        private System.Windows.Forms.Label HostLabel;
+        private System.Windows.Forms.Label PortLabel;
+        private System.Windows.Forms.ComboBox LoginComboBox;
+        private System.Windows.Forms.Label UsernameLabel;
+        private System.Windows.Forms.TextBox UsernameTextBox;
+        private System.Windows.Forms.TextBox KeyTextBox;
+        private System.Windows.Forms.Label LoginLabel;
+        private System.Windows.Forms.Button KeyBrowseButton;
+        private System.Windows.Forms.TextBox PasswordTextBox;
+        private System.Windows.Forms.DataGridView CheckGrid;
+        private System.Windows.Forms.BindingSource CheckBindingSource;
+        private System.Windows.Forms.Button EditCheckButton;
+        private System.Windows.Forms.Button DeleteCheckButton;
+        private System.Windows.Forms.Button NewCheckButton;
+        private System.Windows.Forms.Label CheckActionsDividerLabel;
+        private System.Windows.Forms.Button RunAllButton;
+        private System.Windows.Forms.Button RunButton;
+        private System.Windows.Forms.TabControl CheckTabControl;
+        private System.Windows.Forms.TabPage CheckTabPage;
+        private System.Windows.Forms.TabPage LogTabPage;
+        private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1;
+        private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
+        private System.Windows.Forms.Panel ServerInfoPanel;
+        private System.Windows.Forms.BindingSource CheckResultBindingSource;
+        private System.Windows.Forms.DataGridView LogGrid;
+        private System.Windows.Forms.Label LogCheckLabel;
+        private System.Windows.Forms.ComboBox LogCheckComboBox;
+        private System.Windows.Forms.CheckBox LogWarningCheckBox;
+        private System.Windows.Forms.CheckBox LogErrorCheckBox;
+        private System.Windows.Forms.CheckBox LogInformationCheckBox;
+        private System.Windows.Forms.CheckBox LogSuccessCheckBox;
+        private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn2;
+        private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
+        private System.Windows.Forms.DataGridViewImageColumn LogStatusColumn;
+        private System.Windows.Forms.DataGridViewTextBoxColumn LogNameColumn;
+        private System.Windows.Forms.DataGridViewTextBoxColumn LogMessageColumn;
+        private System.Windows.Forms.DataGridViewTextBoxColumn LogStartTimeColumn;
+        private System.Windows.Forms.DataGridViewTextBoxColumn LogEndTimeColumn;
+        private System.Windows.Forms.DataGridViewImageColumn StatusColumn;
+        private System.Windows.Forms.DataGridViewTextBoxColumn NameColumn;
+        private System.Windows.Forms.DataGridViewTextBoxColumn ScheduleColumn;
+        private System.Windows.Forms.DataGridViewTextBoxColumn LastRunTimeColumn;
+        private System.Windows.Forms.DataGridViewCheckBoxColumn EnabledColumn;
+        private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn3;
+        private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/ServerForm.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,362 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    public partial class ServerForm : Form
+    {
+        private bool isNewServer;
+        private bool changedPassword;
+        private DateTime lastSaveTime;
+        private ServerMonitor monitor;
+        private BindingList<CheckResult> logResults, logResultsFiltered;
+        private CheckStatus[] filteredStatuses;
+        private readonly Dictionary<int, CheckForm> checkForms = new Dictionary<int, CheckForm>();
+        private readonly Dictionary<CheckBox, CheckStatus> filterChecks;
+
+        public Server Server { get; private set; }
+
+        private IEnumerable<Check> SelectedChecks { get { return CheckGrid.SelectedRows.Cast<DataGridViewRow>().Select(r => r.DataBoundItem).Cast<Check>(); } }
+
+        private Check SelectedCheck { get { return SelectedChecks.FirstOrDefault(); } }
+
+        public ServerForm(ServerMonitor monitor, Server server, bool isNewServer = false)
+        {
+            InitializeComponent();
+            this.monitor = monitor;
+            this.isNewServer = isNewServer;
+            Server = server;
+            filterChecks = new Dictionary<CheckBox, CheckStatus>
+            {
+                { LogSuccessCheckBox, CheckStatus.Success },
+                { LogInformationCheckBox, CheckStatus.Information },
+                { LogWarningCheckBox, CheckStatus.Warning },
+                { LogErrorCheckBox, CheckStatus.Error },
+            };
+        }
+
+        private void ServerForm_Load(object sender, EventArgs e)
+        {
+            CheckBindingSource.DataSource = Server.Checks;
+            monitor.CheckStatusChanged += Monitor_CheckStatusChanged;
+            CheckGrid.ClearSelection();
+            if (isNewServer)
+            {
+                LoginComboBox.SelectedIndex = 0;
+            }
+            else
+            {
+                Text = Server.Name;
+                TitleLabel.Text = Server.Name;
+                NameTextBox.Text = Server.Name;
+                HostTextBox.Text = Server.Host;
+                PortTextBox.Text = Server.Port.ToString();
+                UsernameTextBox.Text = Server.Username;
+                PasswordTextBox.Text = "********************";
+                LoginComboBox.SelectedIndex = (int)Server.LoginType;
+                KeyTextBox.Text = Server.KeyFile;
+                changedPassword = false;
+            }
+
+            BindChangeListeners();
+            FormatImageButtons();
+            UpdateCheckButtons();
+        }
+
+        private void Monitor_CheckStatusChanged(object sender, CheckStatusChangedEventArgs e)
+        {
+            CheckGrid.Refresh();
+            if (e.CheckResult != null && logResults != null)
+            {
+                logResults.Insert(0, e.CheckResult);
+                if (logResultsFiltered != null && MatchesFilter(e.CheckResult))
+                    logResultsFiltered.Insert(0, e.CheckResult);
+            }
+        }
+
+        /// <summary>Update the server with the current input values</summary>
+        /// <param name="forceSave">
+        /// If true, immediately update the config file on disk.
+        /// If false, only update the config file if it has not been recently updated.
+        /// </param>
+        private void UpdateServer(bool forceSave = true)
+        {
+            Server.Name = NameTextBox.Text;
+            Server.Host = HostTextBox.Text.Trim();
+            Server.Port = int.TryParse(PortTextBox.Text, out int port) ? port : 0;
+            Server.Username = UsernameTextBox.Text.Trim();
+            Server.LoginType = (LoginType)LoginComboBox.SelectedIndex;
+            Server.KeyFile = KeyTextBox.Text.Trim();
+            if (changedPassword)
+                Server.Password = PasswordTextBox.Text;
+
+            if (forceSave || lastSaveTime < DateTime.Now.AddSeconds(-5))
+            {
+                lastSaveTime = DateTime.Now;
+                monitor.SaveServers();
+            }
+        }
+
+        private void NameTextBox_TextChanged(object sender, EventArgs e)
+        {
+            Text = NameTextBox.Text;
+            TitleLabel.Text = NameTextBox.Text;
+        }
+
+        private void LoginComboBox_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            if (LoginComboBox.SelectedIndex == (int)LoginType.PrivateKey)
+            {
+                PasswordTextBox.Visible = false;
+                KeyTextBox.Visible = true;
+                KeyBrowseButton.Visible = true;
+            }
+            else
+            {
+                PasswordTextBox.Visible = true;
+                KeyTextBox.Visible = false;
+                KeyBrowseButton.Visible = false;
+            }
+        }
+
+        private void NewCheckButton_Click(object sender, EventArgs e)
+        {
+            ShowCheckForm(null);
+        }
+
+        private void EditCheckButton_Click(object sender, EventArgs e)
+        {
+            EditSelectedCheck();
+        }
+
+        private void RunButton_Click(object sender, EventArgs e)
+        {
+            ExecuteChecks(SelectedChecks);
+        }
+
+        private void RunAllButton_Click(object sender, EventArgs e)
+        {
+            ExecuteChecks(Server.Checks);
+        }
+
+        private void ShowCheckForm(Check check)
+        {
+            if (check != null)
+            {
+                if (!checkForms.TryGetValue(check.Id, out CheckForm form))
+                {
+                    form = new CheckForm(monitor, check);
+                    checkForms[check.Id] = form;
+                    form.FormClosing += CheckForm_FormClosing;
+                    form.Show();
+                }
+                else
+                {
+                    form.Activate();
+                }
+            }
+            else
+            {
+                new CheckForm(monitor, Server).Show();
+            }
+        }
+
+        private void EditSelectedCheck()
+        {
+            ShowCheckForm(SelectedCheck);
+        }
+
+        private void DeleteSelectedChecks()
+        {
+            if (Properties.Settings.Default.ConfirmDeleteCheck)
+            {
+                string message = "Delete " + (SelectedChecks.Count() == 1 ? "\"" + SelectedCheck.Name + "\"" : "selected checks") + "?";
+                using (CheckBoxDialog dialog = new CheckBoxDialog { Message = message })
+                {
+                    DialogResult result = dialog.ShowDialog();
+                    if (dialog.Checked && result == DialogResult.OK)
+                    {
+                        Properties.Settings.Default.ConfirmDeleteCheck = false;
+                        Properties.Settings.Default.Save();
+                    }
+                    if (result != DialogResult.OK)
+                        return;
+                }
+            }
+            foreach (Check check in SelectedChecks)
+                Server.DeleteCheck(check);
+        }
+
+        private async void ExecuteChecks(IEnumerable<Check> checks)
+        {
+            await Task.WhenAll(checks.Select(c => monitor.ExecuteCheckAsync(c)));
+        }
+
+        private void ShowLog(Check check)
+        {
+            LogCheckComboBox.SelectedItem = check;
+            CheckTabControl.SelectedTab = LogTabPage;
+        }
+
+        private void UpdateCheckButtons()
+        {
+            RunAllButton.Enabled = CheckGrid.RowCount > 0;
+            RunButton.Enabled = DeleteCheckButton.Enabled = CheckGrid.SelectedRows.Count > 0;
+            EditCheckButton.Enabled = CheckGrid.SelectedRows.Count == 1;
+        }
+
+        private void FormatImageButtons()
+        {
+            Button[] buttons = new Button[] { NewCheckButton, RunAllButton, RunButton, EditCheckButton, DeleteCheckButton };
+            foreach (Button button in buttons)
+                Helpers.FormatImageButton(button);
+        }
+
+        private void BindChangeListeners()
+        {
+            foreach (Control control in ServerInfoPanel.Controls.OfType<Control>().Where(c => c is TextBox))
+                control.TextChanged += (sender, e) => UpdateServer(false);
+            foreach (Control control in Controls.OfType<Control>().Where(c => c is ComboBox))
+                control.TextChanged += (sender, e) => UpdateServer();
+            foreach (CheckBox control in LogTabPage.Controls.OfType<CheckBox>())
+                control.CheckedChanged += FilterChanged;
+        }
+
+        private void CheckForm_FormClosing(object sender, FormClosingEventArgs e)
+        {
+            CheckForm form = (CheckForm)sender;
+            form.FormClosing -= CheckForm_FormClosing;
+            checkForms.Remove(form.CheckId);
+            if (form.DialogResult == DialogResult.OK)
+                CheckGrid.Refresh();
+        }
+
+        private void CheckGrid_SelectionChanged(object sender, EventArgs e)
+        {
+            UpdateCheckButtons();
+        }
+
+        private void PasswordTextBox_TextChanged(object sender, EventArgs e)
+        {
+            changedPassword = true;
+        }
+
+        private void ServerForm_FormClosing(object sender, FormClosingEventArgs e)
+        {
+            UpdateServer();
+        }
+
+        private void CheckGrid_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
+        {
+            EditSelectedCheck();
+        }
+
+        private void CheckGrid_KeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.KeyCode == Keys.Enter)
+            {
+                EditSelectedCheck();
+                e.Handled = true;
+            }
+        }
+
+        private void DeleteCheckButton_Click(object sender, EventArgs e)
+        {
+            DeleteSelectedChecks();
+            UpdateServer();
+        }
+
+        private void CheckGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
+        {
+            if (e.ColumnIndex == StatusColumn.Index)
+            {
+                e.Value = ((CheckStatus)e.Value).GetIcon();
+            }
+        }
+
+        private void LogGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
+        {
+            if (e.ColumnIndex == LogStatusColumn.Index)
+            {
+                e.Value = ((CheckStatus)e.Value).GetIcon();
+            }
+        }
+
+        private void CheckBindingSource_ListChanged(object sender, ListChangedEventArgs e)
+        {
+            if (Server?.Checks != null)
+            {
+                //TODO this might result in the selection being reset to all (if a new check is added, for example.) Restore selected index.
+                LogCheckComboBox.Items.Clear();
+                LogCheckComboBox.Items.Add("(All)");
+                LogCheckComboBox.Items.AddRange(Server.Checks.ToArray());
+                LogCheckComboBox.SelectedIndex = 0;
+            }
+        }
+
+        private void CheckTabControl_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            if (logResults == null && CheckTabControl.SelectedTab == LogTabPage)
+            {
+                logResults = new BindingList<CheckResult>(monitor.GetLog(Server));
+                LogGrid.DataSource = logResults;
+            }
+        }
+
+        private void CheckGrid_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
+        {
+            if (e.ColumnIndex == StatusColumn.Index)
+                CheckGrid.Cursor = Cursors.Hand;
+        }
+
+        private void CheckGrid_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
+        {
+            if (e.ColumnIndex == StatusColumn.Index)
+                CheckGrid.Cursor = Cursors.Default;
+        }
+
+        private void CheckGrid_CellClick(object sender, DataGridViewCellEventArgs e)
+        {
+            if (e.ColumnIndex == StatusColumn.Index)
+                ShowLog((Check)CheckBindingSource[e.RowIndex]);
+        }
+
+        private void CheckGrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
+        {
+            // The status column is set to read-only, and updates are manually done here.
+            // Otherwise, the change doesn't take effect until the cell loses focus.
+            if (e.ColumnIndex == EnabledColumn.Index)
+            {
+                Check check = (Check)CheckBindingSource[e.RowIndex];
+                check.Enabled = !(bool)CheckGrid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value; ;
+                Server.UpdateCheck(check);
+            }
+        }
+
+        private void FilterChanged(object sender, EventArgs e)
+        {
+            filteredStatuses = filterChecks.Where(fc => fc.Key.Checked).Select(fc => fc.Value).ToArray();
+            if (filteredStatuses.Length == filterChecks.Count && LogCheckComboBox.SelectedIndex == 0) {
+                LogGrid.DataSource = logResults;
+                logResultsFiltered = null;
+            }
+            else
+            {
+                logResultsFiltered = new BindingList<CheckResult>(logResults.Where(result => MatchesFilter(result)).ToList());
+                LogGrid.DataSource = logResultsFiltered;
+            }
+        }
+
+        private bool MatchesFilter(CheckResult result)
+        {
+            return filteredStatuses.Contains(result.CheckStatus) && (LogCheckComboBox.SelectedIndex == 0 || LogCheckComboBox.SelectedItem == result.Check);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/ServerForm.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="StatusColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="ScheduleColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="LastRunTimeColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="EnabledColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="CheckBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="RunAllButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADrwAAA68AZW8ckkAAAAWdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjFM78X/AAAF
+        fklEQVR4XtWbP8gdRRTFlyAiQSSEECxCipAihBBSBElhkULEQkKwTJ1aUomIxYqFWIiIhVhYWImlWIqV
+        WImFiIWFiIiIhBBERET0ec/e2e+bOXPe29k/8/4c+IHxzdmdc9++3buz+zVN27xhnG6maq6/olar1SAI
+        8Kvxi/Gc20Zqrr+iVGAGAX4zVsa/xvvG424v1Fx/RanADALcDwF6fjCe9k0UaK6/olRgBgEeRJPvwbf5
+        pvGob8rUNlfDf6XKvdq/A6nADAI8DJNWfGPo4L20r2fYX1EqMIMAv0cTVvxtvGQ84pslaU/MZn9FqcAM
+        AvwRJjrEF8YF33QkPVah/RWlAjNjCgAw9q5vPqht7hibfkYxub+iVGBmbAF6PjWe9N2Y2ua88Xn4rITU
+        X0kqMIPJ/xlNbAy4/L3guzK1zQnjnvGXocYzqb+CVGAGEy+d8Do+NJ7wXZra5orxdfishNS/oFRgZokC
+        gB+Nm75bE67/fo/wj6HGM6l/IanAzFIFAGh+3jIe892b0BF6Z6jGM7l/plRgBpPEdVpNaCrfGtd8CiYc
+        3m3zQfishNQ/QyowU6MAANt82ThuftrmltHfOA2R+ydIBWZqFaAnbX7a5qzxSfishFnNkwrM1C4AUM3T
+        3fD/1XhmcvOkAjOYTOmZei745s/61Ez4Zv0bVmMVqb9AKjCzzQIAnANu+fRM+I37b730KEz9A1KBmW0X
+        oAdXhbh5umbg7K/GKlL/GqnAzK4KANKVI1z/vQ9AP6DGM4MrTyows8sCAOwbHWO88nTTQGeoxjO5P5IK
+        zOy6AD24d7ji0zZ584R7BDVWkfqDVGBmXwoA0JLjbvKET9+Eu8V80XUdmV8FZrCT0t/ctsC6wnmPYMK6
+        ga8fqLGKI78KzGAHaiO7BitMdxDiSOOap86vAjP7WoCej4zjx27ePI05Gjq/Ct6z7wUAPxvPeAVM3jyN
+        ORo6vwoPDqEAAOept414rWFTK/0f/fvIf6gF6EkftPjRgGcOpa105z/kAgB1ubwaPiuh8x9yAfBtv2rE
+        BThl9I0TH/5M5z/UAnxvPOWpg3CCbJufwudgUwE6fx/+0AqQvnvQNieNdw1u5NYVoPPH4Q+lAFgDeN5T
+        B+Eo8G9TjecCdH4O3rPvBeBVJDxveM0ovX/p/Cp4z74WQK0jXjK+Cp8P0flVYAYb3reboS+Ni57a5M8c
+        XzRKn2Ee+VVgBjvYl9thXJ5eMeJnCXjq/JmhxjOZXwVm9qUA3xnXfdpB4947yP0mFZjZdQHw83vHOOlT
+        NrXNGeNjQ41ncn8kFZjZZQFwl/asTzUIL1v6S5dqPJP7SSows6sC8H0+mpr3jNITcupfIxWY2XYB8E4i
+        r/TcMNY1NUzu3yAVmNlmAXA2P+dTM3lT87pRuv/UXyAVmMFESu+lp4LrN67j8d3bZaP0NZrcXygVmKld
+        AIS87NMxjW9qUv9IqcBMrQLgsMbhHT/xOWeUNjW5f4JUYKZGAXBCu+FTCPKmRr2Urcj9E6UCM5jcki9J
+        4VIWNzWnDVyy1Hgm98+UCswsVYD8L0bQpHizosYzVf7iRAVmligA2tYzvkuTNzVoT0ubmtS/oFRgBhOe
+        +qqsenx13cCNiRrP5P6FpQIzmHTpE5YYnM3jB5hYn8etaOkJNfVXkgrMjC2AamouGliEUOOZyU3NFKnA
+        zJgCYDnqkm860lx/RanADAIM/ckMmhIsROqmRHtiNvsrSgVmEGDTqkv3IME3t0ba1zPsrygVmEGAdX82
+        h4cOw01J7h3nrygVmEEAfgcHj5mOn8cPKfWO91eUCswgQPwGNx4wnnJ7odLw4/0VpQIzCIA/fkYRbrtt
+        pDz4dH9FqcAMAuDNiVEvISea668oFThl1fwP+YoBcjB9tUEAAAAASUVORK5CYII=
+</value>
+  </data>
+  <data name="NewCheckButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADrwAAA68AZW8ckkAAAAWdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjFM78X/AAAB
+        M0lEQVR4Xu2YMW7DMBAE9fT8zYWf4SLPSMFICIsMcYUZLVKQs8AUK4AH75Q+WmtbU37cifLjTrCk83G8
+        TtoNPvulaLAZJR0FKEABw6BZFNAvRYPNKOkoQAEKGAbNooB+KRpsRklHAQpQwDBoFgX0S9FgM0o6ClCA
+        AoZBs/yzgJ8fnOTrpBr2Ltf76u4dHtiMUv+I1XhhM0r9YDUUgM0o9YPVUAA2o9QPVkMB2IxSP1gNBWAz
+        Sv1gNRSAzSj1g9VQADaj1A9WQwHYjHL9AZEl8X9AdfcOT2xGSee0fVINexf/EuuXosFmlHQUoAAFDINm
+        UUC/FA02o6SjAAUoYBg0iwL6pWiwGSUdBShAAcOgWRTQL0WDzSjpKEAB2wt4nFwS/sqzX4oGm3+XHSk/
+        7kT5cR/a8Q2f5UrwRSt+lQAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <metadata name="CheckResultBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>178, 17</value>
+  </metadata>
+  <metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>50</value>
+  </metadata>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/ServerSummaryForm.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,74 @@
+namespace ServerMonitorApp
+{
+    partial class ServerSummaryForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.ServerPanel = new System.Windows.Forms.FlowLayoutPanel();
+            this.NewServerButton = new System.Windows.Forms.Button();
+            this.SuspendLayout();
+            // 
+            // ServerPanel
+            // 
+            this.ServerPanel.Location = new System.Drawing.Point(12, 12);
+            this.ServerPanel.Name = "ServerPanel";
+            this.ServerPanel.Size = new System.Drawing.Size(560, 308);
+            this.ServerPanel.TabIndex = 0;
+            // 
+            // NewServerButton
+            // 
+            this.NewServerButton.Image = global::ServerMonitorApp.Properties.Resources.add;
+            this.NewServerButton.Location = new System.Drawing.Point(12, 409);
+            this.NewServerButton.Name = "NewServerButton";
+            this.NewServerButton.Size = new System.Drawing.Size(96, 29);
+            this.NewServerButton.TabIndex = 0;
+            this.NewServerButton.Text = "New server";
+            this.NewServerButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.NewServerButton.UseVisualStyleBackColor = true;
+            this.NewServerButton.Click += new System.EventHandler(this.NewServerButton_Click);
+            // 
+            // ServerSummaryForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(800, 450);
+            this.Controls.Add(this.NewServerButton);
+            this.Controls.Add(this.ServerPanel);
+            this.Name = "ServerSummaryForm";
+            this.Text = "Form1";
+            this.Load += new System.EventHandler(this.ServerSummaryForm_Load);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.FlowLayoutPanel ServerPanel;
+        private System.Windows.Forms.Button NewServerButton;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/ServerSummaryForm.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    public partial class ServerSummaryForm : Form
+    {
+        private readonly Dictionary<Server, ServerForm> serverForms = new Dictionary<Server, ServerForm>();
+        private ServerMonitor monitor;
+
+        public ServerSummaryForm()
+        {
+            InitializeComponent();
+        }
+
+        private void ServerSummaryForm_Load(object sender, EventArgs e)
+        {
+            Helpers.FormatImageButton(NewServerButton);
+            monitor = new ServerMonitor();
+            while (true)
+            {
+                try
+                {
+                    monitor.LoadServers();
+                    break;
+                }
+                catch (Exception ex)
+                {
+                    DialogResult result = MessageBox.Show("Could not load servers. Please fix or delete the file " + monitor.ConfigFile + Environment.NewLine + Environment.NewLine
+                        + "Error details:" + Environment.NewLine + ex.GetAllMessages(),
+                        "Error loading servers", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
+                    if (result == DialogResult.Cancel)
+                    {
+                        Environment.Exit(1);
+                    }
+                }
+            }
+            RefreshDisplay();
+        }
+
+        private void RefreshDisplay()
+        {
+            ServerPanel.Controls.Clear();
+            foreach (Server server in monitor.Servers)
+            {
+                ServerSummaryControl control = new ServerSummaryControl(server);
+                control.Click += ServerSummaryControl_Click;
+                ServerPanel.Controls.Add(control);
+            }
+        }
+
+        private void ShowServerForm(Server server)
+        {
+            bool isNewServer = false;
+            if (server == null)
+            {
+                server = new Server();
+                monitor.AddServer(server);
+                isNewServer = true;
+            }
+            if (serverForms.TryGetValue(server, out ServerForm form))
+            {
+                form.Activate();
+            }
+            else
+            {
+                form = new ServerForm(monitor, server, isNewServer);
+                serverForms[server] = form;
+                form.FormClosing += ServerForm_FormClosing;
+                form.Show();
+            }
+        }
+
+        private void ServerSummaryControl_Click(object sender, EventArgs e)
+        {
+            ShowServerForm(((ServerSummaryControl)sender).Server);
+        }
+
+        private void ServerForm_FormClosing(object sender, FormClosingEventArgs e)
+        {
+            ServerForm form = (ServerForm)sender;
+            form.FormClosing -= ServerForm_FormClosing;
+            serverForms.Remove(form.Server);
+            RefreshDisplay();
+        }
+
+        private void NewServerButton_Click(object sender, EventArgs e)
+        {
+            ShowServerForm(null);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/ServerSummaryForm.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Helpers.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,56 @@
+using ServerMonitorApp.Properties;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    static class Helpers
+    {
+        public static void FormatImageButton(Button button)
+        {
+            button.Image = new Bitmap(button.Image, button.Height - 8, button.Height - 8);
+        }
+
+        public static string GetAllMessages(this Exception ex)
+        {
+            return "\t" + (ex.InnerException == null ? ex.Message : ex.Message + Environment.NewLine + ex.InnerException.GetAllMessages());
+        }
+
+        public static string GetDisplayName(Type type)
+        {
+            return type?.GetAttribute<DisplayNameAttribute>()?.DisplayName ?? type?.Name ?? "null";
+        }
+
+        public static bool IsNullOrEmpty(this string aString)
+        {
+            return aString == null || aString.Trim() == string.Empty;
+        }
+
+        public static T GetAttribute<T>(this Type type) where T : Attribute
+        {
+            return type.GetCustomAttributes(typeof(T), false).SingleOrDefault() as T;
+        }
+
+        public static Image GetIcon(this CheckStatus checkStatus)
+        {
+            switch (checkStatus)
+            {
+                case CheckStatus.Error: return Resources.error;
+                case CheckStatus.Warning: return Resources.warning;
+                case CheckStatus.Information: return Resources.info;
+                case CheckStatus.Success: return Resources.pass;
+                case CheckStatus.Running: return Resources.run;
+                default: return null;
+            }
+        }
+
+        public static bool In(this Enum value, params Enum[] values) {
+            return values.Contains(value);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/Check.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,210 @@
+using System;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+namespace ServerMonitorApp
+{
+    /*public enum CheckType
+    {
+        Command
+    }*/
+
+    public enum CheckStatus
+    {
+        Success,
+        Information,
+        Warning,
+        Error,
+        Running,
+        Disabled,
+    }
+
+    public abstract class Check
+    {
+        private static Type[] _checkTypes;
+
+        public static Type[] CheckTypes
+        {
+            get
+            {
+                return _checkTypes ?? (_checkTypes = typeof(Check).Assembly.GetTypes()
+                    .Where(t => t.IsSubclassOf(typeof(Check)))
+                    .OrderBy(t => t.GetAttribute<DisplayWeightAttribute>()?.DisplayWeight).ToArray());
+            }
+        }
+
+        public int Id { get; set; }
+
+        public string Name { get; set; }
+
+        /*public CheckType Type { get; set; }*/
+
+        public int Timeout { get; set; }
+
+        public bool Enabled { get; set; }
+
+        public Schedule Schedule { get; set; }
+
+        public DateTime LastRunTime { get; set; }
+
+        public DateTime LastScheduledRunTime { get; set; }
+
+        public DateTime NextRunTime { get; set; }
+
+        public string LastMessage { get; set; }
+
+        public CheckStatus Status { get; set; }
+
+        public CheckStatus FailStatus { get; set; }
+
+        [XmlIgnore]
+        public Server Server { get; set; }
+
+        public Check()
+        {
+            FailStatus = CheckStatus.Error;
+        }
+
+        public override string ToString()
+        {
+            return Name;
+        }
+
+        public virtual string Validate(bool saving = true)
+        {
+            string message = string.Empty;
+            if (Name.IsNullOrEmpty() && saving)
+                message += "Name cannot be blank." + Environment.NewLine;
+            return message;
+        }
+
+        //public virtual CheckStatus Execute()
+        //{
+        //    //TODO
+        //    throw new NotImplementedException();
+        //}
+
+        public async Task<CheckResult> ExecuteAsync(CancellationToken token = default(CancellationToken), bool update = true)
+        {
+            //TODO check cancellation token before proceeding
+            CheckResult result;
+            DateTime startTime = DateTime.Now;
+            try
+            {
+                Task<CheckResult> checkTask = ExecuteCheckAsync(token);
+                try
+                {
+                    if (await Task.WhenAny(checkTask, Task.Delay(Timeout, token)) == checkTask)
+                    {
+                        result = await checkTask;
+                    }
+                    else
+                    {
+                        result = Fail("Timed out.");
+                    }
+                }
+                catch (TaskCanceledException)
+                {
+                    return null;
+                }
+            }
+            catch (Exception e)
+            {
+                result = Fail(e.GetBaseException().Message);
+            }
+            result.StartTime = startTime;
+            result.EndTime = DateTime.Now;
+            // If a check is executed from the CheckForm, we don't want to update the status or log the event.
+            if (update)
+            {
+                Status = result.CheckStatus;
+                LastMessage = result.Message;
+                LastRunTime = result.EndTime;
+            }
+            return result;
+        }
+
+        protected CheckResult Pass(string message)
+        {
+            return new CheckResult(this, CheckStatus.Success, message);
+        }
+
+        protected CheckResult Fail(string message)
+        {
+            return new CheckResult(this, FailStatus, message);
+        }
+
+        protected CheckResult Fail(Exception e)
+        {
+            return new CheckResult(this, FailStatus, e.GetBaseException().Message);
+        }
+
+        protected virtual CheckResult GetIntResult(int expectedValue, int resultValue, string description)
+        {
+            if (expectedValue == resultValue)
+                return Pass(string.Format("{0}: {1}", description, resultValue));
+            else
+                return Fail(string.Format("{0}: {1} (expected: {2})", description, resultValue, expectedValue));
+        }
+
+        protected virtual CheckResult GetStringResult(MatchType matchType, string expectedPattern, bool useRegex, string resultValue, string description)
+        {
+            bool match;
+            if (useRegex)
+            {
+                if (matchType.In(MatchType.Equals, MatchType.NotEquals))
+                {
+                    if (!expectedPattern.StartsWith("^"))
+                        expectedPattern = "^" + expectedPattern;
+                    if (!expectedPattern.EndsWith("$"))
+                        expectedPattern += "$";
+                }
+                Regex re = new Regex(expectedPattern, RegexOptions.Singleline);
+                match = re.IsMatch(resultValue);
+            }
+            else
+            {
+                if (matchType.In(MatchType.Equals, MatchType.NotEquals))
+                    match = expectedPattern == resultValue;
+                else
+                    match = resultValue.Contains(expectedPattern);
+            }
+
+            if (matchType.In(MatchType.Equals, MatchType.Contains))
+            {
+                if (match)
+                    return Pass(string.Format("{0} {1} the pattern: {2}", description, matchType.ToString().ToLower(), expectedPattern));
+                else
+                    return Fail(string.Format("{0} does not {1} the pattern: {2} ({0}: {3})", description, matchType.ToString().ToLower().TrimEnd('s'), expectedPattern, resultValue));
+            }
+            else
+            {
+                if (match)
+                    return Fail(string.Format("{0} {1} the pattern: {2} ({0}: {3})", description, matchType.ToString().ToLower().Replace("not", ""), expectedPattern, resultValue));
+                else
+                    return Pass(string.Format("{0} does not {1} the pattern: {2}", description, matchType.ToString().ToLower().TrimEnd('s').Replace("not", ""), expectedPattern));
+            }
+        }
+
+        protected CheckResult MergeResults(params CheckResult[] results)
+        {
+            StringBuilder message = new StringBuilder();
+            bool failed = false;
+            foreach (CheckResult result in results)
+            {
+                if (result == null)
+                    continue;
+                if (result.CheckStatus != CheckStatus.Success)
+                    failed = true;
+                message.AppendLine(result.Message);
+            }
+            return failed ? Fail(message.ToString().Trim()) : Pass(message.ToString().Trim());
+        }
+
+        protected abstract Task<CheckResult> ExecuteCheckAsync(CancellationToken token);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/CheckResult.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,47 @@
+using ServerMonitorApp.Properties;
+using System;
+using System.Drawing;
+
+namespace ServerMonitorApp
+{
+    public class CheckResult
+    {
+        public const string dateFormat = "yyyy-MM-dd HH:mm:ss.fff";
+
+        public Check Check { get; private set; }
+
+        public CheckStatus CheckStatus { get; private set; }
+
+        public string Message { get; private set; }
+
+        public DateTime StartTime { get; set; }
+
+        public DateTime EndTime { get; set; }
+
+        public CheckResult(Check check, CheckStatus status, string message)
+        {
+            Check = check;
+            CheckStatus = status;
+            Message = message;
+        }
+
+        public string ToLogString()
+        {
+            return string.Format("{0:00000} {1} {2} {3} {4}",
+                Check.Id,
+                StartTime.ToString(dateFormat).Replace("T", " "),
+                EndTime.ToString(dateFormat).Replace("T", " "),
+                CheckStatus,
+                Message.Replace("\r\n", "\n").Replace('\r', '\n').Replace("\n", "\\n"));
+        }
+
+        public static CheckResult FromLogString(Check check, string logString)
+        {
+            DateTime startTime = DateTime.Parse(logString.Substring(6, 23));
+            DateTime endTime = DateTime.Parse(logString.Substring(30, 23));
+            int messageStartPos = logString.IndexOf(' ', 54);
+            Enum.TryParse(logString.Substring(54, messageStartPos - 54), out CheckStatus status);
+            return new CheckResult(check, status, logString.Substring(messageStartPos + 1)) { StartTime = startTime, EndTime = endTime };
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/HttpCheck.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ServerMonitorApp
+{
+    [DisplayName("HTTP check"), Description("Check the result of an HTTP request"), DisplayWeight(1)]
+    public class HttpCheck : Check
+    {
+        public string Url { get; set; }
+
+        public bool CheckResponseCode { get; set; }
+
+        public int ResponseCode { get; set; }
+
+        public bool CheckResponseLength { get; set; }
+
+        public string ResponseLengthMin { get; set; }
+
+        public string ResponseLengthMax { get; set; }
+
+        public bool CheckResponseBody { get; set; }
+
+        public MatchType ResponseBodyMatchType { get; set; }
+
+        public string ResponseBodyPattern { get; set; }
+
+        public bool ResponseBodyUseRegex { get; set; }
+
+        protected override Task<CheckResult> ExecuteCheckAsync(CancellationToken token)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override string Validate(bool saving = true)
+        {
+            string message = base.Validate();
+            if (Url.IsNullOrEmpty())
+                message += "URL cannot be blank." + Environment.NewLine;
+            if (!CheckResponseCode && !CheckResponseLength && !CheckResponseBody)
+                message += "At least one check must be enabled." + Environment.NewLine;
+            if (CheckResponseBody && ResponseBodyUseRegex)
+            {
+                try
+                {
+                    Regex re = new Regex(ResponseBodyPattern);
+                }
+                catch (ArgumentException)
+                {
+                    message += "Invalid regular expression for response body." + Environment.NewLine;
+                }
+            }
+            return message;
+        }
+
+        //protected override CheckResult GetIntResult(int expectedValue, int resultValue, string description)
+        //{
+        //    CheckResult result = base.GetIntResult(expectedValue, resultValue, description);
+
+        //}
+
+/*
+100   Continue[RFC7231, Section 6.2.1]
+101   Switching Protocols[RFC7231, Section 6.2.2]
+102   Processing[RFC2518]
+103   Early Hints[RFC8297]
+200   OK[RFC7231, Section 6.3.1]
+201   Created[RFC7231, Section 6.3.2]
+202   Accepted[RFC7231, Section 6.3.3]
+203   Non-Authoritative Information[RFC7231, Section 6.3.4]
+204   No Content[RFC7231, Section 6.3.5]
+205   Reset Content[RFC7231, Section 6.3.6]
+206   Partial Content[RFC7233, Section 4.1]
+207   Multi-Status[RFC4918]
+208   Already Reported[RFC5842]
+226   IM Used[RFC3229]
+300   Multiple Choices[RFC7231, Section 6.4.1]
+301   Moved Permanently[RFC7231, Section 6.4.2]
+302   Found[RFC7231, Section 6.4.3]
+303   See Other[RFC7231, Section 6.4.4]
+304   Not Modified[RFC7232, Section 4.1]
+305   Use Proxy[RFC7231, Section 6.4.5]
+306   (Unused)[RFC7231, Section 6.4.6]
+307   Temporary Redirect[RFC7231, Section 6.4.7]
+308   Permanent Redirect[RFC7538]
+400   Bad Request[RFC7231, Section 6.5.1]
+401   Unauthorized[RFC7235, Section 3.1]
+402   Payment Required[RFC7231, Section 6.5.2]
+403   Forbidden[RFC7231, Section 6.5.3]
+404   Not Found[RFC7231, Section 6.5.4]
+405   Method Not Allowed[RFC7231, Section 6.5.5]
+406   Not Acceptable[RFC7231, Section 6.5.6]
+407   Proxy Authentication Required[RFC7235, Section 3.2]
+408   Request Timeout[RFC7231, Section 6.5.7]
+409   Conflict[RFC7231, Section 6.5.8]
+410   Gone[RFC7231, Section 6.5.9]
+411   Length Required[RFC7231, Section 6.5.10]
+412   Precondition Failed[RFC7232, Section 4.2][RFC8144, Section 3.2]
+413   Payload Too Large[RFC7231, Section 6.5.11]
+414   URI Too Long[RFC7231, Section 6.5.12]
+415   Unsupported Media Type[RFC7231, Section 6.5.13][RFC7694, Section 3]
+416   Range Not Satisfiable[RFC7233, Section 4.4]
+417   Expectation Failed[RFC7231, Section 6.5.14]
+421   Misdirected Request[RFC7540, Section 9.1.2]
+422   Unprocessable Entity[RFC4918]
+423   Locked[RFC4918]
+424   Failed Dependency[RFC4918]
+425   Too Early[RFC8470]
+426   Upgrade Required[RFC7231, Section 6.5.15]
+427   Unassigned
+428   Precondition Required[RFC6585]
+429   Too Many Requests[RFC6585]
+430   Unassigned
+431   Request Header Fields Too Large[RFC6585]
+451   Unavailable For Legal Reasons[RFC7725]
+500   Internal Server Error[RFC7231, Section 6.6.1]
+501   Not Implemented[RFC7231, Section 6.6.2]
+502   Bad Gateway[RFC7231, Section 6.6.3]
+503   Service Unavailable[RFC7231, Section 6.6.4]
+504   Gateway Timeout[RFC7231, Section 6.6.5]
+505   HTTP Version Not Supported[RFC7231, Section 6.6.6]
+506   Variant Also Negotiates[RFC2295]
+507   Insufficient Storage[RFC4918]
+508   Loop Detected[RFC5842]
+509   Unassigned
+510   Not Extended[RFC2774]
+511   Network Authentication Required[RFC6585]
+*/
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/Logger.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ServerMonitorApp
+{
+    public class Logger
+    {
+        private const int logVersion = 1;
+        private readonly string logFile;
+        
+        public Logger(string file)
+        {
+            logFile = file;
+        }
+
+        public async void Log(string logString)
+        {
+            if (!File.Exists(logFile))
+                logString = "Server Monitor log version " + logVersion + Environment.NewLine + logString;
+            using (FileStream stream = new FileStream(logFile, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, true))
+            using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8))
+            {
+                await writer.WriteLineAsync(logString);
+            }
+        }
+
+        public void Log(CheckResult result)
+        {
+            Log(result.ToLogString());
+        }
+
+        public IList<CheckResult> Read(Server server)
+        {
+            Dictionary<int, Check> checks = server.Checks.ToDictionary(c => c.Id);
+            List<CheckResult> results = new List<CheckResult>();
+            using (FileStream stream = new FileStream(logFile, FileMode.Open, FileAccess.Read, FileShare.Read))
+            using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
+            {
+                while (true)
+                {
+                    try
+                    {
+                        string line = reader.ReadLine();
+                        if (line == null)
+                            break;
+                        if (int.TryParse(line.Substring(0, 5), out int id) && checks.TryGetValue(id, out Check check))
+                        {
+                            results.Add(CheckResult.FromLogString(check, line));
+                        }
+                    }
+                    catch (Exception) { }
+                }
+            }
+            results.Reverse();
+            return results;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/PingCheck.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Net.NetworkInformation;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ServerMonitorApp
+{
+    [DisplayName("Ping check"), Description("Check if the server responds to a ping request"), DisplayWeight(0)]
+    public class PingCheck : Check
+    {
+        protected async override Task<CheckResult> ExecuteCheckAsync(CancellationToken token)
+        {
+            using (Ping ping = new Ping())
+            {
+                try
+                {
+                    PingReply reply = await ping.SendPingAsync(Server.Host, Timeout);
+                    if (reply.Status == IPStatus.Success)
+                        return Pass(string.Format("Reply received in {0} ms", reply.RoundtripTime));
+                    else
+                        return Fail("Ping result: " + reply.Status);
+                }
+                catch (PingException e)
+                {
+                    return Fail(e);
+                }
+            }
+
+            // Cancellable version that doesn't actulaly work
+            // might be useful as a TaskCompletionSource example though
+            //
+            //TaskCompletionSource<CheckResult> tcs = new TaskCompletionSource<CheckResult
+            //
+            //using (Ping ping = new Ping())
+            //{
+            //    token.Register(ping.SendAsyncCancel);
+            //    ping.PingCompleted += (sender, e) =>
+            //    {
+            //        if (e.Error != null)
+            //            tcs.SetResult(Fail("Ping failed: " + e.Error.GetBaseException().Message));
+            //        else if (e.Reply.Status != IPStatus.Success)
+            //            tcs.SetResult(Fail("Ping failed: " + e.Reply.Status.ToString()));
+            //        else
+            //            tcs.SetResult(Pass("Ping completed in " + e.Reply.RoundtripTime + "ms"));
+            //    };
+            //    ping.SendAsync(Server.Host, Timeout, null);
+            //}
+
+            //return tcs.Task;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/Schedule.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,72 @@
+using System;
+
+namespace ServerMonitorApp
+{
+    public class Schedule
+    {
+        public Schedule() { }
+
+        public Schedule(FrequencyUnits units, int frequency, TimeSpan startTime, TimeSpan endTime)
+        {
+            Units = units;
+            Frequency = frequency;
+            StartTime = startTime;
+            EndTime = endTime;
+        }
+
+        public int Frequency { get; set; }
+
+        public FrequencyUnits Units { get; set; }
+
+        public TimeSpan StartTime { get; set; }
+
+        public TimeSpan EndTime { get; set; }
+
+        public DateTime GetNextTime(DateTime lastScheduledTime)
+        {
+            return GetNextTime(lastScheduledTime, DateTime.Now);
+        }
+
+        public DateTime GetNextTime(DateTime lastScheduledTime, DateTime minStartTime)
+        {
+            DateTime nextTime = lastScheduledTime;
+            if (Units == FrequencyUnits.Day)
+            {
+                //TODO what if nextTime is null or really long ago?
+                while (nextTime < minStartTime)
+                    nextTime = nextTime.AddDays(Frequency).Date.Add(StartTime);
+            }
+            else
+            {
+                // If the last run time was more than a day ago, fast-forward to reduce the number of loops
+                if (nextTime < minStartTime.AddHours(-24))
+                    nextTime = minStartTime.Date.Add(StartTime).AddHours(-24);
+                //TODO handle start time and end time
+                while (nextTime < minStartTime)
+                {
+                    switch (Units)
+                    {
+                        case FrequencyUnits.Second: nextTime = nextTime.AddSeconds(Frequency); break;
+                        case FrequencyUnits.Minute: nextTime = nextTime.AddMinutes(Frequency); break;
+                        case FrequencyUnits.Hour:   nextTime = nextTime.AddHours(Frequency);   break;
+                        default: throw new InvalidOperationException("Unexpected frequency units: " + Units);
+                    }
+                }
+                if (StartTime < EndTime)
+                {
+                    if (nextTime.TimeOfDay < StartTime)
+                        nextTime = nextTime.Date + StartTime;
+                    else if (nextTime.TimeOfDay > EndTime)
+                        nextTime = nextTime.Date.AddDays(1) + StartTime;
+                }
+                else if (nextTime.TimeOfDay > EndTime && nextTime.TimeOfDay < StartTime)
+                {
+                    nextTime = nextTime.Date + StartTime;
+                }
+            }
+            return nextTime;
+        }
+    }
+
+    public enum FrequencyUnits { Second, Minute, Hour, Day }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/Server.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Security.Cryptography;
+using System.ComponentModel;
+using Renci.SshNet;
+using System.Runtime.Serialization;
+
+namespace ServerMonitorApp
+{
+    public enum LoginType { PrivateKey = 0, Password = 1 };
+
+    public class Server
+    {
+        private string _host;
+        private int _port;
+        private string _username;
+        private LoginType _loginType;
+        private string _keyFile;
+        private SshClient _sshClient;
+        private byte[] passwordHash;
+
+        public event EventHandler CheckModified;
+
+        public readonly BindingList<Check> Checks = new BindingList<Check>();
+
+        public int Id { get; set; }
+
+        public string Name { get; set; }
+
+        public string Host
+        {
+            get { return _host; }
+            set { _host = value; InvalidateSshConnection(); }
+        }
+
+        public int Port
+        {
+            get { return _port; }
+            set { _port = value; InvalidateSshConnection(); }
+        }
+
+        public string Username
+        {
+            get { return _username; }
+            set { _username = value; InvalidateSshConnection(); }
+        }
+
+        public LoginType LoginType
+        {
+            get { return _loginType; }
+            set { _loginType = value; InvalidateSshConnection(); }
+        }
+
+        public string KeyFile
+        {
+            get { return _keyFile; }
+            set { _keyFile = value; InvalidateSshConnection(); }
+        }
+
+        public string Password
+        {
+            get {
+                return passwordHash == null ? null :
+                    Encoding.UTF8.GetString(ProtectedData.Unprotect(passwordHash, Encoding.UTF8.GetBytes("Server".Reverse().ToString()), DataProtectionScope.CurrentUser));
+            }
+            set
+            {
+                passwordHash = ProtectedData.Protect(Encoding.UTF8.GetBytes(value),
+                                 Encoding.UTF8.GetBytes("Server".Reverse().ToString()), // Minor obfuscation of additional entropy
+                                 DataProtectionScope.CurrentUser);
+            }
+        }
+
+        public bool Enabled { get; set; } = true;
+
+        public SshClient SshClient
+        {
+            get
+            {
+                if (_sshClient == null)
+                {
+                    AuthenticationMethod auth = null;
+                    if (LoginType == LoginType.Password)
+                        auth = new PasswordAuthenticationMethod(Username, Password);
+                    else
+                        new PrivateKeyAuthenticationMethod(Username, new PrivateKeyFile(KeyFile));
+                    ConnectionInfo info = new ConnectionInfo(Host, Port, Username, auth);
+                    _sshClient = new SshClient(info);
+                }
+                return _sshClient;
+            }
+        }
+
+        /*public Server() { }
+
+        public Server(Server server)
+        {
+            Name = server.Name;
+            Host = server.Host;
+            Port = server.Port;
+            Username = server.Username;
+            LoginType = server.LoginType;
+            KeyFile = server.KeyFile;
+            Enabled = server.Enabled;
+        }*/
+
+        public void DeleteCheck(Check check)
+        {
+            Checks.Remove(check);
+            check.Server = null;
+            CheckModified?.Invoke(check, new EventArgs());
+        }
+
+        public string Validate()
+        {
+            string message = string.Empty;
+            if (Name.Length == 0)
+                message += "\"Name\" must not be empty" + Environment.NewLine;
+            if (Host.Length == 0)
+                message += "\"Host\" must not be empty" + Environment.NewLine;
+            return message.Length > 0 ? message : null;
+        }
+
+        public void UpdateCheck(Check check)
+        {
+            Check oldCheck = Checks.FirstOrDefault(c => c.Id == check.Id);
+            if (!ReferenceEquals(check, oldCheck))
+            {
+                int index = Checks.IndexOf(oldCheck);
+                if (index == -1)
+                    Checks.Add(check);
+                else
+                    Checks[index] = check;
+            }
+            CheckModified?.Invoke(check, new EventArgs());
+        }
+
+        private void InvalidateSshConnection()
+        {
+            _sshClient?.Dispose();
+            _sshClient = null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/ServerMonitor.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+namespace ServerMonitorApp
+{
+    public class ServerMonitor
+    {
+        private readonly string configFileDir;
+        private readonly Logger logger;
+        private readonly Dictionary<int, CancellationTokenSource> tokens = new Dictionary<int, CancellationTokenSource>();
+        private bool running;
+        private Dictionary<Task<CheckResult>, int> tasks;
+        //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 string ConfigFile { get; private set; }
+
+        public ServerMonitor()
+        {
+            configFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ServerMonitor");
+            ConfigFile = Path.Combine(configFileDir, "servers.xml");
+            logger = new Logger(Path.Combine(configFileDir, "monitor.log"));
+        }
+
+        public void AddServer(Server server)
+        {
+            Servers.Add(server);
+        }
+
+        public void LoadServers()
+        {
+            TextReader reader = null;
+            try
+            {
+                reader = new StreamReader(ConfigFile);
+                XmlSerializer serializer = CreateXmlSerializer();
+                Servers.Clear();
+                Servers.AddRange((List<Server>)serializer.Deserialize(reader));
+                // Update the Checks so they know what Server they belong to.
+                // Would rather do this in the Server object on deserialization, but
+                // that doesn't work when using the XML serializer for some reason.
+                foreach (Server server in Servers)
+                {
+                    foreach (Check check in server.Checks)
+                    {
+                        check.Server = server;
+                    }
+                    server.CheckModified += Server_CheckModified;
+                }
+            }
+            // If the file doesn't exist, no special handling is needed. It will be created later.
+            catch (FileNotFoundException) { }
+            catch (DirectoryNotFoundException) { }
+            catch (InvalidOperationException)
+            {
+                //TODO log
+                throw;
+            }
+            finally
+            {
+                reader?.Close();
+            }
+            Run();
+        }
+
+        public void SaveServers()
+        {
+            GenerateIds();
+            TextWriter writer = null;
+            XmlSerializer serializer = null;
+            try
+            {
+                writer = new StreamWriter(ConfigFile);
+                serializer = CreateXmlSerializer();
+                serializer.Serialize(writer, Servers);
+            }
+            catch (DirectoryNotFoundException)
+            {
+                Directory.CreateDirectory(configFileDir);
+                writer = new StreamWriter(ConfigFile);
+                serializer = CreateXmlSerializer();
+                serializer.Serialize(writer, Servers);
+            }
+            catch (Exception)
+            {
+                //TODO log
+                throw;
+            }
+            finally
+            {
+                writer?.Close();
+            }
+        }
+
+        private async void Run()
+        {
+            if (running)
+                return;
+            running = true;
+            //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)
+            {
+                Task<CheckResult> task = await Task.WhenAny(tasks.Keys);
+                tasks.Remove(task);
+                try
+                {
+                    CheckResult result = await task;
+                    // Result will be null if a scheduled check was disabled
+                    if (result != null && result.CheckStatus != CheckStatus.Disabled)
+                        tasks.Add(ScheduleExecuteCheckAsync(result.Check), result.Check.Id);
+                }
+                catch (OperationCanceledException)
+                {
+
+                }
+            }
+            running = false;
+        }
+
+        public async Task<CheckResult> ExecuteCheckAsync(Check check, CancellationToken token = default(CancellationToken))
+        {
+            check.Status = CheckStatus.Running;
+            OnCheckStatusChanged(check);
+            CheckResult result = await check.ExecuteAsync(token);
+            OnCheckStatusChanged(check, result);
+            logger.Log(result);
+            return result;
+        }
+
+        public IList<CheckResult> GetLog(Server server)
+        {
+            return logger.Read(server);
+        }
+
+        private void OnCheckStatusChanged(Check check, CheckResult result = null)
+        {
+            SaveServers();
+            CheckStatusChanged?.Invoke(check, new CheckStatusChangedEventArgs(check, result));
+        }
+
+        private void Server_CheckModified(object sender, EventArgs e)
+        {
+            Check check = (Check)sender;
+            Task<CheckResult> task = tasks.FirstOrDefault(kvp => kvp.Value == check.Id).Key;
+            if (running)
+            {
+                if (task == null)
+                {
+                    // No tasks associated with the check, so schedule a new one
+                    tasks.Add(ScheduleExecuteCheckAsync(check), check.Id);
+                }
+                else
+                {
+                    // Check was modified or deleted, so remove any waiting tasks
+                    tasks.Remove(task);
+                    if (tokens.TryGetValue(check.Id, out CancellationTokenSource cts))
+                        cts.Cancel();
+                    if (check.Server != null)
+                    {
+                        // If the check was not deleted, schedule the new check.
+                        // But only if it's still running, otherwise restarting the monitor below
+                        // will create a duplicate run.
+                        if (running)
+                            tasks.Add(ScheduleExecuteCheckAsync(check), check.Id);
+                    }
+                }
+            }
+            // Run again in case removing a task above caused it to stop
+            Run();
+        }
+
+        private async Task<CheckResult> ScheduleExecuteCheckAsync(Check check)
+        {
+            if (!check.Enabled)
+                return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null));
+
+            CancellationTokenSource cts = new CancellationTokenSource();
+            tokens[check.Id] = cts;
+            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);
+        }
+
+        private void GenerateIds()
+        {
+            if (Servers.Any())
+            {
+                int id = Servers.Max(s => s.Id);
+                foreach (Server server in Servers)
+                {
+                    if (server.Id == 0)
+                        server.Id = ++id;
+                }
+            }
+
+            //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);
+                foreach (Check check in Checks)
+                {
+                    if (check.Id == 0)
+                        check.Id = ++id;
+                }
+            }
+        }
+
+        private XmlSerializer CreateXmlSerializer()
+        {
+            return new XmlSerializer(typeof(List<Server>), Check.CheckTypes);
+        }
+    }
+
+    public class CheckStatusChangedEventArgs : EventArgs
+    {
+        public Check Check { get; private set; }
+
+        public CheckResult CheckResult { get; private set; }
+
+        public CheckStatusChangedEventArgs(Check check, CheckResult result)
+        {
+            Check = check;
+            CheckResult = result;
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Objects/SshCheck.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,106 @@
+using Renci.SshNet;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ServerMonitorApp
+{
+    [DisplayName("SSH check"), Description("Check the result of a command run over SSH"), DisplayWeight(10)]
+    public class SshCheck : Check
+    {
+        public string Command { get; set; }
+
+        public bool CheckExitCode { get; set; }
+
+        public int ExitCode { get; set; }
+
+        public bool CheckCommandOutput { get; set; }
+
+        public MatchType CommandOutputMatchType { get; set; }
+
+        public string CommandOutputPattern { get; set; }
+
+        public bool CommandOutputUseRegex { get; set; }
+
+        protected override Task<CheckResult> ExecuteCheckAsync(CancellationToken token)
+        {
+            return Task.Run(() =>
+            {
+                try
+                {
+                    if (!Server.SshClient.IsConnected)
+                        Server.SshClient.Connect();
+                    token.ThrowIfCancellationRequested();
+                    using (SshCommand command = Server.SshClient.CreateCommand(Command))
+                    {
+                        token.Register(command.CancelAsync);
+                        IAsyncResult ar = command.BeginExecute();
+                        token.ThrowIfCancellationRequested();
+                        string result = command.EndExecute(ar).Trim() + command.Error.Trim();
+
+                        CheckResult exitCodeResult = null, commandOutputResult = null;
+                        if (CheckExitCode)
+                            exitCodeResult = GetIntResult(ExitCode, command.ExitStatus, "Exit code");
+                        if (CheckCommandOutput)
+                            commandOutputResult = GetStringResult(CommandOutputMatchType, CommandOutputPattern, CommandOutputUseRegex, result, "Command output");
+                        return MergeResults(exitCodeResult, commandOutputResult);
+                    }
+                }
+                catch (Exception e)
+                {
+                    return Fail(e);
+                }
+            }, token);
+            //TaskCompletionSource<CheckResult> tcs = new TaskCompletionSource<CheckResult>();
+
+            ////TODO timeout
+            //if (!Server.SshClient.IsConnected)
+            //    Server.SshClient.Connect();
+            //using (SshCommand command = Server.SshClient.CreateCommand(Command))
+            //{
+            //    token.Register(command.CancelAsync);
+            //    command.BeginExecute(asyncResult =>
+            //    {
+            //        string result = command.EndExecute(asyncResult);
+            //        tcs.SetResult(new CheckResult(this, CheckStatus.Success, result));
+            //    });
+            //}
+
+            //return tcs.Task;
+        }
+
+        public override string Validate(bool saving = true)
+        {
+            string message = base.Validate();
+            if (Server.Port <= 0)
+                message += "Server SSH port is required." + Environment.NewLine;
+            if (Server.Username.IsNullOrEmpty())
+                message += "Server SSH username is required." + Environment.NewLine;
+            if (Server.LoginType == LoginType.Password && Server.Password.IsNullOrEmpty())
+                message += "Server SSH password is required." + Environment.NewLine;
+            if (Server.LoginType == LoginType.PrivateKey && Server.KeyFile.IsNullOrEmpty())
+                message += "Server SSH key is required." + Environment.NewLine;
+            if (Command.IsNullOrEmpty())
+                message += "Command is required." + Environment.NewLine;
+            if (!CheckExitCode && !CheckCommandOutput)
+                message += "At least one check must be enabled." + Environment.NewLine;
+            if (CheckCommandOutput && CommandOutputUseRegex)
+            {
+                try
+                {
+                    Regex re = new Regex(CommandOutputPattern);
+                }
+                catch (ArgumentException)
+                {
+                    message += "Invalid regular expression for command output." + Environment.NewLine;
+                }
+            }
+            return message;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Program.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    static class Program
+    {
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
+            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
+            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
+
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Application.Run(new ServerSummaryForm());
+        }
+
+        static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
+        {
+            //if (!System.Diagnostics.Debugger.IsAttached)
+                ShowError(e.Exception);
+        }
+
+        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+        {
+            //if (!System.Diagnostics.Debugger.IsAttached)
+                ShowError(e.ExceptionObject as Exception);
+        }
+
+        static void ShowError(Exception e)
+        {
+            string simpleStackTrace = string.Join(Environment.NewLine, new System.Diagnostics.StackTrace(e).GetFrames()
+                    .Select(f => f.GetMethod().DeclaringType.Name + "." + f.GetMethod().Name).ToArray());
+            MessageBox.Show(e.Message + Environment.NewLine + Environment.NewLine + simpleStackTrace, "Server Monitor Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Properties/AssemblyInfo.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ServerMonitor")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ServerMonitor")]
+[assembly: AssemblyCopyright("Copyright ©  2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components.  If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("68e905d9-18fd-4adc-9cf7-b5984c3e2158")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Properties/DataSources/CheckResult.datasource	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    This file is automatically generated by Visual Studio .Net. It is 
+    used to store generic object data source configuration information.  
+    Renaming the file extension or editing the content of this file may   
+    cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="CheckResult" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+   <TypeInfo>ServerMonitorApp.CheckResult, ServerMonitor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
+</GenericObjectDataSource>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Properties/Resources.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,173 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace ServerMonitorApp.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ServerMonitorApp.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap add {
+            get {
+                object obj = ResourceManager.GetObject("add", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap delete {
+            get {
+                object obj = ResourceManager.GetObject("delete", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap edit {
+            get {
+                object obj = ResourceManager.GetObject("edit", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap error {
+            get {
+                object obj = ResourceManager.GetObject("error", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap help {
+            get {
+                object obj = ResourceManager.GetObject("help", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap info {
+            get {
+                object obj = ResourceManager.GetObject("info", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap pass {
+            get {
+                object obj = ResourceManager.GetObject("pass", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap run {
+            get {
+                object obj = ResourceManager.GetObject("run", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap runall {
+            get {
+                object obj = ResourceManager.GetObject("runall", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap server {
+            get {
+                object obj = ResourceManager.GetObject("server", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap warning {
+            get {
+                object obj = ResourceManager.GetObject("warning", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Properties/Resources.resx	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="add" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\add.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="error" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\error.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="help" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\help.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="info" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\info.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="pass" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\pass.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="run" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\run.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="runall" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\runall.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="server" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\server.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="warning" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\warning.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Properties/Settings.Designer.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,38 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace ServerMonitorApp.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+        
+        [global::System.Configuration.UserScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("True")]
+        public bool ConfirmDeleteCheck {
+            get {
+                return ((bool)(this["ConfirmDeleteCheck"]));
+            }
+            set {
+                this["ConfirmDeleteCheck"] = value;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Properties/Settings.settings	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ServerMonitor.Properties" GeneratedClassName="Settings">
+  <Profiles />
+  <Settings>
+    <Setting Name="ConfirmDeleteCheck" Type="System.Boolean" Scope="User">
+      <Value Profile="(Default)">True</Value>
+    </Setting>
+  </Settings>
+</SettingsFile>
\ No newline at end of file
Binary file ServerMonitor/Resources/Icon1.ico has changed
Binary file ServerMonitor/Resources/Image1.png has changed
Binary file ServerMonitor/Resources/add.png has changed
Binary file ServerMonitor/Resources/delete.png has changed
Binary file ServerMonitor/Resources/edit.png has changed
Binary file ServerMonitor/Resources/error.png has changed
Binary file ServerMonitor/Resources/help.png has changed
Binary file ServerMonitor/Resources/info.png has changed
Binary file ServerMonitor/Resources/pass.png has changed
Binary file ServerMonitor/Resources/run.png has changed
Binary file ServerMonitor/Resources/runall.png has changed
Binary file ServerMonitor/Resources/server.png has changed
Binary file ServerMonitor/Resources/warning.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/ServerMonitor.csproj	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{68E905D9-18FD-4ADC-9CF7-B5984C3E2158}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>ServerMonitorApp</RootNamespace>
+    <AssemblyName>ServerMonitor</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <TargetFrameworkProfile />
+    <SccProjectName>&lt;Project Location In Database&gt;</SccProjectName>
+    <SccLocalPath>&lt;Local Binding Root of Project&gt;</SccLocalPath>
+    <SccAuxPath>&lt;Source Control Database&gt;</SccAuxPath>
+    <SccProvider>Mercurial Source Control Package</SccProvider>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Renci.SshNet, Version=2016.1.0.0, Culture=neutral, PublicKeyToken=1cee9f8bde3db106, processorArchitecture=MSIL">
+      <HintPath>..\packages\SSH.NET.2016.1.0\lib\net40\Renci.SshNet.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Security" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Attributes.cs" />
+    <Compile Include="Controls\CheckControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\CheckControl.Designer.cs">
+      <DependentUpon>CheckControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\SshCheckControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\SshCheckControl.Designer.cs">
+      <DependentUpon>SshCheckControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\HttpCheckControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\HttpCheckControl.Designer.cs">
+      <DependentUpon>HttpCheckControl.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\IncludesComboBox.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Forms\CheckBoxDialog.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\CheckBoxDialog.Designer.cs">
+      <DependentUpon>CheckBoxDialog.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Forms\CheckForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\CheckForm.Designer.cs">
+      <DependentUpon>CheckForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Forms\QuickHelpForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\QuickHelpForm.Designer.cs">
+      <DependentUpon>QuickHelpForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Helpers.cs" />
+    <Compile Include="Objects\Check.cs" />
+    <Compile Include="Objects\CheckResult.cs" />
+    <Compile Include="Objects\HttpCheck.cs" />
+    <Compile Include="Objects\Logger.cs" />
+    <Compile Include="Objects\PingCheck.cs" />
+    <Compile Include="Objects\Schedule.cs" />
+    <Compile Include="Objects\Server.cs" />
+    <Compile Include="Forms\ServerForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\ServerForm.Designer.cs">
+      <DependentUpon>ServerForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Objects\ServerMonitor.cs" />
+    <Compile Include="Forms\ServerSummaryForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\ServerSummaryForm.Designer.cs">
+      <DependentUpon>ServerSummaryForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Objects\SshCheck.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Controls\ServerSummaryControl.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\ServerSummaryControl.Designer.cs">
+      <DependentUpon>ServerSummaryControl.cs</DependentUpon>
+    </Compile>
+    <EmbeddedResource Include="Controls\CheckControl.resx">
+      <DependentUpon>CheckControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\SshCheckControl.resx">
+      <DependentUpon>SshCheckControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\HttpCheckControl.resx">
+      <DependentUpon>HttpCheckControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\CheckBoxDialog.resx">
+      <DependentUpon>CheckBoxDialog.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\CheckForm.resx">
+      <DependentUpon>CheckForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\QuickHelpForm.resx">
+      <DependentUpon>QuickHelpForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\ServerForm.resx">
+      <DependentUpon>ServerForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Controls\ServerSummaryControl.resx">
+      <DependentUpon>ServerSummaryControl.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\ServerSummaryForm.resx">
+      <DependentUpon>ServerSummaryForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+      <DesignTime>True</DesignTime>
+    </Compile>
+    <None Include="packages.config" />
+    <None Include="Properties\DataSources\CheckResult.datasource" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\Icon1.ico" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\Image1.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\delete.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\run.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\runall.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\add.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\edit.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\server.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\help.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\warning.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\info.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\error.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\pass.png" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/packages.config	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="SSH.NET" version="2016.1.0" targetFramework="net45" />
+</packages>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitorTest/Properties/AssemblyInfo.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("ServerMonitorTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ServerMonitorTest")]
+[assembly: AssemblyCopyright("Copyright ©  2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("bd66790b-eba2-4c11-a770-8901a8196bcd")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitorTest/ScheduleTest.cs	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,118 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using ServerMonitorApp;
+
+namespace ServerMonitorTest
+{
+    [TestClass]
+    public class ScheduleTest
+    {
+        private Schedule secondAllDaySchedule = new Schedule(FrequencyUnits.Second, 5, new TimeSpan(0, 0, 0), new TimeSpan(23, 59, 59));
+        private Schedule secondPartDaySchedule = new Schedule(FrequencyUnits.Second, 5, new TimeSpan(10, 0, 0), new TimeSpan(21, 0, 0));
+        private Schedule secondSpanDaySchedule = new Schedule(FrequencyUnits.Second, 5, new TimeSpan(21, 0, 0), new TimeSpan(10, 0, 0));
+
+        #region All day
+
+        [TestMethod]
+        public void TestAllDay1()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 1, 1, 0, 5),
+                secondAllDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 1, 0, 0), new DateTime(2000, 1, 1, 1, 0, 1)));
+        }
+
+        [TestMethod]
+        public void TestAllDay2()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 1, 1, 0, 15),
+                secondAllDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 1, 0, 0), new DateTime(2000, 1, 1, 1, 0, 11)));
+        }
+
+        [TestMethod]
+        public void TestAllDay3()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 1, 2, 0, 15),
+                secondAllDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 1, 0, 0), new DateTime(2000, 1, 1, 2, 0, 11)));
+        }
+
+        [TestMethod]
+        public void TestAllDay4()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 2, 0, 0, 3),
+                secondAllDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 23, 59, 58), new DateTime(2000, 1, 1, 23, 59, 59)));
+        }
+
+        [TestMethod]
+        public void TestAllDay5()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 2, 0, 0, 8),
+                secondAllDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 23, 59, 58), new DateTime(2000, 1, 2, 0, 0, 4)));
+        }
+
+        #endregion
+
+        #region Part day
+
+        [TestMethod]
+        public void TestPartDay1()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 1, 10, 0, 5),
+                secondPartDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 10, 0, 0), new DateTime(2000, 1, 1, 10, 0, 1)));
+        }
+
+        [TestMethod]
+        public void TestPartDay2()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 2, 10, 0, 0),
+                secondPartDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 10, 0, 3), new DateTime(2000, 1, 1, 20, 59, 59)));
+        }
+
+        [TestMethod]
+        public void TestPartDay3()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 2, 10, 0, 0),
+                secondPartDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 10, 0, 3), new DateTime(2000, 1, 1, 21, 0, 0)));
+        }
+
+        #endregion
+
+        #region Span day
+
+        [TestMethod]
+        public void TestSpanDay1()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 1, 1, 0, 5),
+                secondSpanDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 1, 0, 0), new DateTime(2000, 1, 1, 1, 0, 1)));
+        }
+
+        [TestMethod]
+        public void TestSpanDay2()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 2, 0, 0, 3),
+                secondSpanDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 23, 59, 58), new DateTime(2000, 1, 1, 23, 59, 59)));
+        }
+
+        [TestMethod]
+        public void TestSpanDay3()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 2, 0, 0, 8),
+                secondSpanDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 23, 59, 58), new DateTime(2000, 1, 2, 0, 0, 4)));
+        }
+
+        [TestMethod]
+        public void TestSpanDay4()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 2, 21, 0, 0),
+                secondSpanDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 21, 0, 8), new DateTime(2000, 1, 2, 9, 59, 59)));
+        }
+
+        [TestMethod]
+        public void TestSpanDay5()
+        {
+            Assert.AreEqual(new DateTime(2000, 1, 2, 21, 0, 0),
+                secondSpanDaySchedule.GetNextTime(new DateTime(2000, 1, 1, 21, 0, 8), new DateTime(2000, 1, 2, 11, 0, 0)));
+        }
+
+        #endregion
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitorTest/ServerMonitorTest.csproj	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" />
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{BD66790B-EBA2-4C11-A770-8901A8196BCD}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>ServerMonitorTest</RootNamespace>
+    <AssemblyName>ServerMonitorTest</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+    <IsCodedUITest>False</IsCodedUITest>
+    <TestProjectType>UnitTest</TestProjectType>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ScheduleTest.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\ServerMonitor\ServerMonitor.csproj">
+      <Project>{68e905d9-18fd-4adc-9cf7-b5984c3e2158}</Project>
+      <Name>ServerMonitor</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props'))" />
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets'))" />
+  </Target>
+  <Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" />
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitorTest/packages.config	Mon Dec 31 18:32:14 2018 -0500
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net461" />
+  <package id="MSTest.TestFramework" version="1.3.2" targetFramework="net461" />
+</packages>
\ No newline at end of file