Terzo caso di "[-4] Dati di input non completi" in Creo Distributed Batch, dopo i soppressi e le copie ambigue del commit precedente: falliva 9255728001, e solo lui fra i sei assiemi del batch. Il file in raw/ era identico bit per bit a quello sulla share, quindi non era il modello: mancava il contorno. Il suo sottoassieme _200 e' legato ad altri due (9255708017, 9255708018) da assembly feature e datum copiati, e monta una vite V6-12--_-_U5933_C il cui generico uni5933.prt non era in raw/. Nessuno dei sei file compariva in "members" ne' fra i piazzamenti: i due lettori esistenti non potevano vederli. Quei modelli stanno in una tabella a parte del .asm - array \xf2\xf7\x05 … \xf3 con elementi \xe3\xe2<tipo><nome>\0 - che ora si legge con EstraiCodiciRiferimentiEsterni, riusando la normalizzazione di #AF<n> e istanza<generico> gia' fatta per i membri. A differenza dei soppressi si ricorre dentro: Creo carica il modello citato con tutto il suo albero, e infatti 9255700182 arriva solo per quella via. La tabella contiene anche nomi di feature (MIRROR3, CHAMFER1) che dal record non si distinguono dai modelli. Non e' un problema: come per i soppressi si cerca ogni nome nell'indice e si ignora quello che non c'e'. Per lo stesso motivo questi codici restano fuori dai "non trovati", e la coda di scansione porta ora un flag che tiene fuori anche i rami raggiunti per riferimento esterno - la lista dei componenti mancanti continua a parlare solo della distinta. Documentato in docs/formato-asm-record.md §7.5. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
33 KiB
Decodifica strutturale del .asm Creo: record, membri, family table
Questo documento descrive il formato record per record del .asm Creo, ricavato per
reverse engineering su un campione di assiemi reali e verificato confrontando l'output con
le BOM esportate da Creo per gli stessi file. Sostituisce, per l'estrazione dei componenti,
l'euristica testuale ("codice che compare almeno due volte") descritta in
formato-asm.md: quella era una statistica sui nomi, questa legge la
struttura effettiva dei record.
Implementazione: lettura in PrepStep/AsmRecordReader.cs
(condivisa con la GUI, Form1.EstraiCodiciReferenziati in fase "Trova componenti
referenziati"/"Verifica completezza" - con fallback su AssemblyComponentScanner per i file
che non rispettano il formato validato), resa in formato BOM e ricorsione sui sottoassiemi
in scripts/CreoBom.cs (via #:project).
Stato della validazione. Due livelli di prova, da non confondere:
- Byte-per-byte contro BOM Creo di riferimento (la prova forte): 3 assiemi del
reverse engineering iniziale, più 5 casi reali aggiunti in seguito, ciascuno con la sua BOM
esportata da Creo messa a confronto riga per riga con l'output di
CreoBom.cs—9284218024b.asm(albero profondo, multi-livello),2546220101.asm/2546220101_c04.asm.1(istanza di family table isolata),2546220101.asmrisalvato con un componente soppresso,9244218080_c01.asm(sottoassieme montato due volte),9284208092_D110.asm(revisione del figlio). Ognuno di questi è citato per nome nella sezione del documento a cui si riferisce, così resta verificabile quale affermazione poggia su quale file. - Su tutti i 350
.asmreali diraw/, solo conteggi interni e coerenza con l'euristica testuale (AssemblyComponentScanner), non contro BOM di riferimento: usato per calibrare le soglie dei controlli di sicurezza (§7) su scala, non per dimostrare la correttezza dei valori estratti file per file.
Un'affermazione verificata solo al livello 2 (conteggi su scala) è più debole di una verificata al livello 1 (BOM reale) - il testo lo distingue sempre esplicitamente.
1. Intestazione
Il file si apre con un header ASCII a righe terminate da \ + newline:
#UGC:2 ASSEMBLY 2038 1760 805 1 1 15 4000 2022414 000002d0 \
#- VERS 0 0 \
#- HOST \
#- LINK \
#- DBID \
#- REVS 0, \
ASSEMBLY distingue il tipo di modello. Gli altri campi numerici non sono
stati decodificati e non servono per la BOM. Dopo l'header inizia il corpo
binario. Il corpo non è compresso: i nomi dei modelli sono in chiaro,
1 byte per carattere (Latin1 — Encoding.Latin1.GetString, stesso metodo già
usato in AssemblyComponentScanner.cs per leggere il file come testo senza
alterarne i byte).
2. Il formato dei record
Il corpo è una serializzazione a marcatori. I byte \xe0-\xff sono
marcatori di struttura, tutti gli altri sono dati. I marcatori osservati:
| byte | significato dedotto |
|---|---|
\xe0 |
inizio campo: \xe0 <tipo> <nome campo>\0 <valore> |
\xe1 |
valore nullo / vuoto |
\xe2 |
inizio struttura o unione (per i parametri: il byte dopo è il discriminante) |
\xe3 |
inizio struttura, usato per gli elementi di un array |
\xf1 |
apre un contenitore (visto prima di un array) |
\xf7 |
id di schema: \xf7 <n> identifica il layout dei record che seguono |
\xf8 |
array: \xf8 <conteggio> ... <elementi> |
\xfb |
lo schema è definito in linea nel primo elemento dell'array |
\xf4 |
chiude una sezione (seguito da #\n#<NomeSezione>\n) |
Il byte <tipo> dopo \xe0 codifica il tipo del valore. Osservati:
\x00 struttura/nullable, \x01 intero, \x02 array numerico,
\x0a stringa terminata da \0.
2.1 Interi
Gli interi sono big-endian con la lunghezza codificata nei bit alti del primo
byte, per non collidere con i marcatori \xe0+:
| primo byte | byte totali | valore |
|---|---|---|
0x00-0x7f |
1 | il byte stesso |
0x80-0xbf |
2 | (b0 & 0x3f) << 8 | b1 |
0xc0-0xdf |
3 | (b0 & 0x1f) << 16 | b1 << 8 | b2 |
Esempi verificati: \x2e = 46; \x80\x81 = 129; \xc0\x4d\x51 = 19793.
Questa regola è stata ricavata dalla necessità di far quadrare il
conteggio dei membri: con la lettura a 1 byte fisso i campioni grandi si
disallineavano. Non è confermata da una specifica; regge sui campioni
disponibili al momento del reverse engineering.
In CreoBom.cs è AsmRecordReader.AsInt, applicata al testo decodificato Latin1 (ogni char
della stringa corrisponde 1:1 a un byte, quindi indicizzare la stringa o l'array di byte
originale dà lo stesso offset).
2.2 Compressione per schema
Un array \xf8 <n> \xf7 <idSchema> \xfb scrive il primo elemento con tutti
i nomi di campo per esteso; gli elementi successivi sono posizionali, senza
nomi. È il motivo per cui nel file si vedono i nomi dei componenti "nudi", uno
dopo l'altro, senza etichetta. Per leggere l'elemento N bisogna conoscere lo
schema definito dall'elemento 1.
3. L'elenco dei componenti: la chiave members
La chiave members è l'elenco dei componenti diretti dell'assieme
(livello 1), nell'ordine di montaggio.
\xe0 \x00 "members" \0
\xf1 \xf8 <N> \xf7 <idSchema> \xfb
\xe3 \xe0\x01 "id"\0 <int>
\xe0\x01 "type"\0 <int>
\xe0\x0a "name"\0 <stringa>\0
\xe0\x0a "family_name"\0 <stringa>\0
\xe0\x00 "tha_ref"\0 \xe1
\xe0\x02 "e1"\0 ... "e2" ... "e3" ... "origin" ...
\xe3 <id> <type> <name>\0 <family_name>\0 <resto posizionale>
\xe3 <id> <type> <name>\0 <family_name>\0 <resto posizionale>
...
Campi utili:
id— identificatore del componente dentro questo assieme. È lo stesso numero che Creo mostra come "Componente ID N". Serve da chiave per collegare il membro ad altre sezioni (vedi family table, §4).type—1= sottoassieme (.asm),2= parte (.prt). Sui campioni osservati compaiono solo questi due valori.name— il file su disco, senza estensione. Per le family table è il generico (UNI6592), non l'istanza.family_name— sui campioni disponibili è sempre uguale aname. Non porta quindi l'informazione sull'istanza; il nome è fuorviante.e1,e2,e3,origin— la matrice di posizionamento (assi + origine). Non decodificati: non servono per la BOM, ma occupano il resto del record e hanno lunghezza variabile.
N (il conteggio dichiarato) è il numero totale di occorrenze, non di
codici distinti: una parte montata 4 volte conta 4 e compare in 4 record con id
diversi. Il conteggio va usato come controllo di correttezza: se i record
trovati non sono esattamente N, la lettura si è disallineata e va segnalata
un'eccezione invece di restituire un risultato parziale silenzioso — vedi
AsmRecordReader.LeggiMembri, che lancia InvalidDataException in questo caso.
La sezione termina a \xf4\x04#\n#MdlStatus\n.
3.1 Come lo legge AsmRecordReader.LeggiMembri
Il parser non ricostruisce lo schema completo (servirebbe decodificare le
matrici). Si ancora invece al pattern \xe3 <int> <int> <stringa>\0 <stringa>\0
(AsmRecordReader.MemberRegex, una regex .NET sul testo Latin1 del file) e salta il
resto del record fino al \xe3 successivo che soddisfa il pattern.
La doppia stringa terminata da \0 rende i falsi positivi improbabili, e il
conteggio dichiarato N fa da rete di sicurezza.
ponytail:scansione a pattern invece del parser di schema — il punto debole è che si appoggia al conteggio per accorgersi di un errore. Da sostituire con la lettura dello schema se dovessero comparire campioni che non quadrano.
4. Family table: dove sta il nome dell'istanza
Le parti normalizzate (viti, rondelle, spine, boccole) sono istanze di family
table. In Creo esiste un solo file, il generico (UNI6592.prt), che
contiene una tabella di varianti; ogni variante è un'istanza
(R8--_-_U6592_AISI304). La BOM elenca le istanze, il disco contiene il
generico. La sezione members cita solo il generico: senza risolvere
ConfigName (sotto), il livello 1 produrrebbe UNI6592 dove la BOM dice
R8--_-_U6592_AISI304.
Il collegamento sta in un record di parametro applicato al membro:
\xe3 "ConfigName"\0 \xe2 \x33 [\xf7 <idSchema>] <nome istanza>\0 <id membro> \x82\x7b \xe1\xe1
\xe2\x33— parametro di tipo stringa (\x33='3'). Confronto:\xe2\x32è un parametro numerico, es.DENSITA.<nome istanza>— il nome esatto che la BOM stampa.<id membro>— l'iddel recordmembersa cui il parametro si applica, con la codifica intera di §2.1. È questo il collegamento.\xf7 <idSchema>compare solo sulla prima occorrenza (schema definito in linea), poi è omesso.\x82\x7b— costante (= 635) identica su tutte le occorrenze di tutti i campioni. Significato ignoto, presumibilmente attributi/flag del parametro.AsmRecordReader.ConfigNameRegexla usa come ancora di fine record; se un campione futuro avesse un valore diverso, il match salterebbe silenziosamente (nessun contatore di sicurezza analogo a quello dimemberscopre questo caso).
ConfigName compare esattamente per i membri che sono istanze, e per
nessun altro. L'algoritmo è quindi: leggere members, costruire la mappa
id membro → nome istanza dai record ConfigName
(AsmRecordReader.LeggiMembri, dizionario istanze), e sostituire il nome dove la
mappa ha una voce. Il generico resta disponibile come Membro.Generico.
Avvertenza.
ConfigNamesta in mezzo a parametri palesemente aziendali (TIPO_PARTE,CLASSE_RICAR,GR_PRODOTTO,TECN_RIORDINO), quindi potrebbe non essere un meccanismo standard di Creo ma una convenzione interna. Regge sui campioni disponibili, ma su file provenienti da un'altra installazione va riverificato prima di fidarsene.
4.0.1 Eccezione: componenti da pattern/array feature (#AF<n>)
Un componente creato da un pattern/array feature (stessa geometria ripetuta N volte da
una feature di pattern, non N placement manuali distinti) ha un ConfigName con un nome
nella forma <codice>#AF<n> (n = indice dell'istanza nel pattern), es.
9244250136_C01#AF0. Non è un'istanza di family table: è un tag interno di
disambiguazione fra le ripetizioni del pattern. La BOM reale di Creo lo ignora e mostra
il codice pulito, unendo le occorrenze duplicate in una sola riga con la quantità totale.
Osservato su 9244250136.asm: 4 dei suoi 8 membri hanno ConfigName con suffisso #AF,
due dei quali (_C03#AF0 e _C03#AF1) sono lo stesso codice con n diverso — la BOM li
mostra come un'unica riga 9244250136_C03 con quantità 2. Senza normalizzare il suffisso,
ogni istanza del pattern risulterebbe un componente distinto (e con nome sbagliato).
AsmRecordReader rimuove #AF\d+$ da ogni valore di ConfigName prima di usarlo
(SuffissoArrayFeatureRegex). Scoperto durante un test end-to-end su un albero reale più
ampio degli 8 campioni originali (9284218024b.asm, confrontato con la sua BOM Creo di
riferimento) — non era presente in nessuno degli 8 campioni usati per il reverse engineering
iniziale.
4.0.2 Istanza risolta direttamente in members, senza passare da ConfigName
Non tutti i membri che sono istanze di family table hanno un record ConfigName associato.
Per alcune parti commerciali/normalizzate non-UNI, il campo name (e allo stesso modo
family_name) del record members (§3) porta già per intero la notazione
<nome istanza><generico> invece del solo nome generico:
\xe3 <id> <type> "<istanza><generico>"\0 "<istanza><generico>"\0
È la stessa notazione istanza→generico della §4.1 sotto, ma qui compare dentro il record
members stesso, come valore diretto del componente - non nell'elenco di riferimenti
geometrici di cui parla la §4.1 (quello sì da ignorare per la BOM). Il generico fra < e >
è il file realmente indicizzato sul disco; il nome prima di < è quello che la BOM
mostrerebbe.
AsmRecordReader.LeggiMembri lo riconosce per i membri senza un ConfigName
corrispondente il cui campo name rispetta per intero il pattern <testo><testo>: il testo
dopo l'ultima < (fino alla > finale) diventa il generico da cercare nell'indice, quello
prima diventa il nome mostrato. Senza questo split, il codice cercato nell'indice sarebbe la
stringa intera con le parentesi comprese, mai presente nell'indice - il componente
risulterebbe erroneamente "non trovato".
4.1 Una seconda pista, che NON va usata per la BOM
Altrove nel file compaiono stringhe nella forma istanza<generico>:
\xe2\x02 "R8--_-_U6592_AISI304<UNI6592>"\0
Documenta la stessa relazione istanza→generico in modo autonomo ed è utile come
riscontro incrociato (è lo stesso pattern <codice><GENERICO> già sfruttato
dall'euristica testuale in formato-asm.md per le family table non-UNI).
Non è però una lista dei componenti: su un campione produceva 26 voci
contro 17 membri reali, includeva alcuni nipoti (figli dei sottoassiemi) ma
non tutti, e ripeteva le stesse voci più volte. È la lista dei riferimenti
geometrici dei vincoli di accoppiamento, non una distinta. Usarla per la BOM
dà risultati sbagliati sia in eccesso sia in difetto.
5. Il nome proprio del modello
La BOM intesta i blocchi col nome del modello, non del file: il file
9244248550b.asm.11 produce Assieme 9244248550B contiene:. Il nome sta nel
primo record objname del file:
\x00 "objname"\0 \xe2 \xe0\x01 "object_type"\0 <int> \xe0\x0a "name"\0 <nome>\0
La prima occorrenza è il modello stesso; quelle successive sono altri
oggetti referenziati (disegni, modelli citati). AsmRecordReader.NomeProprio cerca
solo la prima occorrenza (Regex.Match, non Matches), verificato su tutti i
campioni disponibili durante il reverse engineering.
6. Generare la BOM: ricorsione e formato di output
members dà un solo livello. La BOM di Creo è ricorsiva: apre anche i .asm
dei sottoassiemi. Implementato in CreoAsm.GeneraBom/Render in
scripts/CreoBom.cs.
6.1 Struttura del file BOM
- Codifica UTF-8 con BOM (
EF BB BF), fine riga CRLF. - Due righe vuote in testa, poi il primo blocco.
- Tre righe vuote fra un blocco e il successivo, due prima del riepilogo. Il file termina con un solo CRLF.
- Riga voce:
{qta:6} {tipo:>12} {nome}— cioè quantità allineata a destra su 6 caratteri, spazio, tipo allineato a destra su 12, due spazi, nome. - Intestazioni:
Assieme <nome> contiene:per la radice,Sottoassieme <nome> contiene:per gli altri.
6.2 Ordine dei blocchi
Visita depth-first pre-order: si emette il blocco di un sottoassieme e
subito dopo, ricorsivamente, quelli dei suoi sottoassiemi, prima di passare al
fratello successivo. Ogni modello compare una volta sola anche se montato
più volte (deduplica per nome, CreoAsm.Blocchi).
Verificato.
9244218080_C01.asmmonta2605410010_C01due volte (scripts/9244218080_c01.asm.1+scripts/9244218080_c01.bom.2di riferimento): la BOM Creo mostra un solo bloccoSottoassieme 2605410010_C01 contiene:con quantità 2 in testa, non due blocchi separati, eCreoBom.csriproduce l'output esatto senza modifiche. Confermata anche la moltiplicazione delle quantità nel riepilogo (§6.3): le parti di2605410010_C01compaiono raddoppiate (1→2, 1→2, 16→32).
6.3 Riepilogo finale
Summary of items for <radice>: elenca solo le parti, con la quantità
totale moltiplicata lungo l'albero (una parte in 4 esemplari dentro un
sottoassieme montato 2 volte conta 8), nell'ordine di prima comparsa nella
visita depth-first (CreoAsm.Sommario). Una parte che compare a più livelli
occupa la posizione della sua prima comparsa e somma le quantità.
6.4 Risoluzione dei file dei sottoassiemi
È il punto che il formato non risolve: il .asm registra il nome del
modello, non il percorso. Servono quindi cartelle di ricerca fornite
dall'esterno (-p/--path). CreoAsm.CreaFinder indicizza <nome>.asm.<rev>
senza distinzione di maiuscole e, a parità di nome, prende la revisione più
alta presente.
Verificato: è la scelta giusta. Il
.asmpadre non registra la revisione attesa del figlio (cercata e non trovata, vedi §7), ma non serve: confermato che Creo stesso apre sempre la revisione più alta disponibile di un componente, indipendentemente da quale fosse presente quando l'assieme padre fu salvato l'ultima volta. Caso reale controllato:9284208092_D110.asm(salvato 2018-08-01) referenzia9284202481.prt, che sulla share ha revisioni sia del 2014 (v1-v3, presenti all'epoca) sia del 2024 (v4-v6, arrivate anni dopo) - Creo carica v6, non v3.CreaFinderche prende sempre la revisione più alta in cartella replica esattamente questo comportamento, non è un'assunzione azzardata. Vediformato- asm.md§"Risoluzione ricorsiva dell'albero" per le trappole storiche su questo punto (con l'euristica testuale - restano valide, sono su un problema diverso: trovare la cartella giusta, non quale revisione preferire).
Se un sottoassieme non si trova, il blocco viene emesso con
*** NON RISOLTO: file .asm non trovato *** e le sue parti restano fuori dal
riepilogo. Il risultato è dichiaratamente incompleto invece che silenziosamente
sbagliato.
7. Stato di ogni punto aperto
Tabella di riferimento: cosa è provato, come, e cosa resta genuinamente ignoto. Il dettaglio di ciascun punto segue sotto, in due gruppi.
| Punto | Stato | Prova |
|---|---|---|
| Deduplica blocchi + moltiplicazione quantità (§6.2/§6.3) | Risolto | BOM reale (§7.1) |
| Revisione del figlio che il padre si aspetta (§6.4) | Risolto | BOM reale (§7.1) |
Stato soppresso/escluso in members |
Risolto | BOM reale (§7.1) |
| Family table di assiemi (rilevamento) | Risolto: serve la conferma del nome proprio (objname), il solo model_name dava falsi positivi |
BOM reali + albero raw/ completo (§7.1) |
| Family table di assiemi (contenuto di ogni istanza) | Non risolto, nessun impatto sulla BOM (fallback già corretto) | Scan sull'intera share (§7.2) |
Costante \x82\x7b in ConfigName |
Significato ignoto, rischio coperto | Scala 350 file (§7.2) |
Occorrenze multiple di members (simplified rep) |
Mai osservato, rischio coperto | Scala 350 file (§7.2) |
Matrice di posizionamento (e1/e2/e3/origin) |
Non letto, nessun impatto sulla BOM | — (§7.2) |
Campi numerici dell'header dopo ASSEMBLY |
Non letto, nessun impatto sulla BOM | — (§7.2) |
Divergenza family_name ≠ name |
Mai osservata, non letta | — (§7.2) |
"Rischio coperto" vuol dire: il codice non sa cosa significhi il byte, ma verifica un invariante che lo renderebbe visibile (eccezione) se mai smettesse di valere - non è una prova che il byte non conti mai, è la garanzia che se contasse il programma lo segnalerebbe invece di sbagliare in silenzio.
7.1 Risolti con una BOM Creo di riferimento
-
Deduplica blocchi e moltiplicazione quantità.
9244218080_C01.asmmonta2605410010_C01due volte (scripts/9244218080_c01.asm.1+scripts/9244218080_c01.bom.2): la BOM Creo mostra un solo bloccoSottoassieme 2605410010_C01 contiene:con quantità 2 in testa (non due blocchi separati), e le sue parti compaiono raddoppiate nel riepilogo (1→2, 1→2, 16→32).CreoBom.csriproduce l'output esatto senza modifiche: la deduplica per nome (CreoAsm.Blocchi) e la moltiplicazione lungo l'albero (CreoAsm.Sommario) sono il comportamento reale di Creo, non solo un'assunzione. -
Revisione del figlio.
9284208092_D110.asm(salvato 2018-08-01) referenzia9284202481.prt, che sulla share ha revisioni sia del 2014 (v1-v3, presenti quando il padre fu salvato) sia del 2024 (v4-v6, arrivate anni dopo). Confermato direttamente in Creo: apre v6, non v3 - sempre la revisione più alta disponibile, indipendentemente da quale fosse presente all'ultimo salvataggio del padre.CreoAsm.CreaFinder, che prende sempre la revisione più alta trovata in cartella, replica esattamente questo comportamento. -
Stato soppresso/escluso. Stesso assieme
2546220101.asmrisalvato da Creo con2546220101_C02soppresso (scripts/2546220101.asm.1+scripts/2546220101.bom.1, con le parti/sottoassiemi correlati). Un componente soppresso non produce alcun record inmembers: sparisce e basta, il conteggio dichiarato scende di conseguenza (\xf8 03invece di\xf8 04). Nessun marcatore di stato da gestire:CreoBom.cssul file risalvato riproduce esattamente la BOM di riferimento senza modifiche al parser.Sparisce da
members, non dal file. La feature che lo posiziona resta, e con essa la stringaComponente ID <n> (<codice>.PRT)(localizzata come inAssemblyComponentScanner.HaPlacement). Per la BOM va ignorata - Creo non elenca i soppressi - ma il file di quel componente serve comunque per aprire l'assieme: se manca, Creo fallisce il caricamento con "dati di input non completi" ([-4], cioèPRO_TK_E_NOT_FOUND). Verificato su un caso reale: un assieme che non si apriva dalla cartellaraw/, e che si è aperto senza errori copiandoci dentro quel solo file.AsmRecordReader.EstraiCodiciSoppressili restituisce a parte, apposta per questo - vedi §7.3. -
Family table di assiemi — rilevamento. Non solo le parti normalizzate (§4) possono essere family table: un intero assieme può esserlo. Osservato su
2546220101.asm(dentro l'albero di9284218024b.asm): il file generico ha una propria sezionemembersche elenca il contenuto di UNA rappresentazione (3 cuscinetti + 1 sotto-assieme, dati arbitrari), ma la BOM Creo per lo stesso file mostra 4 istanze diverse (2546220101_C01.."_C04", tre "Parte" e una "Sottoassieme" - tipi diversi fra loro), ciascuna con un contenuto diverso. Il nome istanza qui non passa daConfigName(verificato: nessuna occorrenza letterale nel file), ma da una coppia di record distinta subito dopo l'header del modello:\xe0\x01model_t_type\0<int>\xe0\x0amodel_name\0<codice>_C0n\0\xe0\x0ageneric_name\0\0 ...stamp\0\xe2\xf2\xf7<idSchema> (\xe3<type>\x02<codice>_C0n\0\0\xe2)* -- un'occorrenza per ogni istanza sorella \xf4Quando questo succede il file generico è già di per sé un'istanza (
model_nameporta il suffisso_C0n, non è neutro) estampelenca le sorelle.Il record
model_nameda solo non è un'identità. Su file reali quel record nomina quasi sempre un altro modello - tipicamente uno dei membri del file stesso, a volte un codice imparentato - e in quei casimembersè già corretto e va usato. Serve una conferma indipendente: il nome proprio del modello (objname, §5).LeggiMembrisegnala la family table solo seobjnameesiste e coincide conmodel_nameprivato del suffisso_C0n; negli altri casi leggemembersnormalmente.Il controllo più largo (senza la conferma di
objname) era la causa di falsi "componenti mancanti": il parser esatto rifiutava file perfettamente leggibili, si ripiegava sull'euristica testuale e quella estraeva codici che non esistono, riportati poi come componenti non trovati nell'indice. Verificato contro BOM Creo di riferimento: sui file dove il segnale scattava senza conferma diobjname,memberselenca esattamente i componenti della BOM reale (anche nello stesso ordine); uno di questi file è stato inoltre aperto in Creo, e l'albero del modello mostra gli stessi componenti, senza alcuna family table. Con la conferma diobjnamerichiesta, tutti i.asmdi una cartellaraw/reale (351 file, alberi completi di più macchine) si leggono col parser esatto, zero ricadute sull'euristica.Da tenere presente: il sintomo dipende da come il file è stato salvato, non solo dal modello. Rileggendo una revisione più recente dello stesso assieme generico, il record di identità può non esserci più e
memberselencare direttamente tutte le istanze, col tipo giusto per ciascuna - cioè esattamente quello che mostra la BOM. Il blocco quindi non è una proprietà stabile del codice: va valutato file per file, revisione per revisione.Verificato anche che il fallback su
AssemblyComponentScannerrisolve già correttamente il caso vero: i nomi delle istanze compaiono come testo puro nel campostamp, e l'euristica ("codice che compare almeno due volte") li trova senza bisogno di decodificarestamp. E che una singola istanza, se esportata da Creo come file a sé (Family Table → istanza → "Salva una copia"), è un.asm/.prtnormale senza traccia di family table al suo interno - letto correttamente dal parser esatto (2546220101_c04.asm.1, verificato contro2546220101.bom.1).
7.2 Non risolti
-
Family table di assiemi — contenuto di ogni istanza. Come recuperare il contenuto specifico di un'istanza senza che esista già un file separato per essa sul disco (il caso comune, se nessuno l'ha mai esportata a parte con "Salva una copia"): il meccanismo dietro
stampnon è stato decodificato oltre a nome+tipo di ciascuna istanza sorella:stampporta solo i nomi delle sorelle, non il loro contenuto, quindi non è la struttura da cui partire per risolvere questo punto.Resta aperto solo per completezza: sui file dove il segnale scatta davvero (con conferma del nome proprio, §7.1) il parser si ferma invece di restituire un contenuto sbagliato, e il chiamante ripiega sull'euristica testuale, che i nomi delle istanze li trova comunque come testo puro. Nessun comportamento dell'app dipende da questa decodifica.
-
Costante
\x82\x7bnei recordConfigName: significato ignoto. Rischio coperto -LeggiMembriconta le occorrenze grezze diConfigNamee le confronta col numero di match del regex; se un campione avesse un valore diverso, il regex non farebbe più match (prima: in silenzio) e ora lancia eccezione invece di restituire il nome generico al posto dell'istanza senza dirlo. Verificato su tutti i 350.asmdiraw/: 215 usano davveroConfigName, 0 mismatch. -
Occorrenze multiple di
members(rappresentazioni semplificate, mai osservate). Rischio coperto -LeggiMembrilancia eccezione se ne trova più di una invece di prendere silenziosamente la prima senza sapere se sia quella giusta. Verificato su tutti i 350.asmdiraw/: sempre esattamente una occorrenza. -
La matrice di posizionamento (
e1,e2,e3,origin): formato numerico non decodificato, nessun impatto sulla BOM (non letta da nessuna parte del codice). -
I campi numerici dell'header dopo
ASSEMBLY: non letti, nessun impatto sulla BOM. -
family_name: sempre uguale anamesu tutti i campioni disponibili; non si sa in quale situazione divergerebbe, né se il codice dovrebbe fare qualcosa se succedesse (oggi non la usa in nessun modo, legge soloname).
7.3 Due domande diverse: cosa va in distinta, cosa serve per aprire
È la lezione che costa di più riscoprire, quindi sta qui in chiaro: la lista dei componenti della BOM e la lista dei file che servono a Creo per aprire l'assieme non coincidono. La seconda è più lunga.
| Lista | Serve per | Si ottiene con |
|---|---|---|
| Componenti della distinta | BOM, verifica completezza | LeggiMembri / EstraiCodiciReferenziati (sezione members) |
| File necessari al caricamento | popolare raw/ per l'export STEP |
i precedenti più EstraiCodiciSoppressi e EstraiCodiciRiferimentiEsterni (§7.5) |
La differenza sono i componenti soppressi (§7.1) e i riferimenti esterni (§7.5): fuori
dalla BOM, ma senza il loro file Creo non apre l'assieme. Chi popola raw/ deve quindi usare
l'unione; chi genera la BOM o verifica la completezza deve usare solo i membri, altrimenti
quelle voci diventano falsi "componenti mancanti".
Il sintomo, se si sbaglia, non assomiglia per niente alla causa: Creo Distributed Batch
fallisce con Carica modello in Creo Pro | Non riuscito | [-4] Errore: Dati di input non completi - un messaggio che non nomina il file mancante. Il modo più rapido per trovarlo è
aprire l'assieme in Creo dalla cartella raw/ (non dalla share, dove i file ci sono tutti
e quindi si apre senza errori) e cercare nell'albero la voce con l'icona rossa.
7.4 Lo stesso codice esiste in più cartelle della share
Sulla share 13% dei codici (5.193 su 39.274 fra prt e asm) ha la versione più alta in più cartelle, con contenuto diverso. Non sono revisioni: sono copie parallele, lasciate da studi preliminari, allestimenti fiera, cartelle di lavoro datate, raccolte temporanee.
Il .asm non dice quale copia usa: verificato che non registra né il percorso, né
l'UOBJ_ID del figlio (che pure distingue le copie), né la revisione attesa. Cita solo il
nome del modello. Quale copia venga caricata lo decide Creo al momento dell'apertura, e la
regola pratica è: prima la cartella del modello che la referenzia.
Non esiste quindi un criterio globale corretto - e i numeri lo confermano: nel 79% dei
conflitti nessuna delle copie sta in una cartella "ufficiale" (…\M_B, …\A), perché il
conflitto è fra due istantanee parallele, ognuna autoconsistente con i propri componenti. Per
data, per versione e per posizione sono equivalenti: solo il contesto di chi referenzia
discrimina.
Conseguenze per il codice:
Form1.ScegliPerCartellarisolve ogni componente preferendo la copia nella stessa cartella dell'assieme che lo referenzia, e solo in mancanza ripiega su versione più alta e data più recente.Form1.CartellaDiOriginericostruisce quella cartella per gli assiemi di partenza, che arrivano dalla fase precedente senza portarsela dietro.- La vista
files_ultima_versione(vedidb-cad-index.md) rompe il pareggio fra copie di pari versione in modo deterministico invece che arbitrario. - Resta un rischio noto: quando il componente non sta nella cartella del suo assieme, la
scelta torna a essere una scommessa. Se un assieme non si apre da
raw/, è la prima cosa da verificare -9244206098è il caso reale che ha portato a questa sezione: due copie di pari versione, una del 2011 in una cartella di file sciolti e una del 2026 accanto all'assieme, e inraw/finiva quella del 2011.
7.5 Riferimenti esterni: la tabella dei modelli citati
Terzo modo in cui un .asm dipende da un file che non è fra i suoi membri, dopo i soppressi
(§7.1) e le copie ambigue (§7.4). Un assieme che porta assembly feature o datum copiati
da un altro modello lo cita in una tabella a parte, che non è members e non lascia una stringa
Componente ID …: i due lettori esistenti non la vedevano.
Struttura: un array aperto da \xf2\xf7\x05 e chiuso da \xf3, con elementi
\xe3\xe2<tipo><nome>\x00. Il nome può portare il suffisso #AF<n> di §4.0.1 e/o la notazione
istanza<generico> di §4.0.2 - il file su disco è il generico:
f2 f7 05 e3 e2 01 "9255708017#AF2<9255708017>" 00 ... -> 9255708017
e3 e2 02 "9255700181" 00 ... -> 9255700181
f3
Va letta con EstraiCodiciRiferimentiEsterni, e - a differenza dei soppressi - si ricorre
dentro: Creo carica il modello citato con tutti i suoi componenti, che quindi servono anch'essi
in raw/. La tabella contiene anche nomi di feature (MIRROR3, CHAMFER1,
M10_TAPPED_HOLE1), indistinguibili dai modelli guardando il record: non è un problema perché
chi la usa cerca ogni nome nell'indice e ignora quelli che non ci sono - per lo stesso motivo
questi codici non devono finire fra i "non trovati".
Il caso reale: 9255728001 falliva in Distributed Batch con lo stesso [-4] Dati di input non completi di §7.3 perché il suo sottoassieme 9255728001_200 referenzia per assembly feature
9255708017 e 9255708018 (e con loro 9255700181/182/183), e monta una vite
V6-12--_-_U5933_C<UNI5933> il cui generico uni5933.prt non era in raw/. Nessuno dei sei
file compariva in members né fra i piazzamenti.
8. Uso
dotnet run scripts/CreoBom.cs -- raw/9244228558.asm.20 -p M:\modelli -o out\9244228558.bom.1
dotnet run scripts/CreoBom.cs -- raw/9244228558.asm.20 -p M:\modelli # stampa a video
dotnet run scripts/Analyze.cs -- raw/9244228558.asm.20 "ConfigName" # dump dei byte per investigare a mano
Senza -p/--path, la ricerca dei sottoassiemi usa la cartella del file di partenza.
-p è ripetibile per più cartelle. -o scrive il file col BOM UTF-8 e CRLF descritti
al §6.1; senza -o il testo va su stdout (con \n al posto di \r\n, per leggibilità
in console).