Files
prep-step/scripts/BomViaDb.cs
T

83 lines
3.7 KiB
C#
Raw Permalink Normal View History

#:project ../PrepStep/PrepStep.csproj
#:property TargetFramework=net10.0-windows
#:package Microsoft.Data.Sqlite@10.0.12
// dotnet run scripts/BomViaDb.cs -- <file.asm> [cad_index.db] [output.txt]
//
// Stessa risoluzione ricorsiva dell'albero componenti di Form1.RisolviAlberoComponenti
// nella GUI, da riga di comando: usa direttamente PrepStep.AssemblyComponentScanner (via
// "#:project", niente ProjectReference separato) invece di duplicare l'euristica di
// estrazione/placement. Un'unica fonte di verità - niente rischio che script e GUI
// divergano silenziosamente, come accaduto prima di questa riscrittura (vedi
// docs/formato-asm.md).
using Microsoft.Data.Sqlite;
using PrepStep;
if (args.Length < 1)
{
Console.WriteLine("Uso: dotnet run BomViaDb.cs -- <file.asm> [cad_index.db] [output.txt]");
return 1;
}
string asmIniziale = args[0];
string dbPath = args.Length > 1 ? args[1] : "cad_index.db";
string outPath = args.Length > 2 ? args[2] : "BOM.txt";
using var connection = new SqliteConnection($"Data Source={dbPath};Mode=ReadOnly");
connection.Open();
// Tutti i codici prt/asm noti all'indice, caricati una volta sola: il lookup per ogni
// candidato estratto dal binario diventa O(1) invece di una query per file, e serve anche
// a EstraiCodiciReferenziati per validare i token generici delle parti commerciali.
var codiciNoti = new Dictionary<string, List<(string Tipo, string Percorso)>>(StringComparer.OrdinalIgnoreCase);
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT codice, tipo_file, percorso_completo FROM files_ultima_versione "
+ "WHERE LOWER(tipo_file) IN ('prt', 'asm') ORDER BY codice, tipo_file, data_modifica DESC";
using var reader = command.ExecuteReader();
while (reader.Read())
{
string codice = reader.GetString(0);
string tipo = reader.GetString(1).ToLowerInvariant();
var lista = codiciNoti.TryGetValue(codice, out var l) ? l : codiciNoti[codice] = new();
if (!lista.Any(x => x.Tipo == tipo))
lista.Add((tipo, reader.GetString(2)));
}
}
Console.WriteLine($"Codici prt/asm noti nell'indice: {codiciNoti.Count}");
var codiceProprio = Path.GetFileName(asmIniziale).Split('.')[0];
var codiceRadice = AssemblyComponentScanner.CodiceRadice(codiceProprio);
var componentiTrovati = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
var nonTrovati = new List<string>();
var codiciVisti = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { codiceProprio, codiceRadice };
var daScansionare = new Queue<string>(new[] { asmIniziale });
while (daScansionare.Count > 0)
{
var asm = daScansionare.Dequeue();
foreach (var codice in AssemblyComponentScanner.EstraiCodiciReferenziati(asm, codiciNoti.Keys))
{
// Solo i codici già confermati da EstraiCodiciReferenziati arrivano qui: nessun
// rischio del bug "primo fallimento blocca per sempre" (vedi docs/formato-asm.md),
// perché il placement è già stato verificato per questo file specifico al suo
// interno, da zero, indipendentemente da cosa è successo negli altri file.
if (!codiciVisti.Add(codice))
continue;
if (!codiciNoti.TryGetValue(codice, out var componenti))
{
nonTrovati.Add(codice);
continue;
}
componentiTrovati.Add(codice);
foreach (var (tipo, percorso) in componenti)
if (tipo == "asm")
daScansionare.Enqueue(percorso);
}
}
File.WriteAllLines(outPath, componentiTrovati);
Console.WriteLine($"{componentiTrovati.Count} componenti scritti in {outPath}");
Console.WriteLine($"Candidati non nell'indice (scartati, {nonTrovati.Count}): {string.Join(", ", nonTrovati.Take(30))}"
+ (nonTrovati.Count > 30 ? ", ..." : ""));
return 0;