Fix runaway recursion and two missed-component gaps in .asm scanning
The bare root code of an assembly (e.g. 9284208188, without a _D... suffix) is always referenced inside its own suffixed sub-assemblies as a genuine placement pointing back to the general assembly. Following it during recursive resolution pulled in the entire product tree instead of just the requested sub-assembly (598 components collected instead of ~65 on 9284208188_D600). Fixed by seeding the visited-codes set with each starting assembly's root code, in both scripts/ExtractCreoStrings.cs and the two BFS call sites in Form1.cs (RisolviAlberoComponenti, AssiemeCompleto). Also closes two recall gaps found by comparing against a real Creo BOM report: some real placements only show up as the code repeated twice separated by a NUL byte rather than the usual "Componente ID N (...)" text, and family-table bearing codes (CUSCI_...) use the same <...> bracket convention as UNI hardware but weren't recognized. docs/formato-asm.md documents all three findings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9660c4883d
commit
a7e0df0247
4 files changed
+147
-69
No files matched your search
@@ -21,10 +21,12 @@ namespace CadSearchGui;
|
||||
/// "ModelView#<numero>", hanno la stessa forma di un codice (8-12 cifre) e
|
||||
/// compaiono più volte come un riferimento vero. Vedi docs/formato-asm.md.
|
||||
///
|
||||
/// Viti/dadi/rosette normalizzati UNI sono family table Creo: il nome che compare nel
|
||||
/// binario (es. "R6--_-_U6592<UNI6592>") è la singola istanza, non un file separato
|
||||
/// su disco - il file reale indicizzato è il generico "uni6592.prt.<versione>", quindi
|
||||
/// va estratto solo il contenuto tra "<" e ">". Vedi docs/formato-asm.md.
|
||||
/// Viti/dadi/rosette normalizzati UNI (e cuscinetti CUSCI_...) sono family table Creo: il
|
||||
/// nome che compare nel binario (es. "R6--_-_U6592<UNI6592>",
|
||||
/// "2510060303<CUSCI_ACBB7>") è la singola istanza, non un file separato su disco - il
|
||||
/// file reale indicizzato è il generico "uni6592.prt.<versione>"/
|
||||
/// "cusci_acbb7.prt.<versione>", quindi va estratto solo il contenuto tra "<" e
|
||||
/// ">". Vedi docs/formato-asm.md.
|
||||
///
|
||||
/// Lettera di revisione e suffisso "_..." possono comparire insieme sullo stesso codice
|
||||
/// (es. "9284200366A_C01"), e il suffisso dopo "_" può essere puramente numerico (es.
|
||||
@@ -34,20 +36,32 @@ namespace CadSearchGui;
|
||||
/// basta: può trattarsi del riferimento al nome di un altro assieme che lo contiene (non
|
||||
/// noto a priori, quindi non escludibile per confronto diretto come il proprio codice) o
|
||||
/// di un residuo di feature soppresse/superate (vedi docs/formato-asm.md). Per questi si
|
||||
/// richiede anche un vero placement "Componente ID N (<codice>...)"/
|
||||
/// "Component id N (<codice>...)" (la stringa cambia con la localizzazione Creo del
|
||||
/// file). Non si applica alle varianti con suffisso o bracket UNI: in alcuni file il
|
||||
/// placement riporta solo il nome istanza/generico senza il dettaglio del suffisso o del
|
||||
/// bracket, che vive altrove nei metadati - lì resta necessaria la sola euristica delle
|
||||
/// occorrenze ripetute.
|
||||
/// richiede anche un vero placement, in una delle due forme osservate nel binario:
|
||||
/// "Componente ID N (<codice>...)"/"Component id N (<codice>...)" (la stringa
|
||||
/// cambia con la localizzazione Creo del file), oppure il codice ripetuto due volte di
|
||||
/// seguito separato da un singolo byte NUL ("<codice>\0<codice>\0", osservato per
|
||||
/// componenti di family table non-UNI, es. cuscinetti "CUSCI_..."). Non si applica alle
|
||||
/// varianti con suffisso o bracket UNI: in alcuni file il placement riporta solo il nome
|
||||
/// istanza/generico senza il dettaglio del suffisso o del bracket, che vive altrove nei
|
||||
/// metadati - lì resta necessaria la sola euristica delle occorrenze ripetute.
|
||||
/// </summary>
|
||||
static class AssemblyComponentScanner
|
||||
{
|
||||
private static readonly Regex CodiceRegex = new(
|
||||
@"(?<!ModelView#)\b\d{8,12}[A-Za-z]{0,2}(?:_[A-Za-z]?\d{2,4})?\b|(?<=<)UNI\d+(?=>)", RegexOptions.Compiled);
|
||||
@"(?<!ModelView#)\b\d{8,12}[A-Za-z]{0,2}(?:_[A-Za-z]?\d{2,4})?\b|(?<=<)(?:UNI\d+|CUSCI_[A-Za-z0-9]+)(?=>)",
|
||||
RegexOptions.Compiled);
|
||||
private static readonly Regex CodiceNudoRegex = new(@"^\d{8,12}[A-Za-z]{0,2}$", RegexOptions.Compiled);
|
||||
private const int OccorrenzeMinime = 2;
|
||||
|
||||
/// <summary>Codice radice (parte prima del primo "_") di un codice componente. Un
|
||||
/// assieme con suffisso "_D.../_C..." referenzia sempre al suo interno anche l'assieme
|
||||
/// generale a cui appartiene (codice radice, senza suffisso) - riferimento fantasma
|
||||
/// verso l'alto, non un componente vero (vedi docs/formato-asm.md). Chi fa una
|
||||
/// risoluzione ricorsiva dell'albero deve seminare l'insieme dei codici già visitati
|
||||
/// anche con la radice del codice di partenza, non solo col codice stesso, altrimenti
|
||||
/// risale fino all'assieme generale e riporta dentro l'intero albero del macchinario.</summary>
|
||||
public static string CodiceRadice(string codice) => codice.Split('_')[0];
|
||||
|
||||
public static HashSet<string> EstraiCodiciReferenziati(string percorsoAsm)
|
||||
{
|
||||
var testo = Encoding.Latin1.GetString(File.ReadAllBytes(percorsoAsm));
|
||||
@@ -59,7 +73,8 @@ static class AssemblyComponentScanner
|
||||
var candidati = occorrenze.Where(kv => kv.Value >= OccorrenzeMinime).Select(kv => kv.Key).ToHashSet();
|
||||
|
||||
bool HaPlacement(string codice) =>
|
||||
Regex.IsMatch(testo, $@"Component(?:e)? id \d+ \([^()]*{Regex.Escape(codice)}[^()]*\)", RegexOptions.IgnoreCase);
|
||||
Regex.IsMatch(testo, $@"Component(?:e)? id \d+ \([^()]*{Regex.Escape(codice)}[^()]*\)", RegexOptions.IgnoreCase)
|
||||
|| testo.Contains(codice + "\0" + codice + "\0");
|
||||
|
||||
candidati.RemoveWhere(c => CodiceNudoRegex.IsMatch(c) && !HaPlacement(c));
|
||||
return candidati;
|
||||
|
||||
@@ -671,8 +671,10 @@ public partial class Form1 : Form
|
||||
IEnumerable<string> asmIniziali, IProgress<(int Fatti, int Totale)>? progress = null)
|
||||
{
|
||||
var daScansionare = new Queue<string>(asmIniziali);
|
||||
var codiciIniziali = daScansionare.Select(f => Path.GetFileName(f).Split('.')[0]);
|
||||
var codiciVisti = new HashSet<string>(
|
||||
daScansionare.Select(f => Path.GetFileName(f).Split('.')[0]), StringComparer.OrdinalIgnoreCase);
|
||||
codiciIniziali.Concat(codiciIniziali.Select(AssemblyComponentScanner.CodiceRadice)),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var trovati = new List<(string Codice, string Tipo, string Percorso)>();
|
||||
var codiciNonTrovati = new List<string>();
|
||||
@@ -827,7 +829,12 @@ public partial class Form1 : Form
|
||||
var daVisitare = new Queue<string>();
|
||||
daVisitare.Enqueue(percorsoAsmIniziale);
|
||||
var fileVisitati = new HashSet<string> { percorsoAsmIniziale };
|
||||
var codiciVisti = new HashSet<string> { Path.GetFileName(percorsoAsmIniziale).Split('.')[0] };
|
||||
var codiceIniziale = Path.GetFileName(percorsoAsmIniziale).Split('.')[0];
|
||||
var codiciVisti = new HashSet<string>
|
||||
{
|
||||
codiceIniziale,
|
||||
AssemblyComponentScanner.CodiceRadice(codiceIniziale),
|
||||
};
|
||||
|
||||
while (daVisitare.Count > 0)
|
||||
{
|
||||
|
||||
+58
-27
@@ -73,17 +73,43 @@ non essendo un componente. A differenza del proprio codice, non è noto a
|
||||
priori: dipende da dove/come il file è stato aperto o da chi lo referenzia,
|
||||
quindi non si può escludere per confronto diretto come per il caso
|
||||
precedente. La regola generale che discrimina questo caso: un componente
|
||||
vero compare **sempre** anche dentro un vero placement
|
||||
`Componente ID N (<codice>...)`/`Component id N (<codice>...)` (la stringa
|
||||
cambia con la localizzazione Creo con cui il file è stato salvato - osservate
|
||||
sia italiana sia inglese); un riferimento incidentale al nome di un altro
|
||||
assieme, anche se ripetuto più volte, non compare mai in quel contesto.
|
||||
`scripts/ExtractCreoStrings.cs` applica questa regola ai codici "nudi" (senza
|
||||
suffisso `_...` e senza bracket UNI - vedi sotto perché limitata a questi):
|
||||
un codice ripetuto ma mai trovato dentro un placement viene scartato. Questo
|
||||
NON copre i casi fantasma/soppressi sotto, che hanno un placement
|
||||
sintatticamente identico a uno vero e restano quindi indistinguibili a
|
||||
livello testuale.
|
||||
vero compare **sempre** anche dentro un vero placement, in una delle due
|
||||
forme osservate nel binario:
|
||||
|
||||
- `Componente ID N (<codice>...)`/`Component id N (<codice>...)` (la stringa
|
||||
cambia con la localizzazione Creo con cui il file è stato salvato -
|
||||
osservate sia italiana sia inglese);
|
||||
- il codice ripetuto due volte di seguito separato da un singolo byte NUL,
|
||||
`<codice>\0<codice>\0` (osservato per componenti di family table non-UNI,
|
||||
es. cuscinetti `CUSCI_...` - vedi sotto; il placement testuale
|
||||
"Componente ID" non li copre, ma questa coppia doppia sì).
|
||||
|
||||
Un riferimento incidentale al nome di un altro assieme, anche se ripetuto più
|
||||
volte, non compare mai in nessuna delle due forme. `scripts/ExtractCreoStrings.cs`
|
||||
e `AssemblyComponentScanner.cs` applicano questa regola ai codici "nudi" (senza
|
||||
suffisso `_...` e senza bracket family table - vedi sotto perché limitata a
|
||||
questi): un codice ripetuto ma mai trovato dentro un placement/coppia NUL
|
||||
viene scartato. Questo NON copre i casi fantasma/soppressi sotto, che hanno un
|
||||
placement sintatticamente identico a uno vero e restano quindi indistinguibili
|
||||
a livello testuale.
|
||||
|
||||
**Il codice radice del proprio assieme (assieme generale, senza suffisso
|
||||
`_D.../_C...`).** Caso particolare e sistematico del precedente, non solo
|
||||
incidentale: un assieme suddiviso in sotto-assiemi con suffisso
|
||||
(`9284208188_D600.asm`, `_D610.asm`, ...) referenzia **sempre** al suo interno
|
||||
anche il codice radice dell'assieme generale (`9284208188`, senza suffisso) -
|
||||
il "padre" a cui tutti i `_D...` appartengono nella struttura Creo - con un
|
||||
vero placement sintatticamente identico a un componente reale. Non è un
|
||||
componente di quel sotto-assieme, è la risalita verso l'assieme che lo
|
||||
contiene. Osservato su `9284208188_D600.asm`: `Componente ID N (9284208188...)`.
|
||||
Se seguito in una risoluzione ricorsiva, riporta dentro l'intero albero del
|
||||
prodotto invece del solo sotto-assieme richiesto: 598 componenti raccolti
|
||||
invece dei ~65 reali. A differenza del caso generico sopra, qui il codice
|
||||
"padre" _è_ prevedibile a priori (è la parte del proprio codice prima del
|
||||
primo `_`), quindi va escluso esplicitamente seminando l'insieme dei codici
|
||||
già visitati anche con la radice del codice di partenza, non solo con il
|
||||
codice stesso — non basta il controllo generico sul placement, perché il
|
||||
placement qui è reale. Vedi `AssemblyComponentScanner.CodiceRadice`.
|
||||
|
||||
**Riferimenti fantasma a codici superati/rinominati.** Caso osservato:
|
||||
`9244207061.asm` contiene un riferimento a `9044207061` (stesso numero, prima
|
||||
@@ -134,30 +160,35 @@ table il placement può mancare del dettaglio, quindi resta necessaria la
|
||||
sola euristica delle occorrenze ripetute - meno precisa, ma l'unica
|
||||
applicabile senza rischiare falsi negativi su casi come questi.
|
||||
|
||||
## Viti/dadi/rosette normalizzati (family table UNI)
|
||||
## Componenti normalizzati (family table UNI, CUSCI, ...)
|
||||
|
||||
I componenti di minuteria standard (viti, dadi, rosette UNI) sono definiti in
|
||||
Creo come **family table**: un file generico (es. `uni6592.prt`) con tante
|
||||
righe/istanze quante sono le varianti dimensionali. Nel binario dell'assieme
|
||||
compaiono così:
|
||||
I componenti di minuteria standard (viti, dadi, rosette UNI) e i cuscinetti
|
||||
(`CUSCI_...`) sono definiti in Creo come **family table**: un file generico
|
||||
(es. `uni6592.prt`, `cusci_acbb7.prt`) con tante righe/istanze quante sono le
|
||||
varianti dimensionali. Nel binario dell'assieme compaiono così:
|
||||
|
||||
```
|
||||
Componente ID 28755 (R6--_-_U6592<UNI6592>.PRT)
|
||||
2510060303<CUSCI_ACBB7>
|
||||
```
|
||||
|
||||
`R6--_-_U6592` è il nome della singola istanza (riga della family table), **non
|
||||
un file separato su disco** — sulla share esiste solo `uni6592.prt.<versione>`
|
||||
(verificato in `Unificati/`). Cercare l'istanza intera nell'indice fallisce
|
||||
sempre; va estratto solo il contenuto tra `<` e `>` (`UNI6592`), che coincide
|
||||
col nome del file generico realmente indicizzato:
|
||||
`R6--_-_U6592` e `2510060303` sono il nome/codice della singola istanza (riga
|
||||
della family table), **non un file separato su disco** — sulla share esiste
|
||||
solo il generico (`uni6592.prt.<versione>`, `cusci_acbb7.prt.<versione>`,
|
||||
verificato in `Unificati/`). Cercare l'istanza intera nell'indice fallisce
|
||||
sempre; va estratto solo il contenuto tra `<` e `>` (`UNI6592`, `CUSCI_ACBB7`),
|
||||
che coincide col nome del file generico realmente indicizzato:
|
||||
|
||||
```
|
||||
regex aggiuntiva: (?<=<)UNI\d+(?=>)
|
||||
regex aggiuntiva: (?<=<)(?:UNI\d+|CUSCI_[A-Za-z0-9]+)(?=>)
|
||||
```
|
||||
|
||||
Implementata sia in [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs) sia in
|
||||
[`AssemblyComponentScanner.cs`](../CadSearchGui/AssemblyComponentScanner.cs) (fase "Trova componenti
|
||||
referenziati" dell'app).
|
||||
referenziati" dell'app). Il prefisso family table non è garantito essere solo `UNI`/`CUSCI`:
|
||||
se ricompaiono famiglie diverse (altri componenti normalizzati), vanno aggiunte allo stesso modo,
|
||||
verificando prima con `scripts/Analyze.cs` che il contenuto tra `<` e `>` corrisponda a un codice
|
||||
realmente indicizzato in `Unificati/`.
|
||||
|
||||
## Risoluzione ricorsiva dell'albero
|
||||
|
||||
@@ -172,11 +203,11 @@ livello) serve una BFS:
|
||||
3. Un codice non trovato nell'indice va scartato/segnalato: o è rumore
|
||||
binario, o è un componente non ancora indicizzato.
|
||||
|
||||
Stessa logica di estrazione candidati in due punti del codice, con scopi
|
||||
diversi:
|
||||
Stessa logica di estrazione candidati ed euristica del codice radice (sopra),
|
||||
duplicata in due punti del codice per scopi diversi:
|
||||
|
||||
- [`AssemblyComponentScanner.EstraiCodiciReferenziati`](../CadSearchGui/AssemblyComponentScanner.cs) — solo estrazione dei candidati da un file, usata dalla BFS in [`Form1.RisolviAlberoComponenti`](../CadSearchGui/Form1.cs) (fase "Trova componenti referenziati") e [`Form1.AssiemeCompleto`](../CadSearchGui/Form1.cs) (fase "Verifica completezza").
|
||||
- [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs) — estrazione standalone da riga di comando, ma **un solo livello**: riporta solo i riferimenti diretti del file passato (come li mostra Creo per quell'assieme), senza scendere ricorsivamente nei sotto-assiemi. Usato per confrontare l'euristica contro l'albero Creo di un singolo file durante il reverse engineering; non genera una distinta base completa multi-livello.
|
||||
- [`AssemblyComponentScanner.EstraiCodiciReferenziati`](../CadSearchGui/AssemblyComponentScanner.cs) + `CodiceRadice` — estrazione dei candidati da un file, usata dalla BFS in [`Form1.RisolviAlberoComponenti`](../CadSearchGui/Form1.cs) (fase "Trova componenti referenziati") e [`Form1.AssiemeCompleto`](../CadSearchGui/Form1.cs) (fase "Verifica completezza"). Entrambe seminano l'insieme dei codici visitati con codice + radice di ogni file di partenza.
|
||||
- [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs) — stessa BFS ricorsiva standalone da riga di comando (copia indipendente della stessa euristica, nessun `ProjectReference` verso la GUI). Usato per confrontare l'euristica contro l'albero Creo di un singolo file durante il reverse engineering, senza dover passare dalla UI/dal DB `raw`.
|
||||
|
||||
Nota: le query contro `files_ultima_versione` filtrano solo per
|
||||
`(cartella_radice, codice, tipo_file)` - se lo stesso codice esiste in più
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#: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 DIRETTAMENTE (un solo
|
||||
// livello, come li mostra Creo per quell'assieme - nessuna espansione ricorsiva nei
|
||||
// sotto-assiemi). 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.
|
||||
// Estrae dal file .asm i codici dei componenti referenziati, espandendo ricorsivamente
|
||||
// ogni sotto-assieme (.asm) trovato nell'indice, finché non restano solo PRT (foglie) o
|
||||
// codici non presenti nell'indice. 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.
|
||||
// Ogni codice trovato viene validato contro l'indice cad_index.db.
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -21,18 +21,19 @@ 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 ">".
|
||||
// Seconda alternativa: componenti normalizzati definiti come family table in Creo (viti/
|
||||
// dadi/rosette UNI, ma anche cuscinetti CUSCI_...). 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>"/"cusci_acbb7.prt.<versione>" - va quindi estratto solo
|
||||
// il contenuto tra "<" e ">".
|
||||
// Terza alternativa: varianti dimensionali con suffisso puramente numerico (es. cinghie
|
||||
// "2702220182_8188", dove "8188" è una misura, non una lettera di revisione/sotto-assieme
|
||||
// come "_C01"/"_D100") - la lettera dopo "_" è quindi opzionale, non obbligatoria. La
|
||||
// lettera di revisione e il suffisso "_" possono anche comparire insieme sullo stesso
|
||||
// codice (es. "9284200366A_C01", componente di un sotto-assieme già di per sé revisionato).
|
||||
var codiceRegex = new Regex(
|
||||
@"(?<!ModelView#)\b\d{8,12}[A-Za-z]{0,2}(?:_[A-Za-z]?\d{2,4})?\b|(?<=<)UNI\d+(?=>)",
|
||||
@"(?<!ModelView#)\b\d{8,12}[A-Za-z]{0,2}(?:_[A-Za-z]?\d{2,4})?\b|(?<=<)(?:UNI\d+|CUSCI_[A-Za-z0-9]+)(?=>)",
|
||||
RegexOptions.Compiled);
|
||||
const int occorrenzeMinime = 2;
|
||||
|
||||
@@ -48,14 +49,18 @@ HashSet<string> EstraiCodiciReferenziati(string percorsoAsm)
|
||||
// riferimenti al proprio assieme padre (nome sessione/file) o a codici superati/
|
||||
// rinominati (caso "fantasma", docs/formato-asm.md) ripetono comunque il codice più
|
||||
// volte pur non essendo mai un componente vero. Per questi si richiede un vero
|
||||
// placement "Componente ID N (<codice>...)". "Componente ID"/"Component id": la
|
||||
// stringa cambia con la localizzazione Creo con cui il file è stato salvato (osservate
|
||||
// sia italiana sia inglese). Non si applica alle varianti con suffisso "_..." né alle
|
||||
// istanze family table UNI: in alcuni file il placement riporta solo il nome
|
||||
// istanza/generico senza il dettaglio del suffisso o del bracket "<...>", che vive
|
||||
// altrove nei metadati - lì l'occorrenza ripetuta resta l'unico segnale disponibile.
|
||||
// placement, in una delle due forme osservate nel binario: "Componente ID N
|
||||
// (<codice>...)"/"Component id N (<codice>...)" (la stringa cambia con la
|
||||
// localizzazione Creo del file), oppure il codice ripetuto due volte di seguito
|
||||
// separato da un singolo byte NUL ("<codice>\0<codice>\0", osservato per componenti di
|
||||
// family table non-UNI, es. cuscinetti "CUSCI_..."). Non si applica alle varianti con
|
||||
// suffisso "_..." né alle istanze family table UNI: in alcuni file il placement
|
||||
// riporta solo il nome istanza/generico senza il dettaglio del suffisso o del bracket
|
||||
// "<...>", che vive altrove nei metadati - lì l'occorrenza ripetuta resta l'unico
|
||||
// segnale disponibile.
|
||||
bool HaPlacement(string codice) =>
|
||||
Regex.IsMatch(testo, $@"Component(?:e)? id \d+ \([^()]*{Regex.Escape(codice)}[^()]*\)", RegexOptions.IgnoreCase);
|
||||
Regex.IsMatch(testo, $@"Component(?:e)? id \d+ \([^()]*{Regex.Escape(codice)}[^()]*\)", RegexOptions.IgnoreCase)
|
||||
|| testo.Contains(codice + "\0" + codice + "\0");
|
||||
|
||||
candidati.RemoveWhere(c => Regex.IsMatch(c, @"^\d{8,12}[A-Za-z]{0,2}$") && !HaPlacement(c));
|
||||
return candidati;
|
||||
@@ -98,22 +103,42 @@ Dictionary<string, List<(string Tipo, string Percorso)>> CercaComponenti(IReadOn
|
||||
return risultato;
|
||||
}
|
||||
|
||||
// Solo i riferimenti diretti del file indicato (un livello), come li mostra Creo per
|
||||
// quell'assieme - nessuna espansione ricorsiva nei sotto-assiemi.
|
||||
// Espansione ricorsiva: ogni sotto-assieme (.asm) trovato viene a sua volta letto e i suoi
|
||||
// componenti aggiunti alla coda, finché non restano solo PRT (foglie) o codici non presenti
|
||||
// nell'indice - stessa logica di Form1.RisolviAlberoComponenti.
|
||||
var codiceProprio = Path.GetFileName(asmIniziale).Split('.')[0];
|
||||
// Un assieme con suffisso "_D..." referenzia sempre al suo interno anche l'assieme
|
||||
// generale (codice nudo, senza suffisso) a cui appartiene - riferimento fantasma verso
|
||||
// l'alto, non un componente vero (vedi docs/formato-asm.md). Va escluso subito: se
|
||||
// seguito, riporta dentro l'intero albero del macchinario invece del solo sotto-assieme
|
||||
// richiesto (visto su 9284208188_D600 -> 9284208188, che da solo genera 598 falsi
|
||||
// positivi invece di ~90 componenti reali).
|
||||
var codiceRadice = codiceProprio.Split('_')[0];
|
||||
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 });
|
||||
|
||||
var candidati = EstraiCodiciReferenziati(asmIniziale)
|
||||
.Where(c => !string.Equals(c, codiceProprio, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
var trovatiPerCodice = CercaComponenti(candidati);
|
||||
foreach (var codice in candidati)
|
||||
while (daScansionare.Count > 0)
|
||||
{
|
||||
if (trovatiPerCodice[codice].Count == 0)
|
||||
nonTrovati.Add(codice);
|
||||
else
|
||||
var asm = daScansionare.Dequeue();
|
||||
var candidati = EstraiCodiciReferenziati(asm)
|
||||
.Where(codiciVisti.Add)
|
||||
.ToList();
|
||||
var trovatiPerCodice = CercaComponenti(candidati);
|
||||
foreach (var codice in candidati)
|
||||
{
|
||||
var componenti = trovatiPerCodice[codice];
|
||||
if (componenti.Count == 0)
|
||||
{
|
||||
nonTrovati.Add(codice);
|
||||
continue;
|
||||
}
|
||||
componentiTrovati.Add(codice);
|
||||
foreach (var (tipo, percorso) in componenti)
|
||||
if (tipo == "asm")
|
||||
daScansionare.Enqueue(percorso);
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllLines(outPath, componentiTrovati);
|
||||
|
||||
Reference in new issue
Block a user