diff --git a/AiQ_GUI.Designer.cs b/AiQ_GUI.Designer.cs index 9dedde3..0af8497 100644 --- a/AiQ_GUI.Designer.cs +++ b/AiQ_GUI.Designer.cs @@ -32,7 +32,7 @@ namespace AiQ_GUI { components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); - CbBxCameraType = new ComboBox(); + CbBxCameraModel = new ComboBox(); BtnStartTest = new Button(); BtnFindCams = new Button(); CbBxFoundCams = new ComboBox(); @@ -56,7 +56,6 @@ namespace AiQ_GUI lblZoomLock = new Label(); LblLEDI = new Label(); LblLEDV = new Label(); - RhTxBxActions = new RichTextBox(); LblOVModule = new Label(); LblIRModule = new Label(); BtnTest = new Button(); @@ -89,6 +88,13 @@ namespace AiQ_GUI BtnNo = new Button(); BtnYes = new Button(); LblQuestion = new Label(); + PnlInputValue = new Panel(); + BtnRerun = new Button(); + RMANumBox = new NumericUpDown(); + TxBxProductKey = new TextBox(); + BtnDont = new Button(); + BtnDone = new Button(); + LblRMA = new Label(); label16 = new Label(); BtnPrintGB = new Button(); BtnPrintAiQ = new Button(); @@ -111,13 +117,6 @@ namespace AiQ_GUI label9 = new Label(); BtnGenerate = new Button(); TxBxChallenge = new TextBox(); - PnlInputValue = new Panel(); - BtnRerun = new Button(); - RMANumBox = new NumericUpDown(); - TxBxProductKey = new TextBox(); - BtnDont = new Button(); - BtnDone = new Button(); - LblRMA = new Label(); BtnSetGodMode = new Button(); btnPsu12V = new Button(); btnPsu48V = new Button(); @@ -160,6 +159,8 @@ namespace AiQ_GUI LblGUIVers = new Label(); timerTypeIP = new System.Windows.Forms.Timer(components); TimerFlash = new System.Windows.Forms.Timer(components); + RhTxBxActions = new RichTextBox(); + CbBxCamType = new ComboBox(); ((System.ComponentModel.ISupportInitialize)PicBxMAV).BeginInit(); ((System.ComponentModel.ISupportInitialize)PicBxAiQ).BeginInit(); ((System.ComponentModel.ISupportInitialize)PicBxIRF2).BeginInit(); @@ -181,25 +182,23 @@ namespace AiQ_GUI TabSoak.SuspendLayout(); SuspendLayout(); // - // CbBxCameraType - // - CbBxCameraType.BackColor = Color.FromArgb(53, 51, 64); - CbBxCameraType.DropDownHeight = 500; - CbBxCameraType.DropDownStyle = ComboBoxStyle.DropDownList; - CbBxCameraType.DropDownWidth = 350; - CbBxCameraType.FlatStyle = FlatStyle.Flat; - CbBxCameraType.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); - CbBxCameraType.ForeColor = SystemColors.Control; - CbBxCameraType.FormattingEnabled = true; - CbBxCameraType.IntegralHeight = false; - CbBxCameraType.Location = new Point(10, 298); - CbBxCameraType.Margin = new Padding(4, 3, 4, 3); - CbBxCameraType.MaxDropDownItems = 30; - CbBxCameraType.Name = "CbBxCameraType"; - CbBxCameraType.Size = new Size(494, 25); - CbBxCameraType.TabIndex = 188; - CbBxCameraType.SelectedIndexChanged += CmBxCameraType_SelectedIndexChanged; + // CbBxCameraModel // + CbBxCameraModel.BackColor = Color.FromArgb(53, 51, 64); + CbBxCameraModel.DropDownHeight = 500; + CbBxCameraModel.DropDownStyle = ComboBoxStyle.DropDownList; + CbBxCameraModel.DropDownWidth = 350; + CbBxCameraModel.FlatStyle = FlatStyle.Flat; + CbBxCameraModel.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + CbBxCameraModel.ForeColor = SystemColors.Control; + CbBxCameraModel.FormattingEnabled = true; + CbBxCameraModel.IntegralHeight = false; + CbBxCameraModel.Location = new Point(10, 298); + CbBxCameraModel.Margin = new Padding(4, 3, 4, 3); + CbBxCameraModel.MaxDropDownItems = 30; + CbBxCameraModel.Name = "CbBxCameraModel"; + CbBxCameraModel.Size = new Size(494, 25); + CbBxCameraModel.TabIndex = 188; // BtnStartTest // BtnStartTest.BackColor = Color.FromArgb(70, 65, 80); @@ -535,20 +534,6 @@ namespace AiQ_GUI LblLEDV.TabIndex = 185; LblLEDV.Text = "LED Voltage = "; // - // RhTxBxActions - // - RhTxBxActions.BackColor = Color.FromArgb(53, 51, 64); - RhTxBxActions.BorderStyle = BorderStyle.None; - RhTxBxActions.Cursor = Cursors.IBeam; - RhTxBxActions.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); - RhTxBxActions.ForeColor = SystemColors.Control; - RhTxBxActions.Location = new Point(10, 355); - RhTxBxActions.Margin = new Padding(4, 3, 4, 3); - RhTxBxActions.Name = "RhTxBxActions"; - RhTxBxActions.Size = new Size(495, 156); - RhTxBxActions.TabIndex = 187; - RhTxBxActions.Text = ""; - // // LblOVModule // LblOVModule.BackColor = Color.Transparent; @@ -937,6 +922,7 @@ namespace AiQ_GUI PnlQuestion.Controls.Add(BtnNo); PnlQuestion.Controls.Add(BtnYes); PnlQuestion.Controls.Add(LblQuestion); + PnlQuestion.Controls.Add(PnlInputValue); PnlQuestion.Location = new Point(251, 14); PnlQuestion.Margin = new Padding(4, 3, 4, 3); PnlQuestion.Name = "PnlQuestion"; @@ -989,6 +975,108 @@ namespace AiQ_GUI LblQuestion.Text = "Test failed, appeal?\r\nSee Actions textbox for details."; LblQuestion.TextAlign = ContentAlignment.MiddleCenter; // + // PnlInputValue + // + PnlInputValue.BackColor = Color.FromArgb(39, 37, 55); + PnlInputValue.Controls.Add(BtnRerun); + PnlInputValue.Controls.Add(RMANumBox); + PnlInputValue.Controls.Add(TxBxProductKey); + PnlInputValue.Controls.Add(BtnDont); + PnlInputValue.Controls.Add(BtnDone); + PnlInputValue.Controls.Add(LblRMA); + PnlInputValue.Location = new Point(0, 0); + PnlInputValue.Margin = new Padding(4, 3, 4, 3); + PnlInputValue.Name = "PnlInputValue"; + PnlInputValue.Size = new Size(254, 127); + PnlInputValue.TabIndex = 199; + PnlInputValue.Visible = false; + // + // BtnRerun + // + BtnRerun.BackColor = Color.FromArgb(70, 65, 80); + BtnRerun.FlatAppearance.BorderSize = 0; + BtnRerun.FlatStyle = FlatStyle.Flat; + BtnRerun.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + BtnRerun.ForeColor = SystemColors.Control; + BtnRerun.Location = new Point(132, 82); + BtnRerun.Margin = new Padding(4, 3, 4, 3); + BtnRerun.Name = "BtnRerun"; + BtnRerun.Size = new Size(113, 37); + BtnRerun.TabIndex = 177; + BtnRerun.Text = "Rerun"; + BtnRerun.UseVisualStyleBackColor = false; + BtnRerun.Click += BtnRerun_Click; + // + // RMANumBox + // + RMANumBox.BackColor = Color.FromArgb(53, 51, 64); + RMANumBox.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + RMANumBox.ForeColor = SystemColors.Window; + RMANumBox.Location = new Point(10, 38); + RMANumBox.Margin = new Padding(4, 3, 4, 3); + RMANumBox.Maximum = new decimal(new int[] { 99999, 0, 0, 0 }); + RMANumBox.Name = "RMANumBox"; + RMANumBox.Size = new Size(113, 25); + RMANumBox.TabIndex = 175; + // + // TxBxProductKey + // + TxBxProductKey.BackColor = Color.FromArgb(53, 51, 64); + TxBxProductKey.BorderStyle = BorderStyle.FixedSingle; + TxBxProductKey.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + TxBxProductKey.ForeColor = SystemColors.Control; + TxBxProductKey.Location = new Point(10, 46); + TxBxProductKey.Margin = new Padding(4, 3, 4, 3); + TxBxProductKey.Name = "TxBxProductKey"; + TxBxProductKey.Size = new Size(114, 25); + TxBxProductKey.TabIndex = 162; + TxBxProductKey.TextAlign = HorizontalAlignment.Center; + TxBxProductKey.Visible = false; + // + // BtnDont + // + BtnDont.BackColor = Color.FromArgb(70, 65, 80); + BtnDont.FlatAppearance.BorderSize = 0; + BtnDont.FlatStyle = FlatStyle.Flat; + BtnDont.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + BtnDont.ForeColor = SystemColors.Control; + BtnDont.Location = new Point(12, 82); + BtnDont.Margin = new Padding(4, 3, 4, 3); + BtnDont.Name = "BtnDont"; + BtnDont.Size = new Size(112, 37); + BtnDont.TabIndex = 176; + BtnDont.Text = "Don't have one"; + BtnDont.UseVisualStyleBackColor = false; + BtnDont.Click += BtnDont_Click; + // + // BtnDone + // + BtnDone.BackColor = Color.FromArgb(70, 65, 80); + BtnDone.FlatAppearance.BorderSize = 0; + BtnDone.FlatStyle = FlatStyle.Flat; + BtnDone.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + BtnDone.ForeColor = SystemColors.Control; + BtnDone.Location = new Point(132, 38); + BtnDone.Margin = new Padding(4, 3, 4, 3); + BtnDone.Name = "BtnDone"; + BtnDone.Size = new Size(113, 37); + BtnDone.TabIndex = 174; + BtnDone.Text = "Done"; + BtnDone.UseVisualStyleBackColor = false; + BtnDone.Click += BtnDone_Click; + // + // LblRMA + // + LblRMA.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + LblRMA.ForeColor = SystemColors.Control; + LblRMA.Location = new Point(13, 6); + LblRMA.Margin = new Padding(4, 0, 4, 0); + LblRMA.Name = "LblRMA"; + LblRMA.Size = new Size(233, 29); + LblRMA.TabIndex = 145; + LblRMA.Text = "What is the RMA number?"; + LblRMA.TextAlign = ContentAlignment.MiddleCenter; + // // label16 // label16.AutoSize = true; @@ -1318,108 +1406,6 @@ namespace AiQ_GUI TxBxChallenge.TextAlign = HorizontalAlignment.Center; TxBxChallenge.TextChanged += TxBxChallenge_TextChanged; // - // PnlInputValue - // - PnlInputValue.BackColor = Color.FromArgb(39, 37, 55); - PnlInputValue.Controls.Add(BtnRerun); - PnlInputValue.Controls.Add(RMANumBox); - PnlInputValue.Controls.Add(TxBxProductKey); - PnlInputValue.Controls.Add(BtnDont); - PnlInputValue.Controls.Add(BtnDone); - PnlInputValue.Controls.Add(LblRMA); - PnlInputValue.Location = new Point(251, 155); - PnlInputValue.Margin = new Padding(4, 3, 4, 3); - PnlInputValue.Name = "PnlInputValue"; - PnlInputValue.Size = new Size(254, 127); - PnlInputValue.TabIndex = 199; - PnlInputValue.Visible = false; - // - // BtnRerun - // - BtnRerun.BackColor = Color.FromArgb(70, 65, 80); - BtnRerun.FlatAppearance.BorderSize = 0; - BtnRerun.FlatStyle = FlatStyle.Flat; - BtnRerun.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); - BtnRerun.ForeColor = SystemColors.Control; - BtnRerun.Location = new Point(132, 82); - BtnRerun.Margin = new Padding(4, 3, 4, 3); - BtnRerun.Name = "BtnRerun"; - BtnRerun.Size = new Size(113, 37); - BtnRerun.TabIndex = 177; - BtnRerun.Text = "Rerun"; - BtnRerun.UseVisualStyleBackColor = false; - BtnRerun.Click += BtnRerun_Click; - // - // RMANumBox - // - RMANumBox.BackColor = Color.FromArgb(53, 51, 64); - RMANumBox.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); - RMANumBox.ForeColor = SystemColors.Window; - RMANumBox.Location = new Point(10, 38); - RMANumBox.Margin = new Padding(4, 3, 4, 3); - RMANumBox.Maximum = new decimal(new int[] { 99999, 0, 0, 0 }); - RMANumBox.Name = "RMANumBox"; - RMANumBox.Size = new Size(113, 25); - RMANumBox.TabIndex = 175; - // - // TxBxProductKey - // - TxBxProductKey.BackColor = Color.FromArgb(53, 51, 64); - TxBxProductKey.BorderStyle = BorderStyle.FixedSingle; - TxBxProductKey.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); - TxBxProductKey.ForeColor = SystemColors.Control; - TxBxProductKey.Location = new Point(10, 46); - TxBxProductKey.Margin = new Padding(4, 3, 4, 3); - TxBxProductKey.Name = "TxBxProductKey"; - TxBxProductKey.Size = new Size(114, 25); - TxBxProductKey.TabIndex = 162; - TxBxProductKey.TextAlign = HorizontalAlignment.Center; - TxBxProductKey.Visible = false; - // - // BtnDont - // - BtnDont.BackColor = Color.FromArgb(70, 65, 80); - BtnDont.FlatAppearance.BorderSize = 0; - BtnDont.FlatStyle = FlatStyle.Flat; - BtnDont.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); - BtnDont.ForeColor = SystemColors.Control; - BtnDont.Location = new Point(12, 82); - BtnDont.Margin = new Padding(4, 3, 4, 3); - BtnDont.Name = "BtnDont"; - BtnDont.Size = new Size(112, 37); - BtnDont.TabIndex = 176; - BtnDont.Text = "Don't have one"; - BtnDont.UseVisualStyleBackColor = false; - BtnDont.Click += BtnDont_Click; - // - // BtnDone - // - BtnDone.BackColor = Color.FromArgb(70, 65, 80); - BtnDone.FlatAppearance.BorderSize = 0; - BtnDone.FlatStyle = FlatStyle.Flat; - BtnDone.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); - BtnDone.ForeColor = SystemColors.Control; - BtnDone.Location = new Point(132, 38); - BtnDone.Margin = new Padding(4, 3, 4, 3); - BtnDone.Name = "BtnDone"; - BtnDone.Size = new Size(113, 37); - BtnDone.TabIndex = 174; - BtnDone.Text = "Done"; - BtnDone.UseVisualStyleBackColor = false; - BtnDone.Click += BtnDone_Click; - // - // LblRMA - // - LblRMA.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); - LblRMA.ForeColor = SystemColors.Control; - LblRMA.Location = new Point(13, 6); - LblRMA.Margin = new Padding(4, 0, 4, 0); - LblRMA.Name = "LblRMA"; - LblRMA.Size = new Size(233, 29); - LblRMA.TabIndex = 145; - LblRMA.Text = "What is the RMA number?"; - LblRMA.TextAlign = ContentAlignment.MiddleCenter; - // // BtnSetGodMode // BtnSetGodMode.BackColor = Color.FromArgb(70, 65, 80); @@ -2056,6 +2042,39 @@ namespace AiQ_GUI // timerTypeIP.Interval = 2000; timerTypeIP.Tick += timerTypeIP_Tick; + // + // RhTxBxActions + // + RhTxBxActions.BackColor = Color.FromArgb(53, 51, 64); + RhTxBxActions.BorderStyle = BorderStyle.None; + RhTxBxActions.Cursor = Cursors.IBeam; + RhTxBxActions.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + RhTxBxActions.ForeColor = SystemColors.Control; + RhTxBxActions.Location = new Point(10, 355); + RhTxBxActions.Margin = new Padding(4, 3, 4, 3); + RhTxBxActions.Name = "RhTxBxActions"; + RhTxBxActions.Size = new Size(495, 156); + RhTxBxActions.TabIndex = 187; + RhTxBxActions.Text = ""; + // + // CbBxCamType + // + CbBxCamType.BackColor = Color.FromArgb(53, 51, 64); + CbBxCamType.FlatStyle = FlatStyle.Flat; + CbBxCamType.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); + CbBxCamType.ForeColor = SystemColors.Control; + CbBxCamType.FormattingEnabled = true; + CbBxCamType.Items.AddRange(new object[] { "AiQ", "Mobile" }); + CbBxCamType.Location = new Point(251, 262); + CbBxCamType.Margin = new Padding(4, 3, 4, 3); + CbBxCamType.MaxDropDownItems = 20; + CbBxCamType.Name = "CbBxCamType"; + CbBxCamType.Size = new Size(233, 25); + CbBxCamType.TabIndex = 241; + CbBxCamType.Text = "Select Camera Type"; + this.CbBxCamType.SelectedIndexChanged += new System.EventHandler(this.CbBxCamType_SelectedIndexChanged); + + // // MainForm // @@ -2063,23 +2082,23 @@ namespace AiQ_GUI AutoScaleMode = AutoScaleMode.Font; BackgroundImage = Properties.Resources.homepage_banner; ClientSize = new Size(932, 970); + Controls.Add(RhTxBxActions); + Controls.Add(CbBxCamType); Controls.Add(LblGUIVers); Controls.Add(BtnOpenWebpage); Controls.Add(TabImagesandSettings); Controls.Add(PicBxMAV); Controls.Add(PicBxAiQ); Controls.Add(PnlLbls); - Controls.Add(PnlInputValue); Controls.Add(PnlQuestion); Controls.Add(LblActions); - Controls.Add(CbBxCameraType); + Controls.Add(CbBxCameraModel); Controls.Add(CbBxUserName); Controls.Add(BtnPreTest); Controls.Add(BtnFindCams); Controls.Add(BtnStartTest); Controls.Add(CbBxFoundCams); Controls.Add(BtnTest); - Controls.Add(RhTxBxActions); Controls.Add(BtnRestart); Controls.Add(BtnMin); Controls.Add(BtnClose); @@ -2144,9 +2163,8 @@ namespace AiQ_GUI private System.Windows.Forms.Label lblZoomLock; private System.Windows.Forms.Label LblLEDI; private System.Windows.Forms.Label LblLEDV; - private System.Windows.Forms.ComboBox CbBxCameraType; + private System.Windows.Forms.ComboBox CbBxCameraModel; private System.Windows.Forms.Button BtnPreTest; - private System.Windows.Forms.RichTextBox RhTxBxActions; private System.Windows.Forms.Label LblOVModule; private System.Windows.Forms.Label LblIRModule; private System.Windows.Forms.Button BtnTest; @@ -2251,5 +2269,7 @@ namespace AiQ_GUI private Button BtnFactoryDefault; private Button BtnUploadWonwooSetOV; private Button BtnUploadWonwooSetIR; + private RichTextBox RhTxBxActions; + public ComboBox CbBxCamType; } } diff --git a/AiQ_GUI.cs b/AiQ_GUI.cs index 4d23046..b5f49e0 100644 --- a/AiQ_GUI.cs +++ b/AiQ_GUI.cs @@ -65,12 +65,9 @@ namespace AiQ_GUI this.Name = "AiQ GUI V" + GUIUpdate.GUIVerShort; LblGUIVers.Text += GUIUpdate.GUIVerShort; await UniDataTask; // Have to wait for expected GUI version to compare to. - Task modelListTask = Task.Run(() => Access.ReadCamTypes());// Get all model numbers GUIUpdate.UpdateGUI(); // Check if a GUI update is available - string[] ArrayOfModels = await modelListTask; - if (ArrayOfModels != null) // Returns null if no models found or file doesn't exists - CbBxCameraType.Items.AddRange(await modelListTask); // Fill in the model number drop down + // Load local data store localDataStore = await LDSWAIT; @@ -84,7 +81,7 @@ namespace AiQ_GUI Task CheckHWOnline = PingCheck(); // Async check all hardware is online PopulateUIWithLDS(localDataStore); // Update fields that depend on LDS - CbBxCameraType.Text = localDataStore.LastModel; // Set last model that was tested into combobox + CbBxCameraModel.Text = localDataStore.LastModel; // Set last model that was tested into combobox BtnSave.BackColor = BtnSave.Enabled ? Color.ForestGreen : BtnSave.BackColor; // Sets the colour of the save button depending on if it is enabled. BtnRefreshUnix_Click(sender, e); // Reset timestamp @@ -96,6 +93,20 @@ namespace AiQ_GUI Flags.Start = false; } + private async void CbBxCamType_SelectedIndexChanged(object sender, EventArgs e) + { + CbBxCameraModel.Items.Clear(); // REQUIRED + + string camType = CbBxCamType.Text; + + string[] models = await Task.Run(() => Access.ReadCamTypes(camType)); + + if (models != null && models.Length > 0) + CbBxCameraModel.Items.AddRange(models); + } + + + private void PopulateUIWithLDS(LocalDataStore lds) { CbBxUserName.Text = lds.User; @@ -251,7 +262,7 @@ namespace AiQ_GUI sshData = SSH.CollectSSHData(CamOnTest.IP); // SSH into camera to get Vaxtor packages, filesystem size and if tailscale is installed. await SSH.CheckFSSize(CamOnTest.IP, LblFilesystemSize, sshData); // Check Filesystem size is between 100GB & 150GB - + Helper.DCPowerCheck(LblDC); // If the camera is DC powered check it is within limits // Requests, deserialises and checks the diagnostics API is correct @@ -693,7 +704,7 @@ namespace AiQ_GUI { // Update the serial number register with new cameras details // Cam description is in model drop down 6 digit model num + 3 for " - " - string NewSerial = GoogleAPI.UpdateSpreadSheetPreTest(CameraAccessInfo.SpreadsheetID, Vers, CbBxCameraType.Text.Substring(9), CamOnTest.Model); + string NewSerial = GoogleAPI.UpdateSpreadSheetPreTest(CameraAccessInfo.SpreadsheetID, Vers, CbBxCameraModel.Text.Substring(9), CamOnTest.Model); if (NewSerial.Contains("ERROR")) { @@ -731,10 +742,10 @@ namespace AiQ_GUI private void BtnClose_Click(object sender, EventArgs e) { // Save user settings in LDS for next time if values are valid - if (CbBxUserName.Text.Length > 2 && CbBxCameraType.Text.Length > 6) + if (CbBxUserName.Text.Length > 2 && CbBxCameraModel.Text.Length > 6) { localDataStore.User = CbBxUserName.Text; - localDataStore.LastModel = CbBxCameraType.Text; + localDataStore.LastModel = CbBxCameraModel.Text; LDS.SetLDS(localDataStore); } @@ -748,7 +759,7 @@ namespace AiQ_GUI private void BtnRestart_Click(object sender, EventArgs e) { - Helper.RestartApp(); // Restart abruptly don't worry about anything else going on + Helper.RestartApp(); // Restart abruptly don't worry about anything else going on } // ***** Allows moving GUI by grab and dragging ***** @@ -820,12 +831,6 @@ namespace AiQ_GUI Flags.No = true; } - private void CmBxCameraType_SelectedIndexChanged(object sender, EventArgs e) - { - CamOnTest.Model = CbBxCameraType.Text.Substring(0, 6); // Model on test is the first 6 characters of the textbox - Access.ReadModelRow(CamOnTest.Model); // Fill in all info about current model number - TestStartConditions(); - } private void CmBoBoxUserName_TextChanged(object sender, EventArgs e) { @@ -843,19 +848,23 @@ namespace AiQ_GUI timerTypeIP.Enabled = false; // Stop this triggering again CamOnTest.DevPass = TxBxOutput.Text = RhTxBxActions.Text = ""; // Blank password & Clear actions box as it is now a different camera - if (RegexCache.RegexIPPattern().IsMatch(CbBxFoundCams.Text)) // Check IP address is valid + string selectedText = CbBxFoundCams.Text.Trim(); + string ipOnly = selectedText.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0]; + + if (RegexCache.RegexIPPattern().IsMatch(ipOnly)) // Check IP address is valid { - if (!await Network.PingIP(CbBxFoundCams.Text)) + if (!await Network.PingIP(ipOnly)) { CbBxFoundCams.BackColor = Color.Red; return; } - CamOnTest.IP = CbBxFoundCams.Text; // Set the IP address under test + CamOnTest.IP = ipOnly; //Always store clean IP CbBxFoundCams.BackColor = BtnColour; BtnSecret.Enabled = true; - Vers = await FlexiAPI.GetVersions(CamOnTest.IP); + Vers = await FlexiAPI.GetVersions(ipOnly); + // Wont be filled out before the pre test but needed for final test if (RegexCache.SerialRegex().IsMatch(CamOnTest.Serial) && RegexCache.ModelRegex().IsMatch(CamOnTest.Model)) @@ -872,12 +881,12 @@ namespace AiQ_GUI Lics.DisplayDevPassword(Vers, CamOnTest); // Generate and display secret for use later - string networkConfigText = await FlexiAPI.ProcessNetworkConfig(CamOnTest.IP); + string networkConfigText = await FlexiAPI.ProcessNetworkConfig(ipOnly); BtnSet211.Text = string.IsNullOrEmpty(networkConfigText) ? "Set to 211" : networkConfigText; ShowToolTip(BtnSecret); // Set dev password to Tooltip and clipboard } - else if (CbBxFoundCams.Text.Contains("Found")) + else if (selectedText.Contains("Found")) { CbBxFoundCams.BackColor = BtnColour; } @@ -957,7 +966,7 @@ namespace AiQ_GUI RhTxBxActions.SelectionColor = Color.LightGreen; } - RhTxBxActions.AppendText(Mssg + Environment.NewLine); + RhTxBxActions.AppendText(Mssg + Environment.NewLine); RhTxBxActions.SelectionStart = RhTxBxActions.Text.Length; RhTxBxActions.SelectionColor = SystemColors.Control; RhTxBxActions.ScrollToCaret(); @@ -976,7 +985,7 @@ namespace AiQ_GUI if (await Network.PingIP("8.8.8.8")) // Ping to find if we are online { Flags.Offline = false; // Ping succeeded - CbBxCameraType.Enabled = true; + CbBxCameraModel.Enabled = true; } else TSC = SetInvalid("Offline Mode, could not connect to the internet."); // Ping failed @@ -993,7 +1002,7 @@ namespace AiQ_GUI TSC = SetInvalid("Select Username."); // Model number selected - if (CbBxCameraType.SelectedIndex == -1) + if (CbBxCameraModel.SelectedIndex == -1) TSC = SetInvalid("Select Model number."); // Settings IP addresses filled in @@ -1494,15 +1503,19 @@ namespace AiQ_GUI private void BtnOpenWebpage_Click(object sender, EventArgs e) { + // Cut off anything after the first space (e.g. " - Onvif") + string ip = CamOnTest.IP.Split(' ')[0]; + ProcessStartInfo psi = new() { - FileName = $"http://{CamOnTest.IP}", // Just the URL - UseShellExecute = true // Lets the OS decide how to open it + FileName = $"http://{ip}", + UseShellExecute = true }; Process.Start(psi); } + private async void UploadWonwooSetOV_Click(object sender, EventArgs e) { await FlexiAPI.UploadWonwooSet(CbBxFoundCams.Text, false); // false = Colour @@ -1543,7 +1556,7 @@ namespace AiQ_GUI { BtnPrintGB.Enabled = true; - if (CbBxCameraType.SelectedIndex != -1 && RegexCache.SerialRegex().IsMatch(TxBxSerialPrint.Text)) // Check model and serial are known + if (CbBxCameraModel.SelectedIndex != -1 && RegexCache.SerialRegex().IsMatch(TxBxSerialPrint.Text)) // Check model and serial are known { Printer.ZebraIP = localDataStore.ZebraIP; BtnPrintAiQ.Enabled = true; @@ -1739,8 +1752,18 @@ namespace AiQ_GUI // PDF.CreateSoakTestReport(NewCam, "SoakTestRig", DateTime.Now, file); // } - //} + + var password = @"mavPA\$\$"; + + var psi = new ProcessStartInfo + { + FileName = @"C:\Program Files (x86)\Mobatek\MobaXterm\MobaXterm.exe", + Arguments = $@"-newtab -ssh {CamOnTest.IP} -user mav -pass ""{password}""", + UseShellExecute = true + }; + + Process.Start(psi); stopWatchTest.Stop(); AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff"), Level.LOG); } diff --git a/AiQ_GUI_NET_Test.csproj b/AiQ_GUI_NET_Test.csproj index 058f3d1..beb9a14 100644 --- a/AiQ_GUI_NET_Test.csproj +++ b/AiQ_GUI_NET_Test.csproj @@ -55,9 +55,13 @@ + + + + diff --git a/Camera/FlexiAPI.cs b/Camera/FlexiAPI.cs index 5c735ff..3c57b74 100644 --- a/Camera/FlexiAPI.cs +++ b/Camera/FlexiAPI.cs @@ -24,7 +24,7 @@ namespace AiQ_GUI } catch (Exception ex) { - return $"Error during GET request: {ex.Message}{Level.ERROR}"; + return $"Error during GET request: {ex.Message}"; } } @@ -42,7 +42,7 @@ namespace AiQ_GUI } catch (Exception ex) { - return $"Error in HTTP_Update: {ex.Message}{Level.ERROR}"; + return $"Error in HTTP_Update: {ex.Message}"; } } @@ -56,7 +56,7 @@ namespace AiQ_GUI } catch (Exception ex) { - return $"Error in HTTP_Fetch: {ex.Message}{Level.ERROR}"; + return $"Error in HTTP_Fetch: {ex.Message}"; } } @@ -101,22 +101,22 @@ namespace AiQ_GUI string responseBody = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) - return $"Server returned {(int)response.StatusCode}: {response.ReasonPhrase}. Details: {responseBody}{Level.ERROR}"; + return $"Server returned {(int)response.StatusCode}: {response.ReasonPhrase}. Details: {responseBody}"; return responseBody; } catch (TaskCanceledException) { - return $"Timeout uploading to {url}.{Level.ERROR}"; + return $"Timeout uploading to {url}."; } catch (HttpRequestException ex) { - return $"HTTP error uploading to {url}: {ex.Message}{Level.ERROR}"; + return $"HTTP error uploading to {url}: {ex.Message}"; } catch (Exception ex) { - return $"Unexpected error uploading to {url}: {ex.Message} {(ex.InnerException?.Message ?? string.Empty)} {Level.ERROR}"; + return $"Unexpected error uploading to {url}: {ex.Message} {(ex.InnerException?.Message ?? string.Empty)}"; } } @@ -146,7 +146,7 @@ namespace AiQ_GUI if (JSON == null || JSON.Contains("Error") || JSON.Contains("Timeout")) { - MainForm.Instance.AddToActionsList($"Error talking to Flexi, are you sure this is an AiQ?{Level.WARNING}" + Environment.NewLine + JSON); + MainForm.Instance.AddToActionsList($"Error talking to Flexi, are you sure this is an AiQ?" + Environment.NewLine + JSON, Level.WARNING); return null; } @@ -174,13 +174,13 @@ namespace AiQ_GUI // Treat "operation was canceled" as a successful apply if (response.Contains("The operation was canceled", StringComparison.OrdinalIgnoreCase)) { - Logging.LogMessage($"SetVaxtorMinMaxPlate: Camera applied config but closed connection early (safe to ignore).{Level.WARNING}"); + Logging.LogMessage($"SetVaxtorMinMaxPlate: Camera applied config but closed connection early (safe to ignore)."); return true; } if (response.Contains("error", StringComparison.OrdinalIgnoreCase)) { - MainForm.Instance.DisplayQuestion($"SetVaxtorMinMaxPlate: failed - Please set manually{Level.WARNING}"); + MainForm.Instance.DisplayQuestion($"SetVaxtorMinMaxPlate: failed - Please set manually"); return false; } @@ -188,7 +188,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Could not set Vaxtor Plate height and min confidence: {ex.Message}{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not set Vaxtor Plate height and min confidence: {ex.Message}", Level.ERROR); return false; } } @@ -217,7 +217,7 @@ namespace AiQ_GUI } catch { - MainForm.Instance.AddToActionsList($"Error reading trim JSON - {Level.ERROR}" + trimData); + MainForm.Instance.AddToActionsList($"Error reading trim JSON - " + trimData, Level.ERROR); return; } @@ -226,7 +226,7 @@ namespace AiQ_GUI { if (RetryCount >= 3) { - await MainForm.Instance.DisplayOK($"Please align trim in webpage then click OK.{Level.WARNING}"); // Awaited till OK has been clicked + await MainForm.Instance.DisplayOK($"Please align trim in webpage then click OK."); // Awaited till OK has been clicked return; } @@ -254,11 +254,11 @@ namespace AiQ_GUI } else // Ask user to centre the plate in the field of view { - await MainForm.Instance.DisplayOK($"Please centralise plate in view THEN press OK {Level.WARNING}"); // Awaited till OK has been clicked + await MainForm.Instance.DisplayOK($"Please centralise plate in view THEN press OK "); // Awaited till OK has been clicked if (RetryCount >= 3) { - await MainForm.Instance.DisplayOK($"Please align trim in webpage then click OK. {Level.WARNING}"); // Awaited till OK has been clicked + await MainForm.Instance.DisplayOK($"Please align trim in webpage then click OK. "); // Awaited till OK has been clicked return; } await Task.Delay(5000); // Give 5 second delay for it to see a plate @@ -275,7 +275,7 @@ namespace AiQ_GUI string TrimResp = await HTTP_Update("SightingCreator", IPAddress, Trim_JSON); if (!TrimResp.Contains($"\"propInterCameraOffsetX\": {{\"value\": \"{Convert.ToString(TrimX)}\", \"datatype\": \"int\"}}, \"propInterCameraOffsetY\": {{\"value\": \"{Convert.ToString(TrimY)}\", \"datatype\": \"int\"}},")) - MainForm.Instance.AddToActionsList($"Could not set camera trim{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not set camera trim", Level.ERROR); } // Processes the network config from the camera and returns a string indicating the status @@ -349,7 +349,7 @@ namespace AiQ_GUI if (FoundCams.Contains("192.168.1.211")) { - MainForm.Instance.AddToActionsList($"Could not set camera to DHCP please check camera.{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not set camera to DHCP please check camera.", Level.ERROR); return false; } diff --git a/Camera/ImageProcessing.cs b/Camera/ImageProcessing.cs index e409458..3bdf530 100644 --- a/Camera/ImageProcessing.cs +++ b/Camera/ImageProcessing.cs @@ -26,7 +26,7 @@ namespace AiQ_GUI HttpResponseMessage response = await httpClient.GetAsync(requestUrl); if (!response.IsSuccessStatusCode) { - MainForm.Instance.AddToActionsList($"No success from {requestUrl} replied {response.StatusCode}{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"No success from {requestUrl} replied {response.StatusCode}", Level.ERROR); return null; } @@ -34,7 +34,7 @@ namespace AiQ_GUI if (imageBytes.Length == 0) // Check if the imageBytes is empty { - MainForm.Instance.AddToActionsList($"No image data received from {requestUrl}{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"No image data received from {requestUrl}", Level.ERROR); return null; } @@ -43,7 +43,7 @@ namespace AiQ_GUI CvInvoke.Imdecode(imageBytes, ImreadModes.AnyColor, mat); if (mat.IsEmpty) { - MainForm.Instance.AddToActionsList($"Failed to decode image with Emgu CV.{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Failed to decode image with Emgu CV.", Level.ERROR); return null; } @@ -70,12 +70,12 @@ namespace AiQ_GUI } catch (HttpRequestException ex) { - MainForm.Instance.AddToActionsList($"HTTP error: {ex.Message}{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"HTTP error: {ex.Message}", Level.ERROR); return null; } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Error processing image: {ex.Message} {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Error processing image: {ex.Message}", Level.ERROR); return null; } } @@ -123,7 +123,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Error awaiting Colour snapshot: {ex.Message} {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Error awaiting Colour snapshot: {ex.Message}", Level.ERROR); return; } @@ -146,7 +146,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Error calculating luminance: {ex.Message} {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Error calculating luminance: {ex.Message}",Level.ERROR); return; } } diff --git a/Camera/LED.cs b/Camera/LED.cs index 230827c..cbdd61b 100644 --- a/Camera/LED.cs +++ b/Camera/LED.cs @@ -45,7 +45,7 @@ } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Error checking LEDs: {ex.Message} {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Error checking LEDs: {ex.Message}", Level.ERROR); } } } diff --git a/Camera/Licences.cs b/Camera/Licences.cs index 5f7af89..efd6598 100644 --- a/Camera/Licences.cs +++ b/Camera/Licences.cs @@ -25,13 +25,13 @@ namespace AiQ_GUI else if (Type == "Audit") salt = Auditsalt; else - return $"Unrecognised challenge type:{Level.ERROR}" + Type; + return $"Unrecognised challenge type:" + Type; if (string.IsNullOrEmpty(challenge) || challenge.Length != 6) // Check challenge format - return $"Invalid challenge format. Challenge must be 6 characters.{Level.ERROR}"; + return $"Invalid challenge format. Challenge must be 6 characters."; if (string.IsNullOrEmpty(salt) || salt.Length != 32) // Check salt format - return $"Invalid salt format. Salt must be 32 characters.{Level.ERROR}"; + return $"Invalid salt format. Salt must be 32 characters."; // Hash computation using SHA256 algorithm byte[] inputBytes = Encoding.UTF8.GetBytes(challenge + " " + salt); // SHA hash format challenge and salt with space between @@ -60,7 +60,7 @@ namespace AiQ_GUI } catch (Exception ex) { - return $"Error: Could not generate password{Level.ERROR}" + ex.Message; + return $"Error: Could not generate password" + ex.Message; } } @@ -85,7 +85,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Exception in FetchDevPassword: {Level.ERROR}" + ex.Message); + MainForm.Instance.AddToActionsList($"Exception in FetchDevPassword:" + ex.Message, Level.ERROR); return null; } } diff --git a/Camera/Router.cs b/Camera/Router.cs index d78d423..88bf31e 100644 --- a/Camera/Router.cs +++ b/Camera/Router.cs @@ -32,7 +32,7 @@ namespace AiQ_GUI } catch { - MainForm.Instance.AddToActionsList($"Is the router on the network? Has the MAV Config file been applied?{Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Is the router on the network? Has the MAV Config file been applied?", Level.WARNING); } return null; @@ -48,31 +48,31 @@ namespace AiQ_GUI if (Strength < 25.0) { - MainForm.Instance.AddToActionsList($"Router signal strength is {Strength} which is below 25%. Please check the router connection.{Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Router signal strength is {Strength} which is below 25%. Please check the router connection.", Level.WARNING); PassTest = false; } if (!Router.SimStatus.Contains("SIM Ready")) { - MainForm.Instance.AddToActionsList($"SIM card is not ready. {Router.SimStatus} Please check the SIM card status.{Level.WARNING}"); + MainForm.Instance.AddToActionsList($"SIM card is not ready. {Router.SimStatus} Please check the SIM card status.", Level.WARNING); PassTest = false; } if (!Router.Port3Status.Contains("port:3 link:up speed:100baseT full-duplex")) { - MainForm.Instance.AddToActionsList($"Port 3 is not connected properly. {Router.Port3Status} Please check the connection. {Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Port 3 is not connected properly. {Router.Port3Status} Please check the connection.", Level.WARNING); PassTest = false; } if (!Router.Port4Status.Contains("port:4 link:up speed:100baseT full-duplex")) { - MainForm.Instance.AddToActionsList($"Port 4 is not connected properly. {Router.Port4Status} Please check the connection. {Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Port 4 is not connected properly. {Router.Port4Status} Please check the connection. ", Level.WARNING); PassTest = false; } if (!Router.GoodPing) { - MainForm.Instance.AddToActionsList($"Router could not ping 8.8.8.8. Please check the online connection.{Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Router could not ping 8.8.8.8. Please check the online connection.", Level.WARNING); PassTest = false; } diff --git a/Camera/SSH.cs b/Camera/SSH.cs index e8c8c08..df11815 100644 --- a/Camera/SSH.cs +++ b/Camera/SSH.cs @@ -25,7 +25,7 @@ namespace AiQ_GUI } catch (Exception Ex) { - MainForm.Instance.AddToActionsList($"SSH connection failed: {Ex.Message}. Check password or network. {Level.WARNING}"); + MainForm.Instance.AddToActionsList($"SSH connection failed: {Ex.Message}. Check password or network. ", Level.WARNING); } return null; @@ -75,7 +75,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"SSH connection failed: {ex.Message}. Check password or network. {Level.WARNING}"); + MainForm.Instance.AddToActionsList($"SSH connection failed: {ex.Message}. Check password or network. ", Level.WARNING); } string LogMssg = string.Join(" | ", typeof(SSHData).GetProperties().Select(p => $"{p.Name}: {p.GetValue(Data)}")); @@ -170,7 +170,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"SSH connection failed: {ex.Message}. Check password or network. {Level.WARNING}"); + MainForm.Instance.AddToActionsList($"SSH connection failed: {ex.Message}. Check password or network. ", Level.WARNING); } return (string.Empty, string.Empty); @@ -323,7 +323,7 @@ namespace AiQ_GUI if (checkDevice.Result.Trim() != "OK") // Device not found { - MainForm.Instance.AddToActionsList($"Block device {device} not found. {Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Block device {device} not found. ", Level.WARNING); return false; } @@ -378,7 +378,7 @@ namespace AiQ_GUI if (checkDevice.Result.Trim().Length > 1) // Device not found { - MainForm.Instance.AddToActionsList($"Cannot sync files to disk. Replied: {checkDevice.Result}. DO NOT TURN OFF, GET SUPERVISOR {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Cannot sync files to disk. Replied: {checkDevice.Result}. DO NOT TURN OFF, GET SUPERVISOR", Level.ERROR); return; } @@ -387,7 +387,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Cannot sync becuase: {ex.Message}. DO NOT TURN OFF, GET SUPERVISOR {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Cannot sync becuase: {ex.Message}. DO NOT TURN OFF, GET SUPERVISOR", Level.ERROR); } } } diff --git a/GoogleAPI.cs b/GoogleAPI.cs index d905c3e..0582679 100644 --- a/GoogleAPI.cs +++ b/GoogleAPI.cs @@ -107,12 +107,12 @@ namespace AiQ_GUI } else { - return $"Last serial number not found{Level.ERROR}"; + return $"Last serial number not found"; } } catch (Exception ex) { - return $"ERROR: {ex.Message}{Level.ERROR}"; + return $"ERROR: {ex.Message}"; } } @@ -148,12 +148,12 @@ namespace AiQ_GUI } else { - return $"Serial number not found{Level.ERROR}"; + return $"Serial number not found"; } } catch (Exception ex) { - return $"ERROR: {ex.Message}{Level.ERROR}"; + return $"ERROR: {ex.Message}"; } } @@ -196,7 +196,7 @@ namespace AiQ_GUI } catch (Exception ex) { - return $"Failed to update spreadsheet data, please check manually{Level.ERROR}" + ex.Message; + return $"Failed to update spreadsheet data, please check manually" + ex.Message; } } @@ -225,7 +225,7 @@ namespace AiQ_GUI } catch (Exception ex) { - return $"Failed to update spreadsheet data, please check manually{Level.WARNING}" + ex.Message; + return $"Failed to update spreadsheet data, please check manually" + ex.Message; } } diff --git a/Helper.cs b/Helper.cs index b9a1542..d8f2cb5 100644 --- a/Helper.cs +++ b/Helper.cs @@ -83,26 +83,26 @@ namespace AiQ_GUI case 0: if (!await MainForm.Instance.DisplayQuestion("Is the sleeve aligned correctly?")) { - await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Sleeve not aligned{Level.ERROR}"); + await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Sleeve not aligned"); } break; case 1: if (!await MainForm.Instance.DisplayQuestion("Are all the screws fitted in the front?")) { - await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Not all front screws fitted{Level.ERROR}"); + await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Not all front screws fitted"); } break; case 2: if (!await MainForm.Instance.DisplayQuestion("Are all the screws fitted in the rear?")) { - await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Not all rear screws fitted{Level.ERROR}"); + await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Not all rear screws fitted"); } break; case 3: if (await MainForm.Instance.DisplayQuestion("Shake unit, does it rattle?")) { - await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Unit rattles{Level.ERROR}"); + await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Unit rattles"); } break; default: @@ -123,7 +123,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Error fetching versions for {IPAddress}: {ex.Message}{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Error fetching versions for {IPAddress}: {ex.Message}", Level.ERROR); return null; } @@ -187,6 +187,7 @@ namespace AiQ_GUI public bool IsChecked { get; set; } // Is it checked for soak test? public CheckBox CheckBox { get; set; } = new CheckBox(); public string TestReportLoc { get; set; } = string.Empty; // Location of test report file + public string Manfr { get; internal set; } } // Static class for global flags diff --git a/LDS.cs b/LDS.cs index 52f583d..7ffa46a 100644 --- a/LDS.cs +++ b/LDS.cs @@ -30,7 +30,7 @@ namespace AiQ_GUI } catch // If file can't deserialise { - MainForm.Instance.AddToActionsList($"Error loading Local Data Store{Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Error loading Local Data Store", Level.WARNING); return null; // Return null to indicate failure } } @@ -45,7 +45,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Error saving Local Data Store: {ex.Message}{Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Error saving Local Data Store: {ex.Message}", Level.WARNING); } } } diff --git a/Microsoft/Access.cs b/Microsoft/Access.cs index f8d1d29..dea91cc 100644 --- a/Microsoft/Access.cs +++ b/Microsoft/Access.cs @@ -4,14 +4,32 @@ namespace AiQ_GUI { class Access { - public const string connString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=G:\Shared drives\MAV Production GUI's\AiQ\GUI's\AiQ_Final_Test.accdb;Persist Security Info=False;OLE DB Services=-1;"; + + public const string connString = + @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=G:\Shared drives\MAV Production GUI's\AiQ\GUI's\AiQ_Final_Test.accdb;Persist Security Info=False;OLE DB Services=-1;"; - // Reads camera model numbers and descriptions from the database, sorts them alphabetically by model number (except "AB12CD", which appears last), and formats each entry as "ModelNumber - Description". - public static string[] ReadCamTypes() + // Allows Different Cam Types with different schemas to be handled safely + + private static bool HasColumn(OleDbDataReader reader, string columnName) { - List> modelTuples = new(30); // Preallocate list with estimated capacity to reduce internal resizing + for (int i = 0; i < reader.FieldCount; i++) + if (reader.GetName(i).Equals(columnName, StringComparison.OrdinalIgnoreCase)) + return true; + + return false; + } + public static string[] ReadCamTypes(string camType) + { + // No camera type selected + if (string.IsNullOrWhiteSpace(camType)) + return null; + + // Preallocate list to reduce resizing + List> modelTuples = new(30); + using OleDbConnection conn = new(connString); + // Attempt to open the database try { conn.Open(); @@ -22,31 +40,36 @@ namespace AiQ_GUI return null; } - const string query = "SELECT ModelNumber, Description FROM AiQ WHERE MarkNumber > 1"; + // Mobile table does not have MarkNumber + // AiQ and others might do ????? - TODO ask + string query = camType == "Mobile" + ? "SELECT ModelNumber, Description FROM [Mobile]" + : $"SELECT ModelNumber, Description FROM [{camType}] WHERE MarkNumber > 1"; using OleDbCommand cmd = new(query, conn); using OleDbDataReader reader = cmd.ExecuteReader(); + + // Read all models from the selected table while (reader.Read()) { - // Extract each model number and description, using empty string if null string modelNumber = reader["ModelNumber"] as string ?? string.Empty; string description = reader["Description"] as string ?? string.Empty; + modelTuples.Add(Tuple.Create(modelNumber.Trim(), description.Trim())); } - // Sort: push "AB12CD" to the bottom, then sort remaining items alphabetically - IOrderedEnumerable> sorted = modelTuples.OrderBy(t => t.Item1.Equals("AB12CD", StringComparison.OrdinalIgnoreCase) ? 1 : 0) - .ThenBy(t => t.Item1, StringComparer.OrdinalIgnoreCase); + // Sort models, pushing AB12CD to the bottom + var sorted = modelTuples + .OrderBy(t => t.Item1.Equals("AB12CD", StringComparison.OrdinalIgnoreCase) ? 1 : 0) + .ThenBy(t => t.Item1, StringComparer.OrdinalIgnoreCase); - conn.Close(); - // Format the sorted tuples as "ModelNumber - Description" strings and return as array + // Format for combo box display return sorted.Select(t => $"{t.Item1} - {t.Item2}").ToArray(); } - - // Read the universal data table from the database and populate the UniversalData class with the values. public static void ReadUniData() { using OleDbConnection conn = new(connString); + try { conn.Open(); @@ -57,24 +80,27 @@ namespace AiQ_GUI return; } - const string query = "SELECT FlexiVersion, FlexiRevision, WonwooFirmware, AiQGUIVersion, PowerConsumption, LicencingServerURL FROM UniversalData"; // Grab the universal data + const string query = + "SELECT FlexiVersion, FlexiRevision, WonwooFirmware, AiQGUIVersion, PowerConsumption, LicencingServerURL FROM UniversalData"; using OleDbCommand cmd = new(query, conn); using OleDbDataReader reader = cmd.ExecuteReader(); + + // UniversalData is expected to contain a single row reader.Read(); + UniversalData.ExpFlexiVer = Convert.ToString(reader["FlexiVersion"]); UniversalData.ExpFlexiRev = Convert.ToString(reader["FlexiRevision"]); UniversalData.WonwooFirmware = Convert.ToString(reader["WonwooFirmware"]); UniversalData.LatestVersion = Convert.ToString(reader["AiQGUIVersion"]); UniversalData.PowerConsumption = Convert.ToInt16(reader["PowerConsumption"]); UniversalData.LicencingServerURL = Convert.ToString(reader["LicencingServerURL"]); - conn.Close(); } - - // Knowing the model number on test, this function reads the database and populates the Camera class with the values. - public static void ReadModelRow(string ModelOnTest) + // Populates CameraAccessInfo dynamically based on available columns + public static void ReadModelRow(string camType, string ModelOnTest) { using OleDbConnection conn = new(connString); + try { conn.Open(); @@ -85,66 +111,85 @@ namespace AiQ_GUI return; } - string query = $"SELECT * FROM AiQ WHERE ModelNumber = '{ModelOnTest}';"; // Grab all the info for specified model + // Parameterised query to prevent injection + string query = $"SELECT * FROM [{camType}] WHERE ModelNumber = ?"; using OleDbCommand cmd = new(query, conn); + cmd.Parameters.AddWithValue("?", ModelOnTest); + using OleDbDataReader reader = cmd.ExecuteReader(); - reader.Read(); - // Populate the CameraAccessInfo class with the values from the database - CameraAccessInfo.Processor = Convert.ToString(reader["Processor"]); - CameraAccessInfo.VaxtorLic = Convert.ToBoolean(reader["Vaxtor"]); - CameraAccessInfo.HardwareExtras = Convert.ToString(reader["HardwareExtras"]); - CameraAccessInfo.PowerType = Convert.ToString(reader["PowerType"]); - CameraAccessInfo.LED_V = Convert.ToDouble(reader["LEDVoltage"]); - CameraAccessInfo.LED_I = Convert.ToInt32(reader["LEDCurrent"]); - CameraAccessInfo.SpreadsheetID = Convert.ToString(reader["SSID"]); - conn.Close(); + // No matching model found + if (!reader.Read()) + return; + + // Populate CameraAccessInfo only if columns exist + CameraAccessInfo.Processor = + HasColumn(reader, "Processor") ? Convert.ToString(reader["Processor"]) : string.Empty; + + CameraAccessInfo.VaxtorLic = + HasColumn(reader, "Vaxtor") && Convert.ToString(reader["Vaxtor"]) == "Yes"; + + CameraAccessInfo.HardwareExtras = + HasColumn(reader, "HardwareExtras") ? Convert.ToString(reader["HardwareExtras"]) : string.Empty; + + CameraAccessInfo.PowerType = + HasColumn(reader, "PowerType") ? Convert.ToString(reader["PowerType"]) : string.Empty; + + CameraAccessInfo.LED_V = + HasColumn(reader, "LEDVoltage") ? Convert.ToDouble(reader["LEDVoltage"]) : 0; + + CameraAccessInfo.LED_I = + HasColumn(reader, "LEDCurrent") ? Convert.ToInt32(reader["LEDCurrent"]) : 0; + + CameraAccessInfo.SpreadsheetID = + HasColumn(reader, "SSID") ? Convert.ToString(reader["SSID"]) : string.Empty; } - public static void Stats(string TypeOfTest, string modelNumber) { Stats([TypeOfTest], modelNumber); } - public static void Stats(string[] TypeOfTest, string modelNumber) { - using OleDbConnection conn = new(connString); // Opens connection to Access database + using OleDbConnection conn = new(connString); + try { - conn.Open(); // Opens DB + conn.Open(); foreach (string type in TypeOfTest) { - string query = $"UPDATE AiQ SET [{type}] = [{type}] + 1 WHERE [ModelNumber] = ?"; // Add one for every test ran of this type for this model number - using OleDbCommand cmd = new(query, conn); // Create command - cmd.Parameters.AddWithValue("?", modelNumber); // Add model number to prevent injection + string query = + $"UPDATE AiQ SET [{type}] = [{type}] + 1 WHERE [ModelNumber] = ?"; + + using OleDbCommand cmd = new(query, conn); + cmd.Parameters.AddWithValue("?", modelNumber); int rowsAffected = cmd.ExecuteNonQuery(); - // Execute the command and get the number of rows affected - if (rowsAffected == 0) // If one or more rows were updated - MainForm.Instance.AddToActionsList($"No rows affected for {modelNumber}{Level.ERROR}"); + + if (rowsAffected == 0) + MainForm.Instance.AddToActionsList($"No rows affected for {modelNumber}", Level.ERROR); } - conn.Close(); } catch { - MainForm.Instance.AddToActionsList($"Could not access Access in Google Drive. Is it running?{Level.WARNING}"); - return; + MainForm.Instance.AddToActionsList( + "Could not access Access in Google Drive. Is it running?", Level.WARNING); } } - public static void StatsDiags(string redDiagLabels, string RhTxBxActionsText, string ModelNumber) { using OleDbConnection conn = new(connString); conn.Open(); - // Null checks + // Replace null or empty values string redVal = string.IsNullOrWhiteSpace(redDiagLabels) ? "-" : redDiagLabels; string actVal = string.IsNullOrWhiteSpace(RhTxBxActionsText) ? "-" : RhTxBxActionsText; string model = string.IsNullOrWhiteSpace(ModelNumber) ? "-" : ModelNumber; - const string sql = @"INSERT INTO DiagsStats ([Date], [Model], [Red Diags Labels], [RhTxBxActions Contents]) VALUES (?, ?, ?, ?)"; + const string sql = + @"INSERT INTO DiagsStats ([Date], [Model], [Red Diags Labels], [RhTxBxActions Contents]) + VALUES (?, ?, ?, ?)"; using OleDbCommand cmd = new(sql, conn); @@ -153,19 +198,18 @@ namespace AiQ_GUI OleDbType = OleDbType.Date, Value = DateTime.Now }); + cmd.Parameters.AddWithValue("?", model); cmd.Parameters.AddWithValue("?", redVal); cmd.Parameters.AddWithValue("?", actVal); int rows = cmd.ExecuteNonQuery(); - conn.Close(); if (rows == 0) - MainForm.Instance.AddToActionsList($"No rows inserted into DiagsStats (unexpected).{Level.ERROR}"); + MainForm.Instance.AddToActionsList( + "No rows inserted into DiagsStats (unexpected).", Level.ERROR); } } - - // Expected universal data for the GUI, read from the database public class UniversalData { public static string ExpFlexiVer { get; set; } = string.Empty; @@ -176,7 +220,6 @@ namespace AiQ_GUI public static string LicencingServerURL { get; set; } = string.Empty; } - // One object to contain all the camera info from the model info access database public class CameraAccessInfo { public static string Processor { get; set; } = string.Empty; diff --git a/Microsoft/Excel.cs b/Microsoft/Excel.cs index ab281aa..83161fa 100644 --- a/Microsoft/Excel.cs +++ b/Microsoft/Excel.cs @@ -17,7 +17,7 @@ namespace AiQ_GUI } else { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR); } } @@ -31,7 +31,7 @@ namespace AiQ_GUI } else { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR); } return null; } @@ -40,7 +40,7 @@ namespace AiQ_GUI { if (!File.Exists(FilePath)) { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR); return -1; } @@ -62,7 +62,7 @@ namespace AiQ_GUI { if (!File.Exists(FilePath)) { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR); return "Spreadsheet not found"; } @@ -114,7 +114,7 @@ namespace AiQ_GUI { if (!File.Exists(filePath)) { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :) {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :) ", Level.ERROR); return "Spreadsheet not found"; } @@ -161,7 +161,7 @@ namespace AiQ_GUI { if (!File.Exists(FilePath)) { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR); return "Spreadsheet not found"; } @@ -213,7 +213,7 @@ namespace AiQ_GUI { if (!File.Exists(FilePath)) { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR); return $"Spreadsheet not found{ Level.ERROR}" ; } @@ -251,7 +251,7 @@ namespace AiQ_GUI { if (!File.Exists(filePath)) { - MainForm.Instance.AddToActionsList($"Could not find RMA Control spreadsheet :({Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find RMA Control spreadsheet :(", Level.ERROR); return 0; } @@ -293,7 +293,7 @@ namespace AiQ_GUI { if (!File.Exists(filePath)) { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR); return 0; } @@ -329,7 +329,7 @@ namespace AiQ_GUI { if (!File.Exists(filePath)) { - MainForm.Instance.AddToActionsList($"Could not find spreadsheet :({Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not find spreadsheet :(", Level.ERROR); return -1; } @@ -344,7 +344,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Error checking next free row: {Level.ERROR}" + ex.Message); + MainForm.Instance.AddToActionsList($"Error checking next free row:" + ex.Message, Level.ERROR); return -1; } } diff --git a/Microsoft/StatsExcel.cs b/Microsoft/StatsExcel.cs index 0a49c43..5c1549d 100644 --- a/Microsoft/StatsExcel.cs +++ b/Microsoft/StatsExcel.cs @@ -40,7 +40,7 @@ namespace AiQ_GUI const string selectColumns = @" ModelNumber, [Total Tests Run], - [Pre Tests Passed], + [Pre Tests Passed] [Pre Tests Failed], [Final Tests Passed], [Final Tests Failed], diff --git a/Network.cs b/Network.cs index ca373e6..452bc62 100644 --- a/Network.cs +++ b/Network.cs @@ -1,4 +1,6 @@ -using System.Net; +using OnvifDiscovery; +using OnvifDiscovery.Models; +using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; @@ -71,15 +73,15 @@ namespace AiQ_GUI } catch (TaskCanceledException ex) { - return $"HTTP error calling {url}: {ex.Message}{Level.ERROR}"; + return $"HTTP error calling {url}: {ex.Message}"; } catch (HttpRequestException ex) { - return $"HTTP error calling {url}: {ex.Message}{Level.ERROR}"; + return $"HTTP error calling {url}: {ex.Message}"; } catch (Exception ex) { - return $"Unexpected error calling {url}: {ex.Message}{Level.ERROR}"; + return $"Unexpected error calling {url}: {ex.Message}"; } } @@ -88,9 +90,15 @@ namespace AiQ_GUI const int sendPort = 6666; const int receivePort = 6667; const int discoveryTimeoutMs = 1000; - IList FoundCams = []; - byte[] discoveryPacket = [0x50, 0x4f, 0x4c, 0x4c, 0xaf, 0xb0, 0xb3, 0xb3, 0xb6, 0x01, 0xa8, 0xc0, 0x0b, 0x1a, 0x00, 0x00]; + List FoundCams = []; + HashSet discoveredIPs = new(StringComparer.OrdinalIgnoreCase); + + byte[] discoveryPacket = + [ + 0x50, 0x4f, 0x4c, 0x4c, 0xaf, 0xb0, 0xb3, 0xb3, + 0xb6, 0x01, 0xa8, 0xc0, 0x0b, 0x1a, 0x00, 0x00 + ]; async Task SendAndListen(IPAddress localIp) { @@ -101,41 +109,42 @@ namespace AiQ_GUI sender.Send(discoveryPacket, discoveryPacket.Length); } - using UdpClient receiver = new(receivePort); // Listen for replies on fixed port + using UdpClient receiver = new(receivePort); receiver.Client.ReceiveTimeout = discoveryTimeoutMs; DateTime timeout = DateTime.Now.AddMilliseconds(discoveryTimeoutMs); - try + + while (DateTime.Now < timeout) { - while (DateTime.Now < timeout) + if (receiver.Available > 0) { - if (receiver.Available > 0) + UdpReceiveResult result = await receiver.ReceiveAsync(); + byte[] recvBuffer = result.Buffer; + + if (recvBuffer.Length >= 52) { - UdpReceiveResult result = await receiver.ReceiveAsync(); - byte[] recvBuffer = result.Buffer; + byte[] ipBytes = recvBuffer + .Skip(recvBuffer.Length - 52) + .Take(4) + .Reverse() + .ToArray(); - if (recvBuffer.Length >= 52) // Safety check - { - byte[] ipBytes = recvBuffer.Skip(recvBuffer.Length - 52).Take(4).Reverse().ToArray(); - string ipToAdd = string.Join(".", ipBytes); + string ip = string.Join(".", ipBytes); - if (!FoundCams.Contains(ipToAdd)) - FoundCams.Add(ipToAdd); - } + if (discoveredIPs.Add(ip)) + FoundCams.Add(ip); } - - await Task.Delay(50); // brief wait to allow data in } - } - catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) - { - // No data received in time — normal case + + await Task.Delay(50); } } - // Get first IPv4 interface (non-loopback) foreach (IPAddress ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList) { + if (ip.AddressFamily != AddressFamily.InterNetwork) + continue; + try { await SendAndListen(ip); @@ -143,9 +152,31 @@ namespace AiQ_GUI catch { } } + //ONVIF discovery + try + { + Discovery onvifDiscovery = new(); + + await foreach (DiscoveryDevice device in onvifDiscovery.DiscoverAsync(5)) + { + string ip = device.Address; + + // Already found via UDP → skip + if (!discoveredIPs.Add(ip)) + continue; + + // ONVIF-only camera + FoundCams.Add($"{ip} - Onvif"); + } + } + catch + { + } + return FoundCams; } + // Ping to make sure devices are connected to the network, be aware it isn't consistant across subnets. public async static Task PingIP(string ipAddress) { diff --git a/Soak/Selenium.cs b/Soak/Selenium.cs index 27f78e5..88ee13a 100644 --- a/Soak/Selenium.cs +++ b/Soak/Selenium.cs @@ -19,7 +19,7 @@ namespace AiQ_GUI } catch (WebDriverTimeoutException) { - MainForm.Instance.AddToActionsList($"Could not load web page {Level.ERROR}" + url + $"1. Check camera has no password set. {Level.ERROR}" + Environment.NewLine + $"2. If unable to fix speak to supervisor.{Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Could not load web page " + url + $"1. Check camera has no password set. " + Environment.NewLine + $"2. If unable to fix speak to supervisor.", Level.ERROR); } } @@ -71,11 +71,11 @@ namespace AiQ_GUI loginBtn.Click(); - MainForm.Instance.AddToActionsList($"Switched user and logged in.{Level.Success}"); + MainForm.Instance.AddToActionsList($"Switched user and logged in.", Level.Success); } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"SwitchUser failed: {Level.ERROR}" + ex.Message); + MainForm.Instance.AddToActionsList($"SwitchUser failed:" + ex.Message,Level.ERROR); } } @@ -99,7 +99,7 @@ namespace AiQ_GUI ClickElementByID(elementID, driver, false); } - MainForm.Instance.AddToActionsList($"Could not click {Level.WARNING}" + elementID); + MainForm.Instance.AddToActionsList($"Could not click " + elementID, Level.WARNING); } } @@ -134,7 +134,7 @@ namespace AiQ_GUI } catch (Exception ex) { - MainForm.Instance.AddToActionsList($"Failed to create ChromeDriver: {Level.ERROR}" + ex.Message); + MainForm.Instance.AddToActionsList($"Failed to create ChromeDriver: " + ex.Message, Level.ERROR); throw; } } diff --git a/Soak/SoakTest.cs b/Soak/SoakTest.cs index 1de4846..67ffbd6 100644 --- a/Soak/SoakTest.cs +++ b/Soak/SoakTest.cs @@ -37,7 +37,7 @@ namespace AiQ_GUI catch (Exception ex) { SoakError($"Initial connection failed: {ex.Message}", SoakLogFile, CamInfo.CheckBox); - MainForm.Instance.AddToActionsList($"[{CamInfo.IP}] Initial connection failed: {Level.ERROR}{ex.Message}"); + MainForm.Instance.AddToActionsList($"[{CamInfo.IP}] Initial connection failed:{ex.Message}", Level.ERROR); // Wait 10 seconds before trying again await Task.Delay(TimeSpan.FromSeconds(10), token); @@ -52,7 +52,7 @@ namespace AiQ_GUI } catch (Exception ex) { - SoakError($"Failed to get element IDs: {Level.ERROR} {ex.Message}", SoakLogFile, CamInfo.CheckBox); + SoakError($"Failed to get element IDs: {ex.Message}", SoakLogFile, CamInfo.CheckBox); return; } @@ -84,7 +84,7 @@ namespace AiQ_GUI // Retry ping until camera responds or cancelled while (!await Network.PingIP(CamInfo.IP) && !token.IsCancellationRequested) { - SoakError($"Camera did not respond after restart.{Level.ERROR}", SoakLogFile, CamInfo.CheckBox); + SoakError($"Camera did not respond after restart.", SoakLogFile, CamInfo.CheckBox); await Task.Delay(TimeSpan.FromMinutes(1), token); // Retry after delay of 1 minute } @@ -98,7 +98,7 @@ namespace AiQ_GUI } catch (Exception ex) { - SoakError($"Error during power cycle: {Level.ERROR} {ex.Message}", SoakLogFile, CamInfo.CheckBox); + SoakError($"Error during power cycle: {ex.Message}", SoakLogFile, CamInfo.CheckBox); } } @@ -112,7 +112,7 @@ namespace AiQ_GUI } catch (Exception ex) { - SoakError($"ImageCheck failed: {Level.ERROR}{Level.ERROR} {ex.Message}", SoakLogFile, CamInfo.CheckBox); + SoakError($"ImageCheck failed: {ex.Message}", SoakLogFile, CamInfo.CheckBox); } lastHour = currentHour; } @@ -131,7 +131,7 @@ namespace AiQ_GUI } catch (Exception ex) { - SoakError($"ChangeRandomDropdown failed: {Level.WARNING} {ex.Message}", SoakLogFile, CamInfo.CheckBox); + SoakError($"ChangeRandomDropdown failed: {ex.Message}", SoakLogFile, CamInfo.CheckBox); } try @@ -200,7 +200,7 @@ namespace AiQ_GUI File.Delete(SoakTestPath); } else - MainForm.Instance.AddToActionsList($"Failed to link or delete PDFs {Level.ERROR}"); + MainForm.Instance.AddToActionsList($"Failed to link or delete PDFs ", Level.ERROR); } catch (Exception ex) { @@ -240,7 +240,7 @@ namespace AiQ_GUI if (ImageDark == null) { Logging.LogWarningMessage($"Dark image is null for {controlType} at setting {SettingMinMax[1]}", SoakLogFile); - MainForm.Instance.AddToActionsList($"Dark image is null for {controlType} at setting {SettingMinMax[1]}{Level.WARNING}"); + MainForm.Instance.AddToActionsList($"Dark image is null for {controlType} at setting {SettingMinMax[1]}", Level.WARNING); return; }