"File -> Nuova estrazione" (Ctrl+N) azzera distinta/raw/verifica per ripartire senza ricaricare DB e cartella di lavoro; "Aiuto -> Guida" (F1) apre una sintesi interattiva delle 5 fasi. Rimossa "Verifica completezza assiemi": bottone, ReportCompletezzaForm e VerificaCompletezzaAssiemi non più necessari. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
274 lines
18 KiB
Markdown
274 lines
18 KiB
Markdown
# Architettura di PrepStep (Cevolani StepPrep)
|
|
|
|
Panoramica di tutti i file sorgente e di come collaborano. `PrepStep` è il nome
|
|
tecnico del progetto (cartella, namespace, csproj, eseguibile `PrepStep.exe`);
|
|
**Cevolani StepPrep** è il nome mostrato all'utente finale (titolo finestra, icona,
|
|
versione — vedi `<Version>`/`<Product>` in `PrepStep.csproj`). Per il "come si usa" vedi il `README.md` in root; per il
|
|
formato binario `.asm` vedi `docs/formato-asm.md` (euristica testuale) e
|
|
`docs/formato-asm-record.md` (parser esatto dei record); per la parallelizzazione della
|
|
copia in `raw/` vedi `docs/copia-raw-parallela.md`.
|
|
|
|
## Vista d'insieme
|
|
|
|
Un solo eseguibile WinForms (nessuna CLI, nessun processo esterno): indicizza i file CAD di
|
|
una share di rete in un DB SQLite locale, poi guida l'utente in un wizard a 5 fasi che, a
|
|
partire da una distinta base esportata dal gestionale, raccoglie in locale (`raw/`) tutti i
|
|
file CAD dei componenti e dei loro sotto-assiemi e verifica che l'indice li copra, come
|
|
pre-process prima dell'esportazione STEP con Creo Distributed Batch (manuale, fuori
|
|
dall'app: i file esportati finiscono in `step/`, dentro la cartella di lavoro).
|
|
|
|
```
|
|
Distinta (.xls/.xml) → CSV (componente;descrizione) → raw/ (CAD copiati dall'indice)
|
|
│
|
|
scansione .asm ─────┘ (sotto-assiemi/parti mancanti)
|
|
│
|
|
drag&drop .asm su Creo → Distributed Batch → step/ (esportazione manuale)
|
|
```
|
|
|
|
## Il processo, dall'inizio alla fine
|
|
|
|
Le 5 fasi sono una pipeline: ognuna consuma quello che ha prodotto la precedente, e il
|
|
bottone "Avanti" si sblocca solo quando il risultato c'è (`AvantiConsentito`). Sotto, cosa
|
|
entra e cosa esce da ciascuna — il dettaglio dei metodi è nella tabella della sezione
|
|
successiva.
|
|
|
|
**1. Indice della share → `cad_index.db`.** `Indexer.ScanAsync` cammina tutte le cartelle di
|
|
primo livello sotto `X:\`, riconosce i nomi Pro-E `<codice>.<tipo>.<versione>` e fa un upsert
|
|
incrementale (confronto su percorso+dimensione+data), così i giri successivi costano solo il
|
|
delta. Qui si sceglie anche la **cartella di lavoro**, che da qui in poi ospita tutto il resto.
|
|
|
|
**2. Distinta del gestionale → CSV.** `DistintaBaseImporter.ConvertiInCsv` riduce l'export a
|
|
due colonne `componente;descrizione`. Se il nome del file segue la convenzione
|
|
`DBP_<codice>_<timestamp>` aggiunge l'assieme generale come riga in più: da quel momento è un
|
|
componente come tutti gli altri, nessun caso speciale a valle.
|
|
|
|
**3. Componenti del CSV → `raw/`.** Per ogni codice del CSV `TrovaPercorsiCad` cerca
|
|
nell'indice l'ultima versione (vista `files_ultima_versione`) e `CopiaFileTrovati` la copia in
|
|
`raw/` in parallelo. Finisce qui il livello "distinta": in `raw/` ci sono i componenti
|
|
elencati dal gestionale, non ancora quello che serve ad aprirli.
|
|
|
|
**4. Chiusura ricorsiva dell'albero → `raw/` completa.** È il cuore del programma, ed è
|
|
l'unica fase che deve ragionare come Creo invece che come il gestionale.
|
|
`RisolviAlberoComponenti` tiene una coda di `.asm` da leggere, partendo da quelli già in
|
|
`raw/`, e per ognuno raccoglie **tre liste diverse**, che non vanno confuse (vedi
|
|
`docs/formato-asm-record.md` §7.3 e §7.5):
|
|
|
|
| Lista | Da dove | Ricorsione | Se non è nell'indice |
|
|
|---|---|---|---|
|
|
| Componenti della distinta | sezione `members` (`EstraiCodiciReferenziati`) | sì | va nei "non trovati" |
|
|
| Componenti **soppressi** | stringhe di piazzamento (`EstraiCodiciSoppressi`) | no — Creo non carica i figli di un soppresso | ignorato: non è un componente, segnalarlo sarebbe un falso allarme |
|
|
| **Riferimenti esterni** | tabella dei modelli citati (`EstraiCodiciRiferimentiEsterni`) | sì — Creo carica il modello citato con tutto il suo albero | ignorato, stesso motivo |
|
|
|
|
Ogni codice trovato viene copiato in `raw/` scegliendo **quale** copia con `ScegliPerCartella`
|
|
(§7.4: lo stesso codice esiste in più cartelle della share nel 13% dei casi, e vince quella
|
|
nella stessa cartella dell'assieme che lo referenzia); se è un `.asm` torna in coda e il giro
|
|
ricomincia, finché restano solo foglie. I rami raggiunti per riferimento esterno viaggiano con
|
|
un flag `Esterno` che li tiene fuori dall'elenco dei "non trovati": quell'elenco continua a
|
|
parlare solo della distinta, che è quello che l'utente si aspetta di leggerci.
|
|
|
|
Il motivo per cui questa fase è più complicata di come sembra: **la lista dei componenti e la
|
|
lista dei file necessari al caricamento non coincidono**, e la seconda è più lunga. Quando si
|
|
sbaglia, Creo Distributed Batch non dice quale file manca — dice solo `[-4] Dati di input non
|
|
completi`.
|
|
|
|
**5. Verifica e consegna a Creo.** `AggiornaAnteprimaAsm` mostra i `.asm` della distinta già
|
|
in `raw/` come icone trascinabili. L'esportazione STEP vera è manuale e fuori dall'app: si
|
|
trascinano i `.asm` su Creo Distributed Batch, che scrive in `step/`.
|
|
|
|
## Entry point
|
|
|
|
- **Program.cs** — `Main()` standard WinForms: `Application.Run(new Form1())`. Nessuna logica.
|
|
|
|
## Finestra principale (wizard)
|
|
|
|
- **Form1.cs** — tutta la UI è costruita in codice (`BuildUi`/`BuildFase*`), non con il
|
|
designer visuale. È un wizard: `_panelFasi[]` contiene un `Panel` per fase, uno solo
|
|
visibile alla volta (`VaiAllaFase`); si avanza con "Avanti" solo se `AvantiConsentito`
|
|
ritorna vero per la fase corrente. `Form1.Designer.cs` contiene solo lo scheletro minimo
|
|
generato dal template (`InitializeComponent`/`Dispose`), non è usato per costruire i
|
|
controlli.
|
|
|
|
Le 5 fasi e i metodi principali dietro ciascuna:
|
|
|
|
| # | Fase | Bottone → metodo | Cosa fa |
|
|
|---|---|---|---|
|
|
| 1 | Database | "Aggiorna indice" → `Indexer.ScanAsync`; "Cambia cartella..." → `ScegliCartellaLavoro` | Crea/aggiorna `cad_index.db` scansionando la share; sceglie/mostra la cartella di lavoro |
|
|
| 2 | Importa Distinta Base | selezione file → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella cartella di lavoro |
|
|
| 3 | Crea cartella raw | "Crea cartella raw" → `CreaCartellaRaw` | `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela) → `raw/` dentro la cartella di lavoro |
|
|
| 4 | Trova componenti referenziati | "Trova componenti referenziati" → `TrovaPrtReferenziati` → `RisolviAlberoComponenti` | Legge i `.asm` in `raw/`, risolve ricorsivamente sotto-assiemi/parti nell'indice e li copia anch'essi in `raw/`, insieme ai componenti soppressi e ai modelli citati per riferimento esterno che servono a Creo per aprire l'assieme |
|
|
| 5 | Verifica completezza | (ingresso in fase) → `AggiornaAnteprimaAsm` | Mostra le icone trascinabili dei `.asm` della distinta già in `raw/`, da trascinare su Creo per l'esportazione STEP (Distributed Batch) |
|
|
|
|
Stato condiviso tra le fasi (campi privati di `Form1`): `_dbPath`, `_cartellaLavoro`,
|
|
`_percorsoInputCorrente`, `_righeCorrenti` (righe del CSV), `_ultimaVerifica` (cache percorsi
|
|
CAD), `_ultimaCartellaRaw`.
|
|
|
|
Menu (`MenuStrip`, in cima alla finestra): "File → Nuova estrazione" (Ctrl+N) azzera lo
|
|
stato delle fasi 2-5 (`NuovaEstrazione`) per ripartire con una nuova distinta senza
|
|
ricaricare il database/la cartella di lavoro (fase 1, che resta invariata); "Aiuto → Guida"
|
|
(F1) apre `GuidaForm`, sintesi interattiva delle 5 fasi per chi non ha letto questi docs.
|
|
|
|
Altre responsabilità in Form1.cs:
|
|
- `TrovaPercorsiCad` / `CaricaCodiciNoti` — query sul DB (`files_ultima_versione`), la
|
|
seconda carica tutti i codici prt/asm noti in un colpo solo (usata sia per risolvere i
|
|
componenti sia da `AssemblyComponentScanner` per validare i token generici).
|
|
- `CopiaFileTrovati` — copia (in parallelo, grado 6) i file trovati in `raw/`, saltando
|
|
silenziosamente quelli irraggiungibili (share offline/permessi).
|
|
- `PulisciRawEStep` — svuota `raw/` e `step/` nella cartella di lavoro (manutenzione,
|
|
richiede conferma).
|
|
- `InizializzaDatabase` / `ScegliDatabase` — localizzazione del DB (ultimo usato, salvato in
|
|
`%AppData%/PrepStep/ultimo_db.txt`, o `cad_index.db` accanto all'eseguibile).
|
|
- `InizializzaCartellaLavoro` / `ScegliCartellaLavoro` — stesso pattern per la cartella di
|
|
lavoro (`ultima_cartella_lavoro.txt`), fallback `AppContext.BaseDirectory` se non c'è
|
|
ancora nulla di salvato: nessuna dipendenza da un marker di repo, funziona identica
|
|
sull'exe pubblicato standalone spostato su un'altra macchina.
|
|
|
|
## Indicizzazione
|
|
|
|
- **Indexer.cs** — `ScanAsync` cammina tutte le cartelle di primo livello sotto la radice
|
|
(scoperte dinamicamente, non un elenco fisso) con un walk manuale a stack (`Walk`, più
|
|
veloce di `EnumerateFiles(..., AllDirectories)` su share con centinaia di migliaia di
|
|
file), esclude le cartelle `temp_*`, estrae da ogni nome file il pattern Pro-E
|
|
`<codice>.<tipo>.<versione>` (`ParseFilename`, regex), e fa un upsert incrementale riga per
|
|
riga confrontando `percorso_completo` + dimensione + data modifica. Progresso riportato via
|
|
`IProgress<ScanProgress>` (marshalato sul thread UI). Contiene anche lo schema SQL: tabella
|
|
`files` e vista `files_ultima_versione` (ultima versione per `codice`+`tipo_file` via
|
|
`ROW_NUMBER() OVER (...)`, non self-join+`GROUP BY` per evitare tempi quasi quadratici su
|
|
~200k righe).
|
|
|
|
## Lettura dei componenti referenziati in un `.asm`
|
|
|
|
`Form1.EstraiCodiciReferenziati` (usata da `RisolviAlberoComponenti`, fase 4) prova prima
|
|
`AsmRecordReader.EstraiCodiciReferenziati` e ripiega su
|
|
`AssemblyComponentScanner.EstraiCodiciReferenziati` solo se il primo lancia
|
|
`InvalidDataException` (file che non rispetta il formato validato dal reverse engineering).
|
|
|
|
- **AsmRecordReader.cs** — legge esattamente la sezione `members` del binario `.asm` (id/tipo/
|
|
nome dei componenti diretti, con risoluzione del nome istanza di family table dal parametro
|
|
`ConfigName`): nessuna euristica, struttura a record. Lancia `InvalidDataException` se il
|
|
file non ha una sezione `members` o se il conteggio dichiarato non torna. Espone anche le
|
|
due liste "non di distinta": `EstraiCodiciSoppressi` (che passa da `members` per sapere
|
|
quali codici sono già noti, quindi può lanciare la stessa eccezione — `Form1.CodiciSoppressi`
|
|
la intercetta e restituisce vuoto) e `EstraiCodiciRiferimentiEsterni` (scansione a sé, non
|
|
lancia). Dettagli in `docs/formato-asm-record.md`. Condivisa con `scripts/CreoBom.cs` via
|
|
`#:project`.
|
|
- **AssemblyComponentScanner.cs** — estrae euristicamente i codici componente referenziati
|
|
contando le occorrenze di stringhe candidate nel testo binario. Due strategie: codici
|
|
numerici Pro-E riconosciuti da soli via regex (come nei nomi file), e nomi alfabetici di
|
|
parti commerciali tokenizzati genericamente e validati contro l'elenco di codici noti del DB
|
|
(altrimenti il rumore binario sarebbe ingestibile). Dettagli sull'euristica e i falsi
|
|
positivi noti in `docs/formato-asm.md`. Resta il fallback per i file che il parser esatto
|
|
non copre, e la fonte di verità per `ExtractCreoStrings.cs`/`BomViaDb.cs`.
|
|
|
|
Quella lista è **la distinta**, e non coincide con i file che servono a Creo per aprire
|
|
l'assieme: ne restano fuori due categorie, che il loro file serve comunque, altrimenti il
|
|
caricamento fallisce (`docs/formato-asm-record.md` §7.3). `AsmRecordReader` le restituisce a
|
|
parte, e la fase 4 le copia in `raw/` senza contarle fra i "non trovati":
|
|
|
|
- `EstraiCodiciSoppressi` — i componenti **soppressi**: spariti da `members`, ancora citati
|
|
dalla feature che li posiziona. Non si ricorre dentro (Creo non carica i figli di un
|
|
componente soppresso).
|
|
- `EstraiCodiciRiferimentiEsterni` — i modelli legati da **riferimento esterno** (assembly
|
|
feature, datum copiati), elencati in una tabella a parte del binario (§7.5). Qui si ricorre
|
|
dentro, perché Creo carica il modello citato con tutto il suo albero.
|
|
|
|
## Quale copia di un componente si copia in `raw/`
|
|
|
|
Lo stesso codice esiste spesso in più cartelle della share, con contenuto diverso e pari
|
|
versione (13% dei codici, vedi `docs/formato-asm-record.md` §7.4). Il `.asm` non registra
|
|
quale copia usa, quindi `RisolviAlberoComponenti` replica la regola di Creo:
|
|
`Form1.ScegliPerCartella` preferisce la copia nella **stessa cartella dell'assieme che la
|
|
referenzia** (`Form1.CartellaDiOrigine` ricostruisce quella cartella per gli assiemi di
|
|
partenza), e solo in mancanza ripiega su versione più alta e data più recente. Sbagliare copia
|
|
non dà un errore comprensibile: Creo non apre l'assieme e dice solo "dati di input non
|
|
completi".
|
|
|
|
## Import distinta base
|
|
|
|
- **DistintaBaseImporter.cs** — converte l'export del gestionale (SpreadsheetML XML con
|
|
estensione `.xls`/`.xml`, oppure `.xls`/`.xlsx` binario vero, distinti dai primi byte del
|
|
file) nelle sole colonne Componente/Descrizione, foglio `"Distinta Base"`. Se il nome del
|
|
file segue la convenzione `DBP_<codice>_<timestamp>`, aggiunge l'assieme generale come riga
|
|
in più (il foglio elenca solo i componenti sotto l'assieme). Scrive il CSV nella cartella
|
|
passata da `Form1` (la cartella di lavoro scelta in fase 1), non necessariamente accanto al
|
|
file di input.
|
|
|
|
## Finestre di dialogo (modali, mostrate a fine step)
|
|
|
|
- **ProgressForm.cs** — durante l'aggiornamento indice: barra di progresso, conteggi
|
|
nuovi/aggiornati/invariati/rimossi, tempo trascorso e stima del tempo rimanente (dal ritmo
|
|
file/secondo rispetto al totale atteso, preso dal conteggio della scansione precedente
|
|
salvato nel DB).
|
|
- **ProgressoComponentiForm.cs** — dialog minimale (etichetta + barra) durante
|
|
`RisolviAlberoComponenti` in fase 4.
|
|
|
|
## Stile UI condiviso
|
|
|
|
- **UiStile.cs** — `Primario`/`Secondario`: stile piatto per i bottoni (bordo sottile
|
|
per i secondari, blu pieno per l'azione principale di ogni fase), entrambi con
|
|
`AutoSize`+`Padding`+`MinimumSize` invece di una `Size` fissa in pixel indovinata a
|
|
mano (altrimenti il testo va a capo e si taglia in verticale se non ci sta nella
|
|
larghezza scelta). `Arrotonda` applica angoli arrotondati via `Region` calcolata su
|
|
`SizeChanged` (non una volta sola: con `AutoSize` la dimensione finale del bottone si
|
|
conosce solo a layout avvenuto), usata sui bottoni "Avanti"/"Indietro" del wizard.
|
|
- **app.ico** — icona dell'app, referenziata da `<ApplicationIcon>` nel csproj (icona
|
|
dell'exe in Esplora risorse/taskbar) e riletta esplicitamente in `Form1.BuildUi()`
|
|
con `Icon.ExtractAssociatedIcon(Application.ExecutablePath)` per la titlebar, senza
|
|
copiare il file `.ico` nell'output. Sfondo trasparente (nessuna card bianca), 7
|
|
risoluzioni da 16 a 256px.
|
|
- **PrtMancantiForm.cs** — esito della risoluzione ricorsiva dell'albero componenti: PRT e
|
|
sotto-assiemi trovati e copiati in `raw/`, più i codici referenziati che non corrispondono a
|
|
nessun file indicizzato (possono essere disegni, componenti davvero mancanti, o falsi
|
|
positivi della scansione `.asm`).
|
|
- **GuidaForm.cs** — guida interattiva ("Aiuto → Guida"/F1): lista delle 5 fasi a sinistra,
|
|
testo a destra; sintesi, il dettaglio resta solo in questi docs.
|
|
|
|
## Persistenza
|
|
|
|
- **DB** (`cad_index.db`, SQLite) — vedi tabella `files`/vista `files_ultima_versione` sopra.
|
|
Percorso: cercato accanto all'eseguibile o nella working directory all'avvio; altrimenti
|
|
selezionabile con "Apri DB...". Radice disco di scansione di default `X:\`
|
|
(`Form1.RadiceDiscoDefault`, hardcoded).
|
|
- **Cartella di lavoro** — salvata in `%AppData%/PrepStep/ultima_cartella_lavoro.txt`
|
|
(stesso meccanismo del DB sopra), proposta di default `AppContext.BaseDirectory` la prima
|
|
volta; selezionabile/cambiabile con "Cambia cartella..." in fase 1. Tutti i file generati a
|
|
valle ci finiscono dentro: il CSV della distinta, `raw/` (CAD dei componenti e
|
|
sotto-componenti); `step/` non è scritta dall'app ma è dove l'utente esporta gli STEP a
|
|
mano con Creo Distributed Batch, a partire dai file trascinati da `raw/` in fase 5.
|
|
|
|
## Script standalone (`scripts/`)
|
|
|
|
C# file-based apps (.NET 10, `dotnet run scripts/Nome.cs -- arg1 arg2`, nessun `.csproj`,
|
|
dipendenze extra con `#:package Nome@versione` in testa al file):
|
|
|
|
- **ExtractCreoStrings.cs** — BOM di un `.asm` risolvendo ricorsivamente i sotto-assiemi
|
|
contro `cad_index.db` (stessa logica di `AssiemeCompleto` in Form1, solo codici numerici
|
|
Pro-E).
|
|
- **BomViaDb.cs** — come sopra ma con la tokenizzazione generica validata contro il DB, per
|
|
intercettare anche i nomi alfabetici delle parti commerciali.
|
|
- **CreoBom.cs** — usa `AsmRecordReader` (stesso lettore esatto della GUI, fase 4/5, via
|
|
`#:project`) invece dell'euristica testuale. Risolve i sottoassiemi cercandoli in cartelle
|
|
locali (`-p`), non contro `cad_index.db` come `ExtractCreoStrings.cs`/`BomViaDb.cs`.
|
|
- **Analyze.cs** — dump del contesto testo/hex attorno a un pattern in un file binario, per
|
|
investigare a mano nuovi falsi positivi prima di toccare la regex di
|
|
`AssemblyComponentScanner`.
|
|
- **Query.cs** — query SQL ad-hoc contro `cad_index.db` da riga di comando.
|
|
|
|
> ⚠️ Nota importante di sicurezza: se `dotnet run` viene lanciato dentro `PrepStep/`
|
|
> (che contiene un `.csproj` di tipo `WinExe`), esegue l'app GUI vera invece dello script
|
|
> standalone e resta bloccato in attesa che una finestra si chiuda. Lanciare gli script da
|
|
> una cartella senza `.csproj` (es. la root del repo o `scripts/` stessa).
|
|
|
|
## Test
|
|
|
|
- **PrepStep.Tests/** (xUnit) — riferimento al progetto principale via `ProjectReference`
|
|
+ `InternalsVisibleTo` (in `PrepStep.csproj`), così i test chiamano direttamente
|
|
`Indexer.ParseFilename` e `AssemblyComponentScanner.EstraiCodiciReferenziati` senza esporli
|
|
come API pubblica. File: `IndexerParseFilenameTests.cs`, `IndexerScanTests.cs`,
|
|
`AssemblyComponentScannerTests.cs`, `AsmRecordReaderTests.cs`,
|
|
`DistintaBaseImporterTests.cs`, `CaseInsensitiveLookupIntegrationTests.cs`,
|
|
`PipelineEndToEndTests.cs`.
|
|
|
|
`AsmRecordReaderTests.cs` costruisce i `.asm` byte per byte con dei piccoli helper
|
|
(`Membro`, `ConfigName`, `ObjName`, `Riferimenti`): ogni caso strano trovato sulla share
|
|
diventa un test minimale invece di un file binario da versionare.
|