Beginning of the Implementing multple cameras into the AiQ GUI

Features added:
- Onvif discoverable
- New Camera Type combo box
- Access has be changed to be more scalable and dynamic in preparation for more cameras
This commit is contained in:
2025-12-19 16:14:13 +00:00
parent 760987fa75
commit 7aba890514
18 changed files with 455 additions and 333 deletions

316
AiQ_GUI.Designer.cs generated
View File

@@ -32,7 +32,7 @@ namespace AiQ_GUI
{
components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
CbBxCameraType = new ComboBox();
CbBxCameraModel = new ComboBox();
BtnStartTest = new Button();
BtnFindCams = new Button();
CbBxFoundCams = new ComboBox();
@@ -56,7 +56,6 @@ namespace AiQ_GUI
lblZoomLock = new Label();
LblLEDI = new Label();
LblLEDV = new Label();
RhTxBxActions = new RichTextBox();
LblOVModule = new Label();
LblIRModule = new Label();
BtnTest = new Button();
@@ -89,6 +88,13 @@ namespace AiQ_GUI
BtnNo = new Button();
BtnYes = new Button();
LblQuestion = new Label();
PnlInputValue = new Panel();
BtnRerun = new Button();
RMANumBox = new NumericUpDown();
TxBxProductKey = new TextBox();
BtnDont = new Button();
BtnDone = new Button();
LblRMA = new Label();
label16 = new Label();
BtnPrintGB = new Button();
BtnPrintAiQ = new Button();
@@ -111,13 +117,6 @@ namespace AiQ_GUI
label9 = new Label();
BtnGenerate = new Button();
TxBxChallenge = new TextBox();
PnlInputValue = new Panel();
BtnRerun = new Button();
RMANumBox = new NumericUpDown();
TxBxProductKey = new TextBox();
BtnDont = new Button();
BtnDone = new Button();
LblRMA = new Label();
BtnSetGodMode = new Button();
btnPsu12V = new Button();
btnPsu48V = new Button();
@@ -160,6 +159,8 @@ namespace AiQ_GUI
LblGUIVers = new Label();
timerTypeIP = new System.Windows.Forms.Timer(components);
TimerFlash = new System.Windows.Forms.Timer(components);
RhTxBxActions = new RichTextBox();
CbBxCamType = new ComboBox();
((System.ComponentModel.ISupportInitialize)PicBxMAV).BeginInit();
((System.ComponentModel.ISupportInitialize)PicBxAiQ).BeginInit();
((System.ComponentModel.ISupportInitialize)PicBxIRF2).BeginInit();
@@ -181,25 +182,23 @@ namespace AiQ_GUI
TabSoak.SuspendLayout();
SuspendLayout();
//
// CbBxCameraType
//
CbBxCameraType.BackColor = Color.FromArgb(53, 51, 64);
CbBxCameraType.DropDownHeight = 500;
CbBxCameraType.DropDownStyle = ComboBoxStyle.DropDownList;
CbBxCameraType.DropDownWidth = 350;
CbBxCameraType.FlatStyle = FlatStyle.Flat;
CbBxCameraType.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
CbBxCameraType.ForeColor = SystemColors.Control;
CbBxCameraType.FormattingEnabled = true;
CbBxCameraType.IntegralHeight = false;
CbBxCameraType.Location = new Point(10, 298);
CbBxCameraType.Margin = new Padding(4, 3, 4, 3);
CbBxCameraType.MaxDropDownItems = 30;
CbBxCameraType.Name = "CbBxCameraType";
CbBxCameraType.Size = new Size(494, 25);
CbBxCameraType.TabIndex = 188;
CbBxCameraType.SelectedIndexChanged += CmBxCameraType_SelectedIndexChanged;
// CbBxCameraModel
//
CbBxCameraModel.BackColor = Color.FromArgb(53, 51, 64);
CbBxCameraModel.DropDownHeight = 500;
CbBxCameraModel.DropDownStyle = ComboBoxStyle.DropDownList;
CbBxCameraModel.DropDownWidth = 350;
CbBxCameraModel.FlatStyle = FlatStyle.Flat;
CbBxCameraModel.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
CbBxCameraModel.ForeColor = SystemColors.Control;
CbBxCameraModel.FormattingEnabled = true;
CbBxCameraModel.IntegralHeight = false;
CbBxCameraModel.Location = new Point(10, 298);
CbBxCameraModel.Margin = new Padding(4, 3, 4, 3);
CbBxCameraModel.MaxDropDownItems = 30;
CbBxCameraModel.Name = "CbBxCameraModel";
CbBxCameraModel.Size = new Size(494, 25);
CbBxCameraModel.TabIndex = 188;
// BtnStartTest
//
BtnStartTest.BackColor = Color.FromArgb(70, 65, 80);
@@ -535,20 +534,6 @@ namespace AiQ_GUI
LblLEDV.TabIndex = 185;
LblLEDV.Text = "LED Voltage = ";
//
// RhTxBxActions
//
RhTxBxActions.BackColor = Color.FromArgb(53, 51, 64);
RhTxBxActions.BorderStyle = BorderStyle.None;
RhTxBxActions.Cursor = Cursors.IBeam;
RhTxBxActions.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
RhTxBxActions.ForeColor = SystemColors.Control;
RhTxBxActions.Location = new Point(10, 355);
RhTxBxActions.Margin = new Padding(4, 3, 4, 3);
RhTxBxActions.Name = "RhTxBxActions";
RhTxBxActions.Size = new Size(495, 156);
RhTxBxActions.TabIndex = 187;
RhTxBxActions.Text = "";
//
// LblOVModule
//
LblOVModule.BackColor = Color.Transparent;
@@ -937,6 +922,7 @@ namespace AiQ_GUI
PnlQuestion.Controls.Add(BtnNo);
PnlQuestion.Controls.Add(BtnYes);
PnlQuestion.Controls.Add(LblQuestion);
PnlQuestion.Controls.Add(PnlInputValue);
PnlQuestion.Location = new Point(251, 14);
PnlQuestion.Margin = new Padding(4, 3, 4, 3);
PnlQuestion.Name = "PnlQuestion";
@@ -989,6 +975,108 @@ namespace AiQ_GUI
LblQuestion.Text = "Test failed, appeal?\r\nSee Actions textbox for details.";
LblQuestion.TextAlign = ContentAlignment.MiddleCenter;
//
// PnlInputValue
//
PnlInputValue.BackColor = Color.FromArgb(39, 37, 55);
PnlInputValue.Controls.Add(BtnRerun);
PnlInputValue.Controls.Add(RMANumBox);
PnlInputValue.Controls.Add(TxBxProductKey);
PnlInputValue.Controls.Add(BtnDont);
PnlInputValue.Controls.Add(BtnDone);
PnlInputValue.Controls.Add(LblRMA);
PnlInputValue.Location = new Point(0, 0);
PnlInputValue.Margin = new Padding(4, 3, 4, 3);
PnlInputValue.Name = "PnlInputValue";
PnlInputValue.Size = new Size(254, 127);
PnlInputValue.TabIndex = 199;
PnlInputValue.Visible = false;
//
// BtnRerun
//
BtnRerun.BackColor = Color.FromArgb(70, 65, 80);
BtnRerun.FlatAppearance.BorderSize = 0;
BtnRerun.FlatStyle = FlatStyle.Flat;
BtnRerun.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
BtnRerun.ForeColor = SystemColors.Control;
BtnRerun.Location = new Point(132, 82);
BtnRerun.Margin = new Padding(4, 3, 4, 3);
BtnRerun.Name = "BtnRerun";
BtnRerun.Size = new Size(113, 37);
BtnRerun.TabIndex = 177;
BtnRerun.Text = "Rerun";
BtnRerun.UseVisualStyleBackColor = false;
BtnRerun.Click += BtnRerun_Click;
//
// RMANumBox
//
RMANumBox.BackColor = Color.FromArgb(53, 51, 64);
RMANumBox.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
RMANumBox.ForeColor = SystemColors.Window;
RMANumBox.Location = new Point(10, 38);
RMANumBox.Margin = new Padding(4, 3, 4, 3);
RMANumBox.Maximum = new decimal(new int[] { 99999, 0, 0, 0 });
RMANumBox.Name = "RMANumBox";
RMANumBox.Size = new Size(113, 25);
RMANumBox.TabIndex = 175;
//
// TxBxProductKey
//
TxBxProductKey.BackColor = Color.FromArgb(53, 51, 64);
TxBxProductKey.BorderStyle = BorderStyle.FixedSingle;
TxBxProductKey.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
TxBxProductKey.ForeColor = SystemColors.Control;
TxBxProductKey.Location = new Point(10, 46);
TxBxProductKey.Margin = new Padding(4, 3, 4, 3);
TxBxProductKey.Name = "TxBxProductKey";
TxBxProductKey.Size = new Size(114, 25);
TxBxProductKey.TabIndex = 162;
TxBxProductKey.TextAlign = HorizontalAlignment.Center;
TxBxProductKey.Visible = false;
//
// BtnDont
//
BtnDont.BackColor = Color.FromArgb(70, 65, 80);
BtnDont.FlatAppearance.BorderSize = 0;
BtnDont.FlatStyle = FlatStyle.Flat;
BtnDont.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
BtnDont.ForeColor = SystemColors.Control;
BtnDont.Location = new Point(12, 82);
BtnDont.Margin = new Padding(4, 3, 4, 3);
BtnDont.Name = "BtnDont";
BtnDont.Size = new Size(112, 37);
BtnDont.TabIndex = 176;
BtnDont.Text = "Don't have one";
BtnDont.UseVisualStyleBackColor = false;
BtnDont.Click += BtnDont_Click;
//
// BtnDone
//
BtnDone.BackColor = Color.FromArgb(70, 65, 80);
BtnDone.FlatAppearance.BorderSize = 0;
BtnDone.FlatStyle = FlatStyle.Flat;
BtnDone.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
BtnDone.ForeColor = SystemColors.Control;
BtnDone.Location = new Point(132, 38);
BtnDone.Margin = new Padding(4, 3, 4, 3);
BtnDone.Name = "BtnDone";
BtnDone.Size = new Size(113, 37);
BtnDone.TabIndex = 174;
BtnDone.Text = "Done";
BtnDone.UseVisualStyleBackColor = false;
BtnDone.Click += BtnDone_Click;
//
// LblRMA
//
LblRMA.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
LblRMA.ForeColor = SystemColors.Control;
LblRMA.Location = new Point(13, 6);
LblRMA.Margin = new Padding(4, 0, 4, 0);
LblRMA.Name = "LblRMA";
LblRMA.Size = new Size(233, 29);
LblRMA.TabIndex = 145;
LblRMA.Text = "What is the RMA number?";
LblRMA.TextAlign = ContentAlignment.MiddleCenter;
//
// label16
//
label16.AutoSize = true;
@@ -1318,108 +1406,6 @@ namespace AiQ_GUI
TxBxChallenge.TextAlign = HorizontalAlignment.Center;
TxBxChallenge.TextChanged += TxBxChallenge_TextChanged;
//
// PnlInputValue
//
PnlInputValue.BackColor = Color.FromArgb(39, 37, 55);
PnlInputValue.Controls.Add(BtnRerun);
PnlInputValue.Controls.Add(RMANumBox);
PnlInputValue.Controls.Add(TxBxProductKey);
PnlInputValue.Controls.Add(BtnDont);
PnlInputValue.Controls.Add(BtnDone);
PnlInputValue.Controls.Add(LblRMA);
PnlInputValue.Location = new Point(251, 155);
PnlInputValue.Margin = new Padding(4, 3, 4, 3);
PnlInputValue.Name = "PnlInputValue";
PnlInputValue.Size = new Size(254, 127);
PnlInputValue.TabIndex = 199;
PnlInputValue.Visible = false;
//
// BtnRerun
//
BtnRerun.BackColor = Color.FromArgb(70, 65, 80);
BtnRerun.FlatAppearance.BorderSize = 0;
BtnRerun.FlatStyle = FlatStyle.Flat;
BtnRerun.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
BtnRerun.ForeColor = SystemColors.Control;
BtnRerun.Location = new Point(132, 82);
BtnRerun.Margin = new Padding(4, 3, 4, 3);
BtnRerun.Name = "BtnRerun";
BtnRerun.Size = new Size(113, 37);
BtnRerun.TabIndex = 177;
BtnRerun.Text = "Rerun";
BtnRerun.UseVisualStyleBackColor = false;
BtnRerun.Click += BtnRerun_Click;
//
// RMANumBox
//
RMANumBox.BackColor = Color.FromArgb(53, 51, 64);
RMANumBox.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
RMANumBox.ForeColor = SystemColors.Window;
RMANumBox.Location = new Point(10, 38);
RMANumBox.Margin = new Padding(4, 3, 4, 3);
RMANumBox.Maximum = new decimal(new int[] { 99999, 0, 0, 0 });
RMANumBox.Name = "RMANumBox";
RMANumBox.Size = new Size(113, 25);
RMANumBox.TabIndex = 175;
//
// TxBxProductKey
//
TxBxProductKey.BackColor = Color.FromArgb(53, 51, 64);
TxBxProductKey.BorderStyle = BorderStyle.FixedSingle;
TxBxProductKey.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
TxBxProductKey.ForeColor = SystemColors.Control;
TxBxProductKey.Location = new Point(10, 46);
TxBxProductKey.Margin = new Padding(4, 3, 4, 3);
TxBxProductKey.Name = "TxBxProductKey";
TxBxProductKey.Size = new Size(114, 25);
TxBxProductKey.TabIndex = 162;
TxBxProductKey.TextAlign = HorizontalAlignment.Center;
TxBxProductKey.Visible = false;
//
// BtnDont
//
BtnDont.BackColor = Color.FromArgb(70, 65, 80);
BtnDont.FlatAppearance.BorderSize = 0;
BtnDont.FlatStyle = FlatStyle.Flat;
BtnDont.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
BtnDont.ForeColor = SystemColors.Control;
BtnDont.Location = new Point(12, 82);
BtnDont.Margin = new Padding(4, 3, 4, 3);
BtnDont.Name = "BtnDont";
BtnDont.Size = new Size(112, 37);
BtnDont.TabIndex = 176;
BtnDont.Text = "Don't have one";
BtnDont.UseVisualStyleBackColor = false;
BtnDont.Click += BtnDont_Click;
//
// BtnDone
//
BtnDone.BackColor = Color.FromArgb(70, 65, 80);
BtnDone.FlatAppearance.BorderSize = 0;
BtnDone.FlatStyle = FlatStyle.Flat;
BtnDone.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
BtnDone.ForeColor = SystemColors.Control;
BtnDone.Location = new Point(132, 38);
BtnDone.Margin = new Padding(4, 3, 4, 3);
BtnDone.Name = "BtnDone";
BtnDone.Size = new Size(113, 37);
BtnDone.TabIndex = 174;
BtnDone.Text = "Done";
BtnDone.UseVisualStyleBackColor = false;
BtnDone.Click += BtnDone_Click;
//
// LblRMA
//
LblRMA.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
LblRMA.ForeColor = SystemColors.Control;
LblRMA.Location = new Point(13, 6);
LblRMA.Margin = new Padding(4, 0, 4, 0);
LblRMA.Name = "LblRMA";
LblRMA.Size = new Size(233, 29);
LblRMA.TabIndex = 145;
LblRMA.Text = "What is the RMA number?";
LblRMA.TextAlign = ContentAlignment.MiddleCenter;
//
// BtnSetGodMode
//
BtnSetGodMode.BackColor = Color.FromArgb(70, 65, 80);
@@ -2056,6 +2042,39 @@ namespace AiQ_GUI
//
timerTypeIP.Interval = 2000;
timerTypeIP.Tick += timerTypeIP_Tick;
//
// RhTxBxActions
//
RhTxBxActions.BackColor = Color.FromArgb(53, 51, 64);
RhTxBxActions.BorderStyle = BorderStyle.None;
RhTxBxActions.Cursor = Cursors.IBeam;
RhTxBxActions.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
RhTxBxActions.ForeColor = SystemColors.Control;
RhTxBxActions.Location = new Point(10, 355);
RhTxBxActions.Margin = new Padding(4, 3, 4, 3);
RhTxBxActions.Name = "RhTxBxActions";
RhTxBxActions.Size = new Size(495, 156);
RhTxBxActions.TabIndex = 187;
RhTxBxActions.Text = "";
//
// CbBxCamType
//
CbBxCamType.BackColor = Color.FromArgb(53, 51, 64);
CbBxCamType.FlatStyle = FlatStyle.Flat;
CbBxCamType.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
CbBxCamType.ForeColor = SystemColors.Control;
CbBxCamType.FormattingEnabled = true;
CbBxCamType.Items.AddRange(new object[] { "AiQ", "Mobile" });
CbBxCamType.Location = new Point(251, 262);
CbBxCamType.Margin = new Padding(4, 3, 4, 3);
CbBxCamType.MaxDropDownItems = 20;
CbBxCamType.Name = "CbBxCamType";
CbBxCamType.Size = new Size(233, 25);
CbBxCamType.TabIndex = 241;
CbBxCamType.Text = "Select Camera Type";
this.CbBxCamType.SelectedIndexChanged += new System.EventHandler(this.CbBxCamType_SelectedIndexChanged);
//
// MainForm
//
@@ -2063,23 +2082,23 @@ namespace AiQ_GUI
AutoScaleMode = AutoScaleMode.Font;
BackgroundImage = Properties.Resources.homepage_banner;
ClientSize = new Size(932, 970);
Controls.Add(RhTxBxActions);
Controls.Add(CbBxCamType);
Controls.Add(LblGUIVers);
Controls.Add(BtnOpenWebpage);
Controls.Add(TabImagesandSettings);
Controls.Add(PicBxMAV);
Controls.Add(PicBxAiQ);
Controls.Add(PnlLbls);
Controls.Add(PnlInputValue);
Controls.Add(PnlQuestion);
Controls.Add(LblActions);
Controls.Add(CbBxCameraType);
Controls.Add(CbBxCameraModel);
Controls.Add(CbBxUserName);
Controls.Add(BtnPreTest);
Controls.Add(BtnFindCams);
Controls.Add(BtnStartTest);
Controls.Add(CbBxFoundCams);
Controls.Add(BtnTest);
Controls.Add(RhTxBxActions);
Controls.Add(BtnRestart);
Controls.Add(BtnMin);
Controls.Add(BtnClose);
@@ -2144,9 +2163,8 @@ namespace AiQ_GUI
private System.Windows.Forms.Label lblZoomLock;
private System.Windows.Forms.Label LblLEDI;
private System.Windows.Forms.Label LblLEDV;
private System.Windows.Forms.ComboBox CbBxCameraType;
private System.Windows.Forms.ComboBox CbBxCameraModel;
private System.Windows.Forms.Button BtnPreTest;
private System.Windows.Forms.RichTextBox RhTxBxActions;
private System.Windows.Forms.Label LblOVModule;
private System.Windows.Forms.Label LblIRModule;
private System.Windows.Forms.Button BtnTest;
@@ -2251,5 +2269,7 @@ namespace AiQ_GUI
private Button BtnFactoryDefault;
private Button BtnUploadWonwooSetOV;
private Button BtnUploadWonwooSetIR;
private RichTextBox RhTxBxActions;
public ComboBox CbBxCamType;
}
}

