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
|
sotto-assiemi/parti (`RisolviAlberoComponenti`), copiandoli anch'essi in `raw/`. Usa
|
||||||
`AsmRecordReader` (parser esatto dei record binari, `docs/formato-asm-record.md`) e ripiega
|
`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
|
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
|
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
|
Creo per l'esportazione STEP (Distributed Batch, manuale, fuori dall'app); "Verifica
|
||||||
completezza assiemi" controlla ricorsivamente sull'indice (non su `raw/`, stessa
|
completezza assiemi" controlla ricorsivamente sull'indice (non su `raw/`, stessa
|
||||||
|
|||||||
@@ -68,6 +68,25 @@ public class AsmRecordReaderTests
|
|||||||
Assert.Equal("UNI6592", membri[0].Generico);
|
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]
|
[Fact]
|
||||||
public void Componente_senza_ConfigName_non_e_toccato_da_istanze_di_altri_membri()
|
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",
|
@"\x00objname\x00\xe2\xe0\x01object_type\x00[\x00-\x7f]\xe0\x0aname\x00([^\x00]+)\x00",
|
||||||
RegexOptions.Compiled);
|
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.
|
// primo elemento dell'array "members": porta le chiavi per esteso.
|
||||||
private static readonly Regex FirstMemberRegex = new(
|
private static readonly Regex FirstMemberRegex = new(
|
||||||
@"\x01id\x00" + Int + @"\xe0\x01type\x00" + Int
|
@"\x01id\x00" + Int + @"\xe0\x01type\x00" + Int
|
||||||
@@ -78,7 +86,7 @@ public static class AsmRecordReader
|
|||||||
|
|
||||||
var istanze = new Dictionary<int, string>();
|
var istanze = new Dictionary<int, string>();
|
||||||
foreach (Match m in ConfigNameRegex.Matches(testo))
|
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";
|
const string chiaveMembers = "\0members\0";
|
||||||
int start = testo.IndexOf(chiaveMembers, StringComparison.Ordinal);
|
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
|
nessun altro. L'algoritmo è quindi: leggere `members`, costruire la mappa
|
||||||
`id membro → nome istanza` dai record `ConfigName`
|
`id membro → nome istanza` dai record `ConfigName`
|
||||||
(`AsmRecordReader.LeggiMembri`, dizionario `istanze`), e sostituire il nome dove la
|
(`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
|
> **Avvertenza.** `ConfigName` sta in mezzo a parametri palesemente aziendali
|
||||||
> (`TIPO_PARTE`, `CLASSE_RICAR`, `GR_PRODOTTO`, `TECN_RIORDINO`), quindi
|
> (`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
|
> interna. Regge sui campioni disponibili, ma su file provenienti da
|
||||||
> un'altra installazione va riverificato prima di fidarsene.
|
> 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
|
### 4.1 Una seconda pista, che NON va usata per la BOM
|
||||||
|
|
||||||
Altrove nel file compaiono stringhe nella forma `istanza<generico>`:
|
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 è:
|
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
|
- **Stato soppresso / escluso.** Nessuna evidenza cercata o trovata. Non si sa
|
||||||
se un componente soppresso compaia in `members`, né con quale marcatore. Sui
|
se un componente soppresso compaia in `members`, né con quale marcatore. Sui
|
||||||
campioni disponibili il totale coincideva con la BOM, il che *suggerisce* che
|
campioni disponibili il totale coincideva con la BOM, il che *suggerisce* che
|
||||||
|
|||||||
Reference in new issue
Block a user