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(); components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
CbBxCameraType = new ComboBox(); CbBxCameraModel = new ComboBox();
BtnStartTest = new Button(); BtnStartTest = new Button();
BtnFindCams = new Button(); BtnFindCams = new Button();
CbBxFoundCams = new ComboBox(); CbBxFoundCams = new ComboBox();
@@ -56,7 +56,6 @@ namespace AiQ_GUI
lblZoomLock = new Label(); lblZoomLock = new Label();
LblLEDI = new Label(); LblLEDI = new Label();
LblLEDV = new Label(); LblLEDV = new Label();
RhTxBxActions = new RichTextBox();
LblOVModule = new Label(); LblOVModule = new Label();
LblIRModule = new Label(); LblIRModule = new Label();
BtnTest = new Button(); BtnTest = new Button();
@@ -89,6 +88,13 @@ namespace AiQ_GUI
BtnNo = new Button(); BtnNo = new Button();
BtnYes = new Button(); BtnYes = new Button();
LblQuestion = new Label(); 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(); label16 = new Label();
BtnPrintGB = new Button(); BtnPrintGB = new Button();
BtnPrintAiQ = new Button(); BtnPrintAiQ = new Button();
@@ -111,13 +117,6 @@ namespace AiQ_GUI
label9 = new Label(); label9 = new Label();
BtnGenerate = new Button(); BtnGenerate = new Button();
TxBxChallenge = new TextBox(); 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(); BtnSetGodMode = new Button();
btnPsu12V = new Button(); btnPsu12V = new Button();
btnPsu48V = new Button(); btnPsu48V = new Button();
@@ -160,6 +159,8 @@ namespace AiQ_GUI
LblGUIVers = new Label(); LblGUIVers = new Label();
timerTypeIP = new System.Windows.Forms.Timer(components); timerTypeIP = new System.Windows.Forms.Timer(components);
TimerFlash = 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)PicBxMAV).BeginInit();
((System.ComponentModel.ISupportInitialize)PicBxAiQ).BeginInit(); ((System.ComponentModel.ISupportInitialize)PicBxAiQ).BeginInit();
((System.ComponentModel.ISupportInitialize)PicBxIRF2).BeginInit(); ((System.ComponentModel.ISupportInitialize)PicBxIRF2).BeginInit();
@@ -181,25 +182,23 @@ namespace AiQ_GUI
TabSoak.SuspendLayout(); TabSoak.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// CbBxCameraType // CbBxCameraModel
//
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.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
// //
BtnStartTest.BackColor = Color.FromArgb(70, 65, 80); BtnStartTest.BackColor = Color.FromArgb(70, 65, 80);
@@ -535,20 +534,6 @@ namespace AiQ_GUI
LblLEDV.TabIndex = 185; LblLEDV.TabIndex = 185;
LblLEDV.Text = "LED Voltage = "; 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
// //
LblOVModule.BackColor = Color.Transparent; LblOVModule.BackColor = Color.Transparent;
@@ -937,6 +922,7 @@ namespace AiQ_GUI
PnlQuestion.Controls.Add(BtnNo); PnlQuestion.Controls.Add(BtnNo);
PnlQuestion.Controls.Add(BtnYes); PnlQuestion.Controls.Add(BtnYes);
PnlQuestion.Controls.Add(LblQuestion); PnlQuestion.Controls.Add(LblQuestion);
PnlQuestion.Controls.Add(PnlInputValue);
PnlQuestion.Location = new Point(251, 14); PnlQuestion.Location = new Point(251, 14);
PnlQuestion.Margin = new Padding(4, 3, 4, 3); PnlQuestion.Margin = new Padding(4, 3, 4, 3);
PnlQuestion.Name = "PnlQuestion"; PnlQuestion.Name = "PnlQuestion";
@@ -989,6 +975,108 @@ namespace AiQ_GUI
LblQuestion.Text = "Test failed, appeal?\r\nSee Actions textbox for details."; LblQuestion.Text = "Test failed, appeal?\r\nSee Actions textbox for details.";
LblQuestion.TextAlign = ContentAlignment.MiddleCenter; 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
// //
label16.AutoSize = true; label16.AutoSize = true;
@@ -1318,108 +1406,6 @@ namespace AiQ_GUI
TxBxChallenge.TextAlign = HorizontalAlignment.Center; TxBxChallenge.TextAlign = HorizontalAlignment.Center;
TxBxChallenge.TextChanged += TxBxChallenge_TextChanged; 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
// //
BtnSetGodMode.BackColor = Color.FromArgb(70, 65, 80); BtnSetGodMode.BackColor = Color.FromArgb(70, 65, 80);
@@ -2056,6 +2042,39 @@ namespace AiQ_GUI
// //
timerTypeIP.Interval = 2000; timerTypeIP.Interval = 2000;
timerTypeIP.Tick += timerTypeIP_Tick; 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 // MainForm
// //
@@ -2063,23 +2082,23 @@ namespace AiQ_GUI
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
BackgroundImage = Properties.Resources.homepage_banner; BackgroundImage = Properties.Resources.homepage_banner;
ClientSize = new Size(932, 970); ClientSize = new Size(932, 970);
Controls.Add(RhTxBxActions);
Controls.Add(CbBxCamType);
Controls.Add(LblGUIVers); Controls.Add(LblGUIVers);
Controls.Add(BtnOpenWebpage); Controls.Add(BtnOpenWebpage);
Controls.Add(TabImagesandSettings); Controls.Add(TabImagesandSettings);
Controls.Add(PicBxMAV); Controls.Add(PicBxMAV);
Controls.Add(PicBxAiQ); Controls.Add(PicBxAiQ);
Controls.Add(PnlLbls); Controls.Add(PnlLbls);
Controls.Add(PnlInputValue);
Controls.Add(PnlQuestion); Controls.Add(PnlQuestion);
Controls.Add(LblActions); Controls.Add(LblActions);
Controls.Add(CbBxCameraType); Controls.Add(CbBxCameraModel);
Controls.Add(CbBxUserName); Controls.Add(CbBxUserName);
Controls.Add(BtnPreTest); Controls.Add(BtnPreTest);
Controls.Add(BtnFindCams); Controls.Add(BtnFindCams);
Controls.Add(BtnStartTest); Controls.Add(BtnStartTest);
Controls.Add(CbBxFoundCams); Controls.Add(CbBxFoundCams);
Controls.Add(BtnTest); Controls.Add(BtnTest);
Controls.Add(RhTxBxActions);
Controls.Add(BtnRestart); Controls.Add(BtnRestart);
Controls.Add(BtnMin); Controls.Add(BtnMin);
Controls.Add(BtnClose); Controls.Add(BtnClose);
@@ -2144,9 +2163,8 @@ namespace AiQ_GUI
private System.Windows.Forms.Label lblZoomLock; private System.Windows.Forms.Label lblZoomLock;
private System.Windows.Forms.Label LblLEDI; private System.Windows.Forms.Label LblLEDI;
private System.Windows.Forms.Label LblLEDV; 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.Button BtnPreTest;
private System.Windows.Forms.RichTextBox RhTxBxActions;
private System.Windows.Forms.Label LblOVModule; private System.Windows.Forms.Label LblOVModule;
private System.Windows.Forms.Label LblIRModule; private System.Windows.Forms.Label LblIRModule;
private System.Windows.Forms.Button BtnTest; private System.Windows.Forms.Button BtnTest;
@@ -2251,5 +2269,7 @@ namespace AiQ_GUI
private Button BtnFactoryDefault; private Button BtnFactoryDefault;
private Button BtnUploadWonwooSetOV; private Button BtnUploadWonwooSetOV;
private Button BtnUploadWonwooSetIR; 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; this.Name = "AiQ GUI V" + GUIUpdate.GUIVerShort;
LblGUIVers.Text += GUIUpdate.GUIVerShort; LblGUIVers.Text += GUIUpdate.GUIVerShort;
await UniDataTask; // Have to wait for expected GUI version to compare to. 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 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 // Load local data store
localDataStore = await LDSWAIT; localDataStore = await LDSWAIT;
@@ -84,7 +81,7 @@ namespace AiQ_GUI
Task CheckHWOnline = PingCheck(); // Async check all hardware is online Task CheckHWOnline = PingCheck(); // Async check all hardware is online
PopulateUIWithLDS(localDataStore); // Update fields that depend on LDS 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. 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 BtnRefreshUnix_Click(sender, e); // Reset timestamp
@@ -96,6 +93,20 @@ namespace AiQ_GUI
Flags.Start = false; 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) private void PopulateUIWithLDS(LocalDataStore lds)
{ {
CbBxUserName.Text = lds.User; CbBxUserName.Text = lds.User;
@@ -693,7 +704,7 @@ namespace AiQ_GUI
{ {
// Update the serial number register with new cameras details // Update the serial number register with new cameras details
// Cam description is in model drop down 6 digit model num + 3 for " - " // 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")) if (NewSerial.Contains("ERROR"))
{ {
@@ -731,10 +742,10 @@ namespace AiQ_GUI
private void BtnClose_Click(object sender, EventArgs e) private void BtnClose_Click(object sender, EventArgs e)
{ {
// Save user settings in LDS for next time if values are valid // 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.User = CbBxUserName.Text;
localDataStore.LastModel = CbBxCameraType.Text; localDataStore.LastModel = CbBxCameraModel.Text;
LDS.SetLDS(localDataStore); LDS.SetLDS(localDataStore);
} }
@@ -820,12 +831,6 @@ namespace AiQ_GUI
Flags.No = true; 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) private void CmBoBoxUserName_TextChanged(object sender, EventArgs e)
{ {
@@ -843,19 +848,23 @@ namespace AiQ_GUI
timerTypeIP.Enabled = false; // Stop this triggering again 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 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; CbBxFoundCams.BackColor = Color.Red;
return; return;
} }
CamOnTest.IP = CbBxFoundCams.Text; // Set the IP address under test CamOnTest.IP = ipOnly; //Always store clean IP
CbBxFoundCams.BackColor = BtnColour; CbBxFoundCams.BackColor = BtnColour;
BtnSecret.Enabled = true; 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 // Wont be filled out before the pre test but needed for final test
if (RegexCache.SerialRegex().IsMatch(CamOnTest.Serial) && RegexCache.ModelRegex().IsMatch(CamOnTest.Model)) 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 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; BtnSet211.Text = string.IsNullOrEmpty(networkConfigText) ? "Set to 211" : networkConfigText;
ShowToolTip(BtnSecret); // Set dev password to Tooltip and clipboard ShowToolTip(BtnSecret); // Set dev password to Tooltip and clipboard
} }
else if (CbBxFoundCams.Text.Contains("Found")) else if (selectedText.Contains("Found"))
{ {
CbBxFoundCams.BackColor = BtnColour; CbBxFoundCams.BackColor = BtnColour;
} }
@@ -957,7 +966,7 @@ namespace AiQ_GUI
RhTxBxActions.SelectionColor = Color.LightGreen; RhTxBxActions.SelectionColor = Color.LightGreen;
} }
RhTxBxActions.AppendText(Mssg + Environment.NewLine); RhTxBxActions.AppendText(Mssg + Environment.NewLine);
RhTxBxActions.SelectionStart = RhTxBxActions.Text.Length; RhTxBxActions.SelectionStart = RhTxBxActions.Text.Length;
RhTxBxActions.SelectionColor = SystemColors.Control; RhTxBxActions.SelectionColor = SystemColors.Control;
RhTxBxActions.ScrollToCaret(); RhTxBxActions.ScrollToCaret();
@@ -976,7 +985,7 @@ namespace AiQ_GUI
if (await Network.PingIP("8.8.8.8")) // Ping to find if we are online if (await Network.PingIP("8.8.8.8")) // Ping to find if we are online
{ {
Flags.Offline = false; // Ping succeeded Flags.Offline = false; // Ping succeeded
CbBxCameraType.Enabled = true; CbBxCameraModel.Enabled = true;
} }
else else
TSC = SetInvalid("Offline Mode, could not connect to the internet."); // Ping failed TSC = SetInvalid("Offline Mode, could not connect to the internet."); // Ping failed
@@ -993,7 +1002,7 @@ namespace AiQ_GUI
TSC = SetInvalid("Select Username."); TSC = SetInvalid("Select Username.");
// Model number selected // Model number selected
if (CbBxCameraType.SelectedIndex == -1) if (CbBxCameraModel.SelectedIndex == -1)
TSC = SetInvalid("Select Model number."); TSC = SetInvalid("Select Model number.");
// Settings IP addresses filled in // Settings IP addresses filled in
@@ -1494,15 +1503,19 @@ namespace AiQ_GUI
private void BtnOpenWebpage_Click(object sender, EventArgs e) 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() ProcessStartInfo psi = new()
{ {
FileName = $"http://{CamOnTest.IP}", // Just the URL FileName = $"http://{ip}",
UseShellExecute = true // Lets the OS decide how to open it UseShellExecute = true
}; };
Process.Start(psi); Process.Start(psi);
} }
private async void UploadWonwooSetOV_Click(object sender, EventArgs e) private async void UploadWonwooSetOV_Click(object sender, EventArgs e)
{ {
await FlexiAPI.UploadWonwooSet(CbBxFoundCams.Text, false); // false = Colour await FlexiAPI.UploadWonwooSet(CbBxFoundCams.Text, false); // false = Colour
@@ -1543,7 +1556,7 @@ namespace AiQ_GUI
{ {
BtnPrintGB.Enabled = true; 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; Printer.ZebraIP = localDataStore.ZebraIP;
BtnPrintAiQ.Enabled = true; BtnPrintAiQ.Enabled = true;
@@ -1739,8 +1752,18 @@ namespace AiQ_GUI
// PDF.CreateSoakTestReport(NewCam, "SoakTestRig", DateTime.Now, file); // 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(); stopWatchTest.Stop();
AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff"), Level.LOG); 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="ClosedXML" Version="0.105.0" />
<PackageReference Include="Emgu.CV" Version="4.12.0.5764" /> <PackageReference Include="Emgu.CV" Version="4.12.0.5764" />
<PackageReference Include="Emgu.CV.runtime.windows" 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.Auth" Version="1.73.0" />
<PackageReference Include="Google.Apis.Sheets.v4" Version="1.72.0.3966" /> <PackageReference Include="Google.Apis.Sheets.v4" Version="1.72.0.3966" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> <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="PDFsharp-MigraDoc-gdi" Version="6.2.3" />
<PackageReference Include="Selenium.Support" Version="4.38.0" /> <PackageReference Include="Selenium.Support" Version="4.38.0" />
<PackageReference Include="Selenium.WebDriver" Version="4.38.0" /> <PackageReference Include="Selenium.WebDriver" Version="4.38.0" />

View File

@@ -24,7 +24,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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) 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) 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(); string responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode) 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; return responseBody;
} }
catch (TaskCanceledException) catch (TaskCanceledException)
{ {
return $"Timeout uploading to {url}.{Level.ERROR}"; return $"Timeout uploading to {url}.";
} }
catch (HttpRequestException ex) catch (HttpRequestException ex)
{ {
return $"HTTP error uploading to {url}: {ex.Message}{Level.ERROR}"; return $"HTTP error uploading to {url}: {ex.Message}";
} }
catch (Exception ex) 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")) 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; return null;
} }
@@ -174,13 +174,13 @@ namespace AiQ_GUI
// Treat "operation was canceled" as a successful apply // Treat "operation was canceled" as a successful apply
if (response.Contains("The operation was canceled", StringComparison.OrdinalIgnoreCase)) 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; return true;
} }
if (response.Contains("error", StringComparison.OrdinalIgnoreCase)) 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; return false;
} }
@@ -188,7 +188,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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; return false;
} }
} }
@@ -217,7 +217,7 @@ namespace AiQ_GUI
} }
catch catch
{ {
MainForm.Instance.AddToActionsList($"Error reading trim JSON - {Level.ERROR}" + trimData); MainForm.Instance.AddToActionsList($"Error reading trim JSON - " + trimData, Level.ERROR);
return; return;
} }
@@ -226,7 +226,7 @@ namespace AiQ_GUI
{ {
if (RetryCount >= 3) 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; return;
} }
@@ -254,11 +254,11 @@ namespace AiQ_GUI
} }
else // Ask user to centre the plate in the field of view 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) 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; return;
} }
await Task.Delay(5000); // Give 5 second delay for it to see a plate 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); 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\"}},")) 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 // 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")) 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; return false;
} }