View File

@@ -65,12 +65,9 @@ namespace AiQ_GUI
this.Name = "AiQ GUI V" + GUIUpdate.GUIVerShort;
LblGUIVers.Text += GUIUpdate.GUIVerShort;
await UniDataTask; // Have to wait for expected GUI version to compare to.
Task<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;
@@ -84,7 +81,7 @@ namespace AiQ_GUI
Task CheckHWOnline = PingCheck(); // Async check all hardware is online
PopulateUIWithLDS(localDataStore); // Update fields that depend on LDS
CbBxCameraType.Text = localDataStore.LastModel; // Set last model that was tested into combobox
CbBxCameraModel.Text = localDataStore.LastModel; // Set last model that was tested into combobox
BtnSave.BackColor = BtnSave.Enabled ? Color.ForestGreen : BtnSave.BackColor; // Sets the colour of the save button depending on if it is enabled.
BtnRefreshUnix_Click(sender, e); // Reset timestamp
@@ -96,6 +93,20 @@ namespace AiQ_GUI
Flags.Start = false;
}
private async void CbBxCamType_SelectedIndexChanged(object sender, EventArgs e)
{
CbBxCameraModel.Items.Clear(); // REQUIRED
string camType = CbBxCamType.Text;
string[] models = await Task.Run(() => Access.ReadCamTypes(camType));
if (models != null && models.Length > 0)
CbBxCameraModel.Items.AddRange(models);
}
private void PopulateUIWithLDS(LocalDataStore lds)
{
CbBxUserName.Text = lds.User;
@@ -693,7 +704,7 @@ namespace AiQ_GUI
{
// Update the serial number register with new cameras details
// Cam description is in model drop down 6 digit model num + 3 for " - "
string NewSerial = GoogleAPI.UpdateSpreadSheetPreTest(CameraAccessInfo.SpreadsheetID, Vers, CbBxCameraType.Text.Substring(9), CamOnTest.Model);
string NewSerial = GoogleAPI.UpdateSpreadSheetPreTest(CameraAccessInfo.SpreadsheetID, Vers, CbBxCameraModel.Text.Substring(9), CamOnTest.Model);
if (NewSerial.Contains("ERROR"))
{
@@ -731,10 +742,10 @@ namespace AiQ_GUI
private void BtnClose_Click(object sender, EventArgs e)
{
// Save user settings in LDS for next time if values are valid
if (CbBxUserName.Text.Length > 2 && CbBxCameraType.Text.Length > 6)
if (CbBxUserName.Text.Length > 2 && CbBxCameraModel.Text.Length > 6)
{
localDataStore.User = CbBxUserName.Text;
localDataStore.LastModel = CbBxCameraType.Text;
localDataStore.LastModel = CbBxCameraModel.Text;
LDS.SetLDS(localDataStore);
}
@@ -820,12 +831,6 @@ namespace AiQ_GUI
Flags.No = true;
}
private void CmBxCameraType_SelectedIndexChanged(object sender, EventArgs e)
{
CamOnTest.Model = CbBxCameraType.Text.Substring(0, 6); // Model on test is the first 6 characters of the textbox
Access.ReadModelRow(CamOnTest.Model); // Fill in all info about current model number
TestStartConditions();
}
private void CmBoBoxUserName_TextChanged(object sender, EventArgs e)
{
@@ -843,19 +848,23 @@ namespace AiQ_GUI
timerTypeIP.Enabled = false; // Stop this triggering again
CamOnTest.DevPass = TxBxOutput.Text = RhTxBxActions.Text = ""; // Blank password & Clear actions box as it is now a different camera
if (RegexCache.RegexIPPattern().IsMatch(CbBxFoundCams.Text)) // Check IP address is valid
string selectedText = CbBxFoundCams.Text.Trim();
string ipOnly = selectedText.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0];
if (RegexCache.RegexIPPattern().IsMatch(ipOnly)) // Check IP address is valid
{
if (!await Network.PingIP(CbBxFoundCams.Text))
if (!await Network.PingIP(ipOnly))
{
CbBxFoundCams.BackColor = Color.Red;
return;
}
CamOnTest.IP = CbBxFoundCams.Text; // Set the IP address under test
CamOnTest.IP = ipOnly; //Always store clean IP
CbBxFoundCams.BackColor = BtnColour;
BtnSecret.Enabled = true;
Vers = await FlexiAPI.GetVersions(CamOnTest.IP);
Vers = await FlexiAPI.GetVersions(ipOnly);
// Wont be filled out before the pre test but needed for final test
if (RegexCache.SerialRegex().IsMatch(CamOnTest.Serial) && RegexCache.ModelRegex().IsMatch(CamOnTest.Model))
@@ -872,12 +881,12 @@ namespace AiQ_GUI
Lics.DisplayDevPassword(Vers, CamOnTest); // Generate and display secret for use later
string networkConfigText = await FlexiAPI.ProcessNetworkConfig(CamOnTest.IP);
string networkConfigText = await FlexiAPI.ProcessNetworkConfig(ipOnly);
BtnSet211.Text = string.IsNullOrEmpty(networkConfigText) ? "Set to 211" : networkConfigText;
ShowToolTip(BtnSecret); // Set dev password to Tooltip and clipboard
}
else if (CbBxFoundCams.Text.Contains("Found"))
else if (selectedText.Contains("Found"))
{
CbBxFoundCams.BackColor = BtnColour;
}
@@ -976,7 +985,7 @@ namespace AiQ_GUI
if (await Network.PingIP("8.8.8.8")) // Ping to find if we are online
{
Flags.Offline = false; // Ping succeeded
CbBxCameraType.Enabled = true;
CbBxCameraModel.Enabled = true;
}
else
TSC = SetInvalid("Offline Mode, could not connect to the internet."); // Ping failed
@@ -993,7 +1002,7 @@ namespace AiQ_GUI
TSC = SetInvalid("Select Username.");
// Model number selected
if (CbBxCameraType.SelectedIndex == -1)
if (CbBxCameraModel.SelectedIndex == -1)
TSC = SetInvalid("Select Model number.");
// Settings IP addresses filled in
@@ -1494,15 +1503,19 @@ namespace AiQ_GUI
private void BtnOpenWebpage_Click(object sender, EventArgs e)
{
// Cut off anything after the first space (e.g. " - Onvif")
string ip = CamOnTest.IP.Split(' ')[0];
ProcessStartInfo psi = new()
{
FileName = $"http://{CamOnTest.IP}", // Just the URL
UseShellExecute = true // Lets the OS decide how to open it
FileName = $"http://{ip}",
UseShellExecute = true
};
Process.Start(psi);
}
private async void UploadWonwooSetOV_Click(object sender, EventArgs e)
{
await FlexiAPI.UploadWonwooSet(CbBxFoundCams.Text, false); // false = Colour
@@ -1543,7 +1556,7 @@ namespace AiQ_GUI
{
BtnPrintGB.Enabled = true;
if (CbBxCameraType.SelectedIndex != -1 && RegexCache.SerialRegex().IsMatch(TxBxSerialPrint.Text)) // Check model and serial are known
if (CbBxCameraModel.SelectedIndex != -1 && RegexCache.SerialRegex().IsMatch(TxBxSerialPrint.Text)) // Check model and serial are known
{
Printer.ZebraIP = localDataStore.ZebraIP;
BtnPrintAiQ.Enabled = true;
@@ -1739,8 +1752,18 @@ namespace AiQ_GUI
// PDF.CreateSoakTestReport(NewCam, "SoakTestRig", DateTime.Now, file);
// }
//}
var password = @"mavPA\$\$";
var psi = new ProcessStartInfo
{
FileName = @"C:\Program Files (x86)\Mobatek\MobaXterm\MobaXterm.exe",
Arguments = $@"-newtab -ssh {CamOnTest.IP} -user mav -pass ""{password}""",
UseShellExecute = true
};
Process.Start(psi);
stopWatchTest.Stop();
AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff"), Level.LOG);
}

View File

@@ -55,9 +55,13 @@
<PackageReference Include="ClosedXML" Version="0.105.0" />
<PackageReference Include="Emgu.CV" Version="4.12.0.5764" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.12.0.5764" />
<PackageReference Include="FlaUI.Core" Version="5.0.0" />
<PackageReference Include="FlaUI.UIA3" Version="5.0.0" />
<PackageReference Include="Google.Apis.Auth" Version="1.73.0" />
<PackageReference Include="Google.Apis.Sheets.v4" Version="1.72.0.3966" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.3" />
<PackageReference Include="OnvifDiscovery" Version="2.1.1" />
<PackageReference Include="PDFsharp-MigraDoc-gdi" Version="6.2.3" />
<PackageReference Include="Selenium.Support" Version="4.38.0" />
<PackageReference Include="Selenium.WebDriver" Version="4.38.0" />

View File

@@ -24,7 +24,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
return $"Error during GET request: {ex.Message}{Level.ERROR}";
return $"Error during GET request: {ex.Message}";
}
}
@@ -42,7 +42,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
return $"Error in HTTP_Update: {ex.Message}{Level.ERROR}";
return $"Error in HTTP_Update: {ex.Message}";
}
}
@@ -56,7 +56,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
return $"Error in HTTP_Fetch: {ex.Message}{Level.ERROR}";
return $"Error in HTTP_Fetch: {ex.Message}";
}
}
@@ -101,22 +101,22 @@ namespace AiQ_GUI
string responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
return $"Server returned {(int)response.StatusCode}: {response.ReasonPhrase}. Details: {responseBody}{Level.ERROR}";
return $"Server returned {(int)response.StatusCode}: {response.ReasonPhrase}. Details: {responseBody}";
return responseBody;
}
catch (TaskCanceledException)
{
return $"Timeout uploading to {url}.{Level.ERROR}";
return $"Timeout uploading to {url}.";
}
catch (HttpRequestException ex)
{
return $"HTTP error uploading to {url}: {ex.Message}{Level.ERROR}";
return $"HTTP error uploading to {url}: {ex.Message}";
}
catch (Exception ex)
{
return $"Unexpected error uploading to {url}: {ex.Message} {(ex.InnerException?.Message ?? string.Empty)} {Level.ERROR}";
return $"Unexpected error uploading to {url}: {ex.Message} {(ex.InnerException?.Message ?? string.Empty)}";
}
}
@@ -146,7 +146,7 @@ namespace AiQ_GUI
if (JSON == null || JSON.Contains("Error") || JSON.Contains("Timeout"))
{
MainForm.Instance.AddToActionsList($"Error talking to Flexi, are you sure this is an AiQ?{Level.WARNING}" + Environment.NewLine + JSON);
MainForm.Instance.AddToActionsList($"Error talking to Flexi, are you sure this is an AiQ?" + Environment.NewLine + JSON, Level.WARNING);
return null;
}
@@ -174,13 +174,13 @@ namespace AiQ_GUI
// Treat "operation was canceled" as a successful apply
if (response.Contains("The operation was canceled", StringComparison.OrdinalIgnoreCase))
{
Logging.LogMessage($"SetVaxtorMinMaxPlate: Camera applied config but closed connection early (safe to ignore).{Level.WARNING}");
Logging.LogMessage($"SetVaxtorMinMaxPlate: Camera applied config but closed connection early (safe to ignore).");
return true;
}
if (response.Contains("error", StringComparison.OrdinalIgnoreCase))
{
MainForm.Instance.DisplayQuestion($"SetVaxtorMinMaxPlate: failed - Please set manually{Level.WARNING}");
MainForm.Instance.DisplayQuestion($"SetVaxtorMinMaxPlate: failed - Please set manually");
return false;
}
@@ -188,7 +188,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Could not set Vaxtor Plate height and min confidence: {ex.Message}{Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not set Vaxtor Plate height and min confidence: {ex.Message}", Level.ERROR);
return false;
}
}
@@ -217,7 +217,7 @@ namespace AiQ_GUI
}
catch
{
MainForm.Instance.AddToActionsList($"Error reading trim JSON - {Level.ERROR}" + trimData);
MainForm.Instance.AddToActionsList($"Error reading trim JSON - " + trimData, Level.ERROR);
return;
}
@@ -226,7 +226,7 @@ namespace AiQ_GUI
{
if (RetryCount >= 3)
{
await MainForm.Instance.DisplayOK($"Please align trim in webpage then click OK.{Level.WARNING}"); // Awaited till OK has been clicked
await MainForm.Instance.DisplayOK($"Please align trim in webpage then click OK."); // Awaited till OK has been clicked
return;
}
@@ -254,11 +254,11 @@ namespace AiQ_GUI
}
else // Ask user to centre the plate in the field of view
{
await MainForm.Instance.DisplayOK($"Please centralise plate in view THEN press OK {Level.WARNING}"); // Awaited till OK has been clicked
await MainForm.Instance.DisplayOK($"Please centralise plate in view THEN press OK "); // Awaited till OK has been clicked
if (RetryCount >= 3)
{
await MainForm.Instance.DisplayOK($"Please align trim in webpage then click OK. {Level.WARNING}"); // Awaited till OK has been clicked
await MainForm.Instance.DisplayOK($"Please align trim in webpage then click OK. "); // Awaited till OK has been clicked
return;
}
await Task.Delay(5000); // Give 5 second delay for it to see a plate
@@ -275,7 +275,7 @@ namespace AiQ_GUI
string TrimResp = await HTTP_Update("SightingCreator", IPAddress, Trim_JSON);
if (!TrimResp.Contains($"\"propInterCameraOffsetX\": {{\"value\": \"{Convert.ToString(TrimX)}\", \"datatype\": \"int\"}}, \"propInterCameraOffsetY\": {{\"value\": \"{Convert.ToString(TrimY)}\", \"datatype\": \"int\"}},"))
MainForm.Instance.AddToActionsList($"Could not set camera trim{Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not set camera trim", Level.ERROR);
}
// Processes the network config from the camera and returns a string indicating the status
@@ -349,7 +349,7 @@ namespace AiQ_GUI
if (FoundCams.Contains("192.168.1.211"))
{
MainForm.Instance.AddToActionsList($"Could not set camera to DHCP please check camera.{Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not set camera to DHCP please check camera.", Level.ERROR);
return false;
}

