Files
AiQ_GUI/AiQ_GUI.cs
2025-09-16 10:42:51 +01:00

1727 lines
74 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Newtonsoft.Json;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
namespace AiQ_GUI
{
public partial class MainForm : Form
{
// Classes
LocalDataStore localDataStore = new();
Diags DiagsAPI = new();
VaxtorLic VaxtorLicResp = new();
Versions Vers = new();
readonly Camera CamOnTest = new();
SSHData sshData = new();
private List<Camera> soakCameraList = [];
private List<CancellationTokenSource> soakCtsList = [];
private List<Task> soakTasks = [];
// Colours
public static readonly Color BtnColour = Color.FromArgb(70, 65, 80);
public static readonly Color TxBxColour = Color.FromArgb(53, 51, 64);
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public static MainForm? Instance { get; private set; }
public MainForm()
{
InitializeComponent();
Instance = this;
}
private async void AiQGUI_Load(object sender, EventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
Task? closeProcessesTask = Windows.CloseProcesses(); // Fire and forget closing other apps
Windows.UpdateFirewall();
Task UniDataTask = Task.Run(() => Access.ReadUniData()); // Get universal data
Task<LocalDataStore> LDSWAIT = Task.Run(() => LDS.GetLDS()); // Get and deserialise LDS.json
Task<string> guiVerTask = Task.Run(() => GUIUpdate.FindGUIVersion()); // Get GUI Version
Network.Initialize("admin", "admin"); // Initialise HTTP client
if (await Network.PingIP("8.8.8.8")) // Ping to check if we're online
{
if (!GoogleAPI.Setup())
AddToActionsList("Cannot setup Google API");
}
else
{
Flags.Offline = true;
BtnStartTest.Text = "Offline Mode";
}
GUIUpdate.GUIVerShort = await guiVerTask; // Guess the GUI version will be first to finish
this.Name = "AiQ GUI V" + GUIUpdate.GUIVerShort;
LblGUIVers.Text += GUIUpdate.GUIVerShort;
await UniDataTask; // Have to wait for expected GUI version to compare to.
Task<string[]> 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;
Logging.LogMessage("Opening GUI"); // Done after LDS to make sure directory exists.
if (localDataStore == null)
{
AddToActionsList("Could not deserialise LDS.json please help!");
return;
}
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
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
if (RegexCache.FlexiVerRegex().IsMatch(UniversalData.ExpFlexiVer)) // Update Flexi version from universal data
TxBxFlexiVer.Text = UniversalData.ExpFlexiVer;
await CheckHWOnline;
Flags.Start = false;
stopwatch.Stop();
Debug.WriteLine("RunTime " + stopwatch.Elapsed.ToString(@"hh\:mm\:ss\.ff"));
}
private void PopulateUIWithLDS(LocalDataStore lds)
{
CbBxUserName.Text = lds.User;
TxBxPsuIP.Text = PSU.PSUIP = lds.PSUIP;
TxBxEzIP.Text = Ez.PoEPowerIP = lds.EzIP;
TxBxZebraIP.Text = Printer.ZebraIP = lds.ZebraIP;
TxBxTestTubeIP.Text = TestTube.TTPiPicoIP = lds.TestTubeIP;
CbBxShutter.SelectedIndex = lds.Shutter;
CbBxIris.SelectedIndex = lds.Iris;
CbBxGain.SelectedIndex = lds.Gain;
if (lds.User == "Bradley")
BtnTest.Visible = true;
TxBxCheckValid(TxBxPsuIP); // Set save button color if valid
}
private async void MainForm_Shown(object sender, EventArgs e) // Done on show as all UI elements are loaded in properly for find cams to use
{
BtnFindCams_Click(sender, e);
CheckPrintCapable(); // Check if the print buttons can be enabled
TestStartConditions();
await Task.Delay(1000); // Delay for UI to catch up
if (LblPSUPing.ForeColor == Color.ForestGreen) // Check state of PSU if it is connected.
Task.Run(() => PSU.DisplayState(PSU.PSUIP));
}
// ***** Test buttons *****
private async void BtnStartTest_Click(object sender, EventArgs e)
{
// Show user test has started
BtnStartTest.BackColor = Color.Orange;
BtnStartTest.Text = "Test underway";
BtnPreTest.Enabled = BtnStartTest.Enabled = false; // Disable buttons to stop user rnning multiple tests at the same time.
Logging.LogMessage("Final Test Started");
if (LblTestTubePing.Text == "❌") // No test tube so test like an IQ
{
string LEDreply = await FlexiAPI.APIHTTPLED(CamOnTest.IP, LEDPOWER.SAFE); // Set LED's to safe (0x0E) to help with eye safety and trim check.
if (!LEDreply.Contains("Power levels set successfully"))
AddToActionsList($"LED level could not be set: {LEDreply}");
}
else if (!await TestTube.CheckInTestTube(CamOnTest.IP)) // Sets LED's to medium power after checking it is in the test tube
await TestFailed(BtnStartTest, "Camera not in test tube");
Task VisCheck = Helper.VisualCheck(BtnStartTest);
if (!await FlexiAPI.ZoomModules("1F40", CamOnTest.IP)) // Zoom to 8000 (1F40h) at the same time.
await TestFailed(BtnStartTest, "Could not zoom modules to 8000");
if (!await FlexiAPI.SetZoomLockOn(CamOnTest.IP))
Helper.RestartApp();
await Task.Delay(1000); // Without sleep it kept failing the factory reset as camera modules were not ready yet
await CameraModules.FactoryResetModules(CamOnTest.IP); // Reset both modules and double check
string VISCAReply = await FlexiAPI.APIHTTPVISCA(CamOnTest.IP, "8101043903FF", true); // Manual mode to be able to manipulate the SIG settings.
if (VISCAReply != "9041FF9051FF")
AddToActionsList("Couldn't set to manual mode");
await CameraModules.SetSIG(CbBxShutter, CbBxIris, CbBxGain, CamOnTest.IP); // Set SIG according to the drop downs in settings for a good picture ready for image check
await ImageProcessing.ImageCheck(PicBxOV, PicBxIRF2, PicBxIRF16, LblIRImageF2, LblIRImageF16, CamOnTest); // Populates the picture boxes and checks iris changes
await VisCheck; // Before changing UI elements wait for user to finish Visual check
TabImagesandSettings.SelectedIndex = 2; // Swaps to the images tab
this.Refresh(); // Show user things are happening by displaying images taken
// TODO - Force expire sighting.
Task Wait = Task.Delay(5000); // Wait for 5 seconds to allow the camera to zoom in, set settings and capture some plates before auto trim
// While waiting do the SSH tasks.
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
if (CameraAccessInfo.HardwareExtras.Contains("4G")) // If it is a router camera then test the router.
{
LblRouter.Visible = true;
if (Router.CheckRouter(Router.GetRouterInfo()))
LblRouter.Text += "OK";
else
LblRouter.Text += "Error with router";
}
await Wait; // Finished to 5s wait
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");
await Task.Delay(1000); // Wait to be sure cameras are zoomed out.
await CameraModules.FactoryResetModules(CamOnTest.IP); // Reset both modules and double check
if (LblTestTubePing.Text == "❌") // Set LED's to MID in prep for diagnostics API
{
string LEDreply = await FlexiAPI.APIHTTPLED(CamOnTest.IP, LEDPOWER.MID); // Set LED's to medium (0x30)
if (!LEDreply.Contains("Power levels set successfully"))
AddToActionsList($"LED level could not be set: {LEDreply}");
}
DateTime PCTime = DateTime.Now; // Grab PC time as close to the API as possible to pass onto PDF later
await CheckDiagsAPIPt1(); // Requests, deserialises and checks the diagnostics API is correct
await CheckDiagsAPIPt2(); // For only final test parts
// Check module has gone to default config
CameraModules.CheckCamModule(DiagsAPI.IRmodule, LblIRModule); // IR
CameraModules.CheckCamModule(DiagsAPI.OVmodule, LblOVModule); // OV
// Check voltage and current are OK.
LED.CheckLEDs(DiagsAPI.LedVoltage, LblLEDV, "V", CameraAccessInfo.LED_V); // Voltage
LED.CheckLEDs(DiagsAPI.LedCurrent, LblLEDI, "mA", CameraAccessInfo.LED_I); // Current
this.Refresh(); // Make sure all labels are updated before checking them
// If there are any actions identified then fail the test.
// If any labels are red then fail. Only labels in panel so can foreach on labels not controls
if (RhTxBxActions.Text.Length > 2 || PnlLbls.Controls.OfType<Label>().Any(c => c.ForeColor == Color.Red) == true)
await TestFailed(BtnStartTest);// If approved then pass otherwise GUI would have restarted before getting to TestPassed.
await TestPassed(PCTime);
}
private async void BtnPreTest_Click(object sender, EventArgs e)
{
// Show user test has started
BtnPreTest.BackColor = Color.Orange;
BtnPreTest.Text = "Pre-Test underway";
BtnPreTest.Enabled = BtnStartTest.Enabled = false; // Disable buttons to stop user rnning multiple tests at the same time.
Logging.LogMessage("Pre Test Started");
if (!await FlexiAPI.SetZoomLockOn(CamOnTest.IP))
Helper.RestartApp();
string LEDreply = await FlexiAPI.APIHTTPLED(CamOnTest.IP, LEDPOWER.MID); // Set LED's to medium (0x30)
if (!LEDreply.Contains("Power levels set successfully"))
AddToActionsList($"LED level could not be set: {LEDreply}");
await CameraModules.FactoryResetModules(CamOnTest.IP); // Reset both modules and double check
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
await CheckDiagsAPIPt1();
// Check module has gone to default config
CameraModules.CheckCamModule(DiagsAPI.IRmodule, LblIRModule); // IR
CameraModules.CheckCamModule(DiagsAPI.OVmodule, LblOVModule); // OV
// Check voltage and current are OK.
LED.CheckLEDs(DiagsAPI.LedVoltage, LblLEDV, "V", CameraAccessInfo.LED_V); // Voltage
LED.CheckLEDs(DiagsAPI.LedCurrent, LblLEDI, "mA", CameraAccessInfo.LED_I); // Current
this.Refresh(); // Make sure all labels are updated before checking them
// If there are any actions identified then fail the test.
// If any labels are red then fail. Only labels in panel so can foreach on labels not controls
if (RhTxBxActions.Text.Length < 2 && PnlLbls.Controls.OfType<Label>().Any(c => c.ForeColor == Color.Red) == false)
{
// If camera already has a model or serial then ask if it is new
if (RegexCache.SerialRegex().IsMatch(DiagsAPI.serialNumber) || RegexCache.ModelRegex().IsMatch(DiagsAPI.modelNumber))
{
if (await DisplayQuestion($"Would you like to allocate a serial number to this camera?"))
await AllocateSerial();
else if (GoogleAPI.UpdateSpreadSheetRePreTest(CameraAccessInfo.SpreadsheetID, Vers) != "OK") // If rerun might be different values so update SS
AddToActionsList("Failed to write to spreadsheet, please check manually");
}
else // No serial or model so allocate one
await AllocateSerial();
if (RhTxBxActions.Text.Length < 2 && PnlLbls.Controls.OfType<Label>().Any(c => c.ForeColor == Color.Red) == false)
await PreTestPassed();
}
else
await PreTestFailed();
}
// ***** Pass/Fails *****
private async Task TestPassed(DateTime PCTime)
{
// Updates Vaxtor versions, licenses and unticks WIP
string err = GoogleAPI.UpdateSpreadSheetFinalTest(CameraAccessInfo.SpreadsheetID, DiagsAPI, sshData, CamOnTest.RMANum);
if (err != string.Empty) // If there is an error message, display it
AddToActionsList("Failed to write to spreadsheet " + err);
// Purge camera of all reads
await FlexiAPI.APIHTTPRequest("/api/purge-all", CamOnTest.IP);
if (await DisplayQuestion("Do you want to set this camera to 211 and God mode?"))
{
// Turn off God mode
string[,] GOD_JSON = { { "propGodMode", "false" } };
string IntConf = await FlexiAPI.HTTP_Update("Internal Config", CamOnTest.IP, GOD_JSON);
if (!IntConf.Contains("\"propGodMode\": {\"value\": \"false\", \"datatype\": \"boolean\"},"))
AddToActionsList("Could not turn off God mode");
Thread Thr211 = new(async () =>
{
if (!await FlexiAPI.ChangeNetwork211(CamOnTest.IP)) // Change camera IP to 192.168.1.211. Waits for camera to come back.
AddToActionsList("Could not find camera at 192.168.1.211. Please check manually");
});
Thr211.IsBackground = true;
Thr211.Start();
}
this.Refresh(); // To make sure all labels are up to date before reading them
string fulltestvalues = $"GUI Version = {GUIUpdate.GUIVerShort}\n" +
string.Join("\n", PnlLbls.Controls
.OfType<Label>()
.Where(lbl => lbl.Visible) // Only include visible labels
.OrderBy(lbl => lbl.Location.Y) // Sort from top to bottom
.Select(lbl => lbl.Text)); // Get the text of each label
fulltestvalues += Helper.GetOverriddenActions(PnlLbls, RhTxBxActions);
// TODO make sure serial number is in CamOnTest before making PDF
// Serial number is either what came out of the camera under RMA or updated above as new serial number from next empty row in spreadsheet
PDF.CreateFinalTestReport(CamOnTest, CbBxUserName.Text, fulltestvalues, PCTime);
// Indicators to the user the test has passed
BtnStartTest.BackColor = Color.ForestGreen;
BtnStartTest.Text = "Test Passed";
PnlQuestion.Visible = false; // just in case this came from an override
Logging.LogMessage("Final Test Passed");
}
public async Task TestFailed(Button Btn, string ErrMssg)
{
AddToActionsList(ErrMssg);
await TestFailed(Btn);
}
private async Task TestFailed(Button Btn)
{
// Indicators to the user the test has failed
Btn.BackColor = Color.Maroon;
Btn.Text = "Test Failed";
if (await DisplayQuestion("Test failed, appeal?" + Environment.NewLine + "See Actions textbox for details."))
{
if (CbBxUserName.Text == "Bradley")
{
await TestPassed(DateTime.Now); // Debugging
return;
}
BtnYes.Visible = BtnNo.Visible = false; // Remove buttons
LblQuestion.Text = "Please ask for approval";
PnlQuestion.Visible = true;
this.Refresh(); // To make sure all labels are up to date before reading them
// Joins the actions box to any red labels to use as a full failed text
string FullFailureValues = RhTxBxActions.Text + Environment.NewLine +
string.Join(Environment.NewLine, PnlLbls.Controls
.OfType<Label>()
.Where(lbl => lbl.ForeColor == Color.Red) // Only include red labels
.Select(lbl => lbl.Text)); // Extract text
Logging.LogErrorMessage(FullFailureValues);
IList<IList<object>> values = GoogleAPI.service.Spreadsheets.Values.Get(GoogleAPI.spreadsheetId_ModelInfo, "'Approval'!A1:A").Execute().Values;
if (values?.Count > 0)
{
int nextRow = values.Count + 1;
List<object> oblistA = [CbBxUserName.Text];
GoogleAPI.WriteToSS(oblistA, "'Approval'!A" + nextRow, GoogleAPI.spreadsheetId_ModelInfo);
List<object> oblistCD = [CamOnTest.Model, FullFailureValues];
GoogleAPI.WriteToSS(oblistCD, "'Approval'!C" + nextRow + ":D" + nextRow, GoogleAPI.spreadsheetId_ModelInfo);
//await Teams.SendMssg(Convert.ToString(nextRow), CbBxUserName.Text);
GoogleAPI.EmailApproval(Convert.ToString(nextRow), CbBxUserName.Text);
string Approved = "";
while (Approved != "TRUE")
{
await Task.Delay(1000);
values = GoogleAPI.service.Spreadsheets.Values.Get(GoogleAPI.spreadsheetId_ModelInfo, "'Approval'!B" + nextRow).Execute().Values;
if (values?.Count > 0)
Approved = Convert.ToString(values[0][0]);
else
{
await Logging.LogErrorMessage("No values returned from Approval spreadsheet for row " + nextRow);
Helper.RestartApp();
}
}
}
else
Helper.RestartApp();
// Sets back to continue test
PnlQuestion.Visible = false;
BtnYes.Visible = BtnNo.Visible = true;
Btn.BackColor = Color.Orange;
Btn.Text = "Test underway";
}
else
Helper.RestartApp();
}
private async Task PreTestPassed()
{
BtnPreTest.BackColor = Color.ForestGreen; // Indicators to the user the test has passed
BtnPreTest.Text = "Pre Test Passed";
PnlQuestion.Visible = true;
BtnNo.Visible = false;
Logging.LogMessage("Pre Test Passed");
if (await DisplayQuestion("Test passed, restart?"))
Helper.RestartApp();
}
private async Task PreTestFailed()
{
BtnPreTest.BackColor = Color.Maroon; // Indicators to the user the test has failed
BtnPreTest.Text = "Test Failed";
PnlQuestion.Visible = true;
BtnNo.Visible = false;
Logging.LogMessage("Pre Test Failed");
if (await DisplayQuestion("Test failed, restart?" + Environment.NewLine + "See Actions textbox for details."))
Helper.RestartApp();
}
// ***** Testing functions *****
private async Task CheckDiagsAPIPt1() // Parts done on pre and final test
{
DiagsAPI = await FlexiAPI.GetDiagnostics(CamOnTest.IP); // Diagnostic API request
lblFlexiVer.Text += DiagsAPI.FlexiVersion; // Check Flexi Version
if (DiagsAPI.FlexiVersion == UniversalData.ExpFlexiVer)
{
lblFlexiVer.ForeColor = Color.LightGreen;
}
else
{
lblFlexiVer.Text += " Expected = " + UniversalData.ExpFlexiVer;
lblFlexiVer.ForeColor = Color.Red;
}
lblFlexiRev.Text += DiagsAPI.FlexiRevision; // Check Flexi Revision
if (DiagsAPI.FlexiRevision == UniversalData.ExpFlexiRev)
{
lblFlexiRev.ForeColor = Color.LightGreen;
}
else
{
lblFlexiRev.Text += " Expected = " + UniversalData.ExpFlexiRev;
lblFlexiRev.ForeColor = Color.Red;
}
lblMac.Text += DiagsAPI.MAC; // Display MAC
if (RegexCache.MACRegexNVIDIA().IsMatch(DiagsAPI.MAC)) // Checks it is in the right format and is a NVIDIA registered MAC address
{
lblMac.ForeColor = Color.LightGreen;
}
else if (RegexCache.MACRegex().IsMatch(DiagsAPI.MAC)) // Is a valid MAC, but not NVIDIA
{
lblMac.ForeColor = Color.Red;
AddToActionsList($"{DiagsAPI.MAC} not recognised as NVIDIA MAC address");
}
else
lblMac.ForeColor = Color.Red;
// Check timestamp
DateTime dateTime = new(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
dateTime = dateTime.AddSeconds(DiagsAPI.timeStamp).ToLocalTime();
lbltimestamp.Text += dateTime;
long timediff = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - DiagsAPI.timeStamp;
if (timediff > 10) // Over 10 seconds ago
{
lbltimestamp.Text += $" Time behind by {timediff}s";
lbltimestamp.ForeColor = Color.Red;
}
else if (timediff < 0) // Time is in the future.
{
lbltimestamp.Text += $" Time is in the future by {Math.Abs(timediff)}s";
lbltimestamp.ForeColor = Color.Red;
}
else
lbltimestamp.ForeColor = Color.LightGreen;
lblTemp.Text += DiagsAPI.IntTemperature + "°C"; // Diagnostic API request
if (DiagsAPI.IntTemperature > 20 && DiagsAPI.IntTemperature < 70)
lblTemp.ForeColor = Color.LightGreen;
else
lblTemp.ForeColor = Color.Red;
lblZoomLock.Text += DiagsAPI.zoomLock;
if (DiagsAPI.zoomLock == true)
{
if (DiagsAPI.IRmodule.zoom == DiagsAPI.OVmodule.zoom) // Checks if zoomlock is doing what is says it should
{
lblZoomLock.ForeColor = Color.LightGreen;
}
else
{
lblZoomLock.Text += $" Zoomlock on but {DiagsAPI.IRmodule.zoom}≠{DiagsAPI.OVmodule.zoom}";
lblZoomLock.ForeColor = Color.Red;
}
}
else
lblZoomLock.ForeColor = Color.Red;
}
private async Task CheckDiagsAPIPt2() // Parts only done on final test
{
if (RhTxBxActions.Text.Contains("Error reading JSON")) // If failed to deserialise in part 1
return;
try // In case serial or model are blank
{
lblModel.Text += DiagsAPI.modelNumber; // Update labels with serial and model
lblSerial.Text += DiagsAPI.serialNumber;
if (RegexCache.SerialRegex().IsMatch(DiagsAPI.serialNumber) && RegexCache.ModelRegex().IsMatch(DiagsAPI.modelNumber))
{
lblSerial.ForeColor = lblModel.ForeColor = Color.LightGreen; // Set both to green
if (DiagsAPI.modelNumber != CamOnTest.Model)
{
AddToActionsList("Model number in camera doesn't match what has been selected");
lblModel.ForeColor = Color.Red;
}
else if (!GoogleAPI.CheckWIP(DiagsAPI.serialNumber, CameraAccessInfo.SpreadsheetID)) // Check WIP column in serial number register, if not ticked then RMA
{
CamOnTest.RMANum = GoogleAPI.CheckRMANum(DiagsAPI.serialNumber, DiagsAPI.modelNumber); // Corrected by qualifying with the type name
if (CamOnTest.RMANum == 0) // Couldn't find RMA num in spreadsheet
{
CamOnTest.RMANum = Convert.ToInt32(await DisplayInput("What is the RMA number?"));
if (CamOnTest.RMANum == -1) // Means they chose the 'I don't know' option
await TestFailed(BtnStartTest, "Please get RMA number from operations team before continuing");
}
// Found RMA num and want to verify it with user
else if (!await DisplayQuestion($"Is {CamOnTest.RMANum} the RMA Number?")) // '!' because if its not the right RMA number let the user to it manually
{
CamOnTest.RMANum = Convert.ToInt32(await DisplayInput("What is the RMA number?"));
if (CamOnTest.RMANum == -1) // Means they chose the 'I don't know' option
await TestFailed(BtnStartTest, "Please get RMA number from operations team before continuing");
}
}
}
else
{
AddToActionsList("Camera has not been given a valid serial and model number, suggest you run through pre test again and check serial number register for any issues.");
}
}
catch { }
// Check licenses
List<string> licensesOnCam = []; // Temporary list for licenses on camera
if (DiagsAPI.licenses.saf1)
licensesOnCam.Add("SAF1");
if (DiagsAPI.licenses.saf2)
licensesOnCam.Add("SAF2");
if (DiagsAPI.licenses.saf3)
licensesOnCam.Add("SAF3");
if (DiagsAPI.licenses.saf4)
licensesOnCam.Add("SAF4");
if (DiagsAPI.licenses.audit)
licensesOnCam.Add("Audit");
if (DiagsAPI.licenses.stream)
licensesOnCam.Add("Stream");
if (sshData.tailscale)
licensesOnCam.Add("Tailscale");
if (licensesOnCam.Count == 0) // No licenses found
lblLic.Text += "None";
else if (licensesOnCam.Count != 0) // Join them comma and space seperated for displaying
lblLic.Text += string.Join(", ", licensesOnCam);
lblLic.ForeColor = Color.LightGreen;
double CPUround = Math.Round(DiagsAPI.CPUusage); // Check CPU usage isn't near max
LblCPUusage.Text += CPUround + "%";
if (CPUround < 98 && CPUround > 50)
{
LblCPUusage.ForeColor = Color.LightGreen;
}
else if (CPUround <= 50)
{
LblCPUusage.Text += " Unexpectedly low CPU usage";
LblCPUusage.ForeColor = Color.Red;
}
else if (CPUround >= 98)
{
LblCPUusage.Text += " Unexpectedly high CPU usage";
LblCPUusage.ForeColor = Color.Red;
}
// Check Vaxtor if it doesn't need or have license OR has and wants one then pass
if (CameraAccessInfo.VaxtorLic == false && DiagsAPI.licenses.raptorKeyID == "Not Licensed" || CameraAccessInfo.VaxtorLic == true && DiagsAPI.licenses.raptorKeyID != "Not Licensed")
{
lblVaxtor.Text += DiagsAPI.licenses.raptorKeyID;
lblVaxtor.ForeColor = Color.LightGreen;
}
else if (await DisplayQuestion("Was this camera licensed manually?"))
{
string ProdcutKeyID = await DisplayInput("What is the Key ID?", false);
if (RegexCache.VaxtorRegex().IsMatch(ProdcutKeyID)) // Means they chose the 'I don't know' option or isn't valid Key ID
await TestFailed(BtnStartTest, "Please get a valid Vaxtor Product Key before continuing");
DiagsAPI.licenses.raptorKeyID = TxBxProductKey.Text;
lblVaxtor.Text += DiagsAPI.licenses.raptorKeyID;
lblVaxtor.ForeColor = Color.LightGreen;
}
else // Should have license but doesn't OR shouldn't have but does + any other unforseen circumstance then fail
{
lblVaxtor.Text += DiagsAPI.licenses.raptorKeyID;
lblVaxtor.ForeColor = Color.Red;
}
// Check trim is within 10% both horizontally and vertically, from auto set done earlier in the test
lblTrim.Text += "H: " + DiagsAPI.trim[0] + " V: " + DiagsAPI.trim[1];
// Offset accounted for in the SetTrim function, so value should be close to 0,0.
int HMax = 96; // 5% of 1920 each way = ±96
int VMax = 54; // 5% of 1080 each way = ±54
if (Math.Abs(DiagsAPI.trim[0]) <= HMax && Math.Abs(DiagsAPI.trim[1]) <= VMax)
lblTrim.ForeColor = Color.LightGreen;
else
lblTrim.ForeColor = Color.Red;
}
private async Task AllocateSerial()
{
// 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);
if (NewSerial.Contains("ERROR"))
{
AddToActionsList(NewSerial + Environment.NewLine + "Please remove any information that was put into the serial number mistake, change the camera model/serial to 'N/A' and retry the test.");
return;
}
else if (NewSerial == "Last serial number not found")
{
AddToActionsList("Last serial number not found in spreadsheet. Please check spreadsheet is in correct format before retrying.");
return;
}
// Set serial and model into camera
string[,] TEST_JSON = { { "propSerialNumber", NewSerial }, { "propMavModelNumber", CamOnTest.Model } };
string JSONResponse = await FlexiAPI.HTTP_Update("Internal Config", CamOnTest.IP, TEST_JSON);
if (!JSONResponse.Contains(NewSerial) || !JSONResponse.Contains(CamOnTest.Model))
{
AddToActionsList("Could not set model or serial numbers into camera.");
await PreTestFailed();
}
DiagsAPI.modelNumber = CamOnTest.Model; // Update Diags and labels
DiagsAPI.serialNumber = NewSerial;
lblModel.Text += DiagsAPI.modelNumber;
lblModel.ForeColor = lblSerial.ForeColor = Color.LightGreen;
lblSerial.Text += DiagsAPI.serialNumber;
Printer.ZebraIP = localDataStore.ZebraIP;
Printer.PrintGBLbl(); // Print GB label
Printer.PrintSerialLbl(CamOnTest.Model, NewSerial, CameraAccessInfo.Processor); // Print model/serial label
}
// ***** Top right buttons *****
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)
{
localDataStore.User = CbBxUserName.Text;
localDataStore.LastModel = CbBxCameraType.Text;
LDS.SetLDS(localDataStore);
}
Environment.Exit(0); // Close app and dispose of everything, a very abrupt method
}
private void BtnMin_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
private void BtnRestart_Click(object sender, EventArgs e)
{
Helper.RestartApp(); // Restart abruptly don't worry about anything else going on
}
// ***** Allows moving GUI by grab and dragging *****
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Helper.ReleaseCapture();
Helper.SendMessage(Handle, 0xA1, 0x2, 0);
}
}
// ***** Button actions *****
// Find cameras, sending broadcast and receiving simultaneously
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 = BtnFactoryDefault.Enabled = false;
BtnSetGodMode.BackColor = BtnUploadBlob.BackColor = BtnFactoryDefault.BackColor = BtnSetAll211.BackColor = BtnColour;
CbBxFoundCams.Items.Clear();
soakCameraList.Clear();
// Deletes all the checkboxes in TabSoak
List<CheckBox> checkBoxes = TabSoak.Controls.OfType<CheckBox>().Except([CkBxTickAll]).ToList();
foreach (CheckBox? cb in checkBoxes)
{
TabSoak.Controls.Remove(cb);
cb.Dispose();
}
IList<string> FoundCams = await Network.SearchForCams();
foreach (string Cam in FoundCams)
{
CbBxFoundCams.Items.Add(Cam); // Update combo box list
if (TabImagesandSettings.SelectedIndex == 3)
{
Camera NewCam = await Helper.NewCamera(Cam);
if (NewCam != null)
soakCameraList.Add(NewCam);
}
}
// Order soakCameraList by serial
soakCameraList.Sort((a, b) => string.Compare(a.Serial, b.Serial, StringComparison.OrdinalIgnoreCase));
int YLoc = 74;
foreach (Camera soakInfo in soakCameraList)
{
TabSoak.Controls.Add(SoakTest.MakeNewCheckbox(soakInfo, YLoc));
YLoc += 24;
}
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 = BtnFactoryDefault.Enabled = true;
}
private void BtnYes_Click(object sender, EventArgs e)
{
Flags.Yes = true; // Set the Yes flag to true
}
private void BtnNo_Click(object sender, EventArgs e)
{
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)
{
TestStartConditions();
}
private void CmBoFoundCams_TextChanged(object sender, EventArgs e)
{
timerTypeIP.Enabled = false;
timerTypeIP.Enabled = true;
}
private async void CbBxFoundCams_SelectedIndexChanged(object sender, EventArgs e) // Also services the timerTypeIP_Tick
{
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
{
if (!await Network.PingIP(CbBxFoundCams.Text))
{
CbBxFoundCams.BackColor = Color.Red;
return;
}
CamOnTest.IP = CbBxFoundCams.Text; // Set the IP address under test
CbBxFoundCams.BackColor = BtnColour;
BtnSecret.Enabled = true;
Vers = await FlexiAPI.GetVersions(CamOnTest.IP);
// Wont be filled out before the pre test but needed for final test
if (RegexCache.SerialRegex().IsMatch(CamOnTest.Serial) && RegexCache.ModelRegex().IsMatch(CamOnTest.Model))
{
CamOnTest.Serial = Vers.Serial; // Set the serial number from the versions API
CamOnTest.Model = Vers.Model; // Set the serial number from the versions API
}
if (Vers == null) // If failed to get versions then return. Flexi most likely not running yet.
{
AddToActionsList("Failed to get API from camera. Flexi not running yet or not an AiQ");
return;
}
Lics.DisplayDevPassword(Vers, CamOnTest); // Generate and display secret for use later
string networkConfigText = await FlexiAPI.ProcessNetworkConfig(CamOnTest.IP);
BtnSet211.Text = string.IsNullOrEmpty(networkConfigText) ? "Set to 211" : networkConfigText;
ShowToolTip(BtnSecret); // Set dev password to Tooltip and clipboard
}
else if (CbBxFoundCams.Text.Contains("Found"))
{
CbBxFoundCams.BackColor = BtnColour;
}
else
{
CbBxFoundCams.BackColor = Color.Red;
BtnSecret.Enabled = BtnOpenWebpage.Enabled = BtnSet211.Enabled = BtnSetGodMode.Enabled = false;
}
TestStartConditions();
}
private void btnPsuOn_Click(object sender, EventArgs e)
{
PSU.PSU_ON(PSU.PSUIP);
}
private void btnPsuOff_Click(object sender, EventArgs e)
{
PSU.PSU_OFF(PSU.PSUIP);
}
private void btnPsu48V_Click(object sender, EventArgs e)
{
PSU.PSU_OFF(PSU.PSUIP);
PSU.SendDataPsu("V1 48", PSU.PSUIP);
PSU.SendDataPsu("I1 1.5", PSU.PSUIP);
}
private void btnPsu12V_Click(object sender, EventArgs e)
{
PSU.PSU_OFF(PSU.PSUIP);
PSU.SendDataPsu("V1 12", PSU.PSUIP);
PSU.SendDataPsu("I1 3.5", PSU.PSUIP);
}
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 } };
try
{
await FlexiAPI.HTTP_Update("Internal Config", CamOnTest.IP, GOD_JSON);
BtnSetGodMode.Text = newGodModeValue == "true" ? "Set God Mode Off" : "Set God Mode On";
BtnSetGodMode.BackColor = Color.Green;
}
catch (Exception ex)
{
AddToActionsList($"Failed to set God mode for camera {CamOnTest.IP}. Reason: {ex.Message}");
}
}
// ***** Helper functions *****
public void AddToActionsList(string Mssg, bool IsErr = true)
{
if (IsErr)
Logging.LogErrorMessage(Mssg);
else
Logging.LogMessage(Mssg);
RhTxBxActions.AppendText(Mssg + Environment.NewLine);
RhTxBxActions.SelectionStart = RhTxBxActions.Text.Length;
RhTxBxActions.ScrollToCaret();
}
private async void TestStartConditions()
{
if (Flags.Start)
return; // If on GUI load don't evaluate
RhTxBxActions.Text = ""; // Clear actions box
bool TSC = true;
if (Flags.Offline == true) // If start found we are offline
{
if (await Network.PingIP("8.8.8.8")) // Ping to find if we are online
{
Flags.Offline = false; // Ping succeeded
CbBxCameraType.Enabled = true;
}
else
TSC = SetInvalid("Offline Mode, could not connect to the internet."); // Ping failed
}
// Camera IP selected
if (CbBxFoundCams.BackColor != BtnColour || CbBxFoundCams.Text.Contains("Found"))
TSC = SetInvalid("Select camera IP address.");
else
BtnOpenWebpage.Enabled = BtnSet211.Enabled = BtnSetGodMode.Enabled = BtnZoom8000.Enabled = BtnZoomWide.Enabled = true; // Allow user to go to camera webpage & change DHCP/211
// Name chosen
if (CbBxUserName.Text == "Select Operator to Begin Test" || CbBxUserName.Text.Length < 2)
TSC = SetInvalid("Select Username.");
// Model number selected
if (CbBxCameraType.SelectedIndex == -1)
TSC = SetInvalid("Select Model number.");
// Settings IP addresses filled in
if (BtnSave.BackColor != Color.ForestGreen)
TSC = SetInvalid("Fill in hardware accessies IP's & click 'Save & Check'.");
// All hardware accessories are on the network
//if (PanelSettings.Controls.OfType<Label>().Any(label => label.Text.Contains("❌")))
if (LblZebraPing.Text == "❌" || LblEzPing.Text == "❌" || LblPSUPing.Text == "❌") // Testtube not connected then will do 2.7m check
TSC = SetInvalid("Hardware accessories not found on network, see red X's in settings tab for details. Fill in IP addresses and press Save & Check.");
// Shutter, Iris and Gain selected
if (CbBxShutter.SelectedIndex == -1 || CbBxIris.SelectedIndex == -1 || CbBxGain.SelectedIndex == -1)
TSC = SetInvalid("Shutter, iris and gain drop downs not filled in.");
if (TSC)
BtnStartTest.Text = "Start Final test";
BtnStartTest.Enabled = BtnPreTest.Enabled = BtnLicVaxtor.Enabled = TSC; // Licensing Vaxtor requires all the info
}
private bool SetInvalid(string reason)
{
RhTxBxActions.Text += reason + Environment.NewLine;
return false;
}
public async Task<bool> DisplayOK(string QuestionString)
{
BtnNo.Visible = false;
BtnYes.Text = "OK";
bool YesNo = await DisplayQuestion(QuestionString);
BtnNo.Visible = true;
BtnYes.Text = "Yes";
return YesNo;
}
public async Task<bool> DisplayQuestion(string QuestionString)
{
Flags.Yes = Flags.No = false; // Clear flags
PnlQuestion.Visible = true;
LblQuestion.Text = QuestionString; // Show question box and give them the right to appeal
while (!Flags.Yes && !Flags.No)
await Task.Delay(100); // Check flags every 100ms
PnlQuestion.Visible = false;
if (Flags.Yes)
{
Flags.Yes = false; // Clear flag
return true;
}
else
{
Flags.No = false; // Clear flag
return false;
}
}
// Display the input panel for either RMA number input (default) or Vaxtor Key ID input
private async Task<string> DisplayInput(string Request, bool RMAorVaxtor = true)
{
RMANumBox.Visible = BtnRerun.Visible = RMAorVaxtor;
TxBxProductKey.Visible = !RMAorVaxtor;
LblRMA.Text = Request;
PnlInputValue.Visible = true;
while (Flags.Done == false) // Waiting for user input in RMA Num panel
{
await Task.Delay(100); // Check every 100ms
}
Flags.Done = false; // Reset flag
PnlInputValue.Visible = false;
if (RMAorVaxtor)
return RMANumBox.Value.ToString();
else
return TxBxProductKey.Text;
}
// ***** Settings menu *****
// Save all current user settings to disk then check devices are online
private async void BtnSave_Click(object sender, EventArgs e)
{
BtnSave.Enabled = false;
localDataStore.PSUIP = TxBxPsuIP.Text;
localDataStore.EzIP = TxBxEzIP.Text;
localDataStore.ZebraIP = TxBxZebraIP.Text;
localDataStore.TestTubeIP = TxBxTestTubeIP.Text;
localDataStore.Shutter = CbBxShutter.SelectedIndex;
localDataStore.Iris = CbBxIris.SelectedIndex;
localDataStore.Gain = CbBxGain.SelectedIndex;
LDS.SetLDS(localDataStore);
BtnSave.BackColor = Color.ForestGreen;
PSU.PSUIP = localDataStore.PSUIP;
Printer.ZebraIP = localDataStore.ZebraIP;
Ez.PoEPowerIP = localDataStore.EzIP;
TestTube.TTPiPicoIP = localDataStore.TestTubeIP;
await PingCheck();
TestStartConditions();
CheckPrintCapable();
BtnSave.Enabled = true;
}
// Revert settings to last saved
private async void BtnCancel_Click(object sender, EventArgs e)
{
TxBxPsuIP.Text = localDataStore.PSUIP;
TxBxEzIP.Text = localDataStore.EzIP;
TxBxZebraIP.Text = localDataStore.ZebraIP;
TxBxTestTubeIP.Text = localDataStore.TestTubeIP;
CbBxShutter.SelectedIndex = localDataStore.Shutter;
CbBxIris.SelectedIndex = localDataStore.Iris;
CbBxGain.SelectedIndex = localDataStore.Gain;
await PingCheck();
TestStartConditions();
}
private void BtnFirewall_Click(object sender, EventArgs e)
{
Properties.Settings.Default.FirstRun = true;
Properties.Settings.Default.Save();
Windows.UpdateFirewall();
}
private void BtnAdminStart_Click(object sender, EventArgs e)
{
string ExeLoc = Assembly.GetEntryAssembly().Location.Replace("dll", "exe"); // Sometimes trys to open the dll instead of exe
Windows.StartAsAdmin(ExeLoc);
}
// Flips between setting camera to 211 and DHCP
private async void BtnSet211_Click(object sender, EventArgs e)
{
if (BtnSet211.Text == "Set to 211")
{
bool Online = await FlexiAPI.ChangeNetwork211(CamOnTest.IP); // Change camera IP to 192.168.1.211 and hardware reboot. Waits for camera to come back for 50s.
if (!Online)
AddToActionsList("Could not find camera at 192.168.1.211. Please check manually");
else
BtnSet211.Text = "Set to DHCP";
}
else if (BtnSet211.Text == "Set to DHCP")
{
await FlexiAPI.ChangeNetworkToDHCP(CamOnTest.IP);
BtnSet211.Text = "Set to 211";
}
}
private async void BtnSetAll211_Click(object sender, EventArgs e)
{
BtnSetAll211.BackColor = BtnColour; // Reset button colour
if (await DisplayQuestion("Are you sure? This will set all checked cameras to 211."))
{
string[,] Network_JSON = { { "propDHCP", "false" }, { "propHost", "192.168.1.211" }, { "propNetmask", "255.255.255.0" }, { "propGateway", "192.168.1.1" } };
foreach (Camera SCL in soakCameraList.Where(c => c.IsChecked)) // only checked cameras
{
try
{
Network.Initialize("developer", SCL.DevPass); // Ensure network is initialized to the right camera
await FlexiAPI.HTTP_Update("GLOBAL--NetworkConfig", SCL.IP, Network_JSON);
Instance.AddToActionsList($"Setting 211 for camera {SCL.IP}", false);
}
catch (Exception ex)
{
AddToActionsList("Failed to set all cameras to 211. Reason: " + ex.Message); // In case non AiQ's get caught up
}
}
BtnSetAll211.BackColor = Color.Green;
}
}
private async void SetGodModeAll_Click(object sender, EventArgs e)
{
SetGodModeAll.BackColor = BtnColour; // Reset button colour
bool isGodModeCurrentlyOn = SetGodModeAll.Text.Contains("On");
string newGodModeValue = isGodModeCurrentlyOn ? "true" : "false";
string[,] GOD_JSON = { { "propGodMode", newGodModeValue } };
if (await DisplayQuestion("Are you sure? This will toggle God mode for all checked cameras."))
{
foreach (Camera SCL in soakCameraList.Where(c => c.IsChecked)) // only checked cameras
{
try
{
Network.Initialize("developer", SCL.DevPass); // Ensure network is initialized to the right camera
string RESP = await FlexiAPI.HTTP_Update("Internal Config", SCL.IP, GOD_JSON);
Instance.AddToActionsList($"Setting God mode for camera {SCL.IP} to {newGodModeValue}", false);
}
catch (Exception ex)
{
AddToActionsList($"Failed to set God mode for camera {SCL.IP}. Reason: {ex.Message}");
}
}
// Update the button text and flash green only if successful for all
SetGodModeAll.Text = newGodModeValue == "true" ? "Set God Mode Off" : "Set God Mode On";
SetGodModeAll.BackColor = Color.Green;
}
}
private void TxBxZebraIP_TextChanged(object sender, EventArgs e)
{
TxBxCheckValid(TxBxZebraIP);
}
private void TxBxPSUIP_TextChanged(object sender, EventArgs e)
{
TxBxCheckValid(TxBxPsuIP);
}
private void TxBxEzIP_TextChanged(object sender, EventArgs e)
{
TxBxCheckValid(TxBxEzIP);
}
private void TxBxAccBoardIP_TextChanged(object sender, EventArgs e)
{
TxBxCheckValid(TxBxTestTubeIP);
}
// Start a thread that pings each IP in the hardware accessories menu
private async Task PingCheck()
{
List<Task> tasks = // Run all ping checks in parallel
[
Network.PingAndUpdateUI(localDataStore.PSUIP, LblPSUPing, "PSU", ToolTipAvailable, [btnPsuOff, btnPsuOn,btnPsu12V,btnPsu48V]),
Network.PingAndUpdateUI(localDataStore.EzIP, LblEzPing, "Ez", ToolTipAvailable, [BtnEzOff, BtnEzOn]),
Network.PingAndUpdateUI(localDataStore.ZebraIP, LblZebraPing, "Zebra", ToolTipAvailable, [BtnPrintGB]),
Network.PingAndUpdateUI(localDataStore.TestTubeIP, LblTestTubePing, "Test Tube", ToolTipAvailable)
];
await Task.WhenAll(tasks); // Wait for all tasks to complete
}
private void TxBxCheckValid(TextBox TxBx)
{
if (RegexCache.RegexIPPattern().IsMatch(TxBx.Text)) // Check IP address is valid
{
TxBx.BackColor = TxBxColour; // Assume all is good
BtnSave.Enabled = !PanelSettings.Controls.Cast<Control>().Any(tb => tb.BackColor == Color.Red);
}
else
{
TxBx.BackColor = Color.Red;
BtnSave.Enabled = false;
BtnSave.BackColor = BtnColour;
}
TestStartConditions();
}
private void BtnGenerate_Click(object sender, EventArgs e)
{
// Return code straight into rich textbox
string LicCode = Lics.GenerateLicCode(TxBxChallenge.Text, CbBxType.Text);
RhTxBxCode.AppendText(LicCode + Environment.NewLine);
RhTxBxCode.SelectionStart = RhTxBxCode.Text.Length;
RhTxBxCode.ScrollToCaret();
Clipboard.SetText(LicCode); // Copy license code to clipboard
Button button = (Button)sender;
int yOffset = button.Height + 5; // Offset 5 pixels below the button
ToolTipClipboard.Show("Copied to clipboard!", button, 0, yOffset, 2000); // Tool tip visible to show copied
}
private void TxBxChallenge_TextChanged(object sender, EventArgs e) // Also services CboBxType_SelectedIndexChanged
{
CheckCodeFormat(TxBxChallenge.Text);
}
private void CheckCodeFormat(string ChlgCode)
{
if (RegexCache.LicCodeRegex().IsMatch(ChlgCode))
{
if (ChlgCode.Length == 7) // If length is 7 then it has the license type distingusher on front
{
string type = ChlgCode.Substring(0, 1);
TxBxChallenge.Text = ChlgCode.Substring(1, 6); // Cut down text to only the challenge code.
if (type == "F") // SAF
CbBxType.SelectedIndex = 0;
else if (type == "S") // Streaming
CbBxType.SelectedIndex = 1;
else if (type == "A") // Audit
CbBxType.SelectedIndex = 2;
BtnGenerate.Enabled = true;
}
else if (ChlgCode.Length == 6 && CbBxType.SelectedIndex != -1) // Code without distinguser and type chosen
{
TxBxChallenge.Text = ChlgCode;
BtnGenerate.Enabled = true;
}
}
else
{
BtnGenerate.Enabled = false;
}
}
// License Vaxtor button
private async void BtnLicVaxtor_Click(object sender, EventArgs e)
{
if (CameraAccessInfo.VaxtorLic) // If camera should have a Vaxtor license then license it now
{
if (await DisplayQuestion("Are you sure you want to license Vaxtor to this camera?"))
{
// Update server endpoint, username and password before licensing
string[,] Endpoint_JSON = { { "propLicenceKeyEndpointURL", UniversalData.LicencingServerURL } };
await FlexiAPI.HTTP_Update("RaptorOCR", CamOnTest.IP, Endpoint_JSON);
string ALresponse;
try
{
ALresponse = await FlexiAPI.APIHTTPRequest("/api/RaptorOCR-auto-license", CamOnTest.IP, 10); // License Vaxtor
}
catch
{
AddToActionsList("Failed to communicate with camera, please check network and try again.");
return;
}
try // Deserialise the JSON
{
VaxtorLicResp = JsonConvert.DeserializeObject<VaxtorLic>(ALresponse);
if (VaxtorLicResp.protectionKeyId != string.Empty)
{
string err = GoogleAPI.UpdateSpreadSheetVaxtor(VaxtorLicResp, Vers.Serial, CamOnTest.Model);
if (err != string.Empty) // If there is an error message, display it
AddToActionsList("Failed to update Vaxtor spreadsheet: " + err);
RhTxBxCode.AppendText("Licencing Success, Key ID: " + VaxtorLicResp.protectionKeyId + Environment.NewLine + "Waiting for files to save");
if (await DisplayQuestion("Do you want to sync to disk? (If unsure what this means press yes)"))
SSH.Sync(CamOnTest.IP); // Sync everything in RAM to the disk
await DisplayOK("Please wait at least a minute before turning off the unit.");
}
else if (VaxtorLicResp.error != string.Empty)
{
RhTxBxCode.AppendText(VaxtorLicResp.error);
}
else
{
AddToActionsList($"Error reading JSON - {ALresponse}");
}
}
catch
{
AddToActionsList($"Error reading JSON - {ALresponse}");
return;
}
}
}
else
{
RhTxBxCode.AppendText("This model shouldn't have a Vaxtor license. NOT LICENSED");
}
}
// Refresh the unix time that is displayed and used to generate developer password
private void BtnRefreshUnix_Click(object sender, EventArgs e)
{
TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
int secondsSinceEpoch = (int)t.TotalSeconds;
TxBxUnixTime.Text = Convert.ToString(secondsSinceEpoch);
}
// Check if MAC address textbox contains a valid MAC
private void TxBxMAC_TextChanged(object sender, EventArgs e)
{
if (RegexCache.MACRegex().IsMatch(TxBxMAC.Text))
{
TxBxMAC.BackColor = TxBxColour;
BtnSecretMan.Enabled = true;
}
else
{
TxBxMAC.BackColor = Color.Red;
BtnSecretMan.Enabled = false;
}
}
// Check if unix textbox contains a valid Unix time
private void TxBxUnixTime_TextChanged(object sender, EventArgs e)
{
if (long.TryParse(TxBxUnixTime.Text, out long unixTime) && unixTime >= 0 && unixTime <= 2147483647)
{
TxBxUnixTime.BackColor = TxBxColour;
BtnSecretMan.Enabled = true;
}
else
{
TxBxUnixTime.BackColor = Color.Red;
BtnSecretMan.Enabled = false;
}
}
// Check if Flexi version textbox contains a valid version
private void TxBxFlexiVer_TextChanged(object sender, EventArgs e)
{
if (RegexCache.FlexiVerRegex().IsMatch(TxBxFlexiVer.Text)) // Regex on Flexi version that should be three values dot seperated up to three digits each
{
TxBxFlexiVer.BackColor = TxBxColour;
BtnSecretMan.Enabled = true;
}
else
{
TxBxFlexiVer.BackColor = Color.Red;
BtnSecretMan.Enabled = false;
}
}
// Use the version API with connected camera to generate developer password
private async void BtnSecret_Click(object sender, EventArgs e)
{
Vers = await FlexiAPI.GetVersions(CamOnTest.IP);
Lics.DisplayDevPassword(Vers, CamOnTest);
ShowToolTip(BtnSecret);
}
// Generate password using manual inputs
private void BtnSecretMan_Click(object sender, EventArgs e)
{
CamOnTest.DevPass = Lics.GeneratePassword(TxBxMAC.Text, TxBxFlexiVer.Text, Convert.ToInt32(TxBxUnixTime.Text));
ShowToolTip(sender);
}
private void ShowToolTip(object sender) // Shows 'Copied to clipboard!' tooltip below sender button
{
try // Catches sender when it comes from combo box
{
Button button = (Button)sender;
int yOffset = button.Height + 5; // Offset 5 pixels below the button
ToolTipClipboard.Show("Copied to clipboard!", button, 0, yOffset, 2000); // Show tool tip
Clipboard.SetText(CamOnTest.DevPass); // Copy password to clipboard
}
catch { }
TxBxOutput.Text = CamOnTest.DevPass; // Done last to maintain focus on box
}
private void TxBxOutput_Click(object sender, EventArgs e)
{
TxBxOutput.SelectAll(); // Select the password in the box
TxBxOutput.Focus(); // Make it the users focus so it is in focus after generate
}
private async void BtnEzOn_Click(object sender, EventArgs e)
{
BtnEzOff.BackColor = BtnColour;
if (await Ez.EzOutletControl("ON"))
BtnEzOn.BackColor = Color.ForestGreen;
else
BtnEzOn.BackColor = Color.Maroon;
}
private async void BtnEzOff_Click(object sender, EventArgs e)
{
BtnEzOn.BackColor = BtnColour;
if (await Ez.EzOutletControl("OFF"))
BtnEzOff.BackColor = Color.ForestGreen;
else
BtnEzOff.BackColor = Color.Maroon;
}
private async void BtnZoomWide_Click(object sender, EventArgs e)
{
if (await FlexiAPI.ZoomModules("0000", CamOnTest.IP))
BtnZoomWide.BackColor = Color.Green;
else
BtnZoomWide.BackColor = Color.Red;
BtnZoom8000.BackColor = BtnColour;
}
private async void BtnZoom8000_Click(object sender, EventArgs e)
{
if (await FlexiAPI.ZoomModules("1F40", CamOnTest.IP))
BtnZoom8000.BackColor = Color.Green;
else
BtnZoom8000.BackColor = Color.Red;
BtnZoomWide.BackColor = BtnColour;
}
private void BtnOpenWebpage_Click(object sender, EventArgs e)
{
ProcessStartInfo psi = new()
{
FileName = $"http://{CamOnTest.IP}", // Just the URL
UseShellExecute = true // Lets the OS decide how to open it
};
Process.Start(psi);
}
private void TxBxSerialPrint_Click(object sender, EventArgs e)
{
if (TxBxSerialPrint.Text == "K ") // If at default then remove the dashes ready for user to put in number
{
TxBxSerialPrint.Text = "K";
TxBxSerialPrint.SelectionStart = 1;
}
}
private void BtnPrintAiQ_Click(object sender, EventArgs e)
{
Printer.PrintSerialLbl(CamOnTest.Model, TxBxSerialPrint.Text, CameraAccessInfo.Processor); // Print model/serial label
}
private void BtnPrintGB_Click(object sender, EventArgs e)
{
Printer.PrintGBLbl(); // Print GB label
}
private void TxBxSerialPrint_TextChanged(object sender, EventArgs e)
{
CheckPrintCapable(); // Check if the print buttons can be enabled
}
private void CheckPrintCapable()
{
if (LblZebraPing.Text == "✔") // Check the IP address is ok
{
BtnPrintGB.Enabled = true;
if (CbBxCameraType.SelectedIndex != -1 && RegexCache.SerialRegex().IsMatch(TxBxSerialPrint.Text)) // Check model and serial are known
{
Printer.ZebraIP = localDataStore.ZebraIP;
BtnPrintAiQ.Enabled = true;
}
}
}
// ***** RMA Panel *****
private void BtnDone_Click(object sender, EventArgs e)
{
CamOnTest.RMANum = Convert.ToInt16(RMANumBox.Value);
Flags.Done = true;
}
private void BtnDont_Click(object sender, EventArgs e)
{
CamOnTest.RMANum = -1;
Flags.Done = true;
}
private void BtnRerun_Click(object sender, EventArgs e)
{
CamOnTest.RMANum = 0;
Flags.Done = true;
}
// ***** Soak *****
// Sets the soak test running for all cameras that are ticked.
private async void BtnSoak_Click(object sender, EventArgs e)
{
if (BtnSoak.Text == "Start Soak")
{
BtnSoak.BackColor = Color.Orange;
BtnSoak.Text = "Stop Soak";
soakCtsList.Clear();
soakTasks.Clear();
foreach (Camera SCL in soakCameraList)
{
if (!SCL.IsChecked)
continue;
CancellationTokenSource cts = new();
soakCtsList.Add(cts);
soakTasks.Add(SoakTest.StartSoak(SCL, cts.Token));
await Task.Delay(5000);
}
}
else
{
BtnSoak.BackColor = BtnColour; // Reset button colour
BtnSoak.Text = "Start Soak";
foreach (CancellationTokenSource cts in soakCtsList)
cts.Cancel();
soakCtsList.Clear();
soakTasks.Clear();
foreach (Camera SCL in soakCameraList) // Reset all cameras that were being soaked to default module settings
{
if (!SCL.IsChecked)
continue;
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);
}
}
}
private void CkBxTickAll_CheckedChanged(object sender, EventArgs e)
{
List<CheckBox> checkBoxes = TabSoak.Controls.OfType<CheckBox>().Except([CkBxTickAll]).ToList();
bool ToTick = CkBxTickAll.Text == "Tick all"; // True if we are ticking all checkboxes, false if we are unticking them
CkBxTickAll.Text = ToTick ? "Unick all" : "Tick all"; // Toggle the text of the checkbox
foreach (CheckBox? cb in checkBoxes)
cb.Checked = ToTick;
}
private async void timerTypeIP_Tick(object sender, EventArgs e)
{
timerTypeIP.Enabled = false;
if (!RegexCache.RegexIPPattern().IsMatch(CbBxFoundCams.Text) && !await Network.PingIP(CbBxFoundCams.Text)) // Check IP address is valid
return;
if (TabImagesandSettings.SelectedIndex == 3)
{
soakCameraList.Clear();
// Deletes all the checkboxes in TabSoak
List<CheckBox> checkBoxes = TabSoak.Controls.OfType<CheckBox>().Except([CkBxTickAll]).ToList();
foreach (CheckBox? cb in checkBoxes)
{
TabSoak.Controls.Remove(cb);
cb.Dispose();
}
Camera NewCam = await Helper.NewCamera(CbBxFoundCams.Text);
if (NewCam != null)
{
soakCameraList.Add(NewCam);
TabSoak.Controls.Add(SoakTest.MakeNewCheckbox(NewCam, 74));
}
}
CbBxFoundCams_SelectedIndexChanged(sender, e);
}
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;
if (await DisplayQuestion("Do you want the latest Flexi version from the MAV Production folder?"))
{
fileToUpload = Directory.GetFiles(networkFolderPath, "*.blob").OrderByDescending(File.GetLastWriteTime).FirstOrDefault();
if (fileToUpload == null)
{
AddToActionsList("No .blob file found in the directory.", false);
return;
}
}
else
{
using OpenFileDialog openFileDialog1 = new()
{
InitialDirectory = networkFolderPath,
Filter = "Blob files (*.blob)|*.blob",
FilterIndex = 0
};
if (openFileDialog1.ShowDialog() == DialogResult.OK)
fileToUpload = openFileDialog1.FileName;
else
{
AddToActionsList("File selection cancelled.", false);
return;
}
}
string fileName = Path.GetFileName(fileToUpload);
AddToActionsList($"Selected file to upload: {fileToUpload}", false);
foreach (Camera? cam in soakCameraList.Where(c => c.IsChecked))
{
string apiUrl = $"http://{cam.IP}/upload/software-update/2";
Network.Initialize("developer", cam.DevPass);
AddToActionsList($"Uploading to {cam.IP}...", false);
string result = await FlexiAPI.SendBlobFileUpload(apiUrl, fileToUpload, fileName);
// Retry once on transient errors
if (result.Contains("Error while copying content to a stream") || result.Contains("Timeout"))
{
AddToActionsList($"Retrying upload to {cam.IP}...", false);
await Task.Delay(1000);
result = await FlexiAPI.SendBlobFileUpload(apiUrl, fileToUpload, fileName);
}
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)
continue;
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;
}
// 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
{
public long FrameID;
public int PlatePosX;
public int PlatePosY;
public int PlateWidthPixels;
}
public double EstimateSpeed(List<FrameData> 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();
List<FrameData> frames = new List<FrameData>
{
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 }
};
double Spd = EstimateSpeed(frames);
AddToActionsList("Estimated Speed: " + Spd.ToString("F2") + " MPH");
stopWatchTest.Stop();
AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff"));
}
}
}