#:project ../PrepStep/PrepStep.csproj #:property TargetFramework=net10.0-windows #:package Microsoft.Data.Sqlite@10.0.12 // dotnet run scripts/BomViaDb.cs -- [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 -- [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>(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(StringComparer.OrdinalIgnoreCase); var nonTrovati = new List(); var codiciVisti = new HashSet(StringComparer.OrdinalIgnoreCase) { codiceProprio, codiceRadice }; var daScansionare = new Queue(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;