Compare commits

..

7 Commits

21 changed files with 342 additions and 580 deletions

6
AiQ_GUI.Designer.cs generated
View File

@@ -270,9 +270,10 @@ namespace AiQ_GUI
CbBxUserName.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold); CbBxUserName.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
CbBxUserName.ForeColor = SystemColors.Control; CbBxUserName.ForeColor = SystemColors.Control;
CbBxUserName.FormattingEnabled = true; CbBxUserName.FormattingEnabled = true;
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", "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;
@@ -586,7 +587,6 @@ namespace AiQ_GUI
BtnTest.TabIndex = 189; BtnTest.TabIndex = 189;
BtnTest.Text = "Test"; BtnTest.Text = "Test";
BtnTest.UseVisualStyleBackColor = false; BtnTest.UseVisualStyleBackColor = false;
BtnTest.Visible = false;
BtnTest.Click += BtnTest_Click; BtnTest.Click += BtnTest_Click;
// //
// PicBxIRF2 // PicBxIRF2
@@ -1145,7 +1145,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;
// //

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
@@ -28,8 +25,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 +37,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
@@ -108,7 +103,7 @@ namespace AiQ_GUI
CbBxIris.SelectedIndex = lds.Iris; CbBxIris.SelectedIndex = lds.Iris;
CbBxGain.SelectedIndex = lds.Gain; CbBxGain.SelectedIndex = lds.Gain;
if (lds.User == "Bradley" || lds.User == "Sophie") if (lds.User == "Bradley")
BtnTest.Visible = true; BtnTest.Visible = true;
TxBxCheckValid(TxBxPsuIP); // Set save button color if valid TxBxCheckValid(TxBxPsuIP); // Set save button color if valid
@@ -143,16 +138,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 +186,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.
@@ -209,7 +199,6 @@ namespace AiQ_GUI
if (!LEDreply.Contains("Power levels set successfully")) if (!LEDreply.Contains("Power levels set successfully"))
AddToActionsList($"LED level could not be set: {LEDreply}"); AddToActionsList($"LED level could not be set: {LEDreply}");
} }
await FlexiAPI.SetVaxtorMinMaxPlate(CamOnTest.IP);
DateTime PCTime = DateTime.Now; // Grab PC time as close to the API as possible to pass onto PDF later DateTime PCTime = DateTime.Now; // Grab PC time as close to the API as possible to pass onto PDF later
@@ -229,6 +218,7 @@ 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, "Failed due to action box text and/or red label");// 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 TestFailed(BtnStartTest, "Diagnostic Failure");// If approved then pass otherwise GUI would have restarted before getting to TestPassed.
} }
@@ -236,69 +226,6 @@ namespace AiQ_GUI
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();
@@ -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);
} }
} }
@@ -423,6 +352,15 @@ namespace AiQ_GUI
// Indicators to the user the test has failed // Indicators to the user the test has failed
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>()
@@ -430,18 +368,7 @@ namespace AiQ_GUI
.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."))
@@ -461,6 +388,7 @@ namespace AiQ_GUI
Logging.LogErrorMessage(FullFailureValues); 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;
@@ -516,11 +444,11 @@ namespace AiQ_GUI
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
{ {
Stats("Pre Tests Passed"); Access.Stats("Pre Tests Passed", CamOnTest.Model);
} }
else else
{ {
Stats("RMA Pre Tests Passed"); Access.Stats("RMA Pre Tests Passed", CamOnTest.Model);
} }
if (await DisplayQuestion("Test passed, restart?")) if (await DisplayQuestion("Test passed, restart?"))
@@ -535,24 +463,22 @@ 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 +600,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 +676,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 +695,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 +723,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))
{ {
@@ -1136,9 +1060,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;
@@ -1790,75 +1713,15 @@ 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 async 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;
@@ -1883,11 +1746,14 @@ namespace AiQ_GUI
//await Task.Delay(3000); // Wait for server to start //await Task.Delay(3000); // Wait for server to start
//BtnStartTest_Click(sender, e); //BtnPreTest_Click(sender, e);
Teams.SendMssg("10", "Test User");
stopWatchTest.Stop(); stopWatchTest.Stop();
//AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff")); AddToActionsList("RunTime " + stopWatchTest.Elapsed.ToString(@"hh\:mm\:ss\.ff"));
} }
} }
} }

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.0.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="141.0.7390.12200" />
<PackageReference Include="SSH.NET" Version="2025.0.0" /> <PackageReference Include="SSH.NET" Version="2025.0.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