View File

@@ -26,7 +26,7 @@ namespace AiQ_GUI
HttpResponseMessage response = await httpClient.GetAsync(requestUrl); HttpResponseMessage response = await httpClient.GetAsync(requestUrl);
if (!response.IsSuccessStatusCode) 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; return null;
} }
@@ -34,7 +34,7 @@ namespace AiQ_GUI
if (imageBytes.Length == 0) // Check if the imageBytes is empty 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; return null;
} }
@@ -43,7 +43,7 @@ namespace AiQ_GUI
CvInvoke.Imdecode(imageBytes, ImreadModes.AnyColor, mat); CvInvoke.Imdecode(imageBytes, ImreadModes.AnyColor, mat);
if (mat.IsEmpty) 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; return null;
} }
@@ -70,12 +70,12 @@ namespace AiQ_GUI
} }
catch (HttpRequestException ex) catch (HttpRequestException ex)
{ {
MainForm.Instance.AddToActionsList($"HTTP error: {ex.Message}{Level.ERROR}"); MainForm.Instance.AddToActionsList($"HTTP error: {ex.Message}", Level.ERROR);
return null; return null;
} }
catch (Exception ex) 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; return null;
} }
} }
@@ -123,7 +123,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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; return;
} }
@@ -146,7 +146,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) catch (Exception ex)
{ {
MainForm.Instance.AddToActionsList($"Error calculating luminance: {ex.Message} {Level.ERROR}"); MainForm.Instance.AddToActionsList($"Error calculating luminance: {ex.Message}",Level.ERROR);
return; return;
} }
} }