View File

@@ -26,7 +26,7 @@ namespace AiQ_GUI
HttpResponseMessage response = await httpClient.GetAsync(requestUrl);
if (!response.IsSuccessStatusCode)
{
MainForm.Instance.AddToActionsList($"No success from {requestUrl} replied {response.StatusCode}{Level.ERROR}");
MainForm.Instance.AddToActionsList($"No success from {requestUrl} replied {response.StatusCode}", Level.ERROR);
return null;
}
@@ -34,7 +34,7 @@ namespace AiQ_GUI
if (imageBytes.Length == 0) // Check if the imageBytes is empty
{
MainForm.Instance.AddToActionsList($"No image data received from {requestUrl}{Level.ERROR}");
MainForm.Instance.AddToActionsList($"No image data received from {requestUrl}", Level.ERROR);
return null;
}
@@ -43,7 +43,7 @@ namespace AiQ_GUI
CvInvoke.Imdecode(imageBytes, ImreadModes.AnyColor, mat);
if (mat.IsEmpty)
{
MainForm.Instance.AddToActionsList($"Failed to decode image with Emgu CV.{Level.ERROR}");
MainForm.Instance.AddToActionsList($"Failed to decode image with Emgu CV.", Level.ERROR);
return null;
}
@@ -70,12 +70,12 @@ namespace AiQ_GUI
}
catch (HttpRequestException ex)
{
MainForm.Instance.AddToActionsList($"HTTP error: {ex.Message}{Level.ERROR}");
MainForm.Instance.AddToActionsList($"HTTP error: {ex.Message}", Level.ERROR);
return null;
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Error processing image: {ex.Message} {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Error processing image: {ex.Message}", Level.ERROR);
return null;
}
}
@@ -123,7 +123,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Error awaiting Colour snapshot: {ex.Message} {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Error awaiting Colour snapshot: {ex.Message}", Level.ERROR);
return;
}
@@ -146,7 +146,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Error calculating luminance: {ex.Message} {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Error calculating luminance: {ex.Message}",Level.ERROR);
return;
}
}