@@ -35,6 +35,8 @@ 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
MainForm.Instance.AddToActionsList(JSONdata);
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);
} }
@@ -159,37 +161,6 @@ 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)
{
try
{
// Build JSON array for Vaxtor min/max plate configuration
string[,] Vaxtor_JSON = { { "propMinCharHeight", "18" }, { "propMinGlobalConfidence", "30" } };
string response = await HTTP_Update("RaptorOCR".Trim(), IP, Vaxtor_JSON);
// Treat "operation was canceled" as a successful apply
if (response.Contains("The operation was canceled", StringComparison.OrdinalIgnoreCase))
{
Logging.LogMessage("SetVaxtorMinMaxPlate: Camera applied config but closed connection early (safe to ignore).");
return true;
}
if (response.Contains("error", StringComparison.OrdinalIgnoreCase))
{
MainForm.Instance.DisplayQuestion($"SetVaxtorMinMaxPlate: failed - Please set manually");
return false;
}
return true;
}
catch (Exception ex)
{
MainForm.Instance.AddToActionsList($"Could not set Vaxtor Plate height and min confidence: {ex.Message}");
return false;
}
}
public static async Task<bool> SetZoomLockOn(string IP) public static async Task<bool> SetZoomLockOn(string IP)
{ {
@@ -435,13 +406,5 @@ namespace AiQ_GUI
public class Property public class Property
{ {
public string Value { get; set; } = string.Empty; public string Value { get; set; } = string.Empty;
public string datatype { get; set; }
}
public class VaxtorConfig
{
public string id { get; set; }
public long configHash { get; set; }
public Property propMinCharHeight { get; set; }
public Property propMinGlobalConfidence { get; set; }
} }
} }

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