View File

@@ -45,7 +45,7 @@
} }
catch (Exception ex) 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") else if (Type == "Audit")
salt = Auditsalt; salt = Auditsalt;
else else
return $"Unrecognised challenge type:{Level.ERROR}" + Type; return $"Unrecognised challenge type:" + Type;
if (string.IsNullOrEmpty(challenge) || challenge.Length != 6) // Check challenge format 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 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 // Hash computation using SHA256 algorithm
byte[] inputBytes = Encoding.UTF8.GetBytes(challenge + " " + salt); // SHA hash format challenge and salt with space between 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) 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) 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; return null;
} }
} }

View File

@@ -32,7 +32,7 @@ namespace AiQ_GUI
} }
catch 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; return null;
@@ -48,31 +48,31 @@ namespace AiQ_GUI
if (Strength < 25.0) 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; PassTest = false;
} }
if (!Router.SimStatus.Contains("SIM Ready")) 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; PassTest = false;
} }
if (!Router.Port3Status.Contains("port:3 link:up speed:100baseT full-duplex")) 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; PassTest = false;
} }
if (!Router.Port4Status.Contains("port:4 link:up speed:100baseT full-duplex")) 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; PassTest = false;
} }
if (!Router.GoodPing) 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; PassTest = false;
} }

View File