View File

@@ -45,7 +45,7 @@
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Error checking LEDs: {ex.Message} {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Error checking LEDs: {ex.Message}", Level.ERROR);
}
}
}

View File

@@ -25,13 +25,13 @@ namespace AiQ_GUI
else if (Type == "Audit")
salt = Auditsalt;
else
return $"Unrecognised challenge type:{Level.ERROR}" + Type;
return $"Unrecognised challenge type:" + Type;
if (string.IsNullOrEmpty(challenge) || challenge.Length != 6) // Check challenge format
return $"Invalid challenge format. Challenge must be 6 characters.{Level.ERROR}";
return $"Invalid challenge format. Challenge must be 6 characters.";
if (string.IsNullOrEmpty(salt) || salt.Length != 32) // Check salt format
return $"Invalid salt format. Salt must be 32 characters.{Level.ERROR}";
return $"Invalid salt format. Salt must be 32 characters.";
// Hash computation using SHA256 algorithm
byte[] inputBytes = Encoding.UTF8.GetBytes(challenge + " " + salt); // SHA hash format challenge and salt with space between
@@ -60,7 +60,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
return $"Error: Could not generate password{Level.ERROR}" + ex.Message;
return $"Error: Could not generate password" + ex.Message;
}
}
@@ -85,7 +85,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Exception in FetchDevPassword: {Level.ERROR}" + ex.Message);
MainForm.Instance.AddToActionsList($"Exception in FetchDevPassword:" + ex.Message, Level.ERROR);
return null;
}
}