@@ -116,7 +116,7 @@ namespace AiQ_GUI
if (requiresAuth) if (requiresAuth)
{ {
string authHeader = context.Request.Headers["Authorization"]; string authHeader = context.Request.Headers["Authorization"];
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Basic ")) if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Basic "))
{ {
context.Response.AddHeader("WWW-Authenticate", "Basic realm=\"FakeCamera\""); context.Response.AddHeader("WWW-Authenticate", "Basic realm=\"FakeCamera\"");

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

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;

2
LDS.cs
View File

@@ -35,9 +35,7 @@ namespace AiQ_GUI
} }
catch // If file can't deserialise catch // If file can't deserialise
{ {
MainForm.Instance.AddToActionsList("Error loading Local Data Store");
return null; // Return null to indicate failure return null; // Return null to indicate failure
} }
} }

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,59 +14,38 @@ 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(); conn.Open();
MainForm.Instance.AddToActionsList("Connected to Access database."); MainForm.Instance.AddToActionsList("Connected to Access database.");
DateTime now = DateTime.Now; 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)
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}");
}
// ==================== MAIN STATS EXPORT ==================== // ==================== MAIN STATS EXPORT ====================
string selectColumns = @" string selectColumns = @"
ModelNumber, ModelNumber,
[Total Tests Run], [Total Tests Run],
[Pre Tests Passed], [Pre Tests Passed],
[Pre Tests Failed], [Pre Tests Failed],
[Final Tests Passed], [Final Tests Passed],
[Final Tests Failed], [Final Tests Failed],
[RMA Total Tests Run], [RMA Total Tests Run],
[RMA Pre Tests Passed], [RMA Pre Tests Passed],
[RMA Pre Tests Failed], [RMA Pre Tests Failed],
[RMA Final Tests Passed], [RMA Final Tests Passed],
[RMA Final Tests Failed], [RMA Final Tests Failed],
[Diagnostic Failure], [Diagnostic Failure],
[Failed To Set Model Or Serial Number], [Failed To Set Model Or Serial Number],
[Camera Not In Test Tube], [Camera Not In Test Tube],
[Could Not Zoom Modules To 8000], [Could Not Zoom Modules To 8000],
[Could Not Zoom Modules To Full Wide], [Could Not Zoom Modules To Full Wide],
[Please Get RMA Number From Operations Team Before Continuing], [Please Get RMA Number From Operations Team Before Continuing],
[Please Get A Valid Vaxtor Product Key Before Continuing], [Please Get A Valid Vaxtor Product Key Before Continuing],
[Visual Test Fail - Sleeve Not Aligned], [Visual Test Fail - Sleeve Not Aligned],
[Visual Test Fail - Not All Front Screws Fitted], [Visual Test Fail - Not All Front Screws Fitted],
[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 {selectColumns} FROM AiQ";
SELECT
{selectColumns}
FROM AiQ";
OleDbDataAdapter adapter = new(query, conn); OleDbDataAdapter adapter = new(query, conn);
DataTable dataTable = new(); DataTable dataTable = new();
@@ -89,7 +63,7 @@ namespace AiQ_GUI.Microsoft
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.");
@@ -106,30 +80,17 @@ namespace AiQ_GUI.Microsoft
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 ws.Cell(1, dataTable.Columns.Count + 2).Value = "Exported:";
int infoCol = dataTable.Columns.Count + 2; ws.Cell(2, dataTable.Columns.Count + 2).Value = now.ToString("yyyy-MM-dd HH:mm:ss");
ws.Cell(1, infoCol).Value = "Data Period";
string periodText;
if (lastStatsRun.HasValue)
{
periodText = $"{lastStatsRun.Value:yyyy-MM-dd HH:mm:ss} - {now:yyyy-MM-dd HH:mm:ss}";
}
else
{
periodText = $"Up to {now:yyyy-MM-dd HH:mm:ss}";
}
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 = @"
@@ -137,36 +98,33 @@ namespace AiQ_GUI.Microsoft
[Date], [Date],
[Model], [Model],
[Red Diags Labels], [Red Diags Labels],
[RhTxBxActions Contents], [RhTxBxActions Contents]
[IsRma],
[RMA]
FROM DiagsStats FROM DiagsStats
WHERE [Date] >= ? AND [Date] < ?"; WHERE [Date] >= ? AND [Date] < ?";
using (var diagsCmd = new OleDbCommand(diagsQuery, conn)) OleDbDataAdapter diagsAdapter = new(diagsQuery, conn);
diagsAdapter.SelectCommand.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = monthStart });
diagsAdapter.SelectCommand.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = nextMonth });
DataTable diagsTable = new();
diagsAdapter.Fill(diagsTable);
if (diagsTable.Rows.Count > 0)
{ {
diagsCmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = monthStart }); int startRow = ws.LastRowUsed().RowNumber() + 3;
diagsCmd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = nextMonth });
using (var diagsAdapter = new OleDbDataAdapter(diagsCmd)) ws.Cell(startRow, 1).Value = "Diags Stats (Current Month)";
{ ws.Cell(startRow, 1).Style.Font.Bold = true;
DataTable diagsTable = new(); ws.Cell(startRow, 1).Style.Font.FontSize = 12;
diagsAdapter.Fill(diagsTable);
if (diagsTable.Rows.Count > 0) ws.Cell(startRow + 1, 1).InsertTable(diagsTable, "Diags_Stats", true);
{ ws.Columns().AdjustToContents();
int startRow = (ws.LastRowUsed()?.RowNumber() ?? 0) + 3;
ws.Cell(startRow, 1).Value = "Diags Stats (Current Month)"; MainForm.Instance.AddToActionsList($"Added DiagsStats ({diagsTable.Rows.Count} rows) starting at row {startRow}.");
ws.Cell(startRow, 1).Style.Font.Bold = true; }
ws.Cell(startRow, 1).Style.Font.FontSize = 12; else
{
ws.Cell(startRow + 1, 1).InsertTable(diagsTable, "Diags_Stats", true); MainForm.Instance.AddToActionsList("No DiagsStats data found for current month.");
ws.Columns().AdjustToContents();
MainForm.Instance.AddToActionsList($"Added DiagsStats ({diagsTable.Rows.Count} rows) starting at row {startRow}.");
}
}
} }
// ==================== SAVE MAIN SHEET ==================== // ==================== SAVE MAIN SHEET ====================
@@ -183,13 +141,14 @@ namespace AiQ_GUI.Microsoft
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 try
{ {
using (var cmdBackup = new OleDbCommand(backupSql, conn)) using (OleDbCommand cmdBackup = new(backupSql, conn))
{ {
cmdBackup.ExecuteNonQuery(); cmdBackup.ExecuteNonQuery();
} }
@@ -202,70 +161,68 @@ namespace AiQ_GUI.Microsoft
return; return;
} }
using (var tx = conn.BeginTransaction()) using OleDbTransaction tx = conn.BeginTransaction();
try
{ {
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 (OleDbCommand cmdReset = new(resetSql, conn, tx))
{ {
string resetSql = @" int affected = cmdReset.ExecuteNonQuery();
UPDATE AiQ SET MainForm.Instance.AddToActionsList($"Zeroed counters on AiQ rows: {affected} row(s).");
[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)
int updatedRows;
using (OleDbCommand cmdUpd = new("UPDATE UniversalData SET LastStatsRun = ?", conn, tx))
{ {
tx.Rollback(); cmdUpd.Parameters.Add(new OleDbParameter { OleDbType = OleDbType.Date, Value = now });
MainForm.Instance.AddToActionsList($"ERROR during reset, rolled back. Details: {resetEx.Message}"); 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 resetEx)
{
tx.Rollback();
MainForm.Instance.AddToActionsList($"ERROR during reset, rolled back. Details: {resetEx.Message}");
}
conn.Close();
} }
MainForm.Instance.AddToActionsList("=== ExportDatabaseToExcel END ==="); MainForm.Instance.AddToActionsList("=== ExportDatabaseToExcel END ===");

View File

@@ -7,7 +7,7 @@ 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 HttpClient();

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

@@ -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"
}
}