These appear in the .asm binary as a family-table instance name (e.g. "R6--_-_U6592<UNI6592>"), not a separate file on disk - only the generic "uni6592.prt" is actually indexed. Extract just the "<UNI...>" content instead of the full instance name, in both the standalone script and the production scanner, so standard hardware stops being silently dropped as "not found in index". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
99 lines
4.0 KiB
C#
99 lines
4.0 KiB
C#
#: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;
|