View File

@@ -32,7 +32,7 @@ namespace AiQ_GUI
}
catch
{
MainForm.Instance.AddToActionsList($"Is the router on the network? Has the MAV Config file been applied?{Level.WARNING}");
MainForm.Instance.AddToActionsList($"Is the router on the network? Has the MAV Config file been applied?", Level.WARNING);
}
return null;
@@ -48,31 +48,31 @@ namespace AiQ_GUI
if (Strength < 25.0)
{
MainForm.Instance.AddToActionsList($"Router signal strength is {Strength} which is below 25%. Please check the router connection.{Level.WARNING}");
MainForm.Instance.AddToActionsList($"Router signal strength is {Strength} which is below 25%. Please check the router connection.", Level.WARNING);
PassTest = false;
}
if (!Router.SimStatus.Contains("SIM Ready"))
{
MainForm.Instance.AddToActionsList($"SIM card is not ready. {Router.SimStatus} Please check the SIM card status.{Level.WARNING}");
MainForm.Instance.AddToActionsList($"SIM card is not ready. {Router.SimStatus} Please check the SIM card status.", Level.WARNING);
PassTest = false;
}
if (!Router.Port3Status.Contains("port:3 link:up speed:100baseT full-duplex"))
{
MainForm.Instance.AddToActionsList($"Port 3 is not connected properly. {Router.Port3Status} Please check the connection. {Level.WARNING}");
MainForm.Instance.AddToActionsList($"Port 3 is not connected properly. {Router.Port3Status} Please check the connection.", Level.WARNING);
PassTest = false;
}
if (!Router.Port4Status.Contains("port:4 link:up speed:100baseT full-duplex"))
{
MainForm.Instance.AddToActionsList($"Port 4 is not connected properly. {Router.Port4Status} Please check the connection. {Level.WARNING}");
MainForm.Instance.AddToActionsList($"Port 4 is not connected properly. {Router.Port4Status} Please check the connection. ", Level.WARNING);
PassTest = false;
}
if (!Router.GoodPing)
{
MainForm.Instance.AddToActionsList($"Router could not ping 8.8.8.8. Please check the online connection.{Level.WARNING}");
MainForm.Instance.AddToActionsList($"Router could not ping 8.8.8.8. Please check the online connection.", Level.WARNING);
PassTest = false;
}

View File

@@ -25,7 +25,7 @@ namespace AiQ_GUI
}
catch (Exception Ex)
{
MainForm.Instance.AddToActionsList($"SSH connection failed: {Ex.Message}. Check password or network. {Level.WARNING}");
MainForm.Instance.AddToActionsList($"SSH connection failed: {Ex.Message}. Check password or network. ", Level.WARNING);
}
return null;
@@ -75,7 +75,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"SSH connection failed: {ex.Message}. Check password or network. {Level.WARNING}");
MainForm.Instance.AddToActionsList($"SSH connection failed: {ex.Message}. Check password or network. ", Level.WARNING);
}
string LogMssg = string.Join(" | ", typeof(SSHData).GetProperties().Select(p => $"{p.Name}: {p.GetValue(Data)}"));
@@ -170,7 +170,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"SSH connection failed: {ex.Message}. Check password or network. {Level.WARNING}");
MainForm.Instance.AddToActionsList($"SSH connection failed: {ex.Message}. Check password or network. ", Level.WARNING);
}
return (string.Empty, string.Empty);
@@ -323,7 +323,7 @@ namespace AiQ_GUI
if (checkDevice.Result.Trim() != "OK") // Device not found
{
MainForm.Instance.AddToActionsList($"Block device {device} not found. {Level.WARNING}");
MainForm.Instance.AddToActionsList($"Block device {device} not found. ", Level.WARNING);
return false;
}
@@ -378,7 +378,7 @@ namespace AiQ_GUI
if (checkDevice.Result.Trim().Length > 1) // Device not found
{
MainForm.Instance.AddToActionsList($"Cannot sync files to disk. Replied: {checkDevice.Result}. DO NOT TURN OFF, GET SUPERVISOR {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Cannot sync files to disk. Replied: {checkDevice.Result}. DO NOT TURN OFF, GET SUPERVISOR", Level.ERROR);
return;
}
@@ -387,7 +387,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Cannot sync becuase: {ex.Message}. DO NOT TURN OFF, GET SUPERVISOR {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Cannot sync becuase: {ex.Message}. DO NOT TURN OFF, GET SUPERVISOR", Level.ERROR);
}
}
}

