Compare commits

...

13 Commits

22 changed files with 619 additions and 626 deletions

43
AiQ_GUI.Designer.cs generated
View File

@@ -144,6 +144,8 @@ namespace AiQ_GUI
BtnZoom8000 = new Button(); BtnZoom8000 = new Button();
BtnZoomWide = new Button(); BtnZoomWide = new Button();
TabSettings = new TabPage(); TabSettings = new TabPage();
UploadWonwooSetIR = new Button();
UploadWonwooSetOV = new Button();
BtnAdminStart = new Button(); BtnAdminStart = new Button();
BtnFirewall = new Button(); BtnFirewall = new Button();
TabImages = new TabPage(); TabImages = new TabPage();
@@ -273,6 +275,7 @@ namespace AiQ_GUI
CbBxUserName.Items.AddRange(new object[] { "Guest", "Clive", "Conor", "Charlie", "Henry", "Sam C", "Sam L", "Simon", "Sophie", "Toby", "Tom" }); CbBxUserName.Items.AddRange(new object[] { "Guest", "Clive", "Conor", "Charlie", "Henry", "Sam C", "Sam L", "Simon", "Sophie", "Toby", "Tom" });
CbBxUserName.Location = new Point(10, 262); CbBxUserName.Location = new Point(10, 262);
CbBxUserName.Margin = new Padding(4, 3, 4, 3); CbBxUserName.Margin = new Padding(4, 3, 4, 3);
CbBxUserName.MaxDropDownItems = 20;
CbBxUserName.Name = "CbBxUserName"; CbBxUserName.Name = "CbBxUserName";
CbBxUserName.Size = new Size(233, 25); CbBxUserName.Size = new Size(233, 25);
CbBxUserName.TabIndex = 159; CbBxUserName.TabIndex = 159;
@@ -1145,7 +1148,7 @@ namespace AiQ_GUI
TxBxFlexiVer.Name = "TxBxFlexiVer"; TxBxFlexiVer.Name = "TxBxFlexiVer";
TxBxFlexiVer.Size = new Size(205, 25); TxBxFlexiVer.Size = new Size(205, 25);
TxBxFlexiVer.TabIndex = 162; TxBxFlexiVer.TabIndex = 162;
TxBxFlexiVer.Text = "1.6.4"; TxBxFlexiVer.Text = "1.7.1";
TxBxFlexiVer.TextAlign = HorizontalAlignment.Center; TxBxFlexiVer.TextAlign = HorizontalAlignment.Center;
TxBxFlexiVer.TextChanged += TxBxFlexiVer_TextChanged; TxBxFlexiVer.TextChanged += TxBxFlexiVer_TextChanged;
// //
@@ -1835,6 +1838,8 @@ namespace AiQ_GUI
// TabSettings // TabSettings
// //
TabSettings.BackColor = Color.FromArgb(39, 37, 55); TabSettings.BackColor = Color.FromArgb(39, 37, 55);
TabSettings.Controls.Add(UploadWonwooSetIR);
TabSettings.Controls.Add(UploadWonwooSetOV);
TabSettings.Controls.Add(BtnAdminStart); TabSettings.Controls.Add(BtnAdminStart);
TabSettings.Controls.Add(BtnFirewall); TabSettings.Controls.Add(BtnFirewall);
TabSettings.Controls.Add(PanelSettings); TabSettings.Controls.Add(PanelSettings);
@@ -1845,6 +1850,40 @@ namespace AiQ_GUI
TabSettings.TabIndex = 3; TabSettings.TabIndex = 3;
TabSettings.Text = "Settings"; TabSettings.Text = "Settings";
// //
// UploadWonwooSetIR
//
UploadWonwooSetIR.BackColor = Color.FromArgb(70, 65, 80);
UploadWonwooSetIR.FlatAppearance.BorderColor = Color.FromArgb(70, 65, 80);
UploadWonwooSetIR.FlatAppearance.BorderSize = 0;
UploadWonwooSetIR.FlatStyle = FlatStyle.Flat;
UploadWonwooSetIR.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
UploadWonwooSetIR.ForeColor = SystemColors.Control;
UploadWonwooSetIR.Location = new Point(210, 304);
UploadWonwooSetIR.Margin = new Padding(4, 3, 4, 3);
UploadWonwooSetIR.Name = "UploadWonwooSetIR";
UploadWonwooSetIR.Size = new Size(180, 49);
UploadWonwooSetIR.TabIndex = 244;
UploadWonwooSetIR.Text = "Upload Wonwoo Settings IR";
UploadWonwooSetIR.UseVisualStyleBackColor = false;
UploadWonwooSetIR.Click += UploadWonwooSetIR_Click;
//
// UploadWonwooSetOV
//
UploadWonwooSetOV.BackColor = Color.FromArgb(70, 65, 80);
UploadWonwooSetOV.FlatAppearance.BorderColor = Color.FromArgb(70, 65, 80);
UploadWonwooSetOV.FlatAppearance.BorderSize = 0;
UploadWonwooSetOV.FlatStyle = FlatStyle.Flat;
UploadWonwooSetOV.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
UploadWonwooSetOV.ForeColor = SystemColors.Control;
UploadWonwooSetOV.Location = new Point(19, 304);
UploadWonwooSetOV.Margin = new Padding(4, 3, 4, 3);
UploadWonwooSetOV.Name = "UploadWonwooSetOV";
UploadWonwooSetOV.Size = new Size(181, 49);
UploadWonwooSetOV.TabIndex = 243;
UploadWonwooSetOV.Text = "Upload Wonwoo Settings OV";
UploadWonwooSetOV.UseVisualStyleBackColor = false;
UploadWonwooSetOV.Click += UploadWonwooSetOV_Click;
//
// BtnAdminStart // BtnAdminStart
// //
BtnAdminStart.BackColor = Color.FromArgb(70, 65, 80); BtnAdminStart.BackColor = Color.FromArgb(70, 65, 80);
@@ -2210,5 +2249,7 @@ namespace AiQ_GUI
private Button BtnAdminStart; private Button BtnAdminStart;
private Button SetGodModeAll; private Button SetGodModeAll;
private Button BtnFactoryDefault; private Button BtnFactoryDefault;
private Button UploadWonwooSetOV;
private Button UploadWonwooSetIR;
} }
} }

View File

