From 0c463ad9296ffeb8a67317bb567a46bfc9b771d3 Mon Sep 17 00:00:00 2001 From: Bradley Relyea Date: Tue, 16 Sep 2025 10:42:51 +0100 Subject: [PATCH] V3.14 --- AiQ_GUI.cs | 240 ++++++++++------------------------------ AiQ_GUI_NET_Test.csproj | 15 +-- AvailableCondigID.json | 127 +++++++++++++++++++++ Camera/CameraModules.cs | 2 - Camera/FlexiAPI.cs | 84 +++++++++++++- Camera/SSH.cs | 2 +- Network.cs | 8 +- Test.json | 73 ++++++++++++ 8 files changed, 351 insertions(+), 200 deletions(-) create mode 100644 AvailableCondigID.json create mode 100644 Test.json diff --git a/AiQ_GUI.cs b/AiQ_GUI.cs index 41e9e23..17cdb98 100644 --- a/AiQ_GUI.cs +++ b/AiQ_GUI.cs @@ -183,7 +183,7 @@ namespace AiQ_GUI } await Wait; // Finished to 5s wait - await SetTrim(); // Auto trims the cameras, some plates should have been captured in the meantime + await FlexiAPI.SetTrim(CamOnTest.IP, LblTestTubePing.Text); // Auto trims the cameras, some plates should have been captured in the meantime if (!await FlexiAPI.ZoomModules("0000", CamOnTest.IP)) // Zoom to full wide await TestFailed(BtnStartTest, "Could not zoom modules to full wide"); @@ -686,81 +686,6 @@ namespace AiQ_GUI Printer.PrintSerialLbl(CamOnTest.Model, NewSerial, CameraAccessInfo.Processor); // Print model/serial label } - private async Task SetTrim(int RetryCount = 0) // Sets trim by getting plate postion as metric - { - Trim trim; - string trimData = await FlexiAPI.APIHTTPRequest("/SightingCreator-plate-positions", CamOnTest.IP, 5); // Get plate positions - - try // Deserialise the JSON - { - Logging.LogMessage("Trim Data: " + trimData); - trim = JsonConvert.DeserializeObject(trimData); - } - catch - { - await TestFailed(BtnStartTest, "Error reading JSON - " + trimData); - return; - } - - // Check no value is -1 (no plate found) or if the positions are identical (one plate found). If it is then try again 3 times - if (new[] { trim.infraredX, trim.infraredY, trim.colourX, trim.colourY }.Any(value => value == -1) - || (trim.infraredX == trim.colourX && trim.infraredY == trim.colourY)) - { - if (RetryCount >= 3) - { - await 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 - await SetTrim(RetryCount++); - } - - int offset = 105; - - if (LblTestTubePing.Text == "❌") // Test tube not connected so do the 2.7m check. - offset = 98; - - // Horizontal distance offset for 2.7m compared to 30m is 98 pixels. This was empirically found from testing in the car park - // Colour camera is to the right of the infrared so it gets the offset. - // Using similar triangles going from 2.7m -> 0.65m which is the length of the test tube (Also exactly 1/4 length). - // 98 * (29.35/27.3) = 105.35 pixels - int OverviewX = trim.colourX + offset; - - if (OverviewX > 1920) // If adding on the offset has pushed it out of limits then remove 0.1 - { - if (OverviewX < 2120 && trim.infraredX > 400) // Within enough of a limit to automatically do it - { - OverviewX -= 200; - trim.infraredX -= 200; - } - else // Ask user to centre the plate in the field of view - { - await DisplayOK("Please centralise plate in view THEN press OK"); // Awaited till OK has been clicked - - if (RetryCount >= 3) - { - await 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 - await SetTrim(RetryCount++); - } - } - - // Compensated trim values, therefore should be close to 0,0 with limits of ±5% of 1920 and 1080 respectivly being ±96 and ±54 - int TrimX = trim.infraredX - OverviewX; - int TrimY = trim.infraredY - trim.colourY; - - // Update trim values - string[,] Trim_JSON = { { "propInterCameraOffsetX", Convert.ToString(TrimX) }, { "propInterCameraOffsetY", Convert.ToString(TrimY) } }; - string TrimResp = await FlexiAPI.HTTP_Update("SightingCreator", CamOnTest.IP, Trim_JSON); - AddToActionsList(TrimResp, false); - - if (!TrimResp.Contains($"\"propInterCameraOffsetX\": {{\"value\": \"{Convert.ToString(TrimX)}\", \"datatype\": \"int\"}}, \"propInterCameraOffsetY\": {{\"value\": \"{Convert.ToString(TrimY)}\", \"datatype\": \"int\"}},")) - AddToActionsList("Could not set camera trim"); - } - // ***** Top right buttons ***** private void BtnClose_Click(object sender, EventArgs e) { @@ -800,7 +725,8 @@ namespace AiQ_GUI private async void BtnFindCams_Click(object sender, EventArgs e) { CbBxFoundCams.Text = "Searching"; - BtnFindCams.Enabled = BtnSetAll211.Enabled = BtnSoak.Enabled = BtnSet211.Enabled = BtnSetGodMode.Enabled = BtnUploadBlob.Enabled = SetGodModeAll.Enabled = false; + BtnFindCams.Enabled = BtnSetAll211.Enabled = BtnSoak.Enabled = BtnSet211.Enabled = BtnSetGodMode.Enabled = BtnUploadBlob.Enabled = SetGodModeAll.Enabled = BtnFactoryDefault.Enabled = false; + BtnSetGodMode.BackColor = BtnUploadBlob.BackColor = BtnFactoryDefault.BackColor = BtnSetAll211.BackColor = BtnColour; CbBxFoundCams.Items.Clear(); soakCameraList.Clear(); @@ -838,7 +764,7 @@ namespace AiQ_GUI int cameraCount = CbBxFoundCams.Items.Count; CbBxFoundCams.Text = cameraCount > 0 ? $"{cameraCount} Camera{(cameraCount == 1 ? "" : "s")} Found" : "No Cameras Found"; - BtnFindCams.Enabled = BtnSetAll211.Enabled = SetGodModeAll.Enabled = BtnSoak.Enabled = BtnUploadBlob.Enabled = true; + BtnFindCams.Enabled = BtnSetAll211.Enabled = SetGodModeAll.Enabled = BtnSoak.Enabled = BtnUploadBlob.Enabled = BtnFactoryDefault.Enabled = true; } private void BtnYes_Click(object sender, EventArgs e) @@ -947,6 +873,7 @@ namespace AiQ_GUI private async void BtnSetGodMode_Click(object sender, EventArgs e) { + BtnSetGodMode.BackColor = BtnColour; bool isGodModeCurrentlyOn = BtnSetGodMode.Text.Contains("On"); string newGodModeValue = isGodModeCurrentlyOn ? "true" : "false"; string[,] GOD_JSON = { { "propGodMode", newGodModeValue } }; @@ -954,13 +881,8 @@ namespace AiQ_GUI try { await FlexiAPI.HTTP_Update("Internal Config", CamOnTest.IP, GOD_JSON); - BtnSetGodMode.Text = newGodModeValue == "true" ? "Set God Mode Off" : "Set God Mode On"; - - Color originalColor = BtnSetGodMode.BackColor; BtnSetGodMode.BackColor = Color.Green; - await Task.Delay(500); - BtnSetGodMode.BackColor = originalColor; } catch (Exception ex) { @@ -1663,6 +1585,7 @@ namespace AiQ_GUI private async void BtnUploadBlob_Click(object sender, EventArgs e) { + BtnUploadBlob.BackColor = BtnColour; const string networkFolderPath = @"G:\Shared drives\MAV Production\MAV_146_AiQ_Mk2\Flexi"; string fileToUpload = null; @@ -1716,10 +1639,13 @@ namespace AiQ_GUI AddToActionsList($"Upload result for {cam.IP}: {result}", false); await Task.Delay(500); } + BtnUploadBlob.BackColor = Color.Green; } private async void BtnFactoryDefault_Click(object sender, EventArgs e) { + BtnFactoryDefault.BackColor = BtnColour; + foreach (Camera SCL in soakCameraList) // Reset all cameras that were being soaked to default module settings { if (!SCL.IsChecked) @@ -1728,116 +1654,70 @@ namespace AiQ_GUI Network.Initialize("developer", SCL.DevPass); // Ensure network is initialized to the right camera, cannot be done in soak test finally becuase of this. await CameraModules.FactoryResetModules(SCL.IP); } + BtnFactoryDefault.BackColor = Color.Green; } - private async void SwitchTest(string TestingType) + // Constants + const double RealPlateWidthMeters = 0.52; // UK standard plate width + const double FocalLengthPixels = (50 * 1280) / 14.111224; // focal mm * pixel width / sensor width for IQ + // const double FocalLengthPixels = (50 * 1920) / 6.95; // focal mm * pixel width / sensor width for AiQ + const double FrameRate = 25.0; // Frames per second + + public class FrameData { - object sender = new(); - EventArgs e = new(); - - Properties.Settings.Default.UnitTesting = TestingType; - Properties.Settings.Default.Save(); - - CAMTYPE CAMTYPE = TestingType.Substring(0, TestingType.IndexOf('_')) switch - { - "GOOD" => CAMTYPE.GOOD, - "BAD" => CAMTYPE.BAD, - "BIZARRE" => CAMTYPE.BIZARRE, - _ => CAMTYPE.GOOD, - }; - - FakeCamera fakeCamera = new(80); // Create an instance of FakeCamera - - _ = fakeCamera.StartAsync(CAMTYPE).ContinueWith(task => - { - if (task.IsFaulted) - AddToActionsList("Error starting FakeCamera: " + task.Exception?.Message); - else - AddToActionsList($"FakeCamera started successfully. IP: {fakeCamera}", false); - }); - - await Task.Delay(5000); // Wait for server to start - CbBxFoundCams.Text = "localhost"; // Should force update in creds an network reinit - CbBxFoundCams_SelectedIndexChanged(sender, e); - CbBxCameraType.SelectedIndex = CbBxCameraType.Items.Count - 1; // Selects AB12CD as model number - - while(TxBxOutput.Text.Length < 2) // Wait for developer password to be generated - await Task.Delay(1000); - - if (TestingType.Contains("FINAL")) - BtnStartTest_Click(sender, e); // Run final test - else if (TestingType.Contains("PRE")) - BtnPreTest_Click(sender, e); // Run pre-test - - fakeCamera.Stop(); // Stop the server + public long FrameID; + public int PlatePosX; + public int PlatePosY; + public int PlateWidthPixels; } + public double EstimateSpeed(List frames) + { + double TimeElapsed = 0; + int frameCount = frames.Count; + + for (int i = 1; i < frameCount; i++) + { + double time = (frames[i].FrameID - frames[i - 1].FrameID) / FrameRate; + TimeElapsed += time; + } + + double FarDist = (FocalLengthPixels * RealPlateWidthMeters) / frames[0].PlateWidthPixels; + double CloseDist = (FocalLengthPixels * RealPlateWidthMeters) / frames[frameCount - 1].PlateWidthPixels; + + double speedMph = (Math.Abs(FarDist - CloseDist) / TimeElapsed) * 2.237; + return speedMph; + } + + // ***** Test & Debug ***** private void BtnTest_Click(object sender, EventArgs e) { Stopwatch stopWatchTest = Stopwatch.StartNew(); - // *** To be put in startup code!!!! *** - if (Properties.Settings.Default.UnitTesting != "NOT_STARTED") - BtnTest_Click(sender, e); - // *** TO be put in test button *** - if (Properties.Settings.Default.UnitTesting == "NOT_STARTED") + List frames = new List { - SwitchTest("GOOD_PRE"); + new FrameData { FrameID = 60192555, PlatePosX = 1172, PlatePosY = 393, PlateWidthPixels = 108 }, + new FrameData { FrameID = 60192556, PlatePosX = 1103, PlatePosY = 361, PlateWidthPixels = 105 }, + new FrameData { FrameID = 60192558, PlatePosX = 983, PlatePosY = 331, PlateWidthPixels = 99 }, + new FrameData { FrameID = 60192559, PlatePosX = 930, PlatePosY = 301, PlateWidthPixels = 95 }, + new FrameData { FrameID = 60192560, PlatePosX = 880, PlatePosY = 304, PlateWidthPixels = 93 }, + new FrameData { FrameID = 60192561, PlatePosX = 834, PlatePosY = 278, PlateWidthPixels = 89 }, + new FrameData { FrameID = 60192562, PlatePosX = 792, PlatePosY = 229, PlateWidthPixels = 87 }, + new FrameData { FrameID = 60192563, PlatePosX = 752, PlatePosY = 208, PlateWidthPixels = 85 }, + new FrameData { FrameID = 60192565, PlatePosX = 680, PlatePosY = 187, PlateWidthPixels = 81 }, + new FrameData { FrameID = 60192566, PlatePosX = 648, PlatePosY = 167, PlateWidthPixels = 78 }, + new FrameData { FrameID = 60192567, PlatePosX = 617, PlatePosY = 149, PlateWidthPixels = 76 }, + new FrameData { FrameID = 60192568, PlatePosX = 588, PlatePosY = 132, PlateWidthPixels = 75 }, + new FrameData { FrameID = 60192569, PlatePosX = 561, PlatePosY = 100, PlateWidthPixels = 70 }, + new FrameData { FrameID = 60192570, PlatePosX = 535, PlatePosY = 85, PlateWidthPixels = 72 }, + new FrameData { FrameID = 60192572, PlatePosX = 488, PlatePosY = 70, PlateWidthPixels = 69 }, + new FrameData { FrameID = 60192573, PlatePosX = 466, PlatePosY = 55, PlateWidthPixels = 67 } + }; - // Run Good pre test - // Check it was the correct outcome - // Restart GUI - } - else if (Properties.Settings.Default.UnitTesting == "GOOD_PRE") - { - SwitchTest("GOOD_FINAL"); - - // Run Good final test - // Check it was the correct outcome - // Restart GUI - } - else if (Properties.Settings.Default.UnitTesting == "GOOD_FINAL") - { - SwitchTest("BAD_PRE"); - - // Run Bad pre test - // Check it was the correct outcome - // Restart GUI - } - // etc... - - //// SSH testing - //sshData = SSH.CollectSSHData("localhost"); - //AddToActionsList(sshData.packages); - //AddToActionsList(sshData.FilesystemSize); - //AddToActionsList($"{sshData.tailscale}"); - - //// Functionality testing - //// TODO - SSH server needs to be running at this point for this to work - //var server = new FakeCamera(80); // Start the HTTP server in a background task - //_ = server.StartAsync(CAMTYPE.GOOD); // Start the server running with correct responses - //Task.Delay(1000).Wait(); // Wait for server to start - - //CbBxCameraType.SelectedIndex = CbBxCameraType.Items.Count - 1; // Selects AB12CD as model number - //CbBxFoundCams.Text = "localhost"; // Should force update in creds an network reinit - - //BtnPreTest_Click(sender, e); // Run pre-test - //BtnStartTest_Click(sender, e); // Run full test - //server.Stop(); // Stop the server - - //_ = server.StartAsync(CAMTYPE.BAD); // Start the server running with bad responses - //Task.Delay(1000).Wait(); // Wait for server to start - //BtnPreTest_Click(sender, e); // Run pre-test - //BtnStartTest_Click(sender, e); // Run full test - //server.Stop(); // Stop the server - - //_ = server.StartAsync(CAMTYPE.BIZARRE); // Start the server running with incorrect types and odd responses - //Task.Delay(1000).Wait(); // Wait for server to start - //BtnPreTest_Click(sender, e); // Run pre-test - //BtnStartTest_Click(sender, e); // Run full test - //server.Stop(); // Stop the server + double Spd = EstimateSpeed(frames); + AddToActionsList("Estimated Speed: " + Spd.ToString("F2") + " MPH"); stopWatchTest.Stop(); AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff")); diff --git a/AiQ_GUI_NET_Test.csproj b/AiQ_GUI_NET_Test.csproj index 1c5fdd3..3e45c4e 100644 --- a/AiQ_GUI_NET_Test.csproj +++ b/AiQ_GUI_NET_Test.csproj @@ -16,7 +16,7 @@ AiQ GUI MAV Systems Ltd AiQ GUI - 3.12.0 + 3.14.0 A GUI to control and test the AiQ MAV Systems Ltd 2025 MAV - Plain - Blue.png @@ -25,6 +25,7 @@ AiQ_GUI False true + False @@ -41,18 +42,18 @@ - - - + + + - + - + - + diff --git a/AvailableCondigID.json b/AvailableCondigID.json new file mode 100644 index 0000000..1da51cf --- /dev/null +++ b/AvailableCondigID.json @@ -0,0 +1,127 @@ +{ + "ids": [ + "Dispatcher3-meta", + "RaptorOCRColour", + "MMCQueueInfrared", + "Wizards", + "TargetDetection--sequence-target-finder", + "Colour--LiveVideoInput", + "SightingCreator--CropPreview", + "Dispatcher2-json", + "SightingAmmend0-lane-ids", + "SightingAmmend1-custom-fields", + "colourHistory", + "Dispatcher0-json", + "SightingAmmend1-lane-ids", + "TargetDetectionColour-zone-10", + "Audit--Infrared", + "Dispatcher1-utmc-constants", + "-dev-ttyACM1", + "SightingAmmend2-zone-names", + "TargetDetection-zone--2", + "TargetDetection-zone--1", + "Dispatcher3-json", + "-dev-ttyACM0", + "Dispatcher2-utmc-constants", + "SightingAmmend0-overlay", + "SightingAmmend0-zone-name-overrides", + "TargetDetection-zone-4", + "TargetDetection-zone-5", + "GLOBAL--GPS", + "Dispatcher2-bof2-constants", + "TargetDetection-zone-1", + "TargetDetection-zone-2", + "SightingCreator-timing-line-config", + "TargetDetection-zone-3", + "Infrared", + "Dispatcher1-bof2", + "UpdateSentinel", + "Infrared--RTSP", + "Colour", + "TargetDetectionColour--TrakVideoWidget", + "Infrared--VMS", + "Dispatcher0-bof2", + "SightingCreator", + "TargetDetection", + "SightingAmmend3-overlay", + "Telemetry", + "Colour--camera-control-widget-config", + "GLOBAL--NetworkConfig", + "Audit", + "Dispatcher2-meta", + "TargetDetection--VideoPreview", + "TargetDetectionColour--sequence-target-finder", + "MMCQueueColour", + "mergedHistory", + "TargetDetection--TrakVideoWidget", + "SightingCreator--SightingHeatMap", + "GLOBAL--Passwords", + "TargetDetectionColour--VideoPreview", + "LogView", + "Colour--RTSP", + "Dispatcher2-utmc", + "Dispatcher3-utmc-constants", + "SightingAmmend3-zone-name-overrides", + "SightingAmmend0-zone-names", + "SightingAmmend2", + "SightingAmmend1", + "SightingAmmend0", + "SightingCreator-camera-offset", + "SightingAmmend3", + "TargetDetectionColour--TargetDetectionFilter", + "SightingAmmend3-lane-ids", + "Dispatcher3-bof2", + "Dispatcher1-utmc", + "Dispatcher0-utmc-constants", + "Audit--Recorder", + "Dispatcher0-bof2-constants", + "SightingAmmend2-overlay", + "Store2", + "Store1", + "Store3", + "TargetDetectionColour-zone--2", + "TargetDetectionColour", + "TargetDetectionColour-zone--1", + "SightingAmmend0-custom-fields", + "Dispatcher1-bof2-constants", + "RaptorOCR", + "Dispatcher0-utmc", + "Store0", + "Dispatcher1", + "Dispatcher2", + "TargetIdentifiedProcessingQueue", + "Dispatcher0", + "GLOBAL--Device", + "Dispatcher3-utmc", + "SightingAmmend3-zone-names", + "Dispatcher3", + "Dispatcher0-meta", + "SightingCreator--TimingLineVideoWidgetInfrared", + "SightingCreator--TimingLineVideoWidgetColour", + "SightingAmmend2-lane-ids", + "Dispatcher3-bof2-constants", + "WidgetViewer", + "TargetDetectionColour-zone-8", + "TargetDetectionColour-zone-9", + "TargetDetectionColour-zone-6", + "TargetDetectionColour-zone-7", + "Dispatcher1-meta", + "SightingAmmend1-zone-names", + "Audit--Colour", + "SightingAmmend1-zone-name-overrides", + "TargetDetectionProcessingQueue", + "SightingAmmend2-zone-name-overrides", + "Infrared--camera-control-widget-config", + "SightingAmmend3-custom-fields", + "Infrared--LiveVideoInput", + "TargetDetectionProcessingQueueColour", + "VideoFramePairCreator", + "Colour--VMS", + "Dispatcher2-bof2", + "infraredHistory", + "Dispatcher1-json", + "SightingAmmend1-overlay", + "SightingAmmend2-custom-fields", + "TargetDetection--TargetDetectionFilter" + ] +} \ No newline at end of file diff --git a/Camera/CameraModules.cs b/Camera/CameraModules.cs index 85d5b8e..c85f436 100644 --- a/Camera/CameraModules.cs +++ b/Camera/CameraModules.cs @@ -71,9 +71,7 @@ namespace AiQ_GUI string OneshotReply = await FlexiAPI.APIHTTPVISCA(IPAddress, "8101041801FF", true); // Oneshot auto focus if (!ShutterReply.Contains("41") || !IrisReply.Contains("41") || !GainReply.Contains("41") || !OneshotReply.Contains("41")) - { MainForm.Instance.AddToActionsList("Could not set Shutter, Iris, Gain correctly" + Environment.NewLine + "Shutter: " + ShutterReply + Environment.NewLine + "Iris: " + IrisReply + Environment.NewLine + "Gain: " + GainReply + Environment.NewLine + "Oneshot: " + OneshotReply); - } } // Sets back to the latest factory defaults CSV that is in Flexi. diff --git a/Camera/FlexiAPI.cs b/Camera/FlexiAPI.cs index 932228f..43d603e 100644 --- a/Camera/FlexiAPI.cs +++ b/Camera/FlexiAPI.cs @@ -49,9 +49,8 @@ namespace AiQ_GUI { try { - string JSONdata = "{ \"id\":\"" + ID + "\" }"; - string url = $"http://{IPAddress}/api/fetch-config"; - return await Network.SendHttpRequest(url, HttpMethod.Get, Timeout, JSONdata); + string url = $"http://{IPAddress}/api/fetch-config?id={ID}"; + return await Network.SendHttpRequest(url, HttpMethod.Get, Timeout); } catch (Exception ex) { @@ -87,14 +86,13 @@ namespace AiQ_GUI try { Network.Client.DefaultRequestHeaders.ExpectContinue = false; - MultipartFormDataContent content; byte[] fileBytes = await File.ReadAllBytesAsync(filePath).ConfigureAwait(false); MemoryStream ms = new(fileBytes); StreamContent streamContent = new(ms); streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); - content = new MultipartFormDataContent { { streamContent, "upload", fileName } }; + MultipartFormDataContent content = new() { { streamContent, "upload", fileName } }; using HttpResponseMessage response = await Network.Client.PostAsync(url, content); string responseBody = await response.Content.ReadAsStringAsync(); @@ -189,6 +187,80 @@ namespace AiQ_GUI return false; } + public static async Task SetTrim(string IPAddress, string LblTxt, int RetryCount = 0) // Sets trim by getting plate postion as metric + { + Trim trim; + string trimData = await APIHTTPRequest("/SightingCreator-plate-positions", IPAddress, 5); // Get plate positions + + try // Deserialise the JSON + { + Logging.LogMessage("Trim Data: " + trimData); + trim = JsonConvert.DeserializeObject(trimData); + } + catch + { + MainForm.Instance.AddToActionsList("Error reading trim JSON - " + trimData); + return; + } + + // Check no value is -1 (no plate found) or if the positions are identical (one plate found). If it is then try again 3 times + if (new[] { trim.infraredX, trim.infraredY, trim.colourX, trim.colourY }.Any(value => value == -1) + || (trim.infraredX == trim.colourX && trim.infraredY == trim.colourY)) + { + if (RetryCount >= 3) + { + 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 + await SetTrim(IPAddress, LblTxt, RetryCount++); + } + + int offset = 105; + + if (LblTxt == "❌") // Test tube not connected so do the 2.7m check. + offset = 98; + + // Horizontal distance offset for 2.7m compared to 30m is 98 pixels. This was empirically found from testing in the car park + // Colour camera is to the right of the infrared so it gets the offset. + // Using similar triangles going from 2.7m -> 0.65m which is the length of the test tube (Also exactly 1/4 length). + // 98 * (29.35/27.3) = 105.35 pixels + int OverviewX = trim.colourX + offset; + + if (OverviewX > 1920) // If adding on the offset has pushed it out of limits then remove 0.1 + { + if (OverviewX < 2120 && trim.infraredX > 400) // Within enough of a limit to automatically do it + { + OverviewX -= 200; + trim.infraredX -= 200; + } + else // Ask user to centre the plate in the field of view + { + 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."); // Awaited till OK has been clicked + return; + } + await Task.Delay(5000); // Give 5 second delay for it to see a plate + await SetTrim(IPAddress, LblTxt, RetryCount++); + } + } + + // Compensated trim values, therefore should be close to 0,0 with limits of ±5% of 1920 and 1080 respectivly being ±96 and ±54 + int TrimX = trim.infraredX - OverviewX; + int TrimY = trim.infraredY - trim.colourY; + + // Update trim values + string[,] Trim_JSON = { { "propInterCameraOffsetX", Convert.ToString(TrimX) }, { "propInterCameraOffsetY", Convert.ToString(TrimY) } }; + 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"); + } + // Processes the network config from the camera and returns a string indicating the status public static async Task ProcessNetworkConfig(string IPAddress) { @@ -210,7 +282,7 @@ namespace AiQ_GUI } // Knowing the format this builds the message to send to AiQ - private static string BuildJsonUpdate(string[,] jsonData, string id) + public static string BuildJsonUpdate(string[,] jsonData, string id) { if (jsonData == null || jsonData.GetLength(1) != 2) throw new ArgumentException("Input data must be a non-null 2D array with two columns."); diff --git a/Camera/SSH.cs b/Camera/SSH.cs index c8f8db2..09d06dd 100644 --- a/Camera/SSH.cs +++ b/Camera/SSH.cs @@ -14,7 +14,7 @@ namespace AiQ_GUI try { - using SshClient client = new SshClient(IPAddress, SSHUsername, SSHPassword); + using SshClient client = new(IPAddress, SSHUsername, SSHPassword); client.Connect(); try diff --git a/Network.cs b/Network.cs index f1f92d9..e581920 100644 --- a/Network.cs +++ b/Network.cs @@ -12,7 +12,7 @@ namespace AiQ_GUI public static void Initialize(string username, string password) { - HttpClientHandler handler = new HttpClientHandler + HttpClientHandler handler = new() { MaxConnectionsPerServer = 25, Credentials = new NetworkCredential(username, password) @@ -30,7 +30,7 @@ namespace AiQ_GUI { try { - HttpRequestMessage request = new HttpRequestMessage(method, url); + HttpRequestMessage request = new(method, url); if (jsonData != null) // Fills in the body of the request if jsonData is provided request.Content = new StringContent(jsonData, Encoding.UTF8, "application/json"); @@ -48,8 +48,8 @@ namespace AiQ_GUI } } - int timeoutMs = (Timeout ?? 10) * 1000; // Convert from seconds to ms - using CancellationTokenSource cts = new CancellationTokenSource(timeoutMs); + int timeoutMs = (Timeout ?? 10) * 1000; // Convert from seconds to ms, default to 10s if null + using CancellationTokenSource cts = new(timeoutMs); using HttpResponseMessage response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token); response.EnsureSuccessStatusCode(); diff --git a/Test.json b/Test.json new file mode 100644 index 0000000..a14fe51 --- /dev/null +++ b/Test.json @@ -0,0 +1,73 @@ +{ + "id": "GLOBAL--Device", + "configHash": "101478235", + "propHardwarePlatform": { + "value": "JETSON_NANO", + "datatype": "mav.flexi.web.pages.SystemConfig$HardwareConfigurationOption", + "accepted": "[NONE, JETSON_NANO, JETSON_XAVIER, RASPBERRY_PI_CM4, RASPBERRY_PI_CM5, DEVELOPER_WORKSTATION]" + }, + "propApplicationPlatform": { + "value": "CUSTOM", + "datatype": "mav.flexi.web.pages.SystemConfig$ApplicationConfigurationOption", + "accepted": "[NONE, CUSTOM, MONO_BASIC_ANPR, DUAL_CAMERA_BASIC_ANPR, SINGLE_SAF_DUAL_CAMERA_BASIC_ANPR, DUAL_CAMERA_STREAMING, BAYGUARD, SITE_ACCESS_CONTROL_WITH_MODBUS, WAPOL_IQ50_VRM_CORRECTION, BAYWATCH_MONO_CAM, BAYWATCH_DUAL_CAM, BAYWATCH_TRIPLE_CAM, BAYWATCH_QUADRUPLE_CAM, IN_CAR_ANPR]" + }, + "propDeviceName": { + "value": "Test Nano", + "datatype": "java.lang.String" + }, + "propLocalTimeZone": { + "value": "Europe/London (UTC+00)", + "datatype": "mav.util.TimeZoneEnum", + "accepted": "[Africa/Cairo (UTC+02), Africa/Johannesburg (UTC+02), Africa/Lagos (UTC+01), Africa/Monrousing (UTC+00), America/Anchorage (UTC-09), America/Chicago (UTC-06), America/Denver (UTC-07), America/Edmonton (UTC-07), America/Jamaica (UTC-05), America/Los Angeles (UTC-08), America/Mexico City (UTC-06), America/Montreal (UTC-05), America/New/York (UTC-05), America/Phoenix (UTC-07), America/Puerto Rico (UTC-04), America/Sao Paulo (UTC-03), America/Toronto (UTC-05), America/Vancouver (UTC-08), Asia/Hong Kong (UTC+08), Asia/Jerusalem (UTC+02), Asia/Manila (UTC+08), Asia/Seoul (UTC+09), Asia/Tokyo (UTC+09), Atlantic/Reykjavik (UTC+00), Australia/Perth (UTC+08), Australia/Sydney (UTC+10), Europe/Athens (UTC+02), Europe/Berlin (UTC+01), Europe/Brussels (UTC+01), Europe/Copenhagen (UTC+01), Europe/London (UTC+00), Europe/Madrid (UTC+01), Europe/Moscow (UTC+04), Europe/Paris (UTC+01), Europe/Prague (UTC+01), Europe/Rome (UTC+01), Europe/Warsaw (UTC+01), Pacific/Guam (UTC+10), Pacific/Honolulu (UTC-10), UTC (UTC-00)]" + }, + "propTimeSource": { + "value": "SNTP Only", + "datatype": "mav.flexi.web.pages.SystemConfig$TimeSourceEnum", + "accepted": "[SNTP Only, GPS Only, SNTP + Soft GPS]" + }, + "propSNTPServer": { + "value": "1.uk.pool.ntp.org", + "datatype": "java.lang.String" + }, + "propSNTPIntervalMinutes": { + "value": "public long mav.flexi.web.pages.SystemConfig$DeviceConfig.propSNTPIntervalMinutes", + "datatype": "long", + "range": { + "minimum": "1.000000", + "maximum": "99999.000000" + } + }, + "propOutputsMaxTimeSyncAge": { + "value": "24 hours", + "datatype": "mav.flexi.web.pages.SystemConfig$HoursEnum", + "accepted": "[NO LIMIT, 01 hours, 02 hours, 03 hours, 04 hours, 05 hours, 06 hours, 07 hours, 08 hours, 09 hours, 10 hours, 11 hours, 12 hours, 18 hours, 24 hours, 36 hours, 48 hours, 72 hours, 96 hours]" + }, + "propReservedMemoryMegabytes": { + "value": "public long mav.flexi.web.pages.SystemConfig$DeviceConfig.propReservedMemoryMegabytes", + "datatype": "long", + "range": { + "minimum": "1000.000000", + "maximum": "128000.000000" + } + }, + "propClientTrustStorePassword": { + "value": "", + "datatype": "java.lang.String" + }, + "propFlexiScriptPath": { + "value": "/home/mav/FlexiAI/bin/FlexiAI", + "datatype": "java.lang.String" + }, + "propVideo0Tee": { + "value": "video0tee", + "datatype": "java.lang.String" + }, + "propVideo1Tee": { + "value": "video1tee", + "datatype": "java.lang.String" + }, + "propRestartCameraModulesOnBoot": { + "value": "true", + "datatype": "boolean" + } +} \ No newline at end of file