@@ -25,7 +25,7 @@ namespace AiQ_GUI
} }
catch (Exception Ex) 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; return null;
@@ -75,7 +75,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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)}")); string LogMssg = string.Join(" | ", typeof(SSHData).GetProperties().Select(p => $"{p.Name}: {p.GetValue(Data)}"));
@@ -170,7 +170,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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); return (string.Empty, string.Empty);
@@ -323,7 +323,7 @@ namespace AiQ_GUI
if (checkDevice.Result.Trim() != "OK") // Device not found 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; return false;
} }
@@ -378,7 +378,7 @@ namespace AiQ_GUI
if (checkDevice.Result.Trim().Length > 1) // Device not found 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; return;
} }
@@ -387,7 +387,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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 else
{ {
return $"Last serial number not found{Level.ERROR}"; return $"Last serial number not found";
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
return $"ERROR: {ex.Message}{Level.ERROR}"; return $"ERROR: {ex.Message}";
} }
} }
@@ -148,12 +148,12 @@ namespace AiQ_GUI
} }
else else
{ {
return $"Serial number not found{Level.ERROR}"; return $"Serial number not found";
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
return $"ERROR: {ex.Message}{Level.ERROR}"; return $"ERROR: {ex.Message}";
} }
} }
@@ -196,7 +196,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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) 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: case 0:
if (!await MainForm.Instance.DisplayQuestion("Is the sleeve aligned correctly?")) 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; break;
case 1: case 1:
if (!await MainForm.Instance.DisplayQuestion("Are all the screws fitted in the front?")) 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; break;
case 2: case 2:
if (!await MainForm.Instance.DisplayQuestion("Are all the screws fitted in the rear?")) 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; break;
case 3: case 3:
if (await MainForm.Instance.DisplayQuestion("Shake unit, does it rattle?")) 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; break;
default: default:
@@ -123,7 +123,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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; return null;
} }
@@ -187,6 +187,7 @@ namespace AiQ_GUI
public bool IsChecked { get; set; } // Is it checked for soak test? public bool IsChecked { get; set; } // Is it checked for soak test?
public CheckBox CheckBox { get; set; } = new CheckBox(); public CheckBox CheckBox { get; set; } = new CheckBox();
public string TestReportLoc { get; set; } = string.Empty; // Location of test report file public string TestReportLoc { get; set; } = string.Empty; // Location of test report file
public string Manfr { get; internal set; }
} }
// Static class for global flags // Static class for global flags

