Normalizza il suffisso #AF di ConfigName; documenta il limite sulle family table di assiemi
Test end-to-end su un albero reale (9284218024b.asm, confrontato con la sua BOM Creo di riferimento tramite scripts/CreoBom.cs) oltre gli 8 campioni usati per il reverse engineering iniziale. Trovato: i componenti creati da un pattern/array feature hanno un ConfigName con suffisso "<codice>#AF<n>" (n = indice nel pattern), non un'istanza di family table vera - la BOM di Creo lo ignora e unisce le occorrenze duplicate. AsmRecordReader ora rimuove questo suffisso prima di usare il nome istanza. Trovato ma non risolto: un intero ASSIEME (non solo le parti normalizzate) puo' avere una family table, risolta da Creo con un meccanismo diverso da ConfigName (non decodificato). AsmRecordReader non lo rileva e non lancia eccezione in questo caso: il fallback automatico di Form1 non scatta, e il risultato puo' essere silenziosamente incompleto. Documentato come limite noto in formato-asm-record.md e in CLAUDE.md - nessun fix applicato finche' il meccanismo non e' meglio compreso. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cce377a091
commit
28a72c80b2
4 files changed
+70
-3
No files matched your search
@@ -63,7 +63,10 @@ designer visuale):
|
||||
sotto-assiemi/parti (`RisolviAlberoComponenti`), copiandoli anch'essi in `raw/`. Usa
|
||||
`AsmRecordReader` (parser esatto dei record binari, `docs/formato-asm-record.md`) e ripiega
|
||||
su `AssemblyComponentScanner` (euristica testuale, `docs/formato-asm.md`) solo se il file
|
||||
non rispetta il formato validato (`InvalidDataException`).
|
||||
non rispetta il formato validato (`InvalidDataException`). ⚠️ Limite noto non coperto dal
|
||||
fallback: un **assieme** con family table (raro, non solo le parti normalizzate possono
|
||||
averla) può dare un risultato silenziosamente incompleto senza lanciare eccezione — vedi
|
||||
`docs/formato-asm-record.md` §7.
|
||||
5. **Verifica completezza** — mostra i `.asm` della distinta già in `raw/`, trascinabili su
|
||||
Creo per l'esportazione STEP (Distributed Batch, manuale, fuori dall'app); "Verifica
|
||||
completezza assiemi" controlla ricorsivamente sull'indice (non su `raw/`, stessa
|
||||
|
||||
@@ -68,6 +68,25 @@ public class AsmRecordReaderTests
|
||||
Assert.Equal("UNI6592", membri[0].Generico);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rimuove_il_suffisso_array_feature_dal_nome_istanza()
|
||||
{
|
||||
// Regressione: caso reale 9244250136.asm -> ConfigName con suffisso "#AF<n>" per
|
||||
// componenti creati da un pattern/array feature (id 45 e id 78, entrambi variante
|
||||
// "_C03" con suffisso diverso "#AF0"/"#AF1"): non un'istanza di family table vera,
|
||||
// la BOM di Creo mostra il codice pulito e unisce le occorrenze duplicate.
|
||||
byte[] dati = Asm(2, Bytes(
|
||||
Membro(45, 2, "9244250136_C03"),
|
||||
Membro(78, 2, "9244250136_C03")),
|
||||
extra: Bytes(
|
||||
ConfigName("9244250136_C03#AF0", idMembro: 45),
|
||||
ConfigName("9244250136_C03#AF1", idMembro: 78)));
|
||||
|
||||
var membri = AsmRecordReader.LeggiMembri(dati);
|
||||
|
||||
Assert.All(membri, m => Assert.Equal("9244250136_C03", m.Nome));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Componente_senza_ConfigName_non_e_toccato_da_istanze_di_altri_membri()
|
||||
{
|
||||
|
||||
@@ -41,6 +41,14 @@ public static class AsmRecordReader
|
||||
@"\x00objname\x00\xe2\xe0\x01object_type\x00[\x00-\x7f]\xe0\x0aname\x00([^\x00]+)\x00",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
// Un componente creato da un pattern/array feature ha un ConfigName con un tag interno
|
||||
// "<codice>#AF<n>" (Array Feature, n = indice nel pattern), non un'istanza di family
|
||||
// table vera: la BOM reale di Creo lo ignora e mostra il codice pulito - osservato su
|
||||
// 9244250136.asm (4 dei suoi 8 membri, due dei quali stesso codice "_C03" con "#AF0" e
|
||||
// "#AF1" diversi, che la BOM unisce in una sola riga con quantita' 2). Senza questa
|
||||
// normalizzazione ogni istanza del pattern risulterebbe un componente diverso.
|
||||
private static readonly Regex SuffissoArrayFeatureRegex = new(@"#AF\d+$", RegexOptions.Compiled);
|
||||
|
||||
// primo elemento dell'array "members": porta le chiavi per esteso.
|
||||
private static readonly Regex FirstMemberRegex = new(
|
||||
@"\x01id\x00" + Int + @"\xe0\x01type\x00" + Int
|
||||
@@ -78,7 +86,7 @@ public static class AsmRecordReader
|
||||
|
||||
var istanze = new Dictionary<int, string>();
|
||||
foreach (Match m in ConfigNameRegex.Matches(testo))
|
||||
istanze[AsInt(m.Groups[2].Value)] = m.Groups[1].Value;
|
||||
istanze[AsInt(m.Groups[2].Value)] = SuffissoArrayFeatureRegex.Replace(m.Groups[1].Value, "");
|
||||
|
||||
const string chiaveMembers = "\0members\0";
|
||||
int start = testo.IndexOf(chiaveMembers, StringComparison.Ordinal);
|
||||
|
||||
@@ -184,7 +184,7 @@ Il collegamento sta in un record di **parametro** applicato al membro:
|
||||
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.File`.
|
||||
mappa ha una voce. Il generico resta disponibile come `Membro.Generico`.
|
||||
|
||||
> **Avvertenza.** `ConfigName` sta in mezzo a parametri palesemente aziendali
|
||||
> (`TIPO_PARTE`, `CLASSE_RICAR`, `GR_PRODOTTO`, `TECN_RIORDINO`), quindi
|
||||
@@ -192,6 +192,25 @@ mappa ha una voce. Il generico resta disponibile come `Membro.File`.
|
||||
> 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.1 Una seconda pista, che NON va usata per la BOM
|
||||
|
||||
Altrove nel file compaiono stringhe nella forma `istanza<generico>`:
|
||||
@@ -291,6 +310,24 @@ sbagliato.
|
||||
|
||||
Elencato esplicitamente per non far passare per dimostrato ciò che non lo è:
|
||||
|
||||
- **⚠️ Family table di ASSIEMI (non solo di parti), risultato silenziosamente sbagliato.**
|
||||
Non solo le parti normalizzate (§4) possono essere family table: un intero **assieme** può
|
||||
esserlo. Osservato su `2546220101.asm` (dentro l'albero di `9284218024b.asm`): il file
|
||||
generico ha una propria sezione `members` che elenca il contenuto di UNA rappresentazione
|
||||
(nell'esempio, 3 cuscinetti + 1 sotto-assieme), 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 proprio contenuto potenzialmente diverso. Il nome
|
||||
istanza qui **non** passa da un record `ConfigName` (verificato: nessuna occorrenza
|
||||
letterale di `ConfigName` nel file) ma da un meccanismo diverso, imparentato con la
|
||||
"seconda pista" del §4.1 (`\xe3<id>\xe2\x02<stringa>\0`, senza il bracket
|
||||
`<generico>` in questo caso) - non decodificato. **`AsmRecordReader.LeggiMembri` non lo
|
||||
rileva e non lancia eccezione**: restituisce silenziosamente il contenuto della
|
||||
rappresentazione di default sotto un solo nome, invece delle 4 istanze corrette - a
|
||||
differenza degli altri limiti di questa sezione, qui il fallimento non è segnalato in
|
||||
nessun modo al chiamante (né un conteggio che non torna, né un'eccezione). Scoperto sullo
|
||||
stesso test end-to-end del punto `#AF` sopra, mai osservato negli 8 campioni originali.
|
||||
**Prima di fidarsi del parser esatto su un albero con assiemi a family table, verificare a
|
||||
mano contro la BOM Creo.**
|
||||
- **Stato soppresso / escluso.** Nessuna evidenza cercata o trovata. Non si sa
|
||||
se un componente soppresso compaia in `members`, né con quale marcatore. Sui
|
||||
campioni disponibili il totale coincideva con la BOM, il che *suggerisce* che
|
||||
|
||||
Reference in new issue
Block a user