@@ -1,9 +1,6 @@
using AiQ_GUI.Microsoft; using Newtonsoft.Json;
using Newtonsoft.Json;
using System.ComponentModel; using System.ComponentModel;
using System.Data.OleDb;
using System.Diagnostics; using System.Diagnostics;
using System.Numerics;
using System.Reflection; using System.Reflection;
namespace AiQ_GUI namespace AiQ_GUI
@@ -21,6 +18,8 @@ namespace AiQ_GUI
private List<Camera> soakCameraList = []; private List<Camera> soakCameraList = [];
private List<CancellationTokenSource> soakCtsList = []; private List<CancellationTokenSource> soakCtsList = [];
private List<Task> soakTasks = []; private List<Task> soakTasks = [];
const string GoogleDrivePath = @"G:\Shared drives\MAV Production GUI's\AiQ\GUI's\";
// Colours // Colours
public static readonly Color BtnColour = Color.FromArgb(70, 65, 80); public static readonly Color BtnColour = Color.FromArgb(70, 65, 80);
@@ -28,8 +27,6 @@ namespace AiQ_GUI
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public static MainForm? Instance { get; private set; } public static MainForm? Instance { get; private set; }
// For Access Stats
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;";
public MainForm() public MainForm()
{ {
@@ -42,7 +39,7 @@ namespace AiQ_GUI
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
Task? closeProcessesTask = Windows.CloseProcesses(); // Fire and forget closing other apps Task? closeProcessesTask = Windows.CloseProcesses(); // Fire and forget closing other apps
//Windows.UpdateFirewall(); Windows.UpdateFirewall();
Task UniDataTask = Task.Run(() => Access.ReadUniData()); // Get universal data Task UniDataTask = Task.Run(() => Access.ReadUniData()); // Get universal data
Task<LocalDataStore> LDSWAIT = Task.Run(() => LDS.GetLDS()); // Get and deserialise LDS.json Task<LocalDataStore> LDSWAIT = Task.Run(() => LDS.GetLDS()); // Get and deserialise LDS.json
Task<string> guiVerTask = Task.Run(() => GUIUpdate.FindGUIVersion()); // Get GUI Version Task<string> guiVerTask = Task.Run(() => GUIUpdate.FindGUIVersion()); // Get GUI Version
@@ -143,16 +140,13 @@ namespace AiQ_GUI
AddToActionsList($"LED level could not be set: {LEDreply}"); AddToActionsList($"LED level could not be set: {LEDreply}");
} }
else if (!await TestTube.CheckInTestTube(CamOnTest.IP)) // Sets LED's to medium power after checking it is in the test tube else if (!await TestTube.CheckInTestTube(CamOnTest.IP)) // Sets LED's to medium power after checking it is in the test tube
{
await TestFailed(BtnStartTest, "Camera not in test tube"); await TestFailed(BtnStartTest, "Camera not in test tube");
}
Task VisCheck = Helper.VisualCheck(BtnStartTest); Task VisCheck = Helper.VisualCheck(BtnStartTest);
if (!await FlexiAPI.ZoomModules("1F40", CamOnTest.IP)) // Zoom to 8000 (1F40h) at the same time. if (!await FlexiAPI.ZoomModules("1F40", CamOnTest.IP)) // Zoom to 8000 (1F40h) at the same time.
await TestFailed(BtnStartTest, "Could not zoom modules to 8000"); await TestFailed(BtnStartTest, "Could not zoom modules to 8000");
if (!await FlexiAPI.SetZoomLockOn(CamOnTest.IP)) if (!await FlexiAPI.SetZoomLockOn(CamOnTest.IP))
Helper.RestartApp(); Helper.RestartApp();
@@ -194,9 +188,7 @@ namespace AiQ_GUI
await FlexiAPI.SetTrim(CamOnTest.IP, LblTestTubePing.Text); // Auto trims the cameras, some plates should have been captured in the meantime await FlexiAPI.SetTrim(CamOnTest.IP, LblTestTubePing.Text); // Auto trims the cameras, some plates should have been captured in the meantime
if (!await FlexiAPI.ZoomModules("0000", CamOnTest.IP)) // Zoom to full wide if (!await FlexiAPI.ZoomModules("0000", CamOnTest.IP)) // Zoom to full wide
{
await TestFailed(BtnStartTest, "Could not zoom modules to full wide"); await TestFailed(BtnStartTest, "Could not zoom modules to full wide");
}
await Task.Delay(1000); // Wait to be sure cameras are zoomed out. await Task.Delay(1000); // Wait to be sure cameras are zoomed out.
@@ -229,76 +221,11 @@ namespace AiQ_GUI
// If there are any actions identified then fail the test. // If there are any actions identified then fail the test.
// If any labels are red then fail. Only labels in panel so can foreach on labels not controls // If any labels are red then fail. Only labels in panel so can foreach on labels not controls
if (RhTxBxActions.Text.Length > 2 || PnlLbls.Controls.OfType<Label>().Any(c => c.ForeColor == Color.Red) == true) if (RhTxBxActions.Text.Length > 2 || PnlLbls.Controls.OfType<Label>().Any(c => c.ForeColor == Color.Red) == true)
{
await TestFailed(BtnStartTest, "Diagnostic Failure");// If approved then pass otherwise GUI would have restarted before getting to TestPassed. await TestFailed(BtnStartTest, "Diagnostic Failure");// If approved then pass otherwise GUI would have restarted before getting to TestPassed.
}
await TestPassed(PCTime); await TestPassed(PCTime);
} }
public void Stats(string TypeOfTest)
{
Stats([TypeOfTest]);
}
public void Stats(string[] TypeOfTest)
{
using OleDbConnection conn = new(connString); // Opens connection to Access database
try
{
conn.Open(); // Opens DB
string modelNumber = CbBxCameraType.Text.Substring(0, 6); // Get model number from combobox and make sure it is only 6 characters.
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
int rowsAffected = cmd.ExecuteNonQuery();
// Execute the command and get the number of rows affected
//if (rowsAffected > 0) // If one or more rows were updated
// AddToActionsList($"Updated {TypeOfTest} for {modelNumber}");
//else
// AddToActionsList($"No rows found for {modelNumber}");
}
}
catch
{
AddToActionsList("Could not access Access in Google Drive. Is it running?");
return;
}
}
public void StatsDiags(string redDiagLabels, string RhTxBxActionsText, string IsRMA, int RMA)
{
using OleDbConnection conn = new(connString);
conn.Open();
// Null checks
string redVal = string.IsNullOrWhiteSpace(redDiagLabels) ? "-" : redDiagLabels;
string actVal = string.IsNullOrWhiteSpace(RhTxBxActionsText) ? "-" : RhTxBxActionsText;
string model = string.IsNullOrWhiteSpace(CamOnTest?.Model) ? "-" : CamOnTest.Model;
string sql = @"
INSERT INTO DiagsStats ([Date], [Model], [Red Diags Labels], [RhTxBxActions Contents],[IsRMA],[RMA])
VALUES (?, ?, ?, ?, ?, ?)";
using OleDbCommand cmd = new(sql, conn);
// OleDb uses positional parameters — order must match the VALUES list above
cmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = DateTime.Now });
cmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.VarWChar, Value = model });
cmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.VarWChar, Value = redVal });
cmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.VarWChar, Value = actVal });
cmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Boolean, Value = IsRMA });
cmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.VarWChar, Value = RMA });
cmd.ExecuteNonQuery();
}
private async void BtnPreTest_Click(object sender, EventArgs e) private async void BtnPreTest_Click(object sender, EventArgs e)
{ {
// Show user test has started // Show user test has started
@@ -346,6 +273,8 @@ namespace AiQ_GUI
await AllocateSerial(); await AllocateSerial();
else if (GoogleAPI.UpdateSpreadSheetRePreTest(CameraAccessInfo.SpreadsheetID, Vers) != "OK") // If rerun might be different values so update SS else if (GoogleAPI.UpdateSpreadSheetRePreTest(CameraAccessInfo.SpreadsheetID, Vers) != "OK") // If rerun might be different values so update SS
AddToActionsList("Failed to write to spreadsheet, please check manually"); AddToActionsList("Failed to write to spreadsheet, please check manually");
// else if (Excel.UpdateSpreadSheetPreTest(CameraAccessInfo.SpreadsheetID, Vers, CamOnTest.GetCamDesc(), CamOnTest.Model) != "OK")
// AddToActionsList("Failed to write to spreadsheet, please check manually");
} }
else // No serial or model so allocate one else // No serial or model so allocate one
await AllocateSerial(); await AllocateSerial();
@@ -377,7 +306,7 @@ namespace AiQ_GUI
{ {
// Turn off God mode // Turn off God mode
string[,] GOD_JSON = { { "propGodMode", "false" } }; string[,] GOD_JSON = { { "propGodMode", "false" } };
string IntConf = await FlexiAPI.HTTP_Update("Internal Config", CamOnTest.IP, GOD_JSON); string IntConf = await FlexiAPI.HTTP_Update("GLOBAL--FlexiApplication", CamOnTest.IP, GOD_JSON);
if (!IntConf.Contains("\"propGodMode\": {\"value\": \"false\", \"datatype\": \"boolean\"},")) if (!IntConf.Contains("\"propGodMode\": {\"value\": \"false\", \"datatype\": \"boolean\"},"))
AddToActionsList("Could not turn off God mode"); AddToActionsList("Could not turn off God mode");
@@ -409,11 +338,11 @@ namespace AiQ_GUI
Logging.LogMessage("Final Test Passed"); Logging.LogMessage("Final Test Passed");
if (CamOnTest.RMANum == 0) // Yap to check if it is not a RMA if (CamOnTest.RMANum == 0) // Yap to check if it is not a RMA
{ {
Stats("Final Tests Passed"); Access.Stats("Final Tests Passed", CamOnTest.Model);
} }
else else
{ {
Stats("RMA Final Tests Passed"); Access.Stats("RMA Final Tests Passed", CamOnTest.Model);
} }
} }
@@ -424,25 +353,23 @@ namespace AiQ_GUI
Btn.BackColor = Color.Maroon; Btn.BackColor = Color.Maroon;
Btn.Text = "Test Failed"; Btn.Text = "Test Failed";
if (!(CamOnTest.RMANum != 0)) // Yap to check if it is not a RMA
{
Access.Stats(["Final Tests Failed", ErrMssg], CamOnTest.Model);
}
else
{
Access.Stats("RMA Final Tests Failed", CamOnTest.Model);
}
AddToActionsList(ErrMssg);
string RedLbls = string.Join(Environment.NewLine, PnlLbls.Controls string RedLbls = string.Join(Environment.NewLine, PnlLbls.Controls
.OfType<Label>() .OfType<Label>()
.Where(lbl => lbl.ForeColor == Color.Red) // Only include red labels .Where(lbl => lbl.ForeColor == Color.Red) // Only include red labels
.Select(lbl => lbl.Text)); // Extract text .Select(lbl => lbl.Text)); // Extract text
string FullFailureValues = RhTxBxActions.Text + Environment.NewLine + RedLbls; string FullFailureValues = RhTxBxActions.Text + Environment.NewLine + RedLbls;
Access.StatsDiags(RedLbls, RhTxBxActions.Text, CamOnTest.Model); // Log to Access database
// Database logging \\
if (!(CamOnTest.RMANum != 0)) // Yap to check if it is not a RMA
{
Stats(["Final Tests Failed", ErrMssg]);
StatsDiags(RedLbls, RhTxBxActions.Text, "FALSE", CamOnTest.RMANum);
}
else
{
Stats("RMA Final Tests Failed");
StatsDiags(RedLbls, RhTxBxActions.Text, "TRUE", CamOnTest.RMANum);
}
if (await DisplayQuestion("Test failed, appeal?" + Environment.NewLine + "See Actions textbox for details.")) if (await DisplayQuestion("Test failed, appeal?" + Environment.NewLine + "See Actions textbox for details."))
{ {
@@ -460,8 +387,6 @@ namespace AiQ_GUI
// Joins the actions box to any red labels to use as a full failed text // Joins the actions box to any red labels to use as a full failed text
Logging.LogErrorMessage(FullFailureValues); Logging.LogErrorMessage(FullFailureValues);
IList<IList<object>> values = GoogleAPI.service.Spreadsheets.Values.Get(GoogleAPI.spreadsheetId_ModelInfo, "'Approval'!A1:A").Execute().Values; IList<IList<object>> values = GoogleAPI.service.Spreadsheets.Values.Get(GoogleAPI.spreadsheetId_ModelInfo, "'Approval'!A1:A").Execute().Values;
if (values?.Count > 0) if (values?.Count > 0)
@@ -474,15 +399,14 @@ namespace AiQ_GUI
List<object> oblistCD = [CamOnTest.Model, FullFailureValues]; List<object> oblistCD = [CamOnTest.Model, FullFailureValues];
GoogleAPI.WriteToSS(oblistCD, "'Approval'!C" + nextRow + ":D" + nextRow, GoogleAPI.spreadsheetId_ModelInfo); GoogleAPI.WriteToSS(oblistCD, "'Approval'!C" + nextRow + ":D" + nextRow, GoogleAPI.spreadsheetId_ModelInfo);
//await Teams.SendMssg(Convert.ToString(nextRow), CbBxUserName.Text); await Teams.SendMssg(Convert.ToString(nextRow), CbBxUserName.Text);
GoogleAPI.EmailApproval(Convert.ToString(nextRow), CbBxUserName.Text); //GoogleAPI.EmailApproval(Convert.ToString(nextRow), CbBxUserName.Text);
string Approved = ""; string Approved = "";
while (Approved != "TRUE") while (Approved != "TRUE")
{ {
await Task.Delay(1000); await Task.Delay(1000);
values = GoogleAPI.service.Spreadsheets.Values.Get(GoogleAPI.spreadsheetId_ModelInfo, "'Approval'!B" + nextRow).Execute().Values; values = GoogleAPI.service.Spreadsheets.Values.Get(GoogleAPI.spreadsheetId_ModelInfo, "'Approval'!B" + nextRow).Execute().Values;
if (values?.Count > 0) if (values?.Count > 0)
@@ -514,14 +438,11 @@ namespace AiQ_GUI
PnlQuestion.Visible = true; PnlQuestion.Visible = true;
BtnNo.Visible = false; BtnNo.Visible = false;
Logging.LogMessage("Pre Test Passed"); Logging.LogMessage("Pre Test Passed");
if (CamOnTest.RMANum == 0) // Yap to check if it is not a RMA if (CamOnTest.RMANum == 0) // Yap to check if it is not a RMA
{ Access.Stats("Pre Tests Passed", CamOnTest.Model);
Stats("Pre Tests Passed");
}
else else
{ Access.Stats("RMA Pre Tests Passed", CamOnTest.Model);
Stats("RMA Pre Tests Passed");
}
if (await DisplayQuestion("Test passed, restart?")) if (await DisplayQuestion("Test passed, restart?"))
Helper.RestartApp(); Helper.RestartApp();
@@ -535,24 +456,18 @@ namespace AiQ_GUI
BtnNo.Visible = false; BtnNo.Visible = false;
Logging.LogMessage("Pre Test Failed"); Logging.LogMessage("Pre Test Failed");
if (CamOnTest.RMANum == 0) // Yap to check if it is not a RMA
Access.Stats(["Pre Tests Failed", ErrMssg], CamOnTest.Model);
else
Access.Stats("RMA Pre Tests Failed", CamOnTest.Model);
string RedLbls = string.Join(Environment.NewLine, PnlLbls.Controls string RedLbls = string.Join(Environment.NewLine, PnlLbls.Controls
.OfType<Label>() .OfType<Label>()
.Where(lbl => lbl.ForeColor == Color.Red) // Only include red labels .Where(lbl => lbl.ForeColor == Color.Red) // Only include red labels
.Select(lbl => lbl.Text)); // Extract text .Select(lbl => lbl.Text)); // Extract text
string FullFailureValues = RhTxBxActions.Text + Environment.NewLine + RedLbls; string FullFailureValues = RhTxBxActions.Text + Environment.NewLine + RedLbls;
if (CamOnTest.RMANum == 0) // Yap to check if it is not a RMA Access.StatsDiags(RedLbls, RhTxBxActions.Text, CamOnTest.Model); // Log to Access database
{
Stats(["Pre Tests Failed", ErrMssg]);
StatsDiags(RedLbls, RhTxBxActions.Text, "FALSE", CamOnTest.RMANum);
}
else
{
Stats("RMA Pre Tests Failed");
StatsDiags(RedLbls, RhTxBxActions.Text, "TRUE", CamOnTest.RMANum);
}
// Log to Access database
if (await DisplayQuestion("Test failed, restart?" + Environment.NewLine + "See Actions textbox for details.")) if (await DisplayQuestion("Test failed, restart?" + Environment.NewLine + "See Actions textbox for details."))
Helper.RestartApp(); Helper.RestartApp();
@@ -674,9 +589,7 @@ namespace AiQ_GUI
CamOnTest.RMANum = Convert.ToInt32(await DisplayInput("What is the RMA number?")); CamOnTest.RMANum = Convert.ToInt32(await DisplayInput("What is the RMA number?"));
if (CamOnTest.RMANum == -1) // Means they chose the 'I don't know' option if (CamOnTest.RMANum == -1) // Means they chose the 'I don't know' option
{
await TestFailed(BtnStartTest, "Please get RMA number from operations team before continuing"); await TestFailed(BtnStartTest, "Please get RMA number from operations team before continuing");
}
} }
// Found RMA num and want to verify it with user // Found RMA num and want to verify it with user
else if (!await DisplayQuestion($"Is {CamOnTest.RMANum} the RMA Number?")) // '!' because if its not the right RMA number let the user to it manually else if (!await DisplayQuestion($"Is {CamOnTest.RMANum} the RMA Number?")) // '!' because if its not the right RMA number let the user to it manually
@@ -752,7 +665,7 @@ namespace AiQ_GUI
if (RegexCache.VaxtorRegex().IsMatch(ProdcutKeyID)) // Means they chose the 'I don't know' option or isn't valid Key ID if (RegexCache.VaxtorRegex().IsMatch(ProdcutKeyID)) // Means they chose the 'I don't know' option or isn't valid Key ID
{ {
Stats("Please Get A Valid Vaxtor Product Key Before Continuing"); Access.Stats("Please Get A Valid Vaxtor Product Key Before Continuing", CamOnTest.Model);
await TestFailed(BtnStartTest, "Please get a valid Vaxtor Product Key before continuing"); await TestFailed(BtnStartTest, "Please get a valid Vaxtor Product Key before continuing");
} }
@@ -771,8 +684,8 @@ namespace AiQ_GUI
lblTrim.Text += "H: " + DiagsAPI.trim[0] + " V: " + DiagsAPI.trim[1]; lblTrim.Text += "H: " + DiagsAPI.trim[0] + " V: " + DiagsAPI.trim[1];
// Offset accounted for in the SetTrim function, so value should be close to 0,0. // Offset accounted for in the SetTrim function, so value should be close to 0,0.
int HMax = 96; // 5% of 1920 each way = ±96 const int HMax = 96; // 5% of 1920 each way = ±96
int VMax = 54; // 5% of 1080 each way = ±54 const int VMax = 54; // 5% of 1080 each way = ±54
if (Math.Abs(DiagsAPI.trim[0]) <= HMax && Math.Abs(DiagsAPI.trim[1]) <= VMax) if (Math.Abs(DiagsAPI.trim[0]) <= HMax && Math.Abs(DiagsAPI.trim[1]) <= VMax)
lblTrim.ForeColor = Color.LightGreen; lblTrim.ForeColor = Color.LightGreen;
@@ -799,7 +712,7 @@ namespace AiQ_GUI
// Set serial and model into camera // Set serial and model into camera
string[,] TEST_JSON = { { "propSerialNumber", NewSerial }, { "propMavModelNumber", CamOnTest.Model } }; string[,] TEST_JSON = { { "propSerialNumber", NewSerial }, { "propMavModelNumber", CamOnTest.Model } };
string JSONResponse = await FlexiAPI.HTTP_Update("Internal Config", CamOnTest.IP, TEST_JSON); string JSONResponse = await FlexiAPI.HTTP_Update("GLOBAL--FlexiApplication", CamOnTest.IP, TEST_JSON);
if (!JSONResponse.Contains(NewSerial) || !JSONResponse.Contains(CamOnTest.Model)) if (!JSONResponse.Contains(NewSerial) || !JSONResponse.Contains(CamOnTest.Model))
{ {
@@ -1013,7 +926,7 @@ namespace AiQ_GUI
try try
{ {
await FlexiAPI.HTTP_Update("Internal Config", CamOnTest.IP, GOD_JSON); await FlexiAPI.HTTP_Update("GLOBAL--FlexiApplication", CamOnTest.IP, GOD_JSON);
BtnSetGodMode.Text = newGodModeValue == "true" ? "Set God Mode Off" : "Set God Mode On"; BtnSetGodMode.Text = newGodModeValue == "true" ? "Set God Mode Off" : "Set God Mode On";
BtnSetGodMode.BackColor = Color.Green; BtnSetGodMode.BackColor = Color.Green;
} }
@@ -1136,9 +1049,8 @@ namespace AiQ_GUI
PnlInputValue.Visible = true; PnlInputValue.Visible = true;
while (Flags.Done == false) // Waiting for user input in RMA Num panel while (Flags.Done == false) // Waiting for user input in RMA Num panel
{
await Task.Delay(100); // Check every 100ms await Task.Delay(100); // Check every 100ms
}
Flags.Done = false; // Reset flag Flags.Done = false; // Reset flag
PnlInputValue.Visible = false; PnlInputValue.Visible = false;
@@ -1260,7 +1172,7 @@ namespace AiQ_GUI
try try
{ {
Network.Initialize("developer", SCL.DevPass); // Ensure network is initialized to the right camera Network.Initialize("developer", SCL.DevPass); // Ensure network is initialized to the right camera
string RESP = await FlexiAPI.HTTP_Update("Internal Config", SCL.IP, GOD_JSON); string RESP = await FlexiAPI.HTTP_Update("GLOBAL--FlexiApplication", SCL.IP, GOD_JSON);
Instance.AddToActionsList($"Setting God mode for camera {SCL.IP} to {newGodModeValue}", false); Instance.AddToActionsList($"Setting God mode for camera {SCL.IP} to {newGodModeValue}", false);
} }
catch (Exception ex) catch (Exception ex)
@@ -1669,12 +1581,27 @@ namespace AiQ_GUI
{ {
if (!SCL.IsChecked) if (!SCL.IsChecked)
continue; continue;
try
{
Network.Initialize("developer", SCL.DevPass); // Ensure network is initialized to the right camera, cannot be done in soak test finally becuase of this.
await CameraModules.FactoryResetModules(SCL.IP); // Reset camera modules
Network.Initialize("developer", SCL.DevPass); // Ensure network is initialized to the right camera, cannot be done in soak test finally becuase of this. string[,] godJson = { { "propGodMode", "false" } }; // Set God mode explicitly off
await CameraModules.FactoryResetModules(SCL.IP); string GOD = await FlexiAPI.HTTP_Update("GLOBAL--FlexiApplication", SCL.IP, godJson);
if (GOD.Contains("Error"))
throw new Exception("Could not set God mode off");
bool Pass = await FlexiAPI.ChangeNetwork211(SCL.IP); // set camera to 211
if (!Pass)
throw new Exception("Could not set camera to 211");
}
catch (Exception ex)
{
AddToActionsList("Failed to set God mode, reset camera modules or 211 for camera " + SCL.IP + ". Reason: " + ex.Message);
}
} }
} }
} }
private void CkBxTickAll_CheckedChanged(object sender, EventArgs e) private void CkBxTickAll_CheckedChanged(object sender, EventArgs e)
{ {
@@ -1757,7 +1684,7 @@ namespace AiQ_GUI
{ {
string apiUrl = $"http://{cam.IP}/upload/software-update/2"; string apiUrl = $"http://{cam.IP}/upload/software-update/2";
Network.Initialize("developer", cam.DevPass); Network.Initialize("developer", cam.DevPass);
await Task.Delay(1000); // Gives extra time to allow for Network to initialize
AddToActionsList($"Uploading to {cam.IP}...", false); AddToActionsList($"Uploading to {cam.IP}...", false);
string result = await FlexiAPI.SendBlobFileUpload(apiUrl, fileToUpload, fileName); string result = await FlexiAPI.SendBlobFileUpload(apiUrl, fileToUpload, fileName);
@@ -1790,104 +1717,129 @@ namespace AiQ_GUI
BtnFactoryDefault.BackColor = Color.Green; BtnFactoryDefault.BackColor = Color.Green;
} }
// Constants
const double RealPlateWidthMeters = 0.52; // UK standard plate width
// const double FocalLengthPixels = (50 * 1280) / 14.111224; // focal mm * pixel width / sensor width for IQ
const double FocalLengthPixels = (35 * 1920) / 6.95; // focal mm * pixel width / sensor width for AiQ
const double FrameRate = 25.0; // Frames per second
public class FrameData
{
public long FrameID;
public int PlatePosX;
public int PlatePosY;
public int PlateWidthPixels;
}
public double EstimateSpeed(List<FrameData> frames)
{
double TimeElapsed = 0;
int frameCount = frames.Count;
for (int i = 1; i < frameCount; i++)
{
double time = (frames[i].FrameID - frames[i - 1].FrameID) / FrameRate;
TimeElapsed += time;
}
double FarDist = (FocalLengthPixels * RealPlateWidthMeters) / frames[0].PlateWidthPixels;
double CloseDist = (FocalLengthPixels * RealPlateWidthMeters) / frames[frameCount - 1].PlateWidthPixels;
double speedMph = (Math.Abs(FarDist - CloseDist) / TimeElapsed) * 2.237;
return speedMph;
}
// ***** Test & Debug ***** // ***** Test & Debug *****
private async void BtnTest_Click(object sender, EventArgs e) private void BtnTest_Click(object sender, EventArgs e)
{ {
Stopwatch stopWatchTest = Stopwatch.StartNew(); Stopwatch stopWatchTest = Stopwatch.StartNew();
//string[,] GOD_JSON = { { "propURI", "rtsp://ADMIN:1234@192.168.0.49:554/live/main" } };
//string str = FlexiAPI.BuildJsonUpdate(GOD_JSON, "CameraA");
//AddToActionsList(str);
// To estimate speed
//List<FrameData> frames = new List<FrameData>
//{
// new FrameData { FrameID = 60192555, PlatePosX = 1172, PlatePosY = 393, PlateWidthPixels = 108 },
// new FrameData { FrameID = 60192556, PlatePosX = 1103, PlatePosY = 361, PlateWidthPixels = 105 },
// new FrameData { FrameID = 60192558, PlatePosX = 983, PlatePosY = 331, PlateWidthPixels = 99 },
// new FrameData { FrameID = 60192559, PlatePosX = 930, PlatePosY = 301, PlateWidthPixels = 95 },
// new FrameData { FrameID = 60192560, PlatePosX = 880, PlatePosY = 304, PlateWidthPixels = 93 },
// new FrameData { FrameID = 60192561, PlatePosX = 834, PlatePosY = 278, PlateWidthPixels = 89 },
// new FrameData { FrameID = 60192562, PlatePosX = 792, PlatePosY = 229, PlateWidthPixels = 87 },
// new FrameData { FrameID = 60192563, PlatePosX = 752, PlatePosY = 208, PlateWidthPixels = 85 },
// new FrameData { FrameID = 60192565, PlatePosX = 680, PlatePosY = 187, PlateWidthPixels = 81 },
// new FrameData { FrameID = 60192566, PlatePosX = 648, PlatePosY = 167, PlateWidthPixels = 78 },
// new FrameData { FrameID = 60192567, PlatePosX = 617, PlatePosY = 149, PlateWidthPixels = 76 },
// new FrameData { FrameID = 60192568, PlatePosX = 588, PlatePosY = 132, PlateWidthPixels = 75 },
// new FrameData { FrameID = 60192569, PlatePosX = 561, PlatePosY = 100, PlateWidthPixels = 70 },
// new FrameData { FrameID = 60192570, PlatePosX = 535, PlatePosY = 85, PlateWidthPixels = 72 },
// new FrameData { FrameID = 60192572, PlatePosX = 488, PlatePosY = 70, PlateWidthPixels = 69 },
// new FrameData { FrameID = 60192573, PlatePosX = 466, PlatePosY = 55, PlateWidthPixels = 67 }
//};
//double Spd = EstimateSpeed(frames);
//AddToActionsList("Estimated Speed: " + Spd.ToString("F2") + " MPH");
await FlexiAPI.SetVaxtorMinMaxPlate(CamOnTest.IP);
//StatsExcel excelExporter = new(); //StatsExcel excelExporter = new();
//excelExporter.ExportDatabaseToExcel(); //excelExporter.ExportDatabaseToExcel();
//FakeCamera fakeCamera = new FakeCamera(80); // Create an instance of FakeCamera FakeCamera fakeCamera = new FakeCamera(80); // Create an instance of FakeCamera
//CamOnTest.IP = CbBxFoundCams.Text;
////CamOnTest.IP = CbBxFoundCams.Text; _ = fakeCamera.StartAsync(CAMTYPE.GOOD).ContinueWith(task =>
//_ = fakeCamera.StartAsync(CAMTYPE.GOOD).ContinueWith(task => {
//{ //Network.Initialize("developer", "Pass123");
// //Network.Initialize("developer", "Pass123"); if (task.IsFaulted)
{
// if (task.IsFaulted) AddToActionsList("Error starting FakeCamera: " + task.Exception?.Message);
// { }
// AddToActionsList("Error starting FakeCamera: " + task.Exception?.Message); else
// } {
// else AddToActionsList($"FakeCamera started successfully. IP: {fakeCamera}", false);
// { }
// AddToActionsList($"FakeCamera started successfully. IP: {fakeCamera}", false); });
// } await Task.Delay(3000); // Wait for server to start
//}); CbBxFoundCams.Text = "localhost"; // Should force update in creds an network reinit
CmBoFoundCams_TextChanged(sender, e);
//await Task.Delay(3000); // Wait for server to start CbBxCameraType.SelectedIndex = CbBxCameraType.Items.Count - 1; // Selects AB12CD as model number
//CbBxFoundCams.Text = "localhost"; // Should force update in creds an network reinit await Task.Delay(3000); // Wait for server to start
//CmBoFoundCams_TextChanged(sender, e); BtnStartTest_Click(sender, e);
//CbBxCameraType.SelectedIndex = CbBxCameraType.Items.Count - 1; // Selects AB12CD as model number
//await Task.Delay(3000); // Wait for server to start
//BtnStartTest_Click(sender, e);
// /api/config-ids - For getting all available config IDs
stopWatchTest.Stop(); stopWatchTest.Stop();
//AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff")); AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff"));
}
private async Task UploadWonwooSet(string ipAddress, bool isIR)
{
string fileToUpload = null;
using OpenFileDialog openFileDialog1 = new()
{
InitialDirectory = GoogleDrivePath,
Filter = "CSV files (*.csv)|*.csv",
FilterIndex = 0
};
if (openFileDialog1.ShowDialog() == DialogResult.OK)
fileToUpload = openFileDialog1.FileName;
else
{
AddToActionsList("File selection cancelled.", false);
return;
}
//Filename validation
string filename = Path.GetFileName(fileToUpload).ToUpper();
if (isIR)
{
if (!filename.Contains("IR") || filename.Contains("OV"))
{
AddToActionsList("Incorrect file selected. Expected IR file.", false);
return;
}
}
else // OV
{
if (!filename.Contains("OV") || filename.Contains("IR"))
{
AddToActionsList("Incorrect file selected. Expected OV file.", false);
return;
}
}
var lines = File.ReadAllLines(fileToUpload);
for (int i = 1; i < lines.Length; i++)
{
var parts = lines[i].Split(',')
.Select(p => p.Trim())
.ToArray();
if (parts.Length < 3)
{
AddToActionsList($"Invalid row format at line {i + 1}", false);
continue;
}
string name = parts[0];
string command = parts[1];
string expectedResponse = parts[2];
// VISCA format check
if (!RegexCache.VISCAAPIRegex().IsMatch(command))
{
AddToActionsList($"{name}: Invalid VISCA command ({command})", false);
continue; // do not send it if bad
}
string result = await FlexiAPI.APIHTTPVISCA(ipAddress, command, isIR);
if (result.Contains(expectedResponse))
AddToActionsList($"{name}: Success ({(isIR ? "IR" : "Colour")})", true);
else
AddToActionsList($"{name}: Unexpected response ({result})", false);
await Task.Delay(150);
}
AddToActionsList($"Upload complete ({(isIR ? "IR" : "Colour")}).", true);
}
private async void UploadWonwooSetOV_Click(object sender, EventArgs e)
{
await UploadWonwooSet(CbBxFoundCams.Text, false); // false = Colour
}
private async void UploadWonwooSetIR_Click(object sender, EventArgs e)
{
await UploadWonwooSet(CbBxFoundCams.Text, true); // true = Infrared
} }
} }
} }

View File

@@ -16,7 +16,7 @@
<Product>AiQ GUI</Product> <Product>AiQ GUI</Product>
<Authors>MAV Systems Ltd</Authors> <Authors>MAV Systems Ltd</Authors>
<PackageId>AiQ GUI</PackageId> <PackageId>AiQ GUI</PackageId>
<Version>3.14.0</Version> <Version>4.2.0</Version>
<Description>A GUI to control and test the AiQ</Description> <Description>A GUI to control and test the AiQ</Description>
<Copyright>MAV Systems Ltd 2025</Copyright> <Copyright>MAV Systems Ltd 2025</Copyright>
<PackageIcon>MAV - Plain - Blue.png</PackageIcon> <PackageIcon>MAV - Plain - Blue.png</PackageIcon>
@@ -42,7 +42,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="ClosedXML" Version="0.105.0" /> <PackageReference Include="ClosedXML" Version="0.105.0" />
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.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="Google.Apis.Auth" Version="1.72.0" /> <PackageReference Include="Google.Apis.Auth" Version="1.72.0" />
@@ -50,11 +49,11 @@
<PackageReference Include="Google.Apis.Sheets.v4" Version="1.70.0.3819" /> <PackageReference Include="Google.Apis.Sheets.v4" Version="1.70.0.3819" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="PDFsharp-MigraDoc-gdi" Version="6.2.2" /> <PackageReference Include="PDFsharp-MigraDoc-gdi" Version="6.2.2" />
<PackageReference Include="Selenium.Support" Version="4.36.0" /> <PackageReference Include="Selenium.Support" Version="4.38.0" />
<PackageReference Include="Selenium.WebDriver" Version="4.36.0" /> <PackageReference Include="Selenium.WebDriver" Version="4.38.0" />
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="141.0.7390.7600" /> <PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="142.0.7444.5900" />
<PackageReference Include="SSH.NET" Version="2025.0.0" /> <PackageReference Include="SSH.NET" Version="2025.1.0" />
<PackageReference Include="System.Data.OleDb" Version="9.0.9" /> <PackageReference Include="System.Data.OleDb" Version="9.0.10" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,5 +1,6 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.NetworkInformation;
namespace AiQ_GUI namespace AiQ_GUI
{ {
@@ -35,8 +36,10 @@ namespace AiQ_GUI
try try
{ {
string JSONdata = BuildJsonUpdate(jsonArrayData, ID); string JSONdata = BuildJsonUpdate(jsonArrayData, ID);
JSONdata = JSONdata.Replace("\"14\"", "14").Replace("\"30\"", "30"); // Fixes & encoding issue
string url = $"http://{IPAddress}/api/update-config"; string url = $"http://{IPAddress}/api/update-config";
return await Network.SendHttpRequest(url, HttpMethod.Post, 2, JSONdata); return await Network.SendHttpRequest(url, HttpMethod.Post, 2, JSONdata);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -79,7 +82,7 @@ namespace AiQ_GUI
// Always Infrared as LED's are controlled from infrared page // Always Infrared as LED's are controlled from infrared page
// Level can be word eg. SAFE or hex eg. 0x0E // Level can be word eg. SAFE or hex eg. 0x0E
string suffix = $"/Infrared/led-controls?power={LEVEL}"; string suffix = $"/Infrared/led-controls?power={LEVEL}";
return await APIHTTPRequest(suffix, IPAddress); return await APIHTTPRequest(suffix, IPAddress, 5);
} }
public static async Task<string> SendBlobFileUpload(string url, string filePath, string fileName) public static async Task<string> SendBlobFileUpload(string url, string filePath, string fileName)
@@ -159,12 +162,13 @@ namespace AiQ_GUI
return null; // If it fails to parse the JSON return null; // If it fails to parse the JSON
} }
} }
public static async Task<bool> SetVaxtorMinMaxPlate(string IP) public static async Task<bool> SetVaxtorMinMaxPlate(string IP)
{ {
try try
{ {
// Build JSON array for Vaxtor min/max plate configuration // Build JSON array for Vaxtor min/max plate configuration
string[,] Vaxtor_JSON = { { "propMinCharHeight", "18" }, { "propMinGlobalConfidence", "30" } }; string[,] Vaxtor_JSON = { { "propMinCharHeight", "14" }, { "propMaxCharHeight", "40" }, { "propMinGlobalConfidence", "30" } };
string response = await HTTP_Update("RaptorOCR".Trim(), IP, Vaxtor_JSON); string response = await HTTP_Update("RaptorOCR".Trim(), IP, Vaxtor_JSON);
// Treat "operation was canceled" as a successful apply // Treat "operation was canceled" as a successful apply
@@ -189,8 +193,6 @@ namespace AiQ_GUI
} }
} }
public static async Task<bool> SetZoomLockOn(string IP) public static async Task<bool> SetZoomLockOn(string IP)
{ {
// Set Zoomlock on and if it fails ask user to set it manually // Set Zoomlock on and if it fails ask user to set it manually
@@ -236,8 +238,7 @@ namespace AiQ_GUI
} }
// Check no value is -1 (no plate found) or if the positions are identical (one plate found). If it is then try again 3 times // Check no value is -1 (no plate found) or if the positions are identical (one plate found). If it is then try again 3 times
if (new[] { trim.infraredX, trim.infraredY, trim.colourX, trim.colourY }.Any(value => value == -1) if (new[] { trim.infraredX, trim.infraredY, trim.colourX, trim.colourY }.Any(value => value == -1) || (trim.infraredX == trim.colourX && trim.infraredY == trim.colourY))
|| (trim.infraredX == trim.colourX && trim.infraredY == trim.colourY))
{ {
if (RetryCount >= 3) if (RetryCount >= 3)
{ {
@@ -246,7 +247,7 @@ namespace AiQ_GUI
} }
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
await SetTrim(IPAddress, LblTxt, RetryCount++); await SetTrim(IPAddress, LblTxt, RetryCount + 1);
} }
int offset = 105; int offset = 105;
@@ -277,7 +278,7 @@ namespace AiQ_GUI
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
await SetTrim(IPAddress, LblTxt, RetryCount++); await SetTrim(IPAddress, LblTxt, RetryCount + 1);
} }
} }
@@ -354,12 +355,24 @@ namespace AiQ_GUI
} }
// Change network settings to DHCP and restart camera for it to take effect // Change network settings to DHCP and restart camera for it to take effect
public async static Task ChangeNetworkToDHCP(string IPAddress) public async static Task<bool> ChangeNetworkToDHCP(string IPAddress)
{ {
string[,] TEST_JSON = { { "propDHCP", "true" } }; // Update GLOBAL--NetworkConfig with fixed IP and turn off DHCP string[,] TEST_JSON = { { "propDHCP", "true" } };
await HTTP_Update("GLOBAL--NetworkConfig", IPAddress, TEST_JSON); string result = await HTTP_Update("GLOBAL--NetworkConfig", IPAddress, TEST_JSON);
// TODO - Check if this worked, if not return false
await Task.Delay(5000); // Wait for 5 seconds to allow the camera to restart
IList<string> FoundCams = await Network.SearchForCams();
if (FoundCams.Contains("192.168.1.211"))
{
MainForm.Instance.AddToActionsList("Could not set camera to DHCP please check camera.");
return false;
}
MainForm.Instance.AddToActionsList("Camera successfully set to DHCP.");
return true;
} }
} }
//Items recieved in Versions API //Items recieved in Versions API