4
LDS.cs
View File

@@ -30,7 +30,7 @@ namespace AiQ_GUI
} }
catch // If file can't deserialise 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 return null; // Return null to indicate failure
} }
} }
@@ -45,7 +45,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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 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 const string connString =
public static string[] ReadCamTypes() @"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); using OleDbConnection conn = new(connString);
// Attempt to open the database
try try
{ {
conn.Open(); conn.Open();
@@ -22,31 +40,36 @@ namespace AiQ_GUI
return null; 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 OleDbCommand cmd = new(query, conn);
using OleDbDataReader reader = cmd.ExecuteReader(); using OleDbDataReader reader = cmd.ExecuteReader();
// Read all models from the selected table
while (reader.Read()) while (reader.Read())
{ {
// Extract each model number and description, using empty string if null
string modelNumber = reader["ModelNumber"] as string ?? string.Empty; string modelNumber = reader["ModelNumber"] as string ?? string.Empty;
string description = reader["Description"] as string ?? string.Empty; string description = reader["Description"] as string ?? string.Empty;
modelTuples.Add(Tuple.Create(modelNumber.Trim(), description.Trim())); modelTuples.Add(Tuple.Create(modelNumber.Trim(), description.Trim()));
} }
// Sort: push "AB12CD" to the bottom, then sort remaining items alphabetically // Sort models, pushing AB12CD to the bottom
IOrderedEnumerable<Tuple<string, string>> sorted = modelTuples.OrderBy(t => t.Item1.Equals("AB12CD", StringComparison.OrdinalIgnoreCase) ? 1 : 0) var sorted = modelTuples
.ThenBy(t => t.Item1, StringComparer.OrdinalIgnoreCase); .OrderBy(t => t.Item1.Equals("AB12CD", StringComparison.OrdinalIgnoreCase) ? 1 : 0)
.ThenBy(t => t.Item1, StringComparer.OrdinalIgnoreCase);
conn.Close(); // Format for combo box display
// Format the sorted tuples as "ModelNumber - Description" strings and return as array
return sorted.Select(t => $"{t.Item1} - {t.Item2}").ToArray(); 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() public static void ReadUniData()
{ {
using OleDbConnection conn = new(connString); using OleDbConnection conn = new(connString);
try try
{ {
conn.Open(); conn.Open();
@@ -57,24 +80,27 @@ namespace AiQ_GUI
return; 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 OleDbCommand cmd = new(query, conn);
using OleDbDataReader reader = cmd.ExecuteReader(); using OleDbDataReader reader = cmd.ExecuteReader();
// UniversalData is expected to contain a single row
reader.Read(); reader.Read();
UniversalData.ExpFlexiVer = Convert.ToString(reader["FlexiVersion"]); UniversalData.ExpFlexiVer = Convert.ToString(reader["FlexiVersion"]);
UniversalData.ExpFlexiRev = Convert.ToString(reader["FlexiRevision"]); UniversalData.ExpFlexiRev = Convert.ToString(reader["FlexiRevision"]);
UniversalData.WonwooFirmware = Convert.ToString(reader["WonwooFirmware"]); UniversalData.WonwooFirmware = Convert.ToString(reader["WonwooFirmware"]);
UniversalData.LatestVersion = Convert.ToString(reader["AiQGUIVersion"]); UniversalData.LatestVersion = Convert.ToString(reader["AiQGUIVersion"]);
UniversalData.PowerConsumption = Convert.ToInt16(reader["PowerConsumption"]); UniversalData.PowerConsumption = Convert.ToInt16(reader["PowerConsumption"]);
UniversalData.LicencingServerURL = Convert.ToString(reader["LicencingServerURL"]); UniversalData.LicencingServerURL = Convert.ToString(reader["LicencingServerURL"]);
conn.Close();
} }
// Populates CameraAccessInfo dynamically based on available columns
// Knowing the model number on test, this function reads the database and populates the Camera class with the values. public static void ReadModelRow(string camType, string ModelOnTest)
public static void ReadModelRow(string ModelOnTest)
{ {
using OleDbConnection conn = new(connString); using OleDbConnection conn = new(connString);
try try
{ {
conn.Open(); conn.Open();
@@ -85,66 +111,85 @@ namespace AiQ_GUI
return; 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); using OleDbCommand cmd = new(query, conn);
cmd.Parameters.AddWithValue("?", ModelOnTest);
using OleDbDataReader reader = cmd.ExecuteReader(); using OleDbDataReader reader = cmd.ExecuteReader();
reader.Read();
// Populate the CameraAccessInfo class with the values from the database // No matching model found
CameraAccessInfo.Processor = Convert.ToString(reader["Processor"]); if (!reader.Read())
CameraAccessInfo.VaxtorLic = Convert.ToBoolean(reader["Vaxtor"]); return;
CameraAccessInfo.HardwareExtras = Convert.ToString(reader["HardwareExtras"]);
CameraAccessInfo.PowerType = Convert.ToString(reader["PowerType"]); // Populate CameraAccessInfo only if columns exist
CameraAccessInfo.LED_V = Convert.ToDouble(reader["LEDVoltage"]); CameraAccessInfo.Processor =
CameraAccessInfo.LED_I = Convert.ToInt32(reader["LEDCurrent"]); HasColumn(reader, "Processor") ? Convert.ToString(reader["Processor"]) : string.Empty;
CameraAccessInfo.SpreadsheetID = Convert.ToString(reader["SSID"]);
conn.Close(); 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) public static void Stats(string TypeOfTest, string modelNumber)
{ {
Stats([TypeOfTest], modelNumber); Stats([TypeOfTest], modelNumber);
} }
public static void Stats(string[] TypeOfTest, string 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 try
{ {
conn.Open(); // Opens DB conn.Open();
foreach (string type in TypeOfTest) 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 string query =
using OleDbCommand cmd = new(query, conn); // Create command $"UPDATE AiQ SET [{type}] = [{type}] + 1 WHERE [ModelNumber] = ?";
cmd.Parameters.AddWithValue("?", modelNumber); // Add model number to prevent injection
using OleDbCommand cmd = new(query, conn);
cmd.Parameters.AddWithValue("?", modelNumber);
int rowsAffected = cmd.ExecuteNonQuery(); int rowsAffected = cmd.ExecuteNonQuery();
// Execute the command and get the number of rows affected
if (rowsAffected == 0) // If one or more rows were updated if (rowsAffected == 0)
MainForm.Instance.AddToActionsList($"No rows affected for {modelNumber}{Level.ERROR}"); MainForm.Instance.AddToActionsList($"No rows affected for {modelNumber}", Level.ERROR);
} }
conn.Close();
} }
catch catch
{ {
MainForm.Instance.AddToActionsList($"Could not access Access in Google Drive. Is it running?{Level.WARNING}"); MainForm.Instance.AddToActionsList(
return; "Could not access Access in Google Drive. Is it running?", Level.WARNING);
} }
} }
public static void StatsDiags(string redDiagLabels, string RhTxBxActionsText, string ModelNumber) public static void StatsDiags(string redDiagLabels, string RhTxBxActionsText, string ModelNumber)
{ {
using OleDbConnection conn = new(connString); using OleDbConnection conn = new(connString);
conn.Open(); conn.Open();
// Null checks // Replace null or empty values
string redVal = string.IsNullOrWhiteSpace(redDiagLabels) ? "-" : redDiagLabels; string redVal = string.IsNullOrWhiteSpace(redDiagLabels) ? "-" : redDiagLabels;
string actVal = string.IsNullOrWhiteSpace(RhTxBxActionsText) ? "-" : RhTxBxActionsText; string actVal = string.IsNullOrWhiteSpace(RhTxBxActionsText) ? "-" : RhTxBxActionsText;
string model = string.IsNullOrWhiteSpace(ModelNumber) ? "-" : ModelNumber; 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); using OleDbCommand cmd = new(sql, conn);
@@ -153,19 +198,18 @@ namespace AiQ_GUI
OleDbType = OleDbType.Date, OleDbType = OleDbType.Date,
Value = DateTime.Now Value = DateTime.Now
}); });
cmd.Parameters.AddWithValue("?", model); cmd.Parameters.AddWithValue("?", model);
cmd.Parameters.AddWithValue("?", redVal); cmd.Parameters.AddWithValue("?", redVal);
cmd.Parameters.AddWithValue("?", actVal); cmd.Parameters.AddWithValue("?", actVal);
int rows = cmd.ExecuteNonQuery(); int rows = cmd.ExecuteNonQuery();
conn.Close();
if (rows == 0) 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 class UniversalData
{ {
public static string ExpFlexiVer { get; set; } = string.Empty; public static string ExpFlexiVer { get; set; } = string.Empty;
@@ -176,7 +220,6 @@ namespace AiQ_GUI
public static string LicencingServerURL { get; set; } = string.Empty; 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 class CameraAccessInfo
{ {
public static string Processor { get; set; } = string.Empty; public static string Processor { get; set; } = string.Empty;

View File

@@ -17,7 +17,7 @@ namespace AiQ_GUI
} }
else 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 else
{ {
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
} }
return null; return null;
} }
@@ -40,7 +40,7 @@ namespace AiQ_GUI
{ {
if (!File.Exists(FilePath)) if (!File.Exists(FilePath))
{ {
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
return -1; return -1;
} }
@@ -62,7 +62,7 @@ namespace AiQ_GUI
{ {
if (!File.Exists(FilePath)) 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"; return "Spreadsheet not found";
} }
@@ -114,7 +114,7 @@ namespace AiQ_GUI
{ {
if (!File.Exists(filePath)) 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"; return "Spreadsheet not found";
} }
@@ -161,7 +161,7 @@ namespace AiQ_GUI
{ {
if (!File.Exists(FilePath)) 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"; return "Spreadsheet not found";
} }
@@ -213,7 +213,7 @@ namespace AiQ_GUI
{ {
if (!File.Exists(FilePath)) 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}" return $"Spreadsheet not found{ Level.ERROR}"
; ;
} }
@@ -251,7 +251,7 @@ namespace AiQ_GUI
{ {
if (!File.Exists(filePath)) 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; return 0;
} }
@@ -293,7 +293,7 @@ namespace AiQ_GUI
{ {
if (!File.Exists(filePath)) if (!File.Exists(filePath))
{ {
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( {Level.ERROR}"); MainForm.Instance.AddToActionsList($"Could not find spreadsheet :( ", Level.ERROR);
return 0; return 0;
} }
@@ -329,7 +329,7 @@ namespace AiQ_GUI
{ {
if (!File.Exists(filePath)) if (!File.Exists(filePath))
{ {
MainForm.Instance.AddToActionsList($"Could not find spreadsheet :({Level.ERROR}"); MainForm.Instance.AddToActionsList($"Could not find spreadsheet :(", Level.ERROR);
return -1; return -1;
} }
@@ -344,7 +344,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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; return -1;
} }
} }

View File

@@ -40,7 +40,7 @@ namespace AiQ_GUI
const string selectColumns = @" const string selectColumns = @"
ModelNumber, ModelNumber,
[Total Tests Run], [Total Tests Run],
[Pre Tests Passed], [Pre Tests Passed]
[Pre Tests Failed], [Pre Tests Failed],
[Final Tests Passed], [Final Tests Passed],
[Final Tests Failed], [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.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
@@ -71,15 +73,15 @@ namespace AiQ_GUI
} }
catch (TaskCanceledException ex) catch (TaskCanceledException ex)
{ {
return $"HTTP error calling {url}: {ex.Message}{Level.ERROR}"; return $"HTTP error calling {url}: {ex.Message}";
} }
catch (HttpRequestException ex) catch (HttpRequestException ex)
{ {
return $"HTTP error calling {url}: {ex.Message}{Level.ERROR}"; return $"HTTP error calling {url}: {ex.Message}";
} }
catch (Exception ex) 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 sendPort = 6666;
const int receivePort = 6667; const int receivePort = 6667;
const int discoveryTimeoutMs = 1000; 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) async Task SendAndListen(IPAddress localIp)
{ {
@@ -101,41 +109,42 @@ namespace AiQ_GUI
sender.Send(discoveryPacket, discoveryPacket.Length); 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; receiver.Client.ReceiveTimeout = discoveryTimeoutMs;
DateTime timeout = DateTime.Now.AddMilliseconds(discoveryTimeoutMs); DateTime timeout = DateTime.Now.AddMilliseconds(discoveryTimeoutMs);
try
while (DateTime.Now < timeout)
{ {
while (DateTime.Now < timeout) if (receiver.Available > 0)
{ {
if (receiver.Available > 0) UdpReceiveResult result = await receiver.ReceiveAsync();
byte[] recvBuffer = result.Buffer;
if (recvBuffer.Length >= 52)
{ {
UdpReceiveResult result = await receiver.ReceiveAsync(); byte[] ipBytes = recvBuffer
byte[] recvBuffer = result.Buffer; .Skip(recvBuffer.Length - 52)
.Take(4)
.Reverse()
.ToArray();
if (recvBuffer.Length >= 52) // Safety check string ip = string.Join(".", ipBytes);
{
byte[] ipBytes = recvBuffer.Skip(recvBuffer.Length - 52).Take(4).Reverse().ToArray();
string ipToAdd = string.Join(".", ipBytes);
if (!FoundCams.Contains(ipToAdd)) if (discoveredIPs.Add(ip))
FoundCams.Add(ipToAdd); FoundCams.Add(ip);
}
} }
await Task.Delay(50); // brief wait to allow data in
} }
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) await Task.Delay(50);
{
// No data received in time — normal case
} }
} }
// Get first IPv4 interface (non-loopback)
foreach (IPAddress ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList) foreach (IPAddress ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
{ {
if (ip.AddressFamily != AddressFamily.InterNetwork)
continue;
try try
{ {
await SendAndListen(ip); await SendAndListen(ip);
@@ -143,9 +152,31 @@ namespace AiQ_GUI
catch { } 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; return FoundCams;
} }
// Ping to make sure devices are connected to the network, be aware it isn't consistant across subnets. // 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) public async static Task<bool> PingIP(string ipAddress)
{ {

View File

@@ -19,7 +19,7 @@ namespace AiQ_GUI
} }
catch (WebDriverTimeoutException) 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(); 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) 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); 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) 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; throw;
} }
} }

