Due bug reali trovati confrontando l'estrazione BOM con la BOM ufficiale Creo: un componente scartato per placement fallito in un file veniva bloccato per sempre anche quando il file giusto aveva un vero placement (perdendo interi sotto-alberi come 9256510047), e i codici con suffisso (_C01 ecc.) richiedevano sempre placement esplicito invece dell'eccezione per occorrenze ripetute già presente nella GUI. Entrambi corretti. AssemblyComponentScanner reso pubblico e scripts/BomViaDb.cs, ExtractCreoStrings.cs riscritti per referenziarlo via "#:project" invece di duplicare l'euristica: un'unica fonte di verità condivisa da GUI e script standalone. Aggiunti test di integrazione per Indexer.ScanAsync (mai coperto oltre al parsing del nome file) e test end-to-end che ricostruiscono l'intera pipeline disco -> indice -> risoluzione albero componenti senza toccare la UI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
84 lines
3.7 KiB
C#
84 lines
3.7 KiB
C#
#: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;
|