View File

@@ -107,12 +107,12 @@ namespace AiQ_GUI
}
else
{
return $"Last serial number not found{Level.ERROR}";
return $"Last serial number not found";
}
}
catch (Exception ex)
{
return $"ERROR: {ex.Message}{Level.ERROR}";
return $"ERROR: {ex.Message}";
}
}
@@ -148,12 +148,12 @@ namespace AiQ_GUI
}
else
{
return $"Serial number not found{Level.ERROR}";
return $"Serial number not found";
}
}
catch (Exception ex)
{
return $"ERROR: {ex.Message}{Level.ERROR}";
return $"ERROR: {ex.Message}";
}
}
@@ -196,7 +196,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
return $"Failed to update spreadsheet data, please check manually{Level.ERROR}" + ex.Message;
return $"Failed to update spreadsheet data, please check manually" + ex.Message;
}
}
@@ -225,7 +225,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
return $"Failed to update spreadsheet data, please check manually{Level.WARNING}" + ex.Message;
return $"Failed to update spreadsheet data, please check manually" + ex.Message;
}
}

View File

@@ -83,26 +83,26 @@ namespace AiQ_GUI
case 0:
if (!await MainForm.Instance.DisplayQuestion("Is the sleeve aligned correctly?"))
{
await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Sleeve not aligned{Level.ERROR}");
await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Sleeve not aligned");
}
break;
case 1:
if (!await MainForm.Instance.DisplayQuestion("Are all the screws fitted in the front?"))
{
await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Not all front screws fitted{Level.ERROR}");
await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Not all front screws fitted");
}
break;
case 2:
if (!await MainForm.Instance.DisplayQuestion("Are all the screws fitted in the rear?"))
{
await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Not all rear screws fitted{Level.ERROR}");
await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Not all rear screws fitted");
}
break;
case 3:
if (await MainForm.Instance.DisplayQuestion("Shake unit, does it rattle?"))
{
await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Unit rattles{Level.ERROR}");
await MainForm.Instance.TestFailed(Btn, $"Visual Test Fail - Unit rattles");
}
break;
default:
@@ -123,7 +123,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Error fetching versions for {IPAddress}: {ex.Message}{Level.ERROR}");
MainForm.Instance.AddToActionsList($"Error fetching versions for {IPAddress}: {ex.Message}", Level.ERROR);
return null;
}
@@ -187,6 +187,7 @@ namespace AiQ_GUI
public bool IsChecked { get; set; } // Is it checked for soak test?
public CheckBox CheckBox { get; set; } = new CheckBox();
public string TestReportLoc { get; set; } = string.Empty; // Location of test report file
public string Manfr { get; internal set; }
}
// Static class for global flags

4
LDS.cs
View File

@@ -30,7 +30,7 @@ namespace AiQ_GUI
}
catch // If file can't deserialise
{
MainForm.Instance.AddToActionsList($"Error loading Local Data Store{Level.WARNING}");
MainForm.Instance.AddToActionsList($"Error loading Local Data Store", Level.WARNING);
return null; // Return null to indicate failure
}
}
@@ -45,7 +45,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Error saving Local Data Store: {ex.Message}{Level.WARNING}");
MainForm.Instance.AddToActionsList($"Error saving Local Data Store: {ex.Message}", Level.WARNING);
}
}
}

View File

