Porting C# del reverse engineering della struttura a record del .asm Creo (sezione "members" + parametro "ConfigName" per le family table): legge esattamente i componenti diretti e genera la stessa BOM esportata da Creo, senza le euristiche testuali (conteggio occorrenze) di AssemblyComponentScanner. Resta uno script di analisi indipendente, non integrato nella GUI. Aggiorna anche formato-asm.md (non e' piu' vero che "non esiste un parser strutturale") e architettura.md (script mancante nell'elenco). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
385 lines
23 KiB
Markdown
385 lines
23 KiB
Markdown
# Come leggere un .asm Creo per dedurne i componenti
|
|
|
|
Il file `.asm` di Pro/ENGINEER-Creo è un formato binario proprietario, non
|
|
documentato pubblicamente. Questo documento descrive l'euristica testuale usata da
|
|
`AssemblyComponentScanner.cs` (GUI, fase "Trova componenti referenziati"/"Verifica
|
|
completezza") e dagli script che risolvono l'albero componenti contro `cad_index.db`
|
|
(`ExtractCreoStrings.cs`, `BomViaDb.cs`): conta le occorrenze di stringhe candidate nel
|
|
testo binario, validata a mano confrontando l'output con l'albero reale mostrato da Creo.
|
|
Riassume cosa si è osservato e i suoi limiti, in particolare i falsi positivi che
|
|
un conteggio testuale non può escludere con certezza (riferimenti fantasma, componenti
|
|
soppressi con placement residuo — vedi sotto).
|
|
|
|
Un reverse engineering successivo ha invece decodificato la **struttura a record** del
|
|
file (non solo pattern testuali): legge esattamente la sezione `members` di un assieme,
|
|
senza contare occorrenze né rischiare i falsi positivi descritti in questo documento.
|
|
Implementato in [`scripts/CreoBom.cs`](../scripts/CreoBom.cs), documentato in
|
|
[`formato-asm-record.md`](formato-asm-record.md). Resta però uno script di analisi
|
|
indipendente: legge solo `.asm` locali dato un percorso di ricerca esplicito (non
|
|
`cad_index.db`), e non è integrato in `AssemblyComponentScanner`/nella GUI, che
|
|
continuano a usare l'euristica descritta qui.
|
|
|
|
## Struttura osservata
|
|
|
|
Il file contiene, frammentate tra dati binari, molte coppie testuali del tipo
|
|
|
|
```
|
|
<nome_campo>\x00<valore>\x00
|
|
```
|
|
|
|
in encoding a 1 byte per carattere (Latin1/ASCII), es.:
|
|
|
|
```
|
|
6d 6f 64 65 6c 5f 6e 61 6d 65 00 39 30 34 34 32 30 37 30 36 31 5f 43 30 31 23 41 46 30 00
|
|
m o d e l _ n a m e \0 9 0 4 4 2 0 7 0 6 1 _ C 0 1 # A F 0 \0
|
|
```
|
|
|
|
I codici dei componenti (propri e sotto-componenti) compaiono come stringhe
|
|
ASCII leggibili in diversi campi ricorrenti:
|
|
|
|
- `model_name`, `generic_name`, `inst_name`, `gen_name`, `feat_name`,
|
|
`family_name`, `name` — metadati associati a un modello referenziato.
|
|
- `Componente ID N (<codice>.PRT|.ASM)` — riga di placement: `N` è un ID
|
|
progressivo assegnato da Creo al componente posizionato nell'assieme.
|
|
- `<codice>#AF0` — riferimento con suffisso di versione/configurazione.
|
|
|
|
Un codice reale compare **più volte** nel file (una per feature/placement
|
|
diverso); rumore binario che assomiglia casualmente a un codice compare quasi
|
|
sempre una sola volta. Questa è la base del filtro in
|
|
[`AssemblyComponentScanner.cs`](../PrepStep/AssemblyComponentScanner.cs)
|
|
e in [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs):
|
|
|
|
```
|
|
regex: \b\d{8,12}(?:_[A-Za-z]\d{2,4}|[A-Za-z]{0,2})\b
|
|
occorrenze: >= 2
|
|
```
|
|
|
|
`scripts/ExtractCreoStrings.cs` ha una versione più raffinata della regex e un filtro
|
|
aggiuntivo (vedi sotto), non ancora portati in `AssemblyComponentScanner.cs`:
|
|
|
|
```
|
|
regex: \b\d{8,12}[A-Za-z]{0,2}(?:_[A-Za-z]?\d{2,4})?\b
|
|
```
|
|
|
|
(lettera di revisione e suffisso "_..." possono comparire anche insieme, es.
|
|
`9284200366A_C01`; il suffisso dopo "_" può essere puramente numerico, es.
|
|
`2702220182_8188`, non solo lettera+cifre come `_C01`/`_D100`.)
|
|
|
|
## Falsi positivi noti
|
|
|
|
**ID di viste salvate.** Il pattern `ModelView#<numero>` (es.
|
|
`ModelView#1073741828`) genera candidati a 10 cifre indistinguibili da un
|
|
codice reale per forma, ma sono ID interni di viste orientate salvate, non
|
|
componenti. Si escludono con un lookbehind negativo sulla regex:
|
|
`(?<!ModelView#)`.
|
|
|
|
**Il codice dell'assieme stesso.** Il proprio codice compare spesso decine di
|
|
volte nei metadati (`CODICE`, `part_table`, `STAMPIGLIATURA`, ecc.) e supera
|
|
quindi la soglia di occorrenze. Va escluso esplicitamente confrontandolo con
|
|
il codice derivato dal nome del file in ingresso (`nomefile.split('.')[0]`).
|
|
|
|
**Il codice dell'assieme padre (o di qualunque altro assieme che lo
|
|
contiene).** Variante dello stesso problema: un sotto-assieme può contenere
|
|
anche il codice dell'assieme che lo referenzia (riferimento al nome
|
|
sessione/file salvato nei metadati), che supera la soglia di occorrenze pur
|
|
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, in una delle quattro
|
|
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ì);
|
|
- il campo `family_name\0<codice>\0` (con `name` e `family_name` uguali,
|
|
entrambi pari al codice). Osservato una sola volta per file, su un
|
|
sotto-assieme referenziato come family table generica invece che con un
|
|
placement testuale esplicito - caso `9284202452` dentro
|
|
`9284208188_D400.asm`: il sotto-assieme (che contiene a sua volta
|
|
`9284202452_C01/_C02/_C03`) non ha nessun `Componente ID (...)` né coppia
|
|
NUL, solo questo campo `family_name`. Verificato su 5 file diversi
|
|
(`9284208188_D400/D600/D610/D620/D630.asm`): l'unica occorrenza di
|
|
`family_name\0<codice>\0` in ciascuno corrisponde sempre a un componente
|
|
reale confermato dalla BOM Creo, mai al riferimento fantasma verso l'alto;
|
|
- il codice seguito immediatamente da un bracket, `<codice><GENERICO>`
|
|
(senza NUL né spazio in mezzo) - l'istanza di una family table non-UNI
|
|
(es. `2510060303<CUSCI_ACBB7>`, un cuscinetto, confermato aprendo l'assieme
|
|
in Creo: descrizione "CUSC.OBL.SFERE 2COR 17X40"). Il codice dell'istanza
|
|
non è mai indicizzato a sé sulla share (solo il generico tra `<` e `>` lo
|
|
è - stesso meccanismo di UNI, vedi sotto), quindi anche riconosciuto come
|
|
placement reale finisce comunque negli "scartati" alla ricerca nel DB - ma
|
|
in modo tracciabile, invece di sparire in silenzio prima ancora di essere
|
|
cercato.
|
|
|
|
Un riferimento incidentale al nome di un altro assieme, anche se ripetuto più
|
|
volte, non compare mai in nessuna delle quattro 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 uno dei quattro segnali
|
|
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
|
|
di una rinumerazione) con la stessa identica struttura sintattica di un
|
|
componente vero — `Componente ID 46 (9044207061.ASM)`, più i campi
|
|
`model_name`/`generic_name` corrispondenti. Il codice `9044207061` esiste
|
|
davvero nell'indice CAD (il file vecchio non è stato eliminato dalla share),
|
|
quindi supera anche una validazione incrociata contro il DB. Non è però un
|
|
componente realmente presente nell'albero mostrato da Creo per quell'assieme
|
|
(confermato aprendo il file in Creo).
|
|
|
|
Questo caso **non è distinguibile a livello testuale**: la sintassi è
|
|
identica a quella di un placement reale. Nessuna euristica sulla stringa
|
|
riesce a discriminarlo in modo affidabile senza rischiare di scartare
|
|
componenti veri con pattern simili altrove. Se ricompare, l'unica verifica
|
|
sicura resta l'apertura manuale in Creo del sotto-assieme sospetto.
|
|
|
|
**Componenti soppressi/rimossi con placement residuo.** Stessa natura del
|
|
caso precedente: capita che un codice compaia con un vero
|
|
`Componente ID N (<codice>.PRT)`/`Component id N (<codice>.PRT)` - con un ID
|
|
numerico nello stesso intervallo di quelli dei componenti realmente visibili
|
|
nell'albero Creo, quindi non distinguibile per posizione o formato - ma il
|
|
codice non compare nell'albero/BOM che Creo mostra per quell'assieme.
|
|
Osservato su più assiemi in questa sessione (`9044207061` dentro
|
|
`9244207061.asm`, e diversi codici dentro `9284208188_D200.asm`). Ipotesi più
|
|
probabile: feature soppresse, cancellate o superate in una revisione
|
|
successiva, il cui placement resta comunque nella storia binaria del file
|
|
(Creo non riscrive da zero il file a ogni salvataggio, accumula stato). Nella
|
|
pratica, quando compaiono, sono un numero ridotto di codici extra rispetto al
|
|
reale (falsi positivi, non falsi negativi): la BOM generata resta un
|
|
sovrainsieme sicuro dei componenti veri, mai un sottoinsieme. Nessuna
|
|
euristica testuale può escluderli con certezza; se serve un elenco esatto
|
|
l'unica verifica è aprire l'assieme in Creo e controllare a mano quali ID
|
|
sono effettivamente presenti/attivi.
|
|
|
|
**Placement senza il dettaglio del suffisso/bracket.** Il testo dentro
|
|
`Componente ID N (...)` a volte riporta solo il nome generico/istanza, senza
|
|
il suffisso di variante dimensionale o il bracket family-table, che compaiono
|
|
invece solo altrove nei metadati dello stesso file (non vicino al placement).
|
|
Osservato in due forme sullo stesso assieme (`9284208188_D200.asm`):
|
|
`Component id 82 (R8--_-_U6592.PRT)` (nessun `<UNI6592>` nel placement, pure
|
|
presente nel file) e `Component id 150 (2702220182.PRT)` (nessun `_8188`
|
|
nel placement, mentre la variante dimensionale reale `2702220182_8188`
|
|
compare solo come stringa ripetuta altrove). Per questo il requisito del
|
|
placement esplicito (caso sopra) si applica solo ai codici "nudi" (senza
|
|
suffisso `_...` e senza bracket UNI): per le varianti con suffisso o family
|
|
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.
|
|
|
|
## Parti commerciali con nome alfabetico (validazione via DB)
|
|
|
|
Oltre il 90% dei codici indicizzati sono numerici Pro-E (regex sopra), ma una minoranza
|
|
sono parti commerciali scaricate come STEP da cataloghi online (anelli, boccole, guarnizioni,
|
|
manopole...) che in Creo hanno un nome **alfabetico**, assegnato dal file STEP di origine, non
|
|
un codice del gestionale — es. `NOME_PARTE-1-SOLID1`, senza nessuna cifra riconoscibile. Non
|
|
esiste un pattern comune da ipotizzare a priori per questi nomi: variano da fornitore a
|
|
fornitore.
|
|
|
|
Per questa famiglia l'estrazione si inverte: invece di restringere la regex a una forma nota
|
|
(come per i codici numerici), si tokenizza il binario in modo generico —
|
|
|
|
```
|
|
regex: [A-Za-z0-9_-]{3,}
|
|
```
|
|
|
|
— e si lascia decidere al DB (`cad_index.db`, già caricato in memoria una sola volta dal
|
|
chiamante, non per-file) se il token è un codice reale. Senza questo filtro il rumore binario
|
|
sarebbe ingestibile: su un assieme con poche migliaia di stringhe, la tokenizzazione generica
|
|
produce tipicamente qualche migliaio di candidati, di cui solo una manciata sopravvive al
|
|
controllo DB.
|
|
|
|
L'essere nel DB da solo non basta, per lo stesso motivo dei codici numerici "nudi": codici
|
|
corti o generici (es. una keyword interna di Creo come `start_part`, o un codice indicizzato
|
|
per errore/caso limite) possono comparire per puro rumore. Si applica quindi la stessa regola
|
|
di validazione della famiglia numerica:
|
|
|
|
- un token con un suffisso di revisione/variante (`_[A-Za-z]?\d{2,4}` in coda, es. `_C01`,
|
|
`_D100`) o un prefisso di family table nota (`UNI`, `CUSCI_`) è considerato "forma nota":
|
|
basta che occorra almeno 2 volte nel file;
|
|
- un token "nudo" (nessuno dei due segnali) richiede invece un vero placement testuale, come
|
|
per i codici numerici nudi (stesse quattro forme viste sopra).
|
|
|
|
Una parte commerciale referenziata ma **non ancora indicizzata** non è distinguibile dal
|
|
rumore con questo metodo: resta invisibile, come lo era prima che questa famiglia venisse
|
|
gestita. La famiglia numerica invece resta rilevabile anche quando manca dall'indice
|
|
(rinominata/spostata), perché si riconosce dalla forma senza bisogno del DB — è per questo
|
|
che le due famiglie restano due passaggi distinti invece di un unico tokenizer generico
|
|
sempre validato contro il DB: unificarle farebbe perdere la segnalazione dei codici numerici
|
|
mancanti (vedi "Riferimenti fantasma" sopra).
|
|
|
|
`AssemblyComponentScanner.EstraiCodiciReferenziati` accetta quindi un parametro opzionale
|
|
`codiciNoti` (l'insieme di tutti i codici prt/asm noti all'indice) usato solo per questa
|
|
seconda famiglia; se omesso, si estraggono solo i codici numerici. `scripts/BomViaDb.cs`
|
|
è il prototipo standalone usato per validare l'approccio prima di portarlo nella GUI.
|
|
|
|
## Componenti normalizzati (family table UNI, CUSCI, ...)
|
|
|
|
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` 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+|CUSCI_[A-Za-z0-9]+)(?=>)
|
|
```
|
|
|
|
Implementata sia in [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs) sia in
|
|
[`AssemblyComponentScanner.cs`](../PrepStep/AssemblyComponentScanner.cs) (fase "Trova componenti
|
|
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
|
|
|
|
Un `.asm` referenzia solo i propri sotto-componenti diretti, non l'intero
|
|
albero annidato. Per ricostruire la distinta base completa (non solo il primo
|
|
livello) serve una BFS:
|
|
|
|
1. Estrarre i codici candidati dal file corrente (euristica sopra).
|
|
2. Per ogni codice, cercarlo nell'indice `cad_index.db`
|
|
(`files_ultima_versione`) per sapere se è un `.prt` (foglia) o un `.asm`
|
|
(da accodare alla coda per essere scansionato a sua volta).
|
|
3. Un codice non trovato nell'indice va scartato/segnalato: o è rumore
|
|
binario, o è un componente non ancora indicizzato.
|
|
|
|
Stessa logica di estrazione candidati ed euristica del codice radice (sopra),
|
|
duplicata in due punti del codice per scopi diversi:
|
|
|
|
- [`AssemblyComponentScanner.EstraiCodiciReferenziati`](../PrepStep/AssemblyComponentScanner.cs) + `CodiceRadice` — estrazione dei candidati da un file, usata dalla BFS in [`Form1.RisolviAlberoComponenti`](../PrepStep/Form1.cs) (fase "Trova componenti referenziati") e [`Form1.AssiemeCompleto`](../PrepStep/Form1.cs) (fase "Verifica completezza"). Entrambe seminano l'insieme dei codici visitati con codice + radice di ogni file di partenza, e caricano una volta sola tutti i codici noti all'indice (`Form1.CaricaCodiciNoti`) sia per validare i token generici delle parti commerciali sia per risolvere i percorsi dei componenti trovati, senza una query SQL per candidato.
|
|
- [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs) e [`scripts/BomViaDb.cs`](../scripts/BomViaDb.cs) — stessa BFS ricorsiva standalone da riga di comando (copia indipendente della stessa euristica, nessun `ProjectReference` verso la GUI). Usati 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ù
|
|
cartelle radice sulla share (duplicati/copie storiche), va sempre ordinato
|
|
per `data_modifica DESC` e tenuta solo la riga più recente per tipo, altrimenti
|
|
si rischia di risolvere un codice contro una copia diversa (e potenzialmente
|
|
diversa nel contenuto) da quella realmente referenziata.
|
|
|
|
### Trappola nella BFS: un fallimento di placement non è definitivo
|
|
|
|
Un codice può comparire come riferimento fantasma (senza vero placement, vedi
|
|
sopra) in un file scansionato **prima** di quello in cui invece ha un vero
|
|
placement — tipicamente perché lo stesso codice è mostrato anche nel file
|
|
dell'assieme padre come promemoria/metadato, oltre che nel file del proprio
|
|
assieme dove è effettivamente posizionato. Osservato su un caso reale
|
|
(assieme `9244228558.asm`): il componente `9244236104` compare nel file
|
|
dell'assieme generale come riferimento nudo senza placement (viene scartato
|
|
lì, correttamente), ma ha un vero `Component id 45 (9244236104.PRT)` dentro
|
|
`9244228558_c01.asm`, il sotto-assieme che lo contiene realmente — scansionato
|
|
solo _dopo_ il file padre nella BFS.
|
|
|
|
Una BFS che marca un candidato come "già visto" al primo incontro, anche
|
|
quando quell'incontro è un fallimento di placement, blocca per sempre ogni
|
|
ri-verifica successiva: il componente resta perso anche quando il file giusto
|
|
viene scansionato più avanti. Nei casi più gravi il codice bloccato è esso
|
|
stesso un **sotto-assieme** (osservato con `9256510047` dentro
|
|
`9256518042.asm`): se il suo codice viene scartato prematuramente, il suo file
|
|
non viene nemmeno accodato, e si perde l'intero sotto-albero sottostante
|
|
(compresi `9056510047`/`9056510047_C01`/`9056510047_C02` in questo caso).
|
|
|
|
`scripts/BomViaDb.cs` (versione iniziale) aveva esattamente questo bug: un set
|
|
`codiciVisti` unico bloccava sia i candidati confermati sia quelli scartati
|
|
per mancato placement. Corretto separando le due cose: un fallimento di
|
|
placement non aggiunge il codice a nessun set di blocco, quindi resta
|
|
ritestabile ogni volta che lo stesso candidato ricompare in un file successivo
|
|
già in coda. L'accodamento di un nuovo file da scansionare resta invece
|
|
condizionato al placement confermato (non al solo essere un codice noto nel
|
|
DB) — senza questo secondo vincolo, un codice generico che per puro caso
|
|
coincide con un codice noto (es. `"915"`, `"932"`) farebbe esplodere la
|
|
scansione su file estranei in tutto l'indice (verificato: passa da ~100 a
|
|
quasi 19000 "componenti trovati" e la scansione si blocca).
|
|
|
|
**Il codice della GUI (`Form1.RisolviAlberoComponenti` +
|
|
`AssemblyComponentScanner.EstraiCodiciReferenziati`) non ha questo bug**,
|
|
verificato riproducendo lo stesso caso reale con un test dedicato: la
|
|
classificazione placement-sì/no è già interamente delegata a
|
|
`EstraiCodiciReferenziati`, chiamata da zero per ogni file scansionato — un
|
|
fallimento in un file non lascia traccia nel set `codiciVisti` del chiamante,
|
|
che accumula solo codici già confermati. Il file successivo che referenzia
|
|
davvero il componente lo ritrova quindi senza ostacoli, senza bisogno di
|
|
nessuna modifica. Vale comunque come nota di design da preservare: il giorno
|
|
in cui la logica di `RisolviAlberoComponenti` venisse riscritta per fare la
|
|
verifica di placement inline nella BFS (come `BomViaDb.cs`), andrebbe
|
|
applicata la stessa correzione.
|
|
|
|
### Seconda discrepanza trovata: l'eccezione dei codici con suffisso
|
|
|
|
Confrontando `scripts/BomViaDb.cs` (corretto come sopra) con la GUI sullo
|
|
stesso assieme di un caso reale più grande (`9284218024b.asm`, ~530
|
|
componenti), le due liste **non coincidevano**: lo script perdeva l'intero
|
|
sotto-assieme `2546220101` (`_C01`..`_C04`, inclusi i cuscinetti
|
|
`INAFAG_NA6901-XL_...` dentro `_C04`), presente invece nella BOM ufficiale
|
|
Creo e trovato correttamente dalla GUI.
|
|
|
|
Causa: `2546220101_C01`..`_C04` hanno un vero placement nel proprio file
|
|
(`2546220101.asm`), ma **senza il suffisso** - il testo riporta solo
|
|
`Componente ID N (2546220101.PRT)`, il dettaglio `_C01` compare solo altrove
|
|
nei metadati, non vicino al placement (stesso fenomeno già documentato sopra
|
|
in "Placement senza il dettaglio del suffisso/bracket", osservato lì solo per
|
|
famiglie UNI/dimensionali). `AssemblyComponentScanner.EstraiCodiciReferenziati`
|
|
già gestisce questo caso: un codice con suffisso di revisione/variante
|
|
(`SuffissoRegex`, es. `_C01`, `_D100`) o prefisso di family table nota (`UNI`,
|
|
`CUSCI_`) è considerato "forma nota" ed esentato dal placement esplicito -
|
|
basta che occorra almeno 2 volte nel file. `BomViaDb.cs` richiedeva invece
|
|
sempre il placement letterale per ogni codice, senza questa eccezione.
|
|
|
|
Corretto portando la stessa regola (`FormaNota` + conteggio occorrenze) in
|
|
`BomViaDb.cs`. Riverificato su entrambi i casi reali (`9244228558.asm`,
|
|
`9284218024b.asm`): script e GUI producono ora **esattamente lo stesso
|
|
insieme** di componenti, e in entrambi coincide con la BOM ufficiale Creo (a
|
|
meno delle sole istanze di family table risolte al nome del file realmente
|
|
indicizzato, es. `2502020105<CUSCI_DGBB7>` → `CUSCI_DGBB7` - comportamento
|
|
corretto, non una discrepanza).
|
|
|
|
## Strumento di analisi manuale
|
|
|
|
[`scripts/Analyze.cs`](../scripts/Analyze.cs) stampa il contesto testuale ed
|
|
esadecimale attorno a ogni occorrenza di un pattern in un file binario — usato
|
|
per tutte le osservazioni di questo documento:
|
|
|
|
```powershell
|
|
dotnet run scripts/Analyze.cs -- raw/9244207061.asm.1 "9044207061"
|
|
```
|
|
|
|
Utile per verificare a mano un nuovo falso positivo prima di modificare la
|
|
regex o l'esclusione nello script di estrazione.
|