2026-09-21 11:18:43 +02:00
# Architettura di PrepStep (Cevolani StepPrep)
2026-09-21 10:07:46 +02:00
2026-09-21 11:18:43 +02:00
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
2026-09-21 11:00:01 +02:00
formato binario `.asm` vedi `docs/formato-asm.md` ; per la parallelizzazione della
copia in `raw/` vedi `docs/copia-raw-parallela.md` .
2026-09-21 10:07:46 +02:00
## 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/raccoglie gli STEP corrispondenti
(`step_finali/` ).
```
Distinta (.xls/.xml) → CSV (componente;descrizione) → raw/ (CAD copiati dall'indice)
│
scansione .asm ─────┘ (sotto-assiemi/parti mancanti)
│
step/ (esportati a mano da Creo) → step_finali/ (solo i codici distinta)
```
## 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 |
|---|---|---|---|
2026-09-21 11:18:43 +02:00
| 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 |
2026-09-21 10:07:46 +02:00
| 4 | Trova componenti referenziati | "Trova componenti referenziati" → `TrovaPrtReferenziati` → `RisolviAlberoComponenti` | Legge i `.asm` in `raw/` con `AssemblyComponentScanner` , risolve ricorsivamente sotto-assiemi/parti nell'indice, li copia anch'essi in `raw/` |
2026-09-21 11:18:43 +02:00
| 5 | Verifica completezza e STEP | "Verifica completezza assiemi" → `VerificaCompletezzaAssiemi` ; "Crea cartella step_finali" → `GeneraReportStep` | Completezza ricorsiva sui percorsi in indice; confronto con `step/` → `step_finali/` (entrambe dentro la cartella di lavoro). All'ingresso nella fase, `AggiornaAnteprimaAsm` mostra le icone trascinabili dei `.asm` della distinta già in `raw/` |
2026-09-21 10:07:46 +02:00
2026-09-21 11:18:43 +02:00
Stato condiviso tra le fasi (campi privati di `Form1` ): `_dbPath` , `_cartellaLavoro` ,
`_percorsoInputCorrente` , `_righeCorrenti` (righe del CSV), `_ultimaVerifica` (cache percorsi
CAD), `_ultimaCartellaRaw` .
2026-09-21 10:07:46 +02:00
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).
2026-09-21 11:18:43 +02:00
- `PulisciRawEStep` — svuota `raw/` , `step/` , `step_finali/` 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.
2026-09-21 10:07:46 +02:00
## 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).
## Scansione binaria dei `.asm`
- **AssemblyComponentScanner.cs** — estrae euristicamente i codici componente referenziati
dentro un `.asm` (formato per lo più binario, codici come stringhe ASCII leggibili al suo
interno). 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
sul formato, l'euristica e i falsi positivi noti in `docs/formato-asm.md` .
## 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
2026-09-21 11:18:43 +02:00
passata da `Form1` (la cartella di lavoro scelta in fase 1), non necessariamente accanto al
file di input.
2026-09-21 10:07:46 +02:00
## 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.
2026-09-21 11:00:01 +02:00
## 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.
2026-09-21 10:07:46 +02:00
- **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` ).
- **ReportCompletezzaForm.cs** — per ogni assieme della distinta, se tutti i suoi componenti
(ricorsivamente fino ai PRT) sono nell'indice (✓/✗/? per "non trovato").
- **ReportStepForm.cs** — per ogni componente della distinta, `EsitoStep` : `Creato` (STEP
trovato e copiato), `StepMancante` (CAD in indice ma STEP non esportato),
`ComponenteNonTrovato` (nemmeno il CAD è in indice).
## 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).
2026-09-21 11:18:43 +02:00
- **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/` (STEP esportati a mano da Creo, input), `step_finali/` (STEP dei
soli codici della distinta, output).
2026-09-21 10:07:46 +02:00
## 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.
- **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.
2026-09-21 11:18:43 +02:00
> ⚠️ Nota importante di sicurezza: se `dotnet run` viene lanciato dentro `PrepStep/`
2026-09-21 10:07:46 +02:00
> (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
2026-09-21 11:18:43 +02:00
- **PrepStep.Tests/** (xUnit) — riferimento al progetto principale via `ProjectReference`
+ `InternalsVisibleTo` (in `PrepStep.csproj` ), così i test chiamano direttamente
2026-09-21 10:07:46 +02:00
`Indexer.ParseFilename` e `AssemblyComponentScanner.EstraiCodiciReferenziati` senza esporli
come API pubblica. File: `IndexerParseFilenameTests.cs` ,
`AssemblyComponentScannerTests.cs` , `DistintaBaseImporterTests.cs` ,
`CaseInsensitiveLookupIntegrationTests.cs` .