From d1d90f17fc7bce0de23510aee90b62c8432e86c2 Mon Sep 17 00:00:00 2001 From: Bradley Born Date: Tue, 13 Jan 2026 15:31:50 +0000 Subject: [PATCH] =?UTF-8?q?Mobile=20API=20Refactoring=20=E2=80=A2=20Introd?= =?UTF-8?q?uced=20new=20Mobile=20API.cs=20module=20=E2=80=A2=20Encapsulate?= =?UTF-8?q?s=20firmware=20checks,=20day/night=20mode=20switching,=20and=20?= =?UTF-8?q?snapshot=20operations=20=E2=80=A2=20Methods:=20CheckFirmwareAsy?= =?UTF-8?q?nc(),=20SetDayModeAsync(),=20SetNightModeAsync()=20=E2=80=A2=20?= =?UTF-8?q?Improves=20code=20maintainability=20and=20separates=20API=20log?= =?UTF-8?q?ic=20from=20test=20execution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI Components • DayImgPcbx PictureBox: Displays day snapshot • NightImgPcbx PictureBox: Displays night snapshot • DayImage Label: Shows day luminance percentage • NightImage Label: Shows night luminance percentage Features Implemented • Luminance-based validation: Uses ImageProcessing.GetMeanLuminance() to quantitatively analyze day and night snapshots • Validation logic: Day luminance must exceed night luminance to pass the test • Real-time luminance display: Appends calculated luminance percentages (0-100%) to day and night image labels • Enhanced error reporting: Logs detailed luminance values when test fails for debugging purposes • Captures day mode snapshot → Analyzes luminance • Captures night mode snapshot → Analyzes luminance • Compares values and updates UI labels with percentages • Result: "Day/Night Mode = Passed" (green) or "Day/Night Mode = Failed" (red) with error details --- AiQ_GUI.Designer.cs | 119 ++++++++++++------------- AiQ_GUI.cs | 7 +- AiQ_GUI.resx | 3 + Mobile Tests/Mobile API.cs | 164 +++++++++++++++++++++++++++++++++++ Mobile Tests/Mobile Tests.cs | 68 +++++++++++++++ Mobile Tests/MobileTests.cs | 95 -------------------- Mobile Tests/VLC.cs | 14 +-- 7 files changed, 308 insertions(+), 162 deletions(-) create mode 100644 Mobile Tests/Mobile API.cs create mode 100644 Mobile Tests/Mobile Tests.cs delete mode 100644 Mobile Tests/MobileTests.cs diff --git a/AiQ_GUI.Designer.cs b/AiQ_GUI.Designer.cs index 814f3c5..6b5f27e 100644 --- a/AiQ_GUI.Designer.cs +++ b/AiQ_GUI.Designer.cs @@ -132,10 +132,10 @@ namespace AiQ_GUI TabImages = new TabPage(); Video = new TabPage(); VLCVideo = new Label(); - pictureBox2 = new PictureBox(); - label7 = new Label(); - OVImage = new Label(); - pictureBox1 = new PictureBox(); + NightImgPcbx = new PictureBox(); + NightImage = new Label(); + DayImage = new Label(); + DayImgPcbx = new PictureBox(); TabSoak = new TabPage(); BtnFactoryDefault = new Button(); SetGodModeAll = new Button(); @@ -170,8 +170,8 @@ namespace AiQ_GUI TabSettings.SuspendLayout(); TabImages.SuspendLayout(); Video.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)pictureBox2).BeginInit(); - ((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NightImgPcbx).BeginInit(); + ((System.ComponentModel.ISupportInitialize)DayImgPcbx).BeginInit(); TabSoak.SuspendLayout(); Mobile.SuspendLayout(); SuspendLayout(); @@ -1664,10 +1664,10 @@ namespace AiQ_GUI Video.BackColor = Color.FromArgb(39, 37, 55); Video.Controls.Add(VidView); Video.Controls.Add(VLCVideo); - Video.Controls.Add(pictureBox2); - Video.Controls.Add(label7); - Video.Controls.Add(OVImage); - Video.Controls.Add(pictureBox1); + Video.Controls.Add(NightImgPcbx); + Video.Controls.Add(NightImage); + Video.Controls.Add(DayImage); + Video.Controls.Add(DayImgPcbx); Video.Location = new Point(4, 24); Video.Name = "Video"; Video.Padding = new Padding(3); @@ -1688,55 +1688,55 @@ namespace AiQ_GUI VLCVideo.TabIndex = 238; VLCVideo.Text = "Live Video"; // - // pictureBox2 + // NightImgPcbx // - pictureBox2.BackColor = Color.Transparent; - pictureBox2.BorderStyle = BorderStyle.FixedSingle; - pictureBox2.Location = new Point(7, 303); - pictureBox2.Margin = new Padding(4, 3, 4, 3); - pictureBox2.Name = "pictureBox2"; - pictureBox2.Size = new Size(392, 221); - pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage; - pictureBox2.TabIndex = 237; - pictureBox2.TabStop = false; + NightImgPcbx.BackColor = Color.Transparent; + NightImgPcbx.BorderStyle = BorderStyle.FixedSingle; + NightImgPcbx.Location = new Point(7, 303); + NightImgPcbx.Margin = new Padding(4, 3, 4, 3); + NightImgPcbx.Name = "NightImgPcbx"; + NightImgPcbx.Size = new Size(392, 221); + NightImgPcbx.SizeMode = PictureBoxSizeMode.StretchImage; + NightImgPcbx.TabIndex = 237; + NightImgPcbx.TabStop = false; // - // label7 + // NightImage // - label7.AutoSize = true; - label7.BackColor = Color.Transparent; - label7.Font = new Font("Segoe UI Semibold", 12F, FontStyle.Bold); - label7.ForeColor = SystemColors.Control; - label7.Location = new Point(12, 279); - label7.Margin = new Padding(4, 0, 4, 0); - label7.Name = "label7"; - label7.Size = new Size(101, 21); - label7.TabIndex = 236; - label7.Text = "Night Image"; + NightImage.AutoSize = true; + NightImage.BackColor = Color.Transparent; + NightImage.Font = new Font("Segoe UI Semibold", 12F, FontStyle.Bold); + NightImage.ForeColor = SystemColors.Control; + NightImage.Location = new Point(12, 279); + NightImage.Margin = new Padding(4, 0, 4, 0); + NightImage.Name = "NightImage"; + NightImage.Size = new Size(101, 21); + NightImage.TabIndex = 236; + NightImage.Text = "Night Image"; // - // OVImage + // DayImage // - OVImage.AutoSize = true; - OVImage.BackColor = Color.Transparent; - OVImage.Font = new Font("Segoe UI Semibold", 12F, FontStyle.Bold); - OVImage.ForeColor = SystemColors.Control; - OVImage.Location = new Point(12, 14); - OVImage.Margin = new Padding(4, 0, 4, 0); - OVImage.Name = "OVImage"; - OVImage.Size = new Size(87, 21); - OVImage.TabIndex = 235; - OVImage.Text = "Day Image"; + DayImage.AutoSize = true; + DayImage.BackColor = Color.Transparent; + DayImage.Font = new Font("Segoe UI Semibold", 12F, FontStyle.Bold); + DayImage.ForeColor = SystemColors.Control; + DayImage.Location = new Point(12, 14); + DayImage.Margin = new Padding(4, 0, 4, 0); + DayImage.Name = "DayImage"; + DayImage.Size = new Size(87, 21); + DayImage.TabIndex = 235; + DayImage.Text = "Day Image"; // - // pictureBox1 + // DayImgPcbx // - pictureBox1.BackColor = Color.Transparent; - pictureBox1.BorderStyle = BorderStyle.FixedSingle; - pictureBox1.Location = new Point(7, 46); - pictureBox1.Margin = new Padding(4, 3, 4, 3); - pictureBox1.Name = "pictureBox1"; - pictureBox1.Size = new Size(392, 221); - pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; - pictureBox1.TabIndex = 234; - pictureBox1.TabStop = false; + DayImgPcbx.BackColor = Color.Transparent; + DayImgPcbx.BorderStyle = BorderStyle.FixedSingle; + DayImgPcbx.Location = new Point(7, 46); + DayImgPcbx.Margin = new Padding(4, 3, 4, 3); + DayImgPcbx.Name = "DayImgPcbx"; + DayImgPcbx.Size = new Size(392, 221); + DayImgPcbx.SizeMode = PictureBoxSizeMode.StretchImage; + DayImgPcbx.TabIndex = 234; + DayImgPcbx.TabStop = false; // // TabSoak // @@ -1979,8 +1979,8 @@ namespace AiQ_GUI TabImages.PerformLayout(); Video.ResumeLayout(false); Video.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)pictureBox2).EndInit(); - ((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit(); + ((System.ComponentModel.ISupportInitialize)NightImgPcbx).EndInit(); + ((System.ComponentModel.ISupportInitialize)DayImgPcbx).EndInit(); TabSoak.ResumeLayout(false); TabSoak.PerformLayout(); Mobile.ResumeLayout(false); @@ -1992,7 +1992,7 @@ namespace AiQ_GUI #endregion public System.Windows.Forms.Button BtnStartTest; private System.Windows.Forms.Button BtnFindCams; - private System.Windows.Forms.ComboBox CbBxFoundCams; + public System.Windows.Forms.ComboBox CbBxFoundCams; private System.Windows.Forms.Button BtnRestart; private System.Windows.Forms.Button BtnMin; private System.Windows.Forms.Button BtnClose; @@ -2121,12 +2121,13 @@ namespace AiQ_GUI public ComboBox CbBxCamType; public TabPage Mobile; private Button BtnUpFirm; - private LibVLCSharp.WinForms.VideoView VidView; + public LibVLCSharp.WinForms.VideoView VidView; private TabPage Video; - public Label label7; - public Label OVImage; + public Label NightImage; + public Label DayImage; public PictureBox pictureBox1; public Label VLCVideo; - public PictureBox pictureBox2; + public PictureBox NightImgPcbx; + public PictureBox DayImgPcbx; } } diff --git a/AiQ_GUI.cs b/AiQ_GUI.cs index 5d868cc..4819958 100644 --- a/AiQ_GUI.cs +++ b/AiQ_GUI.cs @@ -1,4 +1,5 @@ using AiQ_GUI.AiQ_Tests; +using AiQ_GUI.Mobile_Tests; using LibVLCSharp.Shared; using LibVLCSharp.WinForms; using Newtonsoft.Json; @@ -1449,10 +1450,12 @@ namespace AiQ_GUI //StatsExcel excelExporter = new(); //excelExporter.ExportDatabaseToExcel(); //await MobilePreTest.CheckFirmwareAsync(); - + VLC.Play(VidView); await Task.Delay(5000); - VLC.TakeSnapshot(VidView); + //VLC.TakeSnapshot(VidView); + + await MobileAPI.SetDayModeAsync(); AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff"), Level.LOG); } diff --git a/AiQ_GUI.resx b/AiQ_GUI.resx index 3ba17b2..0bef68b 100644 --- a/AiQ_GUI.resx +++ b/AiQ_GUI.resx @@ -123,6 +123,9 @@ 159, 17 + + 17, 17 + 531, 18 diff --git a/Mobile Tests/Mobile API.cs b/Mobile Tests/Mobile API.cs new file mode 100644 index 0000000..6a91de2 --- /dev/null +++ b/Mobile Tests/Mobile API.cs @@ -0,0 +1,164 @@ +using AiQ_GUI; +using System; +using System.Net.Http; +using System.Net.Http.Json; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace AiQ_GUI.Mobile_Tests +{ + public static class MobileAPI + { + public static async Task CheckFirmwareAsync() + { + using HttpClient client = new HttpClient + { + BaseAddress = new Uri($"http://{MainForm.Instance.CamOnTest.IP}") + }; + + try + { + // Login and get JWT token + string token = await LoginAsync(client, "ADMIN", "1234"); + + // Attach JWT to all requests + client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + + // Request firmware information + HttpResponseMessage response = await client.GetAsync("/app/v1/system/firmware"); + + string body = await response.Content.ReadAsStringAsync(); + + response.EnsureSuccessStatusCode(); + + JsonElement json = JsonSerializer.Deserialize(body); + + string version = json.GetProperty("version").GetString()?.Trim(); + + // Compare against expected SRZFirmware from UniversalData + if (string.IsNullOrEmpty(UniversalData.SRZFirmware)) + { + MainForm.Instance.AddToActionsList($"Firmware check failed: Expected SRZFirmware not loaded in UniversalData", Level.ERROR); + MainForm.Instance.AddLabelToPanel($"Firmware = {version}", true); + } + else if (version == UniversalData.SRZFirmware) + { + MainForm.Instance.AddLabelToPanel($"Firmware = {version}", false); + } + else + { + MainForm.Instance.AddLabelToPanel($"Firmware = {version}", true); + } + } + catch (Exception ex) + { + MainForm.Instance.AddToActionsList($"Firmware check failed: {ex.Message}", Level.ERROR); + MainForm.Instance.AddLabelToPanel("Firmware = Unknown", true); + } + } + + public static async Task ChangeDayNightModeAsync(uint mode) + { + // mode: 0 = Auto, 1 = Day, 2 = Night + string modeName = mode switch + { + 0 => "Auto", + 1 => "Day", + 2 => "Night", + _ => "Unknown" + }; + + // operatingMode required by firmware (!) + // day -> 1, night -> 0 + int operatingMode = mode switch + { + 1 => 1, // Day + 2 => 0, // Night + _ => 1 // Auto: keep day as safe default + }; + + using HttpClient client = new HttpClient + { + BaseAddress = new Uri($"http://{MainForm.Instance.CamOnTest.IP}") + }; + + try + { + // Login + string token = await LoginAsync(client, "ADMIN", "1234"); + client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + + var payload = new + { + dayNightMode = new + { + mode = mode, + dayToNightSens = 4, + nightToDaySens = 24, + transitionDelay = 20, + operatingMode = operatingMode + } + }; + + HttpResponseMessage response = await client.PostAsJsonAsync("/app/v1/camera/image", payload); + + if (response.IsSuccessStatusCode) + { + MainForm.Instance.AddToActionsList($"Camera set to {modeName} mode successfully",Level.Success); + } + else + { + string error = await response.Content.ReadAsStringAsync(); + MainForm.Instance.AddToActionsList($"Failed to set {modeName} mode: {response.StatusCode} - {error}",Level.ERROR); + } + } + catch (Exception ex) + { + MainForm.Instance.AddToActionsList($"Error changing to {modeName} mode: {ex.Message}",Level.ERROR); + } + } + + + public static async Task SetDayModeAsync() + { + await ChangeDayNightModeAsync(1); + } + + public static async Task SetNightModeAsync() + { + await ChangeDayNightModeAsync(2); + } + + public static async Task SetAutoModeAsync() + { + await ChangeDayNightModeAsync(0); + } + + private static async Task LoginAsync(HttpClient client, string user, string password) + { + var payload = new + { + userId = user, + userPassword = Sha256(password) + }; + + HttpResponseMessage response = await client.PostAsJsonAsync("/app/v1/login", payload); + + response.EnsureSuccessStatusCode(); + + JsonElement json = await response.Content.ReadFromJsonAsync(); + + return json.GetProperty("token").GetString(); + } + + private static string Sha256(string input) + { + using SHA256 sha = SHA256.Create(); + byte[] bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(input)); + return BitConverter.ToString(bytes).Replace("-", "").ToLower(); + } + } +} + diff --git a/Mobile Tests/Mobile Tests.cs b/Mobile Tests/Mobile Tests.cs new file mode 100644 index 0000000..ad4d407 --- /dev/null +++ b/Mobile Tests/Mobile Tests.cs @@ -0,0 +1,68 @@ +using LibVLCSharp.WinForms; +using System; +using System.Threading.Tasks; + +namespace AiQ_GUI.Mobile_Tests +{ + public static class MobilePreTest + { + public static async Task RunPreTestAsync() + { + + await MobileAPI.CheckFirmwareAsync();// Check firmware version + + VLC.Play(MainForm.Instance.VidView);// Test Live Video streaming + await Task.Delay(5000); + + VLC.TakeSnapshot(MainForm.Instance.VidView); + + if (!VLC.IsPLaying(MainForm.Instance.VidView))// Check Live Video streaming + { + MainForm.Instance.AddToActionsList("Live Video streaming test failed: Video is not playing.", Level.ERROR); + MainForm.Instance.AddLabelToPanel("Live Video = Not Playing", true); + } + else + { + MainForm.Instance.AddLabelToPanel("Live Video = Playing", false); + } + + await TestDayNightMode();// Test day/night mode cycling + + double dayLuminance = ImageProcessing.GetMeanLuminance(MainForm.Instance.DayImgPcbx.Image); // Calculates the Luminance + double nightLuminance = ImageProcessing.GetMeanLuminance(MainForm.Instance.NightImgPcbx.Image); + + MainForm.Instance.DayImage.Text += $" - Luminance: {dayLuminance}%"; + MainForm.Instance.NightImage.Text += $" - Luminance: {nightLuminance}%"; + + if (dayLuminance > nightLuminance) + { + MainForm.Instance.AddLabelToPanel("Day/Night Mode = Passed", false); + } + else + { + MainForm.Instance.AddToActionsList($"Day/Night mode test failed: Day luminance ({dayLuminance}%) is not greater than night luminance ({nightLuminance}%).", Level.ERROR); + MainForm.Instance.AddLabelToPanel("Day/Night Mode = Failed", true); + + } + } + + private static async Task TestDayNightMode() + { + await MobileAPI.SetDayModeAsync();// Set to Day mode + + await Task.Delay(5000); + + VLC.TakeSnapshot(MainForm.Instance.VidView, "Mobile_day_snapshot.png");// Take Day Image Snapshot + + MainForm.Instance.DayImgPcbx.Image = System.Drawing.Image.FromFile(Path.Combine(LDS.MAVPath, "Mobile_day_snapshot.png"));// Display Day mode snapshot + + await MobileAPI.SetNightModeAsync();// Set to Night mode + + await Task.Delay(5000); + + VLC.TakeSnapshot(MainForm.Instance.VidView, "Mobile_night_snapshot.png");// Take Night Image Snapshot + MainForm.Instance.NightImgPcbx.Image = System.Drawing.Image.FromFile(Path.Combine(LDS.MAVPath, "Mobile_night_snapshot.png"));// Display Night mode snapshot + } + } +} + diff --git a/Mobile Tests/MobileTests.cs b/Mobile Tests/MobileTests.cs deleted file mode 100644 index c1cd00c..0000000 --- a/Mobile Tests/MobileTests.cs +++ /dev/null @@ -1,95 +0,0 @@ -using AiQ_GUI; -using System; -using System.IO; -using System.Net.Http; -using System.Net.Http.Json; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Diagnostics; -using System.Drawing; - -public static class MobilePreTest -{ - public static async Task CheckFirmwareAsync() - { - - using HttpClient client = new HttpClient - { - BaseAddress = new Uri($"http://{MainForm.Instance.CamOnTest.IP}") - }; - - try - { - // Login and get JWT token - string token = await LoginAsync(client, "ADMIN", "1234"); - - // Attach JWT to all requests - client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); - - // Request firmware information - HttpResponseMessage response = await client.GetAsync("/app/v1/system/firmware"); - - string body = await response.Content.ReadAsStringAsync(); - - response.EnsureSuccessStatusCode(); - - JsonElement json = JsonSerializer.Deserialize(body); - - string version = json.GetProperty("version").GetString()?.Trim(); - - // Compare against expected SRZFirmware from UniversalData - if (string.IsNullOrEmpty(UniversalData.SRZFirmware)) - { - MainForm.Instance.AddToActionsList($"Firmware check failed: Expected SRZFirmware not loaded in UniversalData",Level.ERROR); - MainForm.Instance.AddLabelToPanel($"Firmware = {version}", true); - } - else if (version == UniversalData.SRZFirmware) - { - MainForm.Instance.AddLabelToPanel($"Firmware = {version}", false); - } - else - { - MainForm.Instance.AddLabelToPanel($"Firmware = {version}", true); - } - - } - catch (Exception ex) - { - MainForm.Instance.AddToActionsList($"Firmware check failed: {ex.Message}",Level.ERROR); - MainForm.Instance.AddLabelToPanel("Firmware = Unknown", true); - } - } - - private static async Task LoginAsync(HttpClient client, string user, string password) - { - var payload = new - { - userId = user, - userPassword = Sha256(password) - }; - - HttpResponseMessage response = await client.PostAsJsonAsync("/app/v1/login", payload); - - response.EnsureSuccessStatusCode(); - - JsonElement json = await response.Content.ReadFromJsonAsync(); - - return json.GetProperty("token").GetString(); - } - - private static string Sha256(string input) - { - using SHA256 sha = SHA256.Create(); - byte[] bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(input)); - return BitConverter.ToString(bytes).Replace("-", "").ToLower(); - } - public static async Task RunPreTestAsync() - { - await CheckFirmwareAsync(); - - } -} - diff --git a/Mobile Tests/VLC.cs b/Mobile Tests/VLC.cs index 7203e68..8ef94fc 100644 --- a/Mobile Tests/VLC.cs +++ b/Mobile Tests/VLC.cs @@ -1,7 +1,9 @@ using AiQ_GUI; +using Emgu.CV.Dai; using LibVLCSharp.Shared; using LibVLCSharp.WinForms; using System.IO; +using System.Security.Policy; internal class VLC { @@ -14,12 +16,12 @@ internal class VLC libVLC = new LibVLC(); } - public static string RtspUrl = $"rtsp://ADMIN:1234@192.168.0.85:554/live/main"; public static string SnapshotPath = Path.Combine(LDS.MAVPath, "Mobile_ov_snapshot.png"); public static void Play(VideoView VLCVV) { - var media = new Media(libVLC, RtspUrl, FromType.FromLocation); + var media = new Media(libVLC, $"rtsp://ADMIN:1234@{MainForm.Instance.CamOnTest.IP}:554/live/main", FromType.FromLocation); + VLCVV.MediaPlayer.Play(media); } @@ -28,10 +30,10 @@ internal class VLC return VLCVV.MediaPlayer.IsPlaying; } - public static void TakeSnapshot(VideoView VLCVV) + public static void TakeSnapshot(VideoView VLCVV, string filename = "Mobile_ov_snapshot.png") { - VLCVV.MediaPlayer.TakeSnapshot(0, SnapshotPath, 1280, 720); - + string path = Path.Combine(LDS.MAVPath, filename); + VLCVV.MediaPlayer.TakeSnapshot(0, path, 1280, 720); } public static void Stop(VideoView VLCVV) @@ -41,7 +43,7 @@ internal class VLC public static void Capture(VideoView VLCVV) { - var media = new Media(libVLC, RtspUrl, FromType.FromLocation); + var media = new Media(libVLC, $"rtsp://ADMIN:1234@{MainForm.Instance.CamOnTest.IP}:554/live/main", FromType.FromLocation); media.AddOption(":rtsp-tcp"); VLCVV.MediaPlayer.Play(media); }