View File

@@ -37,7 +37,7 @@ namespace AiQ_GUI
catch (Exception ex) catch (Exception ex)
{ {
SoakError($"Initial connection failed: {ex.Message}", SoakLogFile, CamInfo.CheckBox); 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 // Wait 10 seconds before trying again
await Task.Delay(TimeSpan.FromSeconds(10), token); await Task.Delay(TimeSpan.FromSeconds(10), token);
@@ -52,7 +52,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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; return;
} }
@@ -84,7 +84,7 @@ namespace AiQ_GUI
// Retry ping until camera responds or cancelled // Retry ping until camera responds or cancelled
while (!await Network.PingIP(CamInfo.IP) && !token.IsCancellationRequested) 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 await Task.Delay(TimeSpan.FromMinutes(1), token); // Retry after delay of 1 minute
} }
@@ -98,7 +98,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) 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) 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; lastHour = currentHour;
} }
@@ -131,7 +131,7 @@ namespace AiQ_GUI
} }
catch (Exception ex) catch (Exception ex)
{ {
SoakError($"ChangeRandomDropdown failed: {Level.WARNING} {ex.Message}", SoakLogFile, CamInfo.CheckBox); SoakError($"ChangeRandomDropdown failed: {ex.Message}", SoakLogFile, CamInfo.CheckBox);
} }
try try
@@ -200,7 +200,7 @@ namespace AiQ_GUI
File.Delete(SoakTestPath); File.Delete(SoakTestPath);
} }
else 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) catch (Exception ex)
{ {
@@ -240,7 +240,7 @@ namespace AiQ_GUI
if (ImageDark == null) if (ImageDark == null)
{ {
Logging.LogWarningMessage($"Dark image is null for {controlType} at setting {SettingMinMax[1]}", SoakLogFile); 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; return;
} }