Beginning of the Implementing multple cameras into the AiQ GUI

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

View File

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