Files
prep-step/scripts/ExtractCreoStrings.cs
T

98 lines
4.0 KiB
C#
Raw Normal View History

#:package Microsoft.Data.Sqlite@10.0.12
// dotnet run scripts/ExtractCreoStrings.cs -- <file.asm> [cad_index.db] [output.txt]
//
// Estrae dal file .asm i codici dei componenti referenziati (stessa euristica di
// CadSearchGui/AssemblyComponentScanner.cs: i codici veri compaiono più volte nel
// file, il rumore binario quasi sempre una sola volta; gli ID interni
// "ModelView#<numero>" sono esclusi via lookbehind - vedi scripts/Analyze.cs) e poi
// risolve ricorsivamente i sotto-assiemi cercandoli nell'indice cad_index.db, con lo
// stesso BFS di Form1.AssiemeCompleto: un assieme può referenziare solo i propri
// sotto-assiemi diretti, non l'intero albero, quindi va camminato livello per livello.
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Data.Sqlite;
if (args.Length < 1)
{
Console.WriteLine("Uso: dotnet run ExtractCreoStrings.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";
// Seconda alternativa: viti/dadi/rosette normalizzati UNI, definiti come family table in
// Creo. Compaiono come "Componente ID N (R12--_-_U6592<UNI6592>.PRT)": "R12--_-_U6592" è
// il nome della singola istanza (riga della family table, non un file separato su disco),
// il file reale indicizzato è il generico "uni6592.prt.<versione>" - va quindi estratto
// solo il contenuto tra "<" e ">".
var codiceRegex = new Regex(
@"(?<!ModelView#)\b\d{8,12}(?:_[A-Za-z]\d{2,4}|[A-Za-z]{0,2})\b|(?<=<)UNI\d+(?=>)",
RegexOptions.Compiled);
const int occorrenzeMinime = 2;
HashSet<string> EstraiCodiciReferenziati(string percorsoAsm)
{
var testo = Encoding.Latin1.GetString(File.ReadAllBytes(percorsoAsm));
var occorrenze = new Dictionary<string, int>();
foreach (Match m in codiceRegex.Matches(testo))
occorrenze[m.Value] = occorrenze.GetValueOrDefault(m.Value) + 1;
return occorrenze.Where(kv => kv.Value >= occorrenzeMinime).Select(kv => kv.Key).ToHashSet();
}
using var connection = new SqliteConnection($"Data Source={dbPath};Mode=ReadOnly");
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "SELECT tipo_file, percorso_completo FROM files_ultima_versione "
+ "WHERE LOWER(codice) = LOWER(@codice) AND LOWER(tipo_file) IN ('prt', 'asm')";
var pCodice = command.Parameters.Add("@codice", SqliteType.Text);
List<(string Tipo, string Percorso)> CercaComponenti(string codice)
{
pCodice.Value = codice;
var risultato = new List<(string, string)>();
var tipiVisti = new HashSet<string>();
using var reader = command.ExecuteReader();
while (reader.Read())
{
string tipo = reader.GetString(0).ToLowerInvariant();
if (tipiVisti.Add(tipo))
risultato.Add((tipo, reader.GetString(1)));
}
return risultato;
}
var codiciVisti = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { Path.GetFileName(asmIniziale).Split('.')[0] };
var daVisitare = new Queue<string>();
daVisitare.Enqueue(asmIniziale);
var componentiTrovati = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
var nonTrovati = new List<string>();
while (daVisitare.Count > 0)
{
var file = daVisitare.Dequeue();
foreach (var codice in EstraiCodiciReferenziati(file))
{
if (!codiciVisti.Add(codice))
continue;
var componenti = CercaComponenti(codice);
if (componenti.Count == 0)
{
nonTrovati.Add(codice);
continue;
}
componentiTrovati.Add(codice);
var percorsoAsm = componenti.FirstOrDefault(c => c.Tipo == "asm").Percorso;
if (percorsoAsm is not null)
daVisitare.Enqueue(percorsoAsm);
}
}
File.WriteAllLines(outPath, componentiTrovati);
Console.WriteLine($"{componentiTrovati.Count} componenti scritti in {outPath}");
if (nonTrovati.Count > 0)
Console.WriteLine($"Codici trovati nel file ma non nell'indice (scartati): {string.Join(", ", nonTrovati)}");
return 0;