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:
davideandClaude Sonnet 5 committed 2026-09-17 15:32:55 +02:00
1 parent 9660c4883d
commit a7e0df0247
4 files changed
+147 -69

No files matched your search

+58 -27
View File
@@ -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ù