# 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 ``/`` in `PrepStep.csproj`). Per il "come si usa" vedi il `README.md` in root; per il formato binario `.asm` vedi `docs/formato-asm.md`; 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) ``` ## 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/` con `AssemblyComponentScanner`, risolve ricorsivamente sotto-assiemi/parti nell'indice, li copia anch'essi in `raw/` | | 5 | Verifica completezza | "Verifica completezza assiemi" → `VerificaCompletezzaAssiemi` | Completezza ricorsiva sui percorsi in indice. All'ingresso nella 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`. 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 `..` (`ParseFilename`, regex), e fa un upsert incrementale riga per riga confrontando `percorso_completo` + dimensione + data modifica. Progresso riportato via `IProgress` (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__`, 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 `` 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`). - **ReportCompletezzaForm.cs** — per ogni assieme della distinta, se tutti i suoi componenti (ricorsivamente fino ai PRT) sono nell'indice (✓/✗/? per "non trovato"). ## 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. - **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`, `AssemblyComponentScannerTests.cs`, `DistintaBaseImporterTests.cs`, `CaseInsensitiveLookupIntegrationTests.cs`.