@@ -4,14 +4,32 @@ namespace AiQ_GUI
{
class Access
{
public const string connString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=G:\Shared drives\MAV Production GUI's\AiQ\GUI's\AiQ_Final_Test.accdb;Persist Security Info=False;OLE DB Services=-1;";
// Reads camera model numbers and descriptions from the database, sorts them alphabetically by model number (except "AB12CD", which appears last), and formats each entry as "ModelNumber - Description".
public static string[] ReadCamTypes()
public const string connString =
@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=G:\Shared drives\MAV Production GUI's\AiQ\GUI's\AiQ_Final_Test.accdb;Persist Security Info=False;OLE DB Services=-1;";
// Allows Different Cam Types with different schemas to be handled safely
private static bool HasColumn(OleDbDataReader reader, string columnName)
{
List<Tuple<string, string>> modelTuples = new(30); // Preallocate list with estimated capacity to reduce internal resizing
for (int i = 0; i < reader.FieldCount; i++)
if (reader.GetName(i).Equals(columnName, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
public static string[] ReadCamTypes(string camType)
{
// No camera type selected
if (string.IsNullOrWhiteSpace(camType))
return null;
// Preallocate list to reduce resizing
List<Tuple<string, string>> modelTuples = new(30);
using OleDbConnection conn = new(connString);
// Attempt to open the database
try
{
conn.Open();
@@ -22,31 +40,36 @@ namespace AiQ_GUI
return null;
}
const string query = "SELECT ModelNumber, Description FROM AiQ WHERE MarkNumber > 1";
// Mobile table does not have MarkNumber
// AiQ and others might do ????? - TODO ask
string query = camType == "Mobile"
? "SELECT ModelNumber, Description FROM [Mobile]"
: $"SELECT ModelNumber, Description FROM [{camType}] WHERE MarkNumber > 1";
using OleDbCommand cmd = new(query, conn);
using OleDbDataReader reader = cmd.ExecuteReader();
// Read all models from the selected table
while (reader.Read())
{
// Extract each model number and description, using empty string if null
string modelNumber = reader["ModelNumber"] as string ?? string.Empty;
string description = reader["Description"] as string ?? string.Empty;
modelTuples.Add(Tuple.Create(modelNumber.Trim(), description.Trim()));
}
// Sort: push "AB12CD" to the bottom, then sort remaining items alphabetically
IOrderedEnumerable<Tuple<string, string>> sorted = modelTuples.OrderBy(t => t.Item1.Equals("AB12CD", StringComparison.OrdinalIgnoreCase) ? 1 : 0)
// Sort models, pushing AB12CD to the bottom
var sorted = modelTuples
.OrderBy(t => t.Item1.Equals("AB12CD", StringComparison.OrdinalIgnoreCase) ? 1 : 0)
.ThenBy(t => t.Item1, StringComparer.OrdinalIgnoreCase);
conn.Close();
// Format the sorted tuples as "ModelNumber - Description" strings and return as array
// Format for combo box display
return sorted.Select(t => $"{t.Item1} - {t.Item2}").ToArray();
}
// Read the universal data table from the database and populate the UniversalData class with the values.
public static void ReadUniData()
{
using OleDbConnection conn = new(connString);
try
{
conn.Open();
@@ -57,24 +80,27 @@ namespace AiQ_GUI
return;
}
const string query = "SELECT FlexiVersion, FlexiRevision, WonwooFirmware, AiQGUIVersion, PowerConsumption, LicencingServerURL FROM UniversalData"; // Grab the universal data
const string query =
"SELECT FlexiVersion, FlexiRevision, WonwooFirmware, AiQGUIVersion, PowerConsumption, LicencingServerURL FROM UniversalData";
using OleDbCommand cmd = new(query, conn);
using OleDbDataReader reader = cmd.ExecuteReader();
// UniversalData is expected to contain a single row
reader.Read();
UniversalData.ExpFlexiVer = Convert.ToString(reader["FlexiVersion"]);
UniversalData.ExpFlexiRev = Convert.ToString(reader["FlexiRevision"]);
UniversalData.WonwooFirmware = Convert.ToString(reader["WonwooFirmware"]);
UniversalData.LatestVersion = Convert.ToString(reader["AiQGUIVersion"]);
UniversalData.PowerConsumption = Convert.ToInt16(reader["PowerConsumption"]);
UniversalData.LicencingServerURL = Convert.ToString(reader["LicencingServerURL"]);
conn.Close();
}
// Knowing the model number on test, this function reads the database and populates the Camera class with the values.
public static void ReadModelRow(string ModelOnTest)
// Populates CameraAccessInfo dynamically based on available columns
public static void ReadModelRow(string camType, string ModelOnTest)
{
using OleDbConnection conn = new(connString);
try
{
conn.Open();
@@ -85,66 +111,85 @@ namespace AiQ_GUI
return;
}
string query = $"SELECT * FROM AiQ WHERE ModelNumber = '{ModelOnTest}';"; // Grab all the info for specified model
// Parameterised query to prevent injection
string query = $"SELECT * FROM [{camType}] WHERE ModelNumber = ?";
using OleDbCommand cmd = new(query, conn);
cmd.Parameters.AddWithValue("?", ModelOnTest);
using OleDbDataReader reader = cmd.ExecuteReader();
reader.Read();
// Populate the CameraAccessInfo class with the values from the database
CameraAccessInfo.Processor = Convert.ToString(reader["Processor"]);
CameraAccessInfo.VaxtorLic = Convert.ToBoolean(reader["Vaxtor"]);
CameraAccessInfo.HardwareExtras = Convert.ToString(reader["HardwareExtras"]);
CameraAccessInfo.PowerType = Convert.ToString(reader["PowerType"]);
CameraAccessInfo.LED_V = Convert.ToDouble(reader["LEDVoltage"]);
CameraAccessInfo.LED_I = Convert.ToInt32(reader["LEDCurrent"]);
CameraAccessInfo.SpreadsheetID = Convert.ToString(reader["SSID"]);
conn.Close();
// No matching model found
if (!reader.Read())
return;
// Populate CameraAccessInfo only if columns exist
CameraAccessInfo.Processor =
HasColumn(reader, "Processor") ? Convert.ToString(reader["Processor"]) : string.Empty;
CameraAccessInfo.VaxtorLic =
HasColumn(reader, "Vaxtor") && Convert.ToString(reader["Vaxtor"]) == "Yes";
CameraAccessInfo.HardwareExtras =
HasColumn(reader, "HardwareExtras") ? Convert.ToString(reader["HardwareExtras"]) : string.Empty;
CameraAccessInfo.PowerType =
HasColumn(reader, "PowerType") ? Convert.ToString(reader["PowerType"]) : string.Empty;
CameraAccessInfo.LED_V =
HasColumn(reader, "LEDVoltage") ? Convert.ToDouble(reader["LEDVoltage"]) : 0;
CameraAccessInfo.LED_I =
HasColumn(reader, "LEDCurrent") ? Convert.ToInt32(reader["LEDCurrent"]) : 0;
CameraAccessInfo.SpreadsheetID =
HasColumn(reader, "SSID") ? Convert.ToString(reader["SSID"]) : string.Empty;
}
public static void Stats(string TypeOfTest, string modelNumber)
{
Stats([TypeOfTest], modelNumber);
}
public static void Stats(string[] TypeOfTest, string modelNumber)
{
using OleDbConnection conn = new(connString); // Opens connection to Access database
using OleDbConnection conn = new(connString);
try
{
conn.Open(); // Opens DB
conn.Open();
foreach (string type in TypeOfTest)
{
string query = $"UPDATE AiQ SET [{type}] = [{type}] + 1 WHERE [ModelNumber] = ?"; // Add one for every test ran of this type for this model number
using OleDbCommand cmd = new(query, conn); // Create command
cmd.Parameters.AddWithValue("?", modelNumber); // Add model number to prevent injection
string query =
$"UPDATE AiQ SET [{type}] = [{type}] + 1 WHERE [ModelNumber] = ?";
using OleDbCommand cmd = new(query, conn);
cmd.Parameters.AddWithValue("?", modelNumber);
int rowsAffected = cmd.ExecuteNonQuery();
// Execute the command and get the number of rows affected
if (rowsAffected == 0) // If one or more rows were updated
MainForm.Instance.AddToActionsList($"No rows affected for {modelNumber}{Level.ERROR}");
if (rowsAffected == 0)
MainForm.Instance.AddToActionsList($"No rows affected for {modelNumber}", Level.ERROR);
}
conn.Close();
}
catch
{
MainForm.Instance.AddToActionsList($"Could not access Access in Google Drive. Is it running?{Level.WARNING}");
return;
MainForm.Instance.AddToActionsList(
"Could not access Access in Google Drive. Is it running?", Level.WARNING);
}
}
public static void StatsDiags(string redDiagLabels, string RhTxBxActionsText, string ModelNumber)
{
using OleDbConnection conn = new(connString);
conn.Open();
// Null checks
// Replace null or empty values
string redVal = string.IsNullOrWhiteSpace(redDiagLabels) ? "-" : redDiagLabels;
string actVal = string.IsNullOrWhiteSpace(RhTxBxActionsText) ? "-" : RhTxBxActionsText;
string model = string.IsNullOrWhiteSpace(ModelNumber) ? "-" : ModelNumber;
const string sql = @"INSERT INTO DiagsStats ([Date], [Model], [Red Diags Labels], [RhTxBxActions Contents]) VALUES (?, ?, ?, ?)";
const string sql =
@"INSERT INTO DiagsStats ([Date], [Model], [Red Diags Labels], [RhTxBxActions Contents])
VALUES (?, ?, ?, ?)";
using OleDbCommand cmd = new(sql, conn);
@@ -153,19 +198,18 @@ namespace AiQ_GUI
OleDbType = OleDbType.Date,
Value = DateTime.Now
});
cmd.Parameters.AddWithValue("?", model);
cmd.Parameters.AddWithValue("?", redVal);
cmd.Parameters.AddWithValue("?", actVal);
int rows = cmd.ExecuteNonQuery();
conn.Close();
if (rows == 0)
MainForm.Instance.AddToActionsList($"No rows inserted into DiagsStats (unexpected).{Level.ERROR}");
MainForm.Instance.AddToActionsList(
"No rows inserted into DiagsStats (unexpected).", Level.ERROR);
}
}
// Expected universal data for the GUI, read from the database
public class UniversalData
{
public static string ExpFlexiVer { get; set; } = string.Empty;
@@ -176,7 +220,6 @@ namespace AiQ_GUI
public static string LicencingServerURL { get; set; } = string.Empty;
}
// One object to contain all the camera info from the model info access database
public class CameraAccessInfo
{
public static string Processor { get; set; } = string.Empty;

View File

@@ -17,7 +17,7 @@ namespace AiQ_GUI
}
else
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
}
}
@@ -31,7 +31,7 @@ namespace AiQ_GUI
}
else
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
}
return null;
}
@@ -40,7 +40,7 @@ namespace AiQ_GUI
{
if (!File.Exists(FilePath))
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
return -1;
}
@@ -62,7 +62,7 @@ namespace AiQ_GUI
{
if (!File.Exists(FilePath))
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
return "Spreadsheet not found";
}
@@ -114,7 +114,7 @@ namespace AiQ_GUI
{
if (!File.Exists(filePath))
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :) {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :) ", Level.ERROR);
return "Spreadsheet not found";
}
@@ -161,7 +161,7 @@ namespace AiQ_GUI
{
if (!File.Exists(FilePath))
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
return "Spreadsheet not found";
}
@@ -213,7 +213,7 @@ namespace AiQ_GUI
{
if (!File.Exists(FilePath))
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
return $"Spreadsheet not found{ Level.ERROR}"
;
}
@@ -251,7 +251,7 @@ namespace AiQ_GUI
{
if (!File.Exists(filePath))
{
MainForm.Instance.AddToActionsList($"Could not find RMA Control spreadsheet :({Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find RMA Control spreadsheet :(", Level.ERROR);
return 0;
}
@@ -293,7 +293,7 @@ namespace AiQ_GUI
{
if (!File.Exists(filePath))
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
return 0;
}
@@ -329,7 +329,7 @@ namespace AiQ_GUI
{
if (!File.Exists(filePath))
{
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :({Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :(", Level.ERROR);
return -1;
}
@@ -344,7 +344,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Error checking next free row: {Level.ERROR}" + ex.Message);
MainForm.Instance.AddToActionsList($"Error checking next free row:" + ex.Message, Level.ERROR);
return -1;
}
}

View File

@@ -40,7 +40,7 @@ namespace AiQ_GUI
const string selectColumns = @"
ModelNumber,
[Total Tests Run],
[Pre Tests Passed],
[Pre Tests Passed]
[Pre Tests Failed],
[Final Tests Passed],
[Final Tests Failed],

View File

@@ -1,4 +1,6 @@
using System.Net;
using OnvifDiscovery;
using OnvifDiscovery.Models;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
@@ -71,15 +73,15 @@ namespace AiQ_GUI
}
catch (TaskCanceledException ex)
{
return $"HTTP error calling {url}: {ex.Message}{Level.ERROR}";
return $"HTTP error calling {url}: {ex.Message}";
}
catch (HttpRequestException ex)
{
return $"HTTP error calling {url}: {ex.Message}{Level.ERROR}";
return $"HTTP error calling {url}: {ex.Message}";
}
catch (Exception ex)
{
return $"Unexpected error calling {url}: {ex.Message}{Level.ERROR}";
return $"Unexpected error calling {url}: {ex.Message}";
}
}
@@ -88,9 +90,15 @@ namespace AiQ_GUI
const int sendPort = 6666;
const int receivePort = 6667;
const int discoveryTimeoutMs = 1000;
IList<string> FoundCams = [];
byte[] discoveryPacket = [0x50, 0x4f, 0x4c, 0x4c, 0xaf, 0xb0, 0xb3, 0xb3, 0xb6, 0x01, 0xa8, 0xc0, 0x0b, 0x1a, 0x00, 0x00];
List<string> FoundCams = [];
HashSet<string> discoveredIPs = new(StringComparer.OrdinalIgnoreCase);
byte[] discoveryPacket =
[
0x50, 0x4f, 0x4c, 0x4c, 0xaf, 0xb0, 0xb3, 0xb3,
0xb6, 0x01, 0xa8, 0xc0, 0x0b, 0x1a, 0x00, 0x00
];
async Task SendAndListen(IPAddress localIp)
{
@@ -101,12 +109,11 @@ namespace AiQ_GUI
sender.Send(discoveryPacket, discoveryPacket.Length);
}
using UdpClient receiver = new(receivePort); // Listen for replies on fixed port
using UdpClient receiver = new(receivePort);
receiver.Client.ReceiveTimeout = discoveryTimeoutMs;
DateTime timeout = DateTime.Now.AddMilliseconds(discoveryTimeoutMs);
try
{
while (DateTime.Now < timeout)
{
if (receiver.Available > 0)
@@ -114,28 +121,30 @@ namespace AiQ_GUI
UdpReceiveResult result = await receiver.ReceiveAsync();
byte[] recvBuffer = result.Buffer;
if (recvBuffer.Length >= 52) // Safety check
if (recvBuffer.Length >= 52)
{
byte[] ipBytes = recvBuffer.Skip(recvBuffer.Length - 52).Take(4).Reverse().ToArray();
string ipToAdd = string.Join(".", ipBytes);
byte[] ipBytes = recvBuffer
.Skip(recvBuffer.Length - 52)
.Take(4)
.Reverse()
.ToArray();
if (!FoundCams.Contains(ipToAdd))
FoundCams.Add(ipToAdd);
string ip = string.Join(".", ipBytes);
if (discoveredIPs.Add(ip))
FoundCams.Add(ip);
}
}
await Task.Delay(50); // brief wait to allow data in
}
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
{
// No data received in time — normal case
await Task.Delay(50);
}
}
// Get first IPv4 interface (non-loopback)
foreach (IPAddress ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
{
if (ip.AddressFamily != AddressFamily.InterNetwork)
continue;
try
{
await SendAndListen(ip);
@@ -143,9 +152,31 @@ namespace AiQ_GUI
catch { }
}
//ONVIF discovery
try
{
Discovery onvifDiscovery = new();
await foreach (DiscoveryDevice device in onvifDiscovery.DiscoverAsync(5))
{
string ip = device.Address;
// Already found via UDP → skip
if (!discoveredIPs.Add(ip))
continue;
// ONVIF-only camera
FoundCams.Add($"{ip} - Onvif");
}
}
catch
{
}
return FoundCams;
}
// Ping to make sure devices are connected to the network, be aware it isn't consistant across subnets.
public async static Task<bool> PingIP(string ipAddress)
{

View File

@@ -19,7 +19,7 @@ namespace AiQ_GUI
}
catch (WebDriverTimeoutException)
{
MainForm.Instance.AddToActionsList($"Could not load web page {Level.ERROR}" + url + $"1. Check camera has no password set. {Level.ERROR}" + Environment.NewLine + $"2. If unable to fix speak to supervisor.{Level.ERROR}");
MainForm.Instance.AddToActionsList($"Could not load web page " + url + $"1. Check camera has no password set. " + Environment.NewLine + $"2. If unable to fix speak to supervisor.", Level.ERROR);
}
}
@@ -71,11 +71,11 @@ namespace AiQ_GUI
loginBtn.Click();
MainForm.Instance.AddToActionsList($"Switched user and logged in.{Level.Success}");
MainForm.Instance.AddToActionsList($"Switched user and logged in.", Level.Success);
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"SwitchUser failed: {Level.ERROR}" + ex.Message);
MainForm.Instance.AddToActionsList($"SwitchUser failed:" + ex.Message,Level.ERROR);
}
}
@@ -99,7 +99,7 @@ namespace AiQ_GUI
ClickElementByID(elementID, driver, false);
}
MainForm.Instance.AddToActionsList($"Could not click {Level.WARNING}" + elementID);
MainForm.Instance.AddToActionsList($"Could not click " + elementID, Level.WARNING);
}
}
@@ -134,7 +134,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Failed to create ChromeDriver: {Level.ERROR}" + ex.Message);
MainForm.Instance.AddToActionsList($"Failed to create ChromeDriver: " + ex.Message, Level.ERROR);
throw;
}
}