View File

@@ -10,11 +10,11 @@ namespace AiQ_GUI
internal class ImageProcessing internal class ImageProcessing
{ {
// API to get snapshot then downsize and downscale image to save size. // API to get snapshot then downsize and downscale image to save size.
public static async Task<Image?> GetProcessedImage(string suffix, string IPAddress, string DevPass, string? savePath = null, PictureBox? PcBx = null, bool SaveDisplay = false) public static async Task<Image?> GetProcessedImage(string Module, string IPAddress, string DevPass, string? savePath = null, PictureBox? PcBx = null, bool SaveDisplay = false)
{ {
try try
{ {
string requestUrl = $"http://{IPAddress}/{suffix}"; string requestUrl = $"http://{IPAddress}/{Module}-snapshot";
HttpClientHandler handler = new() HttpClientHandler handler = new()
{ {
@@ -85,7 +85,7 @@ namespace AiQ_GUI
public static async Task ImageCheck(PictureBox PicBxOV, PictureBox PicBxF2, PictureBox PicBxF16, Label LblF2, Label LblF16, Camera CamOnTest) public static async Task ImageCheck(PictureBox PicBxOV, PictureBox PicBxF2, PictureBox PicBxF16, Label LblF2, Label LblF16, Camera CamOnTest)
{ {
// Take OV snapshot // Take OV snapshot
Task<Image?> Colour_Response = GetProcessedImage("Colour-snapshot", CamOnTest.IP, CamOnTest.DevPass, LDS.MAVPath + LDS.OVsavePath, PicBxOV, true); Task<Image?> Colour_Response = GetProcessedImage("Colour", CamOnTest.IP, CamOnTest.DevPass, LDS.MAVPath + LDS.OVsavePath, PicBxOV, true);
// Change to wide iris F2.0 // Change to wide iris F2.0
await FlexiAPI.APIHTTPVISCA(CamOnTest.IP, "8101044B00000100FF", true); await FlexiAPI.APIHTTPVISCA(CamOnTest.IP, "8101044B00000100FF", true);
@@ -93,7 +93,7 @@ namespace AiQ_GUI
await Task.Delay(200); // Wait for iris to settle before taking IR image await Task.Delay(200); // Wait for iris to settle before taking IR image
// Take IR bright light image // Take IR bright light image
Image? F2_Response = await GetProcessedImage("Infrared-snapshot", CamOnTest.IP, CamOnTest.DevPass, LDS.MAVPath + LDS.IROpensavePath, PicBxF2, true); Image? F2_Response = await GetProcessedImage("Infrared", CamOnTest.IP, CamOnTest.DevPass, LDS.MAVPath + LDS.IROpensavePath, PicBxF2, true);
if (F2_Response == null) if (F2_Response == null)
{ {
MainForm.Instance.AddToActionsList("IR F2.0 image response is blank."); MainForm.Instance.AddToActionsList("IR F2.0 image response is blank.");
@@ -106,7 +106,7 @@ namespace AiQ_GUI
await Task.Delay(200); // Wait for iris to settle before taking IR image await Task.Delay(200); // Wait for iris to settle before taking IR image
// Take IR low light image // Take IR low light image
Image? F16_Response = await GetProcessedImage("Infrared-snapshot", CamOnTest.IP, CamOnTest.DevPass, LDS.MAVPath + LDS.IRTightsavePath, PicBxF16, true); Image? F16_Response = await GetProcessedImage("Infrared", CamOnTest.IP, CamOnTest.DevPass, LDS.MAVPath + LDS.IRTightsavePath, PicBxF16, true);
if (F16_Response == null) if (F16_Response == null)
{ {
MainForm.Instance.AddToActionsList("IR F16.0 image response is blank."); MainForm.Instance.AddToActionsList("IR F16.0 image response is blank.");

View File

@@ -1,4 +1,5 @@
using Renci.SshNet; using Renci.SshNet;
using Renci.SshNet.Common;
namespace AiQ_GUI namespace AiQ_GUI
{ {
@@ -6,6 +7,29 @@ namespace AiQ_GUI
{ {
public const string SSHUsername = "mav"; public const string SSHUsername = "mav";
public const string SSHPassword = "mavPA$$"; public const string SSHPassword = "mavPA$$";
public const string SSHPasswordNEW = "#!mavsoftMESA19"; // New password for SSH after last one got leaked
private static SshClient SshConnect(string IPAddress)
{
SshClient client = new(IPAddress, SSHUsername, SSHPasswordNEW);
try
{
client.Connect();
return client;
}
catch (SshAuthenticationException)
{
client = new(IPAddress, SSHUsername, SSHPassword);
client.Connect();
return client;
}
catch (Exception Ex)
{
MainForm.Instance.AddToActionsList($"SSH connection failed: {Ex.Message}. Check password or network.");
}
return null;
}
// Connects to camera over SSH and collects the Vaxtor packages, filesystem name, filesystem size, and tailscale status. // Connects to camera over SSH and collects the Vaxtor packages, filesystem name, filesystem size, and tailscale status.
public static SSHData CollectSSHData(string IPAddress) public static SSHData CollectSSHData(string IPAddress)
@@ -14,8 +38,7 @@ namespace AiQ_GUI
try try
{ {
using SshClient client = new(IPAddress, SSHUsername, SSHPassword); SshClient client = SshConnect(IPAddress);
client.Connect();
try try
{ {
@@ -34,8 +57,7 @@ namespace AiQ_GUI
catch (Exception ex) catch (Exception ex)
{ {
MainForm.Instance.AddToActionsList($"Failed to get filesystem info: {ex.Message}"); MainForm.Instance.AddToActionsList($"Failed to get filesystem info: {ex.Message}");
Data.FilesystemName = "Unknown"; Data.FilesystemName = Data.FilesystemSize = "Unknown";
Data.FilesystemSize = "Unknown";
} }
try try
@@ -49,6 +71,7 @@ namespace AiQ_GUI
} }
client.Disconnect(); client.Disconnect();
client.Dispose();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -131,8 +154,7 @@ namespace AiQ_GUI
{ {
try try
{ {
using SshClient client = new SshClient(IPAddress, SSHUsername, SSHPassword); SshClient client = SshConnect(IPAddress);
client.Connect();
try try
{ {
@@ -144,6 +166,7 @@ namespace AiQ_GUI
} }
client.Disconnect(); client.Disconnect();
client.Dispose();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -191,10 +214,10 @@ namespace AiQ_GUI
{ {
try try
{ {
using SshClient client = new(IPAddress, SSHUsername, SSHPassword); SshClient client = SshConnect(IPAddress);
client.Connect();
(string FilesystemName, string FilesystemSize) = GetRootFilesystemInfo(client); (string FilesystemName, string FilesystemSize) = GetRootFilesystemInfo(client);
client.Disconnect(); client.Disconnect();
client.Dispose();
return (FilesystemName, FilesystemSize); return (FilesystemName, FilesystemSize);
} }
catch (Exception ex) catch (Exception ex)
@@ -207,19 +230,19 @@ namespace AiQ_GUI
// Checks the filesystem size and expands it if necessary, displays on the label how big the SD card is. // Checks the filesystem size and expands it if necessary, displays on the label how big the SD card is.
public static async Task<SSHData> CheckFSSize(string IPAddress, Label LblFSSize, SSHData sshData) public static async Task<SSHData> CheckFSSize(string IPAddress, Label LblFSSize, SSHData sshData)
{ {
const double MinGoodSize = 100.0; // 100GB const double GoodSize = 128.0; // 128GB
const double MaxGoodSize = 150.0; // 150GB const double Deviation = 20.0; // ±20GB
double currentSize = NormaliseFSSize(sshData.FilesystemSize); double currentSize = NormaliseFSSize(sshData.FilesystemSize);
LblFSSize.Text = $"Filesystem Size = {currentSize}GB"; LblFSSize.Text = $"Filesystem Size = {currentSize}GB";
if (currentSize >= MinGoodSize && currentSize <= MaxGoodSize) if (Math.Abs(GoodSize - currentSize) < Deviation)
{ {
LblFSSize.ForeColor = Color.LightGreen; LblFSSize.ForeColor = Color.LightGreen;
return sshData; return sshData;
} }
if (currentSize < MinGoodSize) if (currentSize < GoodSize - Deviation)
{ {
try try
{ {
@@ -230,7 +253,7 @@ namespace AiQ_GUI
double newSize = NormaliseFSSize(sshData.FilesystemSize); double newSize = NormaliseFSSize(sshData.FilesystemSize);
LblFSSize.Text = $"Filesystem Size = {newSize}GB"; LblFSSize.Text = $"Filesystem Size = {newSize}GB";
if (newSize >= MinGoodSize && newSize <= MaxGoodSize) if (Math.Abs(GoodSize - newSize) < Deviation)
{ {
LblFSSize.ForeColor = Color.LightGreen; LblFSSize.ForeColor = Color.LightGreen;
return sshData; return sshData;
@@ -261,10 +284,9 @@ namespace AiQ_GUI
// Extract value & unit // Extract value & unit
System.Text.RegularExpressions.Match match = RegexCache.FileSizeRegex().Match(rootSize.Trim()); System.Text.RegularExpressions.Match match = RegexCache.FileSizeRegex().Match(rootSize.Trim());
if (!match.Success)
return 0;
if (!double.TryParse(match.Groups["value"].Value, out double value)) // Return 0 if no match or invalid number
if (!match.Success || !double.TryParse(match.Groups["value"].Value, out double value))
return 0; return 0;
string unit = match.Groups["unit"].Value.ToUpperInvariant(); string unit = match.Groups["unit"].Value.ToUpperInvariant();
@@ -288,15 +310,14 @@ namespace AiQ_GUI
return 0; return 0;
} }
// Expands the filesystem to max // Expands the filesystem to max
public async static Task<bool> ExpandFS(string device, string IPAddress) public async static Task<bool> ExpandFS(string device, string IPAddress)
{ {
try try
{ {
using SshClient ssh = new SshClient(IPAddress, SSHUsername, SSHPassword); SshClient client = SshConnect(IPAddress);
ssh.Connect();
SshCommand checkDevice = ssh.RunCommand($"[ -b {device} ] && echo OK || echo NOT_FOUND"); SshCommand checkDevice = client.RunCommand($"[ -b {device} ] && echo OK || echo NOT_FOUND");
if (!string.IsNullOrWhiteSpace(checkDevice.Error)) if (!string.IsNullOrWhiteSpace(checkDevice.Error))
throw new Exception(checkDevice.Error); throw new Exception(checkDevice.Error);
@@ -306,7 +327,7 @@ namespace AiQ_GUI
return false; return false;
} }
SshCommand umountCmd = ssh.RunCommand($"sudo umount {device}"); SshCommand umountCmd = client.RunCommand($"sudo umount {device}");
if (!string.IsNullOrWhiteSpace(umountCmd.Error) && !umountCmd.Error.Contains("not mounted")) if (!string.IsNullOrWhiteSpace(umountCmd.Error) && !umountCmd.Error.Contains("not mounted"))
{ {
MainForm.Instance.AddToActionsList($"Unmount error: {umountCmd.Error}"); MainForm.Instance.AddToActionsList($"Unmount error: {umountCmd.Error}");
@@ -315,15 +336,16 @@ namespace AiQ_GUI
await Task.Delay(1000); // Wait for mount to settle await Task.Delay(1000); // Wait for mount to settle
SshCommand fsckCmd = ssh.RunCommand($"sudo e2fsck -f -y -v -C 0 {device}"); SshCommand fsckCmd = client.RunCommand($"sudo e2fsck -f -y -v -C 0 {device}");
if (!string.IsNullOrWhiteSpace(fsckCmd.Error)) if (!string.IsNullOrWhiteSpace(fsckCmd.Error))
{ {
MainForm.Instance.AddToActionsList($"e2fsck error: {fsckCmd.Error}"); MainForm.Instance.AddToActionsList($"e2fsck error: {fsckCmd.Error}");
return false; return false;
} }
SshCommand resizeFs = ssh.RunCommand($"sudo resize2fs {device}"); SshCommand resizeFs = client.RunCommand($"sudo resize2fs {device}");
ssh.Disconnect(); client.Disconnect();
client.Dispose();
if (!string.IsNullOrWhiteSpace(resizeFs.Error)) if (!string.IsNullOrWhiteSpace(resizeFs.Error))
{ {
@@ -348,10 +370,9 @@ namespace AiQ_GUI
{ {
try try
{ {
using SshClient ssh = new SshClient(IPAddress, SSHUsername, SSHPassword); SshClient client = SshConnect(IPAddress);
ssh.Connect();
SshCommand checkDevice = ssh.RunCommand("sync"); SshCommand checkDevice = client.RunCommand("sync");
if (!string.IsNullOrWhiteSpace(checkDevice.Error)) if (!string.IsNullOrWhiteSpace(checkDevice.Error))
throw new Exception(checkDevice.Error); throw new Exception(checkDevice.Error);
@@ -360,6 +381,9 @@ namespace AiQ_GUI
MainForm.Instance.AddToActionsList($"Cannot sync files to disk. Replied: {checkDevice.Result}. DO NOT TURN OFF, GET SUPERVISOR"); MainForm.Instance.AddToActionsList($"Cannot sync files to disk. Replied: {checkDevice.Result}. DO NOT TURN OFF, GET SUPERVISOR");
return; return;
} }
client.Disconnect();
client.Dispose();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,7 +1,7 @@
{ {
"cameraName": "AiQ-ANPR-Camera", "cameraName": "AiQ-ANPR-Camera",
"version": "1.6.4", "version": "1.7.1",
"revision": "bf16134", "revision": "4b63df5",
"serialNumber": "K1005001", "serialNumber": "K1005001",
"modelNumber": "AB12CD", "modelNumber": "AB12CD",
"MAC": "3C:6D:66:0A:BA:18", "MAC": "3C:6D:66:0A:BA:18",

View File

@@ -293,7 +293,7 @@ namespace AiQ_GUI
{ {
await HTTPReplySetup(context, "Update successful.", 200, "text/plain"); await HTTPReplySetup(context, "Update successful.", 200, "text/plain");
} }
else if (fo.Id == "Internal Config") else if (fo.Id == "GLOBAL--FlexiApplication")
{ {
string SerialNumber = ""; string SerialNumber = "";
string ModelNumber = ""; string ModelNumber = "";

View File

@@ -1,6 +1,6 @@
{ {
"version": "1.6.4", "version": "1.7.1",
"revision": "77e042f", "revision": "4b63df5",
"buildtime": "2025-07-24T10:35:36.445241784Z", "buildtime": "2025-07-24T10:35:36.445241784Z",
"appname": "FlexiAI", "appname": "FlexiAI",
"MAC": "3C:6D:66:0A:BA:18", "MAC": "3C:6D:66:0A:BA:18",

View File

@@ -15,9 +15,7 @@ namespace AiQ_GUI
// If there is no dot in the version string, return the version as is // If there is no dot in the version string, return the version as is
if (dotLocation < 0) if (dotLocation < 0)
{
return GUIVersion; return GUIVersion;
}
// Check if the next character after the dot is "0" // Check if the next character after the dot is "0"
if (dotLocation + 1 < GUIVersion.Length && GUIVersion[dotLocation + 1] == '0') if (dotLocation + 1 < GUIVersion.Length && GUIVersion[dotLocation + 1] == '0')
@@ -48,7 +46,7 @@ namespace AiQ_GUI
{ {
if (ComapreVersions()) // Checks if the current version is older than the latest version if (ComapreVersions()) // Checks if the current version is older than the latest version
{ {
string GUIPath = $"{GoogleAPI.DrivePath}AiQ\\GUI's\\AiQ_Final_Test\\AiQ_GUI.application"; const string GUIPath = $"{GoogleAPI.DrivePath}AiQ\\GUI's\\AiQ_Final_Test\\AiQ_GUI.application";
// Check if path is real // Check if path is real
if (!File.Exists(GUIPath)) if (!File.Exists(GUIPath))

View File

@@ -24,7 +24,7 @@ namespace AiQ_GUI
{ {
try try
{ {
string streamPath = $"{DrivePath}R50IQ\\client_secret.json"; const string streamPath = $"{DrivePath}R50IQ\\client_secret.json";
string[] Scopes = [SheetsService.Scope.Spreadsheets]; string[] Scopes = [SheetsService.Scope.Spreadsheets];
FileStream stream = new(streamPath, FileMode.Open, FileAccess.Read); FileStream stream = new(streamPath, FileMode.Open, FileAccess.Read);
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromStream(stream).Secrets, Scopes, "user", CancellationToken.None, new FileDataStore(credPath, true)).Result; credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromStream(stream).Secrets, Scopes, "user", CancellationToken.None, new FileDataStore(credPath, true)).Result;
@@ -231,7 +231,7 @@ namespace AiQ_GUI
// Checks RMA control sheet for a model and serial that match current camera // Checks RMA control sheet for a model and serial that match current camera
public static int CheckRMANum(string serial, string model) public static int CheckRMANum(string serial, string model)
{ {
string spreadsheetId_RMAControl = "1tZhkYrqBQ3BcL7ZS4q3ghzCgHSJ8f5LVSj7nh6fIRC8"; const string spreadsheetId_RMAControl = "1tZhkYrqBQ3BcL7ZS4q3ghzCgHSJ8f5LVSj7nh6fIRC8";
try try
{ {
// Get all info in H and I columns // Get all info in H and I columns
@@ -297,7 +297,7 @@ namespace AiQ_GUI
} }
// Build the MIME message with attachment // Build the MIME message with attachment
using MailMessage mail = new MailMessage(); using MailMessage mail = new();
mail.From = new MailAddress("me"); mail.From = new MailAddress("me");
mail.To.Add("richard.porter@mav-systems.com"); mail.To.Add("richard.porter@mav-systems.com");
mail.To.Add("bradley.relyea@mav-systems.com"); mail.To.Add("bradley.relyea@mav-systems.com");

View File

@@ -54,21 +54,36 @@ namespace AiQ_GUI
public static void PrintGBLbl() public static void PrintGBLbl()
{ {
// New Label with UKCA logo // New Label with UKCA logo
const string MadeInGB = "^XA~TA000~JSN^LT0^MNW^MTT^PON^PMN^LH0,0^JMA^PR2,2~SD15^JUS^LRN^CI0^XZ\n" + const string MadeInGB = "^CT~~CD,~CC^~CT~\n" +
"^XA\n" + "^XA\n" +
"^MMT\n" + "~TA000\n" +
"^PW384\n" + "~JSN\n" +
"^LL0096\n" + "^LT0\n" +
"^LS0\n" + "^MNW\n" +
"^FO192,0^GFA,01152,01152,00012,:Z64:\n" + "^MTT\n" +
"eJzt0DEKgDAMBdCUDo49Qm9ij2aP1qN4BEeHYvziL0TURRcHA4VXaEryRf76agWciVZcaQ8nuoMHOsBKxypO2ZqK+Jke0NK+XNAy0hUuOx3exkzjbZSzPdw3j0cnzY999efdDHZOO/9hL7tvyuIrHSfEyGbk5vQiT5uzzX+bwDX/9a5WFTRBPA==:FF83\n" + "^PON\n" +
"^FO320,0^GFA,00768,00768,00008,:Z64:\n" + "^PMN\n" +
"eJxjYBjOgAWIHYCYA4gNGOwYBEBs9v8HFEBycnY2IDkmOwabBiDNaP//L1jTBAYxMB3AIAOmDaC0AIMNmJawLwDTMvUTwLQQswCYlmRSANMcLVA6A0obQOkKVHlOqHouRgjNw/AAIs+QALEHSuswfADTLow/QBRjC2MNiGZi//8L7Ec2xo8MUD82gJ3JANaoAHY65UD+////D0ighxYAAPB/L1Q=:B1A7\n" + "^LH0,0\n" +
"^FO0,0^GFA,02688,02688,00028,:Z64:\n" + "^JMA\n" +
"eJzt071OwzAQB/BEHjL6ETzwAowMSH4Y3gMXdejII/RRcJWBsY9QVx0YsdTFEsbH3fmjSaGMDKhXZ/oldc73T9dd61r/sLTXcACnu8ENXsBCAHRGLhRbyLaQTqKFbDZbUmxWsaVsLhtINvwpemjJ5osNv9jI5o1TEW1kC8X2bAGvKLweQ4cWi3m2z2zqwJaqGTLAKwkvs0GxqIuZRzJIZGsyBUmRGXCPRvhhahJAsr27NLFdsRWZRtPNdkBGLZGprYt6WffbsvUAezL5vMdDqe/5ynYL4HlGEm2s/Y1sdwbCw8nMODVs8IbtAHhA5Tw3bPeKZnayPAfLphVAtjd6SShzN2wrujfbcKxzt5ptSXuwObQwsz1t4y5kEOB40Qx8XDLMJ1wyzCfNPOJd1FASoUs115hPMrC0qtmWTzZeaENEcy2fbJ5WNd/yObc0Md8skEmy0PJJtvRaBN3h2ZHlXDPPDNBSNTUz9YIG1WTZr9oitcxD7W9g02zrmXEPZIpsN7PQzKaS66k5Mhx6Krmu9hTprMnApZLrn208N8X/id/AueHnK32zzfm7DGQ9m/1m1HsfRUg116fe+cz6wKZnZvERNBH6WO1a1/qD+gLCtYx7:603A\n" + "^PR2,2\n" +
"^FO256,0^GFA,01152,01152,00012,:Z64:\n" + "~SD15\n" +
"eJzt0DEKwzAMBVAJDxlzBB/F18pQcI7mo/gIGlVwqzpBrkSbDoGO+V4egXy+DXDlv8EKtMCyO1QkRjZPZJ6LOa7mDOZmfrBZyLk4rz++1+9/kZDuzcxPtQhx0suIVI7FPGtRlsJBi6LAuOPpRKFpDM1CcYzrG5Lw21mNu5tuBrj1M9zUoU/kkxbt9N46D83O21tpD3hn5+R85SMvvQiYIA==:00E3\n" + "^JUS\n" +
"^PQ1,0,1,Y^XZ"; "^LRN\n" +
"^CI27\n" +
"^PA0,1,1,0\n" +
"^XZ\n" +
"^XA\n" +
"^MMT\n" +
"^PW384\n" +
"^LL96\n" +
"^LS0\n" +
"^FO212,34^GFA,225,280,8,:Z64:eJxVkDEKwkAQRZ+GRbs0oo2CtYcwV0hno+AR0oiVkKOk9jTbWAa2TLllKte/uJi1WIbHnz/zZ5l3cABMAyfVaqQILVwsphcHTxlgdoNNDcWoFsnGwlly6eCtutBrIktTCztZvOpRFrWwFl8TD0vYil/1l4MMK/G9mTjX3f7f79J8m+377U95Yr4HKW835ScMuie7r/JaGAek++N/PFs+dGoy4g==:0511\n" +
"^FO336,26^GFA,217,384,8,:Z64:eJxrYEAAFiBWAGIOME8ACIFsvncdIDEWFhERByDNZLdqXwOQZhRkFAQrW8DABaYDGETAtAGDFVQ7hM+htQNMi9QLgGkuZQUwzQi1lKMDwmdwgPINoHwBVHmYei4mBbhjwfIMD6DKIQZoMHyASDBCDOjhfgWmWUMDQBQTE8MGJC+D/boAia/AQBng////P8hB8kD6ABKfHwcfACScKCU=:8B00\n" +
"^FO11,27^GFA,817,1125,25,:Z64:eJxtkkFu00AYhf/pgBwJKcOCbjM3KDewc4QusFhyBZYsAp7UCyIhkSuwZsEZJgKJJSdAmaoLsmqmQhUjYfnn/WMnrVRsRbYz8+nNe/+jlVxuQhP5rWi1ognh9ZNiubwlQ5ZsRw2TLbwh0nkhVC7f7Jip0sGc0JGoqCHLXggd7R0RsBlE4N7ZNpnVUcM31HhbxAVBo7M0PxCR3d9Q6pQIGr1xR+KG6befqUzo/p4GZ4JS/5lsy+uLyUjMmh/qJswoNR00tnc+ymrdXkUQ3ENjvyZSdV0LYY26jDN6I4T+viUi59qRCGkgbHvFDitUCPHIFLdpplL2sWH5n4wQZN5zOvi4T5ApuJvpwYdnOVWqBmLNfcW77APBId1YDYT5thjTLRAciMAj4crjPCr4kE2ZwAAt5wlqn7NqhTij6Ud39mXnrq/d9CJMRQOnYU8PLmTPicPDBdUVHP9LwMaOw2T8nOQ7E7Dh2Z++oPpcndf1/BmdqvljELDhZIKOexVllrlXIMQr+iF2VGzwOPSqRZOEALlMIDj3Cj5gg9CSl/7tq02sb+dYKBKIE9ggZKVDMsuoA6GP0iuZRnwKQvtFsYkqqj94RxMxL97BefkkdGaTdFB7b9tMwMbrrDESv/zQK8JhYvbRPxcNfRWGXlE+fvbRYH/UmzD0SnO/TkcfQkTb7vcyj65AuhZYJwSEbPF1bEkUH1GyQkr4zr1CS+xALMpM4FtvMrGTCZYfUldBYwfi0CvfCLGNCyuE+dkderXk7GPMyrp3h15BJfsYnFtajL3q15x9uM5cwkfpSmggq38uZhFX:58F1\n" +
"^FO278,28^GFA,273,360,8,:Z64:eJx1j7ENwjAQRX8UoRQUKel8LcoMyG6yB2ukCCSlR0EMgSgYgBEyAUrpwspxZwcJIWFLPj3/++fvC/YNGqBHb+RAgLVyoMPBFHPmeso63TM7ZF3alV1I3NO88pR1mr508dPaLw+s86Qu15jmDxxMJdVxsKUYdjynPFseLW74v7yHH4ENx2J4AjUvJYufOFYcdd4iV8CpbelxBM5yJVvygWTrVy1qzQOj9cOdctYdc9JJ6y8v3rsXa/6Y/PJe8pMypTyJ30wJYOg=:5E2C\n" +
"^PQ1,0,1,Y\n" +
"^XZ";
if (!PrintToZebra(MadeInGB)) if (!PrintToZebra(MadeInGB))
MainForm.Instance.AddToActionsList("Error printing GB label"); MainForm.Instance.AddToActionsList("Error printing GB label");

View File

@@ -77,15 +77,15 @@ namespace AiQ_GUI
{ {
foreach (int ShuffleOrder in Shuffle()) foreach (int ShuffleOrder in Shuffle())
{ {
switch (ShuffleOrder) switch (ShuffleOrder)
{ {
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"); 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?"))
@@ -101,7 +101,7 @@ namespace AiQ_GUI
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"); await MainForm.Instance.TestFailed(Btn, "Visual Test Fail - Unit rattles");
} }
break; break;

View File

@@ -4,7 +4,7 @@ namespace AiQ_GUI
{ {
class Access class Access
{ {
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;"; 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". // 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 static string[] ReadCamTypes()
@@ -38,6 +38,7 @@ namespace AiQ_GUI
IOrderedEnumerable<Tuple<string, string>> sorted = modelTuples.OrderBy(t => t.Item1.Equals("AB12CD", StringComparison.OrdinalIgnoreCase) ? 1 : 0) IOrderedEnumerable<Tuple<string, string>> sorted = modelTuples.OrderBy(t => t.Item1.Equals("AB12CD", StringComparison.OrdinalIgnoreCase) ? 1 : 0)
.ThenBy(t => t.Item1, StringComparer.OrdinalIgnoreCase); .ThenBy(t => t.Item1, StringComparer.OrdinalIgnoreCase);
conn.Close();
// Format the sorted tuples as "ModelNumber - Description" strings and return as array // 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();
} }
@@ -67,6 +68,7 @@ namespace AiQ_GUI
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();
} }
// Knowing the model number on test, this function reads the database and populates the Camera class with the values. // Knowing the model number on test, this function reads the database and populates the Camera class with the values.
@@ -97,12 +99,76 @@ namespace AiQ_GUI
CameraAccessInfo.LED_V = Convert.ToDouble(reader["LEDVoltage"]); CameraAccessInfo.LED_V = Convert.ToDouble(reader["LEDVoltage"]);
CameraAccessInfo.LED_I = Convert.ToInt32(reader["LEDCurrent"]); CameraAccessInfo.LED_I = Convert.ToInt32(reader["LEDCurrent"]);
CameraAccessInfo.SpreadsheetID = Convert.ToString(reader["SSID"]); CameraAccessInfo.SpreadsheetID = Convert.ToString(reader["SSID"]);
conn.Close();
}
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
try
{
conn.Open(); // Opens DB
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
int rowsAffected = cmd.ExecuteNonQuery();
// Execute the command and get the number of rows affected
//if (rowsAffected > 0) // If one or more rows were updated
// AddToActionsList($"Updated {TypeOfTest} for {modelNumber}");
//else
// AddToActionsList($"No rows found for {modelNumber}");
}
conn.Close();
}
catch
{
MainForm.Instance.AddToActionsList("Could not access Access in Google Drive. Is it running?");
return;
}
}
public static void StatsDiags(string redDiagLabels, string RhTxBxActionsText, string ModelNumber)
{
using OleDbConnection conn = new(connString);
conn.Open();
// Null checks
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 (?, ?, ?, ?)";
using OleDbCommand cmd = new(sql, conn);
cmd.Parameters.Add(new OleDbParameter
{
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)
// AddToActionsList($"DiagsStats inserted ({rows} row) for model '{model}' on {DateTime.Now:yyyy-MM-dd}");
//else
// AddToActionsList("No rows inserted into DiagsStats (unexpected).");
} }
} }
// Expected universal data for the GUI, read from the database // Expected universal data for the GUI, read from the database
public class UniversalData public class UniversalData
{ {

View File

@@ -417,7 +417,6 @@ namespace AiQ_GUI
// int nextFree = Excel.CheckNextFree(filePath); // int nextFree = Excel.CheckNextFree(filePath);
// Debug.WriteLine("Next free row in Vaxtor sheet (Column C): " + nextFree); // Debug.WriteLine("Next free row in Vaxtor sheet (Column C): " + nextFree);
//} //}
} }
} }
} }

View File

@@ -1,17 +1,12 @@
using System; using ClosedXML.Excel;
using System.Data; using System.Data;
using System.Data.OleDb; using System.Data.OleDb;
using ClosedXML.Excel;
namespace AiQ_GUI.Microsoft namespace AiQ_GUI
{ {
internal class StatsExcel internal class StatsExcel
{ {
private const string connString = private const string exportPath = @"G:\Shared drives\MAV Production GUI's\AiQ\GUI's\AiQ_Test_Stats.xlsx";
@"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;";
private readonly string exportPath =
@"G:\Shared drives\MAV Production GUI's\AiQ\GUI's\AiQ_Test_Stats.xlsx";
public void ExportDatabaseToExcel() public void ExportDatabaseToExcel()
{ {
@@ -19,32 +14,30 @@ namespace AiQ_GUI.Microsoft
{ {
MainForm.Instance.AddToActionsList("=== ExportDatabaseToExcel START ==="); MainForm.Instance.AddToActionsList("=== ExportDatabaseToExcel START ===");
using (OleDbConnection conn = new(connString)) using OleDbConnection conn = new(Access.connString);
conn.Open();
MainForm.Instance.AddToActionsList("Connected to Access database.");
DateTime now = DateTime.Now;
// Read LastStatsRun from UniversalData (if any)
DateTime? lastStatsRun = null;
try
{ {
conn.Open(); using (OleDbCommand cmdPeriod = new OleDbCommand("SELECT LastStatsRun FROM UniversalData", conn))
MainForm.Instance.AddToActionsList("Connected to Access database.");
DateTime now = DateTime.Now;
// Read LastStatsRun from UniversalData (if any)
DateTime? lastStatsRun = null;
try
{ {
using (var cmdPeriod = new OleDbCommand("SELECT LastStatsRun FROM UniversalData", conn)) object res = cmdPeriod.ExecuteScalar();
{ if (res != null && res != DBNull.Value)
object res = cmdPeriod.ExecuteScalar(); lastStatsRun = (DateTime)res;
if (res != null && res != DBNull.Value)
lastStatsRun = (DateTime)res;
}
MainForm.Instance.AddToActionsList($"Fetched LastStatsRun: {(lastStatsRun.HasValue ? lastStatsRun.Value.ToString("yyyy-MM-dd HH:mm:ss") : "none")}");
}
catch (Exception periodEx)
{
MainForm.Instance.AddToActionsList($"Warning: could not read LastStatsRun. Details: {periodEx.Message}");
} }
MainForm.Instance.AddToActionsList($"Fetched LastStatsRun: {(lastStatsRun.HasValue ? lastStatsRun.Value.ToString("yyyy-MM-dd HH:mm:ss") : "none")}");
}
catch (Exception periodEx)
{
MainForm.Instance.AddToActionsList($"Warning: could not read LastStatsRun. Details: {periodEx.Message}");
}
// ==================== MAIN STATS EXPORT ==================== // ==================== MAIN STATS EXPORT ====================
string selectColumns = @" string selectColumns = @"
ModelNumber, ModelNumber,
[Total Tests Run], [Total Tests Run],
[Pre Tests Passed], [Pre Tests Passed],
@@ -68,71 +61,71 @@ namespace AiQ_GUI.Microsoft
[Visual Test Fail - Not All Rear Screws Fitted], [Visual Test Fail - Not All Rear Screws Fitted],
[Visual Test Fail - Unit rattles]"; [Visual Test Fail - Unit rattles]";
string query = $@" string query = $@"
SELECT SELECT
{selectColumns} {selectColumns}
FROM AiQ"; FROM AiQ";
OleDbDataAdapter adapter = new(query, conn); OleDbDataAdapter adapter = new(query, conn);
DataTable dataTable = new(); DataTable dataTable = new();
adapter.Fill(dataTable); adapter.Fill(dataTable);
if (dataTable.Rows.Count == 0) if (dataTable.Rows.Count == 0)
{ {
MainForm.Instance.AddToActionsList("No data found in AiQ table."); MainForm.Instance.AddToActionsList("No data found in AiQ table.");
return; return;
} }
string monthName = now.ToString("MMMM"); string monthName = now.ToString("MMMM");
string sheetName = $"{monthName} Stats"; string sheetName = $"{monthName} Stats";
MainForm.Instance.AddToActionsList($"Adding sheet: {sheetName}"); MainForm.Instance.AddToActionsList($"Adding sheet: {sheetName}");
XLWorkbook workbook; XLWorkbook workbook;
if (System.IO.File.Exists(exportPath)) if (File.Exists(exportPath))
{ {
workbook = new XLWorkbook(exportPath); workbook = new XLWorkbook(exportPath);
MainForm.Instance.AddToActionsList("Opened existing workbook."); MainForm.Instance.AddToActionsList("Opened existing workbook.");
} }
else else
{ {
workbook = new XLWorkbook(); workbook = new XLWorkbook();
MainForm.Instance.AddToActionsList("Created new workbook (file not found)."); MainForm.Instance.AddToActionsList("Created new workbook (file not found).");
} }
if (workbook.Worksheets.Contains(sheetName)) if (workbook.Worksheets.Contains(sheetName))
{ {
workbook.Worksheet(sheetName).Delete(); workbook.Worksheet(sheetName).Delete();
MainForm.Instance.AddToActionsList($"Deleted old sheet: {sheetName}"); MainForm.Instance.AddToActionsList($"Deleted old sheet: {sheetName}");
} }
var ws = workbook.Worksheets.Add(sheetName); IXLWorksheet ws = workbook.Worksheets.Add(sheetName);
ws.Cell(1, 1).InsertTable(dataTable, "AiQ_Stats", true); ws.Cell(1, 1).InsertTable(dataTable, "AiQ_Stats", true);
ws.Columns().AdjustToContents(); ws.Columns().AdjustToContents();
// Write Data Period label and computed period string to the right of the table // Write Data Period label and computed period string to the right of the table
int infoCol = dataTable.Columns.Count + 2; int infoCol = dataTable.Columns.Count + 2;
ws.Cell(1, infoCol).Value = "Data Period"; ws.Cell(1, infoCol).Value = "Data Period";
string periodText; string periodText;
if (lastStatsRun.HasValue) if (lastStatsRun.HasValue)
{ {
periodText = $"{lastStatsRun.Value:yyyy-MM-dd HH:mm:ss} - {now:yyyy-MM-dd HH:mm:ss}"; periodText = $"{lastStatsRun.Value:yyyy-MM-dd HH:mm:ss} - {now:yyyy-MM-dd HH:mm:ss}";
} }
else else
{ {
periodText = $"Up to {now:yyyy-MM-dd HH:mm:ss}"; periodText = $"Up to {now:yyyy-MM-dd HH:mm:ss}";
} }
ws.Cell(2, infoCol).Value = periodText; ws.Cell(2, infoCol).Value = periodText;
// ==================== DIAGS STATS EXPORT (CURRENT MONTH ONLY) ==================== // ==================== DIAGS STATS EXPORT (CURRENT MONTH ONLY) ====================
MainForm.Instance.AddToActionsList("Exporting DiagsStats for current month..."); MainForm.Instance.AddToActionsList("Exporting DiagsStats for current month...");
DateTime monthStart = new DateTime(now.Year, now.Month, 1); DateTime monthStart = new(now.Year, now.Month, 1);
DateTime nextMonth = monthStart.AddMonths(1); DateTime nextMonth = monthStart.AddMonths(1);
string diagsQuery = @" string diagsQuery = @"
SELECT SELECT
[Date], [Date],
[Model], [Model],
@@ -143,129 +136,128 @@ namespace AiQ_GUI.Microsoft
FROM DiagsStats FROM DiagsStats
WHERE [Date] >= ? AND [Date] < ?"; WHERE [Date] >= ? AND [Date] < ?";
using (var diagsCmd = new OleDbCommand(diagsQuery, conn)) using (OleDbCommand diagsCmd = new OleDbCommand(diagsQuery, conn))
{
diagsCmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = monthStart });
diagsCmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = nextMonth });
using (OleDbDataAdapter diagsAdapter = new OleDbDataAdapter(diagsCmd))
{ {
diagsCmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = monthStart }); DataTable diagsTable = new();
diagsCmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = nextMonth }); diagsAdapter.Fill(diagsTable);
using (var diagsAdapter = new OleDbDataAdapter(diagsCmd)) if (diagsTable.Rows.Count > 0)
{ {
DataTable diagsTable = new(); int startRow = (ws.LastRowUsed()?.RowNumber() ?? 0) + 3;
diagsAdapter.Fill(diagsTable);
if (diagsTable.Rows.Count > 0) ws.Cell(startRow, 1).Value = "Diags Stats (Current Month)";
{ ws.Cell(startRow, 1).Style.Font.Bold = true;
int startRow = (ws.LastRowUsed()?.RowNumber() ?? 0) + 3; ws.Cell(startRow, 1).Style.Font.FontSize = 12;
ws.Cell(startRow, 1).Value = "Diags Stats (Current Month)"; ws.Cell(startRow + 1, 1).InsertTable(diagsTable, "Diags_Stats", true);
ws.Cell(startRow, 1).Style.Font.Bold = true; ws.Columns().AdjustToContents();
ws.Cell(startRow, 1).Style.Font.FontSize = 12;
ws.Cell(startRow + 1, 1).InsertTable(diagsTable, "Diags_Stats", true); MainForm.Instance.AddToActionsList($"Added DiagsStats ({diagsTable.Rows.Count} rows) starting at row {startRow}.");
ws.Columns().AdjustToContents();
MainForm.Instance.AddToActionsList($"Added DiagsStats ({diagsTable.Rows.Count} rows) starting at row {startRow}.");
}
} }
} }
}
// ==================== SAVE MAIN SHEET ==================== // ==================== SAVE MAIN SHEET ====================
workbook.SaveAs(exportPath); workbook.SaveAs(exportPath);
MainForm.Instance.AddToActionsList($"Added {sheetName} to workbook: {exportPath}"); MainForm.Instance.AddToActionsList($"Added {sheetName} to workbook: {exportPath}");
workbook.Dispose(); workbook.Dispose();
// ==================== BACKUP TABLE (ALWAYS, BEFORE RESET) ==================== // ==================== BACKUP TABLE (ALWAYS, BEFORE RESET) ====================
string ts = now.ToString("yyyyMMdd_HHmmss"); string ts = now.ToString("yyyyMMdd_HHmmss");
string backupTableName = $"Ztats_{ts}"; string backupTableName = $"Ztats_{ts}";
if (backupTableName.Length > 64) if (backupTableName.Length > 64)
backupTableName = backupTableName.Substring(0, 64); backupTableName = backupTableName.Substring(0, 64);
string backupSql = $@" string backupSql = $@"
SELECT SELECT
{selectColumns} {selectColumns}
INTO [{backupTableName}] INTO [{backupTableName}]
FROM AiQ"; FROM AiQ";
try
{
using (OleDbCommand cmdBackup = new(backupSql, conn))
{
cmdBackup.ExecuteNonQuery();
}
MainForm.Instance.AddToActionsList($"Created backup table: [{backupTableName}]");
}
catch (Exception backupEx)
{
MainForm.Instance.AddToActionsList($"ERROR creating backup table [{backupTableName}]. Aborting reset to protect data. Details: {backupEx.Message}");
MainForm.Instance.AddToActionsList("=== ExportDatabaseToExcel END (backup failed, no reset) ===");
return;
}
using (OleDbTransaction tx = conn.BeginTransaction())
{
try try
{ {
using (var cmdBackup = new OleDbCommand(backupSql, conn)) string resetSql = @"
UPDATE AiQ SET
[Total Tests Run] = 0,
[Pre Tests Passed] = 0,
[Pre Tests Failed] = 0,
[Final Tests Passed] = 0,
[Final Tests Failed] = 0,
[RMA Total Tests Run] = 0,
[RMA Pre Tests Passed] = 0,
[RMA Pre Tests Failed] = 0,
[RMA Final Tests Passed] = 0,
[RMA Final Tests Failed] = 0,
[Diagnostic Failure] = 0,
[Failed To Set Model Or Serial Number] = 0,
[Camera Not In Test Tube] = 0,
[Could Not Zoom Modules To 8000] = 0,
[Could Not Zoom Modules To Full Wide] = 0,
[Please Get RMA Number From Operations Team Before Continuing] = 0,
[Please Get A Valid Vaxtor Product Key Before Continuing] = 0,
[Visual Test Fail - Sleeve Not Aligned] = 0,
[Visual Test Fail - Not All Front Screws Fitted] = 0,
[Visual Test Fail - Not All Rear Screws Fitted] = 0,
[Visual Test Fail - Unit rattles] = 0;";
using (OleDbCommand cmdReset = new(resetSql, conn, tx))
{ {
cmdBackup.ExecuteNonQuery(); int affected = cmdReset.ExecuteNonQuery();
MainForm.Instance.AddToActionsList($"Zeroed counters on AiQ rows: {affected} row(s).");
} }
MainForm.Instance.AddToActionsList($"Created backup table: [{backupTableName}]");
int updatedRows;
using (OleDbCommand cmdUpd = new("UPDATE UniversalData SET LastStatsRun = ?", conn, tx))
{
cmdUpd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = now });
updatedRows = cmdUpd.ExecuteNonQuery();
}
if (updatedRows == 0)
{
using OleDbCommand cmdIns = new("INSERT INTO UniversalData (LastStatsRun) VALUES (?)", conn, tx);
cmdIns.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = now });
cmdIns.ExecuteNonQuery();
MainForm.Instance.AddToActionsList("Inserted LastStatsRun row.");
}
else
{
MainForm.Instance.AddToActionsList("Updated LastStatsRun successfully.");
}
tx.Commit();
MainForm.Instance.AddToActionsList("Reset committed.");
} }
catch (Exception backupEx) catch (Exception resetEx)
{ {
MainForm.Instance.AddToActionsList($"ERROR creating backup table [{backupTableName}]. Aborting reset to protect data. Details: {backupEx.Message}"); tx.Rollback();
MainForm.Instance.AddToActionsList("=== ExportDatabaseToExcel END (backup failed, no reset) ==="); MainForm.Instance.AddToActionsList($"ERROR during reset, rolled back. Details: {resetEx.Message}");
return;
} }
using (var tx = conn.BeginTransaction()) conn.Close();
{
try
{
string resetSql = @"
UPDATE AiQ SET
[Total Tests Run] = 0,
[Pre Tests Passed] = 0,
[Pre Tests Failed] = 0,
[Final Tests Passed] = 0,
[Final Tests Failed] = 0,
[RMA Total Tests Run] = 0,
[RMA Pre Tests Passed] = 0,
[RMA Pre Tests Failed] = 0,
[RMA Final Tests Passed] = 0,
[RMA Final Tests Failed] = 0,
[Diagnostic Failure] = 0,
[Failed To Set Model Or Serial Number] = 0,
[Camera Not In Test Tube] = 0,
[Could Not Zoom Modules To 8000] = 0,
[Could Not Zoom Modules To Full Wide] = 0,
[Please Get RMA Number From Operations Team Before Continuing] = 0,
[Please Get A Valid Vaxtor Product Key Before Continuing] = 0,
[Visual Test Fail - Sleeve Not Aligned] = 0,
[Visual Test Fail - Not All Front Screws Fitted] = 0,
[Visual Test Fail - Not All Rear Screws Fitted] = 0,
[Visual Test Fail - Unit rattles] = 0;";
using (var cmdReset = new OleDbCommand(resetSql, conn, tx))
{
int affected = cmdReset.ExecuteNonQuery();
MainForm.Instance.AddToActionsList($"Zeroed counters on AiQ rows: {affected} row(s).");
}
int updatedRows;
using (var cmdUpd = new OleDbCommand("UPDATE UniversalData SET LastStatsRun = ?", conn, tx))
{
cmdUpd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = now });
updatedRows = cmdUpd.ExecuteNonQuery();
}
if (updatedRows == 0)
{
using (var cmdIns = new OleDbCommand("INSERT INTO UniversalData (LastStatsRun) VALUES (?)", conn, tx))
{
cmdIns.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = now });
cmdIns.ExecuteNonQuery();
MainForm.Instance.AddToActionsList("Inserted LastStatsRun row.");
}
}
else
{
MainForm.Instance.AddToActionsList("Updated LastStatsRun successfully.");
}
tx.Commit();
MainForm.Instance.AddToActionsList("Reset committed.");
}
catch (Exception resetEx)
{
tx.Rollback();
MainForm.Instance.AddToActionsList($"ERROR during reset, rolled back. Details: {resetEx.Message}");
}
}
} }
MainForm.Instance.AddToActionsList("=== ExportDatabaseToExcel END ==="); MainForm.Instance.AddToActionsList("=== ExportDatabaseToExcel END ===");

View File

@@ -7,9 +7,9 @@ namespace AiQ_GUI
{ {
const string webhookUrl = "https://default71bd136a1c65418fb59e927135629c.ac.environment.api.powerplatform.com:443/powerautomate/automations/direct/workflows/b27c5192e83f4f48b20c1b115985b0b3/triggers/manual/paths/invoke/?api-version=1&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=1-eCbYXms6xInRKHwz3tgAcdQ9x7CSjl3Yzw2V_1MlA"; const string webhookUrl = "https://default71bd136a1c65418fb59e927135629c.ac.environment.api.powerplatform.com:443/powerautomate/automations/direct/workflows/b27c5192e83f4f48b20c1b115985b0b3/triggers/manual/paths/invoke/?api-version=1&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=1-eCbYXms6xInRKHwz3tgAcdQ9x7CSjl3Yzw2V_1MlA";
public static async Task SendMssg(string ApprovalRow, string User) public static async Task SendMssg(string ApprovalRow, string User) // Sometimes You will need to reauthenticate the workflow
{ {
using HttpClient client = new HttpClient(); using HttpClient client = new();
string link = $"https://docs.google.com/spreadsheets/d/1bCcCr4OYqfjmydt6UqtmN4FQETezXmZRSStJdCCcqZM/edit#gid=1931079354&range=A{ApprovalRow}"; // Has to be parsed like this as teams doesnt hyperlink otherwise string link = $"https://docs.google.com/spreadsheets/d/1bCcCr4OYqfjmydt6UqtmN4FQETezXmZRSStJdCCcqZM/edit#gid=1931079354&range=A{ApprovalRow}"; // Has to be parsed like this as teams doesnt hyperlink otherwise
@@ -21,7 +21,7 @@ namespace AiQ_GUI
}; };
string json = JsonConvert.SerializeObject(payload); string json = JsonConvert.SerializeObject(payload);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json"); StringContent content = new(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(webhookUrl, content); HttpResponseMessage response = await client.PostAsync(webhookUrl, content);
} }

View File

@@ -1,6 +1,5 @@
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Principal; using System.Security.Principal;
namespace AiQ_GUI namespace AiQ_GUI
@@ -16,14 +15,11 @@ namespace AiQ_GUI
.Where(p => targetProcesses.Any(tp => p.ProcessName.Contains(tp))) .Where(p => targetProcesses.Any(tp => p.ProcessName.Contains(tp)))
.Select(clsProcess => .Select(clsProcess =>
{ {
using (clsProcess) try
{ {
try clsProcess.CloseMainWindow();
{
clsProcess.CloseMainWindow();
}
catch { }
} }
catch { }
return Task.CompletedTask; return Task.CompletedTask;
}); });
@@ -34,7 +30,7 @@ namespace AiQ_GUI
{ {
Logging.LogMessage($"Starting exe from {ExeLoc}"); Logging.LogMessage($"Starting exe from {ExeLoc}");
ProcessStartInfo processInfo = new ProcessStartInfo(ExeLoc) ProcessStartInfo processInfo = new(ExeLoc)
{ {
UseShellExecute = true, UseShellExecute = true,
Verb = "runas" Verb = "runas"
@@ -64,7 +60,7 @@ namespace AiQ_GUI
{ {
StartAsAdmin(ExeLoc); StartAsAdmin(ExeLoc);
} }
else if (runAsAdmin) else if (Properties.Settings.Default.FirstRun && runAsAdmin)
{ {
try try
{ {
@@ -97,43 +93,4 @@ namespace AiQ_GUI
} }
} }
} }
[ComImport, Guid("AF230D27-BABA-4E42-ACED-F524F22CFCE2")]
public interface INetFwRule
{
string Name { get; set; }
string Description { get; set; }
string ApplicationName { get; set; }
string ServiceName { get; set; }
int Protocol { get; set; }
string LocalPorts { get; set; }
string RemotePorts { get; set; }
string LocalAddresses { get; set; }
string RemoteAddresses { get; set; }
string IcmpTypesAndCodes { get; set; }
int Direction { get; set; }
object Interfaces { get; set; }
string InterfaceTypes { get; set; }
bool Enabled { get; set; }
string Grouping { get; set; }
int Profiles { get; set; }
bool EdgeTraversal { get; set; }
int Action { get; set; }
}
[ComImport, Guid("98325047-C671-4174-8D81-DEFCD3F03186")]
public interface INetFwPolicy2
{
int CurrentProfileTypes { get; }
void get_FirewallEnabled(int profileType, out bool enabled);
void put_FirewallEnabled(int profileType, bool enabled);
void get_ExcludedInterfaces(int profileType, out object interfaces);
void put_ExcludedInterfaces(int profileType, object interfaces);
int BlockAllInboundTraffic { get; set; }
int NotificationsDisabled { get; set; }
int UnicastResponsesToMulticastBroadcastDisabled { get; set; }
object Rules { get; }
object ServiceRestriction { get; }
// ...other members omitted for brevity
}
} }

View File

@@ -1,7 +1,9 @@
using System.Net; using System.Diagnostics;
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;
using System.Threading;
namespace AiQ_GUI namespace AiQ_GUI
{ {
@@ -10,7 +12,7 @@ namespace AiQ_GUI
public static HttpClient? SingleHTTPClient; public static HttpClient? SingleHTTPClient;
public static HttpClient Client => SingleHTTPClient ?? throw new InvalidOperationException("Client not initialized."); public static HttpClient Client => SingleHTTPClient ?? throw new InvalidOperationException("Client not initialized.");
public static void Initialize(string username, string password) public async static void Initialize(string username, string password)
{ {
HttpClientHandler handler = new() HttpClientHandler handler = new()
{ {
@@ -23,6 +25,21 @@ namespace AiQ_GUI
{ {
Timeout = TimeSpan.FromSeconds(20) Timeout = TimeSpan.FromSeconds(20)
}; };
bool UP = false;
int i = 0;
while (!UP)
{
UP = await PingIP("10.10.10.137");
await Task.Delay(500);
if (i >= 5)
{
//MainForm.Instance.AddToActionsList($"Could not initialise new network with USER: {username} & PSWD: {password}");
return; // Try 5 times max
}
i++;
}
} }
// Handles get and post to the camera API // Handles get and post to the camera API

View File

@@ -39,14 +39,8 @@ namespace AiQ_GUI
return elementID; return elementID;
} }
// Clicks the element with the specified ID using Chromedriver
public static void ClickElementByID(string elementID, ChromeDriver driver)
{
ClickElementByID(elementID, true, driver);
}
// Attempts to click the web element with the specified ID; refreshes the page and retries once if the initial attempt fails // Attempts to click the web element with the specified ID; refreshes the page and retries once if the initial attempt fails
public static void ClickElementByID(string elementID, bool tryagain, ChromeDriver driver) public static void ClickElementByID(string elementID, ChromeDriver driver, bool tryagain = true)
{ {
try try
{ {
@@ -61,7 +55,7 @@ namespace AiQ_GUI
driver.Navigate().Refresh(); driver.Navigate().Refresh();
WebDriverWait wait = new(driver, TimeSpan.FromSeconds(10)); WebDriverWait wait = new(driver, TimeSpan.FromSeconds(10));
wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete")); wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));
ClickElementByID(elementID, false, driver); ClickElementByID(elementID, driver, false);
} }
MainForm.Instance.AddToActionsList("Could not click " + elementID); MainForm.Instance.AddToActionsList("Could not click " + elementID);

View File

@@ -138,7 +138,6 @@ namespace AiQ_GUI
// Create Test report in the same directory as the final test reports. // Create Test report in the same directory as the final test reports.
string soakLogPath = LDS.MAVPath + SoakLogFile; string soakLogPath = LDS.MAVPath + SoakLogFile;
string SoakTestPath = PDF.CreateSoakTestReport(CamInfo, MainForm.Instance.CbBxUserName.Text, DateTime.Now, soakLogPath); string SoakTestPath = PDF.CreateSoakTestReport(CamInfo, MainForm.Instance.CbBxUserName.Text, DateTime.Now, soakLogPath);
if (SoakTestPath != null) if (SoakTestPath != null)
@@ -207,7 +206,7 @@ namespace AiQ_GUI
await Task.Delay(500); await Task.Delay(500);
// Take bright image // Take bright image
Image ImageBright = await ImageProcessing.GetProcessedImage("Infrared-snapshot", IP, DevPass); Image ImageBright = await ImageProcessing.GetProcessedImage("Infrared", IP, DevPass);
if (ImageBright == null) if (ImageBright == null)
{ {
Logging.LogWarningMessage($"Bright image is null for {controlType} at setting {SettingMinMax[0]}", SoakLogFile); Logging.LogWarningMessage($"Bright image is null for {controlType} at setting {SettingMinMax[0]}", SoakLogFile);
@@ -221,7 +220,7 @@ namespace AiQ_GUI
await Task.Delay(500); await Task.Delay(500);
// Take dark image // Take dark image
Image ImageDark = await ImageProcessing.GetProcessedImage("Infrared-snapshot", IP, DevPass); Image ImageDark = await ImageProcessing.GetProcessedImage("Infrared", IP, DevPass);
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);

View File

@@ -1,73 +0,0 @@
{
"id": "GLOBAL--Device",
"configHash": "101478235",
"propHardwarePlatform": {
"value": "JETSON_NANO",
"datatype": "mav.flexi.web.pages.SystemConfig$HardwareConfigurationOption",
"accepted": "[NONE, JETSON_NANO, JETSON_XAVIER, RASPBERRY_PI_CM4, RASPBERRY_PI_CM5, DEVELOPER_WORKSTATION]"
},
"propApplicationPlatform": {
"value": "CUSTOM",
"datatype": "mav.flexi.web.pages.SystemConfig$ApplicationConfigurationOption",
"accepted": "[NONE, CUSTOM, MONO_BASIC_ANPR, DUAL_CAMERA_BASIC_ANPR, SINGLE_SAF_DUAL_CAMERA_BASIC_ANPR, DUAL_CAMERA_STREAMING, BAYGUARD, SITE_ACCESS_CONTROL_WITH_MODBUS, WAPOL_IQ50_VRM_CORRECTION, BAYWATCH_MONO_CAM, BAYWATCH_DUAL_CAM, BAYWATCH_TRIPLE_CAM, BAYWATCH_QUADRUPLE_CAM, IN_CAR_ANPR]"
},
"propDeviceName": {
"value": "Test Nano",
"datatype": "java.lang.String"
},
"propLocalTimeZone": {
"value": "Europe/London (UTC+00)",
"datatype": "mav.util.TimeZoneEnum",
"accepted": "[Africa/Cairo (UTC+02), Africa/Johannesburg (UTC+02), Africa/Lagos (UTC+01), Africa/Monrousing (UTC+00), America/Anchorage (UTC-09), America/Chicago (UTC-06), America/Denver (UTC-07), America/Edmonton (UTC-07), America/Jamaica (UTC-05), America/Los Angeles (UTC-08), America/Mexico City (UTC-06), America/Montreal (UTC-05), America/New/York (UTC-05), America/Phoenix (UTC-07), America/Puerto Rico (UTC-04), America/Sao Paulo (UTC-03), America/Toronto (UTC-05), America/Vancouver (UTC-08), Asia/Hong Kong (UTC+08), Asia/Jerusalem (UTC+02), Asia/Manila (UTC+08), Asia/Seoul (UTC+09), Asia/Tokyo (UTC+09), Atlantic/Reykjavik (UTC+00), Australia/Perth (UTC+08), Australia/Sydney (UTC+10), Europe/Athens (UTC+02), Europe/Berlin (UTC+01), Europe/Brussels (UTC+01), Europe/Copenhagen (UTC+01), Europe/London (UTC+00), Europe/Madrid (UTC+01), Europe/Moscow (UTC+04), Europe/Paris (UTC+01), Europe/Prague (UTC+01), Europe/Rome (UTC+01), Europe/Warsaw (UTC+01), Pacific/Guam (UTC+10), Pacific/Honolulu (UTC-10), UTC (UTC-00)]"
},
"propTimeSource": {
"value": "SNTP Only",
"datatype": "mav.flexi.web.pages.SystemConfig$TimeSourceEnum",
"accepted": "[SNTP Only, GPS Only, SNTP + Soft GPS]"
},
"propSNTPServer": {
"value": "1.uk.pool.ntp.org",
"datatype": "java.lang.String"
},
"propSNTPIntervalMinutes": {
"value": "public long mav.flexi.web.pages.SystemConfig$DeviceConfig.propSNTPIntervalMinutes",
"datatype": "long",
"range": {
"minimum": "1.000000",
"maximum": "99999.000000"
}
},
"propOutputsMaxTimeSyncAge": {
"value": "24 hours",
"datatype": "mav.flexi.web.pages.SystemConfig$HoursEnum",
"accepted": "[NO LIMIT, 01 hours, 02 hours, 03 hours, 04 hours, 05 hours, 06 hours, 07 hours, 08 hours, 09 hours, 10 hours, 11 hours, 12 hours, 18 hours, 24 hours, 36 hours, 48 hours, 72 hours, 96 hours]"
},
"propReservedMemoryMegabytes": {
"value": "public long mav.flexi.web.pages.SystemConfig$DeviceConfig.propReservedMemoryMegabytes",
"datatype": "long",
"range": {
"minimum": "1000.000000",
"maximum": "128000.000000"
}
},
"propClientTrustStorePassword": {
"value": "",
"datatype": "java.lang.String"
},
"propFlexiScriptPath": {
"value": "/home/mav/FlexiAI/bin/FlexiAI",
"datatype": "java.lang.String"
},
"propVideo0Tee": {
"value": "video0tee",
"datatype": "java.lang.String"
},
"propVideo1Tee": {
"value": "video1tee",
"datatype": "java.lang.String"
},
"propRestartCameraModulesOnBoot": {
"value": "true",
"datatype": "boolean"
}
}