Files
prep-step/docs/formato-asm.md
T
davideandClaude Sonnet 5 e7b5d85f6b Add family_name and bracket-adjacent placement signals for .asm scanning
Two more real placement forms found by comparing against Creo BOM reports:

- A sub-assembly can be referenced only through a "family_name\0<code>\0"
  metadata field instead of a textual "Componente ID" placement or the
  doubled-NUL pattern (case 9284202452 inside 9284208188_D400.asm, which
  contains 9284202452_C01/_C02/_C03 - previously dropped entirely, along
  with its three children).
- A bare numeric code immediately followed by a family-table bracket
  (<code><GENERIC>, e.g. bearing instances like 2510060303<CUSCI_ACBB7>)
  is a real reference to that instance, even though only the generic file
  is indexed on disk. Previously the instance code silently vanished before
  ever reaching the DB lookup; now it survives placement filtering and ends
  up in the "scartati" (discarded) list instead, so it's traceable rather
  than invisible.

docs/formato-asm.md documents both, bringing the total to four recognized
placement forms.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 16:14:52 +02:00

14 KiB

Come leggere un .asm Creo per dedurne i componenti

Il file .asm di Pro/ENGINEER-Creo è un formato binario proprietario, non documentato pubblicamente. Non esiste un parser strutturale completo in questo progetto: l'estrazione dei componenti si basa su un'euristica testuale, validata a mano confrontando l'output con l'albero reale mostrato da Creo. Questo documento riassume cosa si è osservato e i suoi limiti.

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 e in 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.

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 sia in 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 + CodiceRadice — estrazione dei candidati da un file, usata dalla BFS in Form1.RisolviAlberoComponenti (fase "Trova componenti referenziati") e Form1.AssiemeCompleto (fase "Verifica completezza"). Entrambe seminano l'insieme dei codici visitati con codice + radice di ogni file di partenza.
  • 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ù 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.

Strumento di analisi manuale

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:

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.