View File

@@ -37,7 +37,7 @@ namespace AiQ_GUI
catch (Exception ex)
{
SoakError($"Initial connection failed: {ex.Message}", SoakLogFile, CamInfo.CheckBox);
MainForm.Instance.AddToActionsList($"[{CamInfo.IP}] Initial connection failed: {Level.ERROR}{ex.Message}");
MainForm.Instance.AddToActionsList($"[{CamInfo.IP}] Initial connection failed:{ex.Message}", Level.ERROR);
// Wait 10 seconds before trying again
await Task.Delay(TimeSpan.FromSeconds(10), token);
@@ -52,7 +52,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
SoakError($"Failed to get element IDs: {Level.ERROR} {ex.Message}", SoakLogFile, CamInfo.CheckBox);
SoakError($"Failed to get element IDs: {ex.Message}", SoakLogFile, CamInfo.CheckBox);
return;
}
@@ -84,7 +84,7 @@ namespace AiQ_GUI
// Retry ping until camera responds or cancelled
while (!await Network.PingIP(CamInfo.IP) && !token.IsCancellationRequested)
{
SoakError($"Camera did not respond after restart.{Level.ERROR}", SoakLogFile, CamInfo.CheckBox);
SoakError($"Camera did not respond after restart.", SoakLogFile, CamInfo.CheckBox);
await Task.Delay(TimeSpan.FromMinutes(1), token); // Retry after delay of 1 minute
}
@@ -98,7 +98,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
SoakError($"Error during power cycle: {Level.ERROR} {ex.Message}", SoakLogFile, CamInfo.CheckBox);
SoakError($"Error during power cycle: {ex.Message}", SoakLogFile, CamInfo.CheckBox);
}
}
@@ -112,7 +112,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
SoakError($"ImageCheck failed: {Level.ERROR}{Level.ERROR} {ex.Message}", SoakLogFile, CamInfo.CheckBox);
SoakError($"ImageCheck failed: {ex.Message}", SoakLogFile, CamInfo.CheckBox);
}
lastHour = currentHour;
}
@@ -131,7 +131,7 @@ namespace AiQ_GUI
}
catch (Exception ex)
{
SoakError($"ChangeRandomDropdown failed: {Level.WARNING} {ex.Message}", SoakLogFile, CamInfo.CheckBox);
SoakError($"ChangeRandomDropdown failed: {ex.Message}", SoakLogFile, CamInfo.CheckBox);
}
try
@@ -200,7 +200,7 @@ namespace AiQ_GUI
File.Delete(SoakTestPath);
}
else
MainForm.Instance.AddToActionsList($"Failed to link or delete PDFs {Level.ERROR}");
MainForm.Instance.AddToActionsList($"Failed to link or delete PDFs ", Level.ERROR);
}
catch (Exception ex)
{
@@ -240,7 +240,7 @@ namespace AiQ_GUI
if (ImageDark == null)
{
Logging.LogWarningMessage($"Dark image is null for {controlType} at setting {SettingMinMax[1]}", SoakLogFile);
MainForm.Instance.AddToActionsList($"Dark image is null for {controlType} at setting {SettingMinMax[1]}{Level.WARNING}");
MainForm.Instance.AddToActionsList($"Dark image is null for {controlType} at setting {SettingMinMax[1]}", Level.WARNING);
return;
}