CopiaFileTrovati now copies with Parallel.ForEach (degree 6) instead of sequentially: the bottleneck is network latency to the share, not CPU, so overlapping copies gives a large speedup (~5x in a synthetic benchmark with simulated SMB latency, documented in docs/copia-raw-parallela.md). Also adds docs/architettura.md, a full architecture overview of every source file, the wizard's 5 phases, dialogs, persistence and standalone scripts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
147 lines
9.1 KiB
Markdown
147 lines
9.1 KiB
Markdown
# Architettura di CadSearchGui
|
|
|
|
Panoramica di tutti i file sorgente e di come collaborano. 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/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 |
|
|
|---|---|---|---|
|
|
| 1 | Database | "Aggiorna indice" → `Indexer.ScanAsync` | Crea/aggiorna `cad_index.db` scansionando la share |
|
|
| 2 | Importa Distinta Base | selezione file → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella root del repo (`TrovaRadiceRepo`) |
|
|
| 3 | Crea cartella raw | "Crea cartella raw" → `CreaCartellaRaw` | `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela) → `raw/` |
|
|
| 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 e STEP | "Verifica completezza assiemi" → `VerificaCompletezzaAssiemi`; "Crea cartella step_finali" → `GeneraReportStep` | Completezza ricorsiva sui percorsi in indice; confronto con `step/` → `step_finali/`. All'ingresso nella fase, `AggiornaAnteprimaAsm` mostra le icone trascinabili dei `.asm` della distinta già in `raw/` |
|
|
|
|
Stato condiviso tra le fasi (campi privati di `Form1`): `_dbPath`, `_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/`, `step/`, `step_finali/` (manutenzione, richiede conferma).
|
|
- `InizializzaDatabase` / `ScegliDatabase` / `TrovaRadiceRepo` — localizzazione del DB e
|
|
della root del repo (quest'ultima risalendo da `AppContext.BaseDirectory` fino alla
|
|
cartella che contiene `CLAUDE.md`).
|
|
|
|
## 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
|
|
passata da `Form1` (root del repo), 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.
|
|
- **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).
|
|
- **File su disco**, tutti nella root del repo (non nella cartella dell'eseguibile): 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).
|
|
|
|
## 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 `CadSearchGui/`
|
|
> (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
|
|
|
|
- **CadSearchGui.Tests/** (xUnit) — riferimento al progetto principale via `ProjectReference`
|
|
+ `InternalsVisibleTo` (in `CadSearchGui.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`.
|