diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 0000000..a8b4925 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,15 @@ +{ + // Regole base; disattivate quelle che non servono a prosa italiana con tabelle larghe + "config": { + "default": true, + "MD013": false, // lunghezza riga + "MD033": false, // HTML inline + "MD024": { "siblings_only": true }, // CHANGELOG ripete "Aggiunto/Modificato" + "MD036": false, + "MD040": false, // dump esadecimali/testuali senza linguaggio + "MD060": false, // stile colonne tabelle + "MD041": false + }, + "globs": ["**/*.md"], + "ignores": ["node_modules", "raw", "step", "**/bin", "**/obj"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6acadab..6796b09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ Tutte le modifiche rilevanti di questo progetto sono documentate in questo file. Il formato segue [Keep a Changelog](https://keepachangelog.com/it/1.1.0/), e il progetto aderisce a [Semantic Versioning](https://semver.org/lang/it/). +## [Non rilasciato] + +### Aggiunto + +- Docs-as-code: `scripts/CheckDocs.cs`, markdownlint, `docs/README.md` (indice Diátaxis e fonte unica). +- Nuovi doc: `tutorial-primo-giro`, `risoluzione-problemi`, `sviluppo`, `rilascio`, `glossario` e registro delle decisioni `docs/decisioni/` (6 ADR). + +### Modificato + +- Il README rimanda a `docs/sviluppo.md` e `docs/rilascio.md` per build, test e setup, invece di duplicarli. + ## [1.0.0] - 2026-09-23 Prima versione distribuita agli utenti tramite setup di installazione. diff --git a/CLAUDE.md b/CLAUDE.md index a215204..83fb067 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,8 +63,14 @@ Dove scrivere cosa: | [`docs/formato-asm.md`](docs/formato-asm.md) | euristica testuale di fallback | | [`docs/db-cad-index.md`](docs/db-cad-index.md) | schema e vista di `cad_index.db` | | [`docs/copia-raw-parallela.md`](docs/copia-raw-parallela.md) | parallelizzazione della copia in `raw/` | +| [`docs/README.md`](docs/README.md) | indice Diátaxis di tutti i doc (tutorial, how-to, reference, decisioni) e regola di fonte unica | | [`CHANGELOG.md`](CHANGELOG.md) | storico delle versioni (formato Keep a Changelog); ogni release che cambia `` nel csproj aggiunge qui la sua voce | +I doc sono trattati come codice: stanno in git e si verificano con +`dotnet run scripts/CheckDocs.cs` (link, versione/CHANGELOG, copertura di `architettura.md`) e +`npx markdownlint-cli2` (config `.markdownlint-cli2.jsonc`) — da lanciare prima di ogni commit che +tocca i doc. Organizzazione (Diátaxis) e regola di fonte unica: [`docs/README.md`](docs/README.md). + Quando il cambiamento nasce da un fallimento reale di Creo, scrivere anche **il sintomo**: i messaggi di Creo non nominano il file o la causa, e senza quel ponte la prossima volta si riparte da zero (i §7.3/§7.4/§7.5 di `formato-asm-record.md` sono l'esempio del formato diff --git a/README.md b/README.md index 203cfb6..11099ef 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ passaggio, non lo sostituisce. manuale, fuori da quest'app): scrive in `step/`, dentro la cartella di lavoro. -Guida utente passo-passo completa in [`docs/guida-utente.md`](docs/guida-utente.md) +Primo uso: [`docs/tutorial-primo-giro.md`](docs/tutorial-primo-giro.md). Guida utente completa in +[`docs/guida-utente.md`](docs/guida-utente.md) (stessa sintesi anche in-app da **Aiuto → Guida**, F1). ## Requisiti @@ -50,83 +51,20 @@ Guida utente passo-passo completa in [`docs/guida-utente.md`](docs/guida-utente. - Accesso alla share di rete con i file CAD (`X:\` di default) per l'indicizzazione -## Build +## Build, test e rilascio -```powershell -cd PrepStep -dotnet build -c Release -``` +Build, avvio e test: [`docs/sviluppo.md`](docs/sviluppo.md). Pubblicazione dell'eseguibile e +generazione del setup di installazione: [`docs/rilascio.md`](docs/rilascio.md). -## Avvio - -Da sorgente: - -```powershell -cd PrepStep -dotnet run -c Release -``` - -Oppure l'eseguibile già compilato: - -```powershell -cd PrepStep\bin\Release\net10.0-windows -.\PrepStep.exe -``` - -## Pubblicazione (eseguibile standalone) - -Genera un `.exe` self-contained per Windows x64, senza bisogno del runtime -.NET installato sulla macchina di destinazione: - -```powershell -cd PrepStep -dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish -``` - -L'eseguibile risultante è in `PrepStep\publish\PrepStep.exe` — un unico -file, si copia e si lancia su un'altra macchina Windows senza bisogno di .NET -installato. - -## Setup di installazione - -Per distribuire l'app agli utenti si genera un setup con -[Inno Setup 6](https://jrsoftware.org/isinfo.php). - -Una volta sola, per installare Inno Setup (per l'utente corrente, senza admin): - -```powershell -winget install --id JRSoftware.InnoSetup -e --scope user -``` - -A ogni release, dalla root del repo: - -```powershell -# 1. aggiorna in PrepStep\PrepStep.csproj e la voce in CHANGELOG.md -# 2. chiudi l'app se è aperta (l'exe in uso blocca la build) -dotnet publish PrepStep -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o PrepStep\publish -& "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" installer\PrepStep.iss -``` - -Produce `installer\Output\PrepStep-Setup-.exe` (versione letta dall'exe, cioè -`` del csproj): è l'unico file da distribuire. Per provarlo sul proprio PC basta -lanciarlo (`.\installer\Output\PrepStep-Setup-.exe`). Il setup: - -- installa **solo per l'utente corrente**, in `%LOCALAPPDATA%\Programs\PrepStep`, senza - chiedere i diritti di amministratore; -- crea nel menu Start i collegamenti al programma e al disinstallatore (quello sul desktop - è facoltativo); -- compare in "Impostazioni → App installate", da cui si disinstalla. - -Se l'app è già installata, il setup chiede prima cosa fare: **Sì** aggiorna/reinstalla (DB e -impostazioni restano), **No** disinstalla, **Annulla** esce. Installazione silenziosa (aggiorna -senza chiedere) — es. per distribuirla da script: -`PrepStep-Setup-.exe /VERYSILENT /SUPPRESSMSGBOXES`. +Il setup installa **solo per l'utente corrente**, in `%LOCALAPPDATA%\Programs\PrepStep`, senza +diritti di amministratore; si disinstalla da "Impostazioni → App installate". ## Struttura del progetto Il codice dell'app è tutto in `PrepStep/` (GUI e indicizzazione integrate); accanto ci sono il progetto di test `PrepStep.Tests/`, gli script standalone `scripts/` e lo script del setup -`installer/`. Mappa file-per-file completa in [`docs/architettura.md`](docs/architettura.md). +`installer/`. Mappa file-per-file completa in [`docs/architettura.md`](docs/architettura.md); indice di tutti i doc in +[`docs/README.md`](docs/README.md). - `Program.cs` — entry point. - `Form1.cs` — finestra principale, wizard a 5 fasi e menu; UI costruita interamente in @@ -145,18 +83,7 @@ Il database generato (`cad_index.db`), i file Excel/CSV della distinta trascinati nella root e le cartelle `raw/`/`step/` sono rigenerabili e non sono tracciati in git (vedi `.gitignore`). -## Test - -`PrepStep.Tests/` (xUnit) contiene unit test (lettore esatto e euristica di -estrazione dei componenti dai `.asm`, parsing del nome file Pro-E, import della -distinta) e integration test (scansione su disco, schema/vista SQL reale con focus -sui bug di case-sensitivity/duplicati già risolti, pipeline end-to-end). Da -eseguire: - -```powershell -cd PrepStep.Tests -dotnet test -``` +Per i test vedi [`docs/sviluppo.md`](docs/sviluppo.md#test). ## Licenza diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..888e7e6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,54 @@ +# Documentazione di PrepStep + +Indice dei doc, organizzati secondo [Diátaxis](https://diataxis.fr/): ogni file ha **uno** scopo, +e cambia solo quando cambia il programma. + +## Tutorial — imparare facendo + +- [`tutorial-primo-giro.md`](tutorial-primo-giro.md) — il primo giro completo con una distinta vera. + +## How-to — svolgere un compito + +- [`guida-utente.md`](guida-utente.md) — i passi dell'utente fino a Creo Distributed Batch. +- [`risoluzione-problemi.md`](risoluzione-problemi.md) — sintomo → causa → cosa fare. +- [`sviluppo.md`](sviluppo.md) — build, test, script e regole per scrivere i doc. +- [`rilascio.md`](rilascio.md) — pubblicare e generare il setup di una nuova versione. + +## Reference — consultare + +- [`architettura.md`](architettura.md) — il processo delle 5 fasi e la mappa file-per-file. +- [`db-cad-index.md`](db-cad-index.md) — schema, vista e query di `cad_index.db`. +- [`formato-asm-record.md`](formato-asm-record.md) — parser esatto dei record `.asm`, casi limite (§7). +- [`formato-asm.md`](formato-asm.md) — euristica testuale di fallback. +- [`glossario.md`](glossario.md) — i termini usati nei doc e nell'app. +- [`../CHANGELOG.md`](../CHANGELOG.md) — storico delle versioni. + +## Explanation — capire il perché + +- [`decisioni/`](decisioni/README.md) — registro delle scelte di progetto (ADR). +- [`copia-raw-parallela.md`](copia-raw-parallela.md) — perché e come la copia in `raw/` è parallela. + +## Fonte unica (SSOT) + +Ogni informazione vive in **un** posto; altrove si mette un link, non una copia. + +| Informazione | Fonte | +|---|---| +| Versione e nome prodotto | `PrepStep/PrepStep.csproj` (``, ``) | +| Schema e vista del DB | [`db-cad-index.md`](db-cad-index.md) | +| Formato dei record `.asm` e casi limite | [`formato-asm-record.md`](formato-asm-record.md) | +| Processo delle 5 fasi, mappa dei file | [`architettura.md`](architettura.md) | +| Passi dell'utente | [`guida-utente.md`](guida-utente.md) | +| Build, test, regole sui doc | [`sviluppo.md`](sviluppo.md) | +| Procedura di rilascio e setup | [`rilascio.md`](rilascio.md) | +| Perché una scelta è fatta così | [`decisioni/`](decisioni/README.md) | +| Storico release | [`../CHANGELOG.md`](../CHANGELOG.md) | + +`CLAUDE.md` e `README.md` riassumono soltanto e rimandano a questi file. + +## Verifica + +```powershell +dotnet run scripts/CheckDocs.cs +npx markdownlint-cli2 +``` diff --git a/docs/architettura.md b/docs/architettura.md index cceb139..cfbd051 100644 --- a/docs/architettura.md +++ b/docs/architettura.md @@ -3,7 +3,7 @@ Panoramica di tutti i file sorgente e di come collaborano. `PrepStep` è il nome tecnico del progetto (cartella, namespace, csproj, eseguibile `PrepStep.exe`); **PrepStep - Cevolani** è 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 +versione — vedi ``/`` in `PrepStep.csproj`). Per il "come si usa" vedi il `README.md` in root e la [`guida-utente.md`](guida-utente.md); 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`. @@ -282,7 +282,7 @@ l'`UninstallString`), perché Inno da solo aggiornerebbe sempre; con `/VERYSILEN La disinstallazione rimuove solo i file installati: `cad_index.db`, `raw/`/`step/`/CSV creati accanto all'exe e `%AppData%/PrepStep/` restano. -Comandi di build del setup nel `README.md`. +Comandi di build del setup in [`rilascio.md`](rilascio.md). ## Script standalone (`scripts/`) @@ -301,6 +301,9 @@ dipendenze extra con `#:package Nome@versione` in testa al file): 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. +- **CheckDocs.cs** — controllo dei doc (docs-as-code): link relativi e ancore, `` del csproj + uguale all'ultima voce del `CHANGELOG.md`, ogni `.cs` di `PrepStep/` e `scripts/` descritto qui. + Exit 1 se qualcosa non torna. > ⚠️ 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 @@ -310,7 +313,7 @@ dipendenze extra con `#:package Nome@versione` in testa al file): ## Test - **PrepStep.Tests/** (xUnit) — riferimento al progetto principale via `ProjectReference` - + `InternalsVisibleTo` (in `PrepStep.csproj`), così i test chiamano direttamente + e `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`, diff --git a/docs/decisioni/0001-indice-sqlite-locale.md b/docs/decisioni/0001-indice-sqlite-locale.md new file mode 100644 index 0000000..21e8876 --- /dev/null +++ b/docs/decisioni/0001-indice-sqlite-locale.md @@ -0,0 +1,22 @@ +# 0001 — Indice dei file in un DB SQLite locale + +**Stato:** accettata + +## Contesto + +La share `X:\` contiene centinaia di migliaia di file (ordine di ~200k righe indicizzate). +Cercare un codice direttamente sul disco di rete a ogni distinta è lento: il costo è dominato +dalla latenza di rete. + +## Decisione + +Scansionare la share una volta in un DB SQLite locale (`cad_index.db`) e interrogare quello. +L'aggiornamento è incrementale (confronto su percorso, dimensione e data), la vista +`files_ultima_versione` usa `ROW_NUMBER()` e non un self-join con `GROUP BY`. + +## Conseguenze + +- Le ricerche sono istantanee e non richiedono la share (la copia sì). +- L'indice può essere vecchio: per questo la fase 2 ha "Verifica presenza". +- Il DB è rigenerabile, quindi non è tracciato in git. +- Schema e criteri di pareggio: [`../db-cad-index.md`](../db-cad-index.md). diff --git a/docs/decisioni/0002-ui-winforms-in-codice.md b/docs/decisioni/0002-ui-winforms-in-codice.md new file mode 100644 index 0000000..bc9bbbe --- /dev/null +++ b/docs/decisioni/0002-ui-winforms-in-codice.md @@ -0,0 +1,18 @@ +# 0002 — UI WinForms costruita interamente in codice + +**Stato:** accettata + +## Contesto + +L'app è un wizard a 5 fasi con stile condiviso (bottoni, card, barre) e stato comune tra fasi. + +## Decisione + +Nessun designer visuale: `Form1.BuildUi` e i `BuildFase*` costruiscono i controlli, `UiStile` +centralizza lo stile. `Form1.Designer.cs` resta lo scheletro del template. + +## Conseguenze + +- Layout e stile modificabili e rivedibili come codice normale (diff leggibili). +- Dimensioni con `AutoSize`+`Padding` invece di pixel fissi, per non tagliare il testo. +- `Form1.cs` è grande (~1400 righe) perché contiene wizard e logica delle fasi. diff --git a/docs/decisioni/0003-parser-esatto-con-fallback.md b/docs/decisioni/0003-parser-esatto-con-fallback.md new file mode 100644 index 0000000..4d55a25 --- /dev/null +++ b/docs/decisioni/0003-parser-esatto-con-fallback.md @@ -0,0 +1,29 @@ +# 0003 — Parser esatto dei `.asm` con fallback euristico + +**Stato:** accettata + +## Contesto + +Il `.asm` Creo è un binario proprietario non documentato. La prima soluzione, un'euristica +testuale (`AssemblyComponentScanner`), contava le occorrenze di stringhe candidate e produceva +falsi positivi (riferimenti fantasma, componenti soppressi con placement residuo). + +Inoltre la lista dei componenti della distinta **non coincide** con i file necessari a Creo per +aprire l'assieme: ne servono di più (soppressi, riferimenti esterni). Se ne manca uno Creo +risponde solo `[-4] Dati di input non completi`. + +## Decisione + +Per reverse engineering si è decodificata la struttura a record (`AsmRecordReader`), validata +contro BOM esportate da Creo. È il metodo preferito. L'euristica resta come **fallback** quando +il parser lancia `InvalidDataException`, perché la validazione copre solo un campione di file. +La fase 4 tiene tre liste separate (distinta, soppressi, riferimenti esterni) con regole di +ricorsione diverse. + +## Conseguenze + +- Estrazione esatta sui file che rispettano il formato; nessun falso positivo da conteggio. +- Due implementazioni da mantenere, per ora; l'euristica serve anche a due script. +- Ogni caso strano diventa un test minimale costruito byte per byte. +- Dettagli: [`../formato-asm-record.md`](../formato-asm-record.md), + [`../formato-asm.md`](../formato-asm.md). diff --git a/docs/decisioni/0004-installazione-per-utente.md b/docs/decisioni/0004-installazione-per-utente.md new file mode 100644 index 0000000..3f730b5 --- /dev/null +++ b/docs/decisioni/0004-installazione-per-utente.md @@ -0,0 +1,27 @@ +# 0004 — Setup per-utente con DB nella cartella di installazione + +**Stato:** accettata + +## Contesto + +Gli utenti non hanno diritti di amministratore. L'app scrive `cad_index.db` e, di default, la +cartella di lavoro accanto all'eseguibile. In `C:\Program Files` non sarebbe scrivibile. + +Prima il DB era scelto dall'utente e ricordato in `ultimo_db.txt`: si finiva con DB diversi tra +postazioni e ricerche incoerenti. + +## Decisione + +- Setup Inno Setup 6 con `PrivilegesRequired=lowest`: installa in + `%LOCALAPPDATA%\Programs\PrepStep`. +- Il DB è sempre `cad_index.db` nella cartella di installazione; "Apri DB..." vale solo per la + sessione e nulla viene ricordato. +- `AppId` fisso per riconoscere gli aggiornamenti; se l'app è già installata il setup chiede se + aggiornare o disinstallare. + +## Conseguenze + +- Nessun admin necessario; aggiornare non tocca DB e impostazioni. +- La disinstallazione lascia DB, `raw/`, `step/`, CSV e `%AppData%\PrepStep\`. +- In sviluppo il DB è nella cartella di build ([`../db-cad-index.md`](../db-cad-index.md)). +- Procedura: [`../rilascio.md`](../rilascio.md). diff --git a/docs/decisioni/0005-copia-nella-cartella-dell-assieme.md b/docs/decisioni/0005-copia-nella-cartella-dell-assieme.md new file mode 100644 index 0000000..2cd91e9 --- /dev/null +++ b/docs/decisioni/0005-copia-nella-cartella-dell-assieme.md @@ -0,0 +1,22 @@ +# 0005 — Scegliere la copia di un componente dalla cartella dell'assieme + +**Stato:** accettata + +## Contesto + +Per il 13% dei codici esiste più di una copia sulla share, con contenuto diverso e stessa +versione. Il `.asm` non registra quale copia usa. Scegliere a caso, o per data, può mettere in +`raw/` un modello diverso da quello atteso: Creo lo carica, i vincoli non si risolvono e +l'assieme non si apre, con il solito `[-4]`. + +## Decisione + +`ScegliPerCartella` preferisce la copia nella stessa cartella dell'assieme che la referenzia +(replica la regola di Creo) e solo in mancanza ripiega su versione più alta e data più recente. +Nella vista `files_ultima_versione` il pareggio è deterministico (`data_modifica` e percorso +nell'`ORDER BY`), ma serve solo a non sbagliare a caso. + +## Conseguenze + +- La fase 4 legge `files` (tutte le copie) e non la vista. +- Dettaglio e casi reali: [`../formato-asm-record.md`](../formato-asm-record.md) §7.4. diff --git a/docs/decisioni/0006-script-che-riusano-il-progetto.md b/docs/decisioni/0006-script-che-riusano-il-progetto.md new file mode 100644 index 0000000..567831d --- /dev/null +++ b/docs/decisioni/0006-script-che-riusano-il-progetto.md @@ -0,0 +1,21 @@ +# 0006 — Script standalone che referenziano il progetto principale + +**Stato:** accettata + +## Contesto + +Servono strumenti da riga di comando (BOM, query, analisi di binari) senza duplicare la logica di +lettura dei `.asm`. + +## Decisione + +Gli script in `scripts/` sono C# file-based apps (.NET 10) che referenziano +`PrepStep/PrepStep.csproj` con `#:project`. La logica vive **solo** nel progetto principale: una +sola fonte di verità per GUI e script. + +## Conseguenze + +- Si lanciano dalla root del repo; da dentro `PrepStep/` partirebbe la GUI. +- Due famiglie non intercambiabili: euristica + DB (`ExtractCreoStrings`, `BomViaDb`) e parser + esatto + cartelle locali (`CreoBom`). Vedi [`../architettura.md`](../architettura.md). +- `CheckDocs.cs` segue lo stesso schema per i doc. diff --git a/docs/decisioni/README.md b/docs/decisioni/README.md new file mode 100644 index 0000000..a60b0fb --- /dev/null +++ b/docs/decisioni/README.md @@ -0,0 +1,14 @@ +# Registro delle decisioni + +Perché il programma è fatto così. Ogni voce spiega il contesto, la scelta e le conseguenze, così +chi ci rimette mano non rifà il ragionamento da zero. Se cambia una scelta non si riscrive la +vecchia voce: se ne aggiunge una nuova che la sostituisce. + +| N. | Decisione | Stato | +|---|---|---| +| [0001](0001-indice-sqlite-locale.md) | Indice dei file in un DB SQLite locale | accettata | +| [0002](0002-ui-winforms-in-codice.md) | UI WinForms costruita interamente in codice | accettata | +| [0003](0003-parser-esatto-con-fallback.md) | Parser esatto dei `.asm` con fallback euristico | accettata | +| [0004](0004-installazione-per-utente.md) | Setup per-utente con DB nella cartella di installazione | accettata | +| [0005](0005-copia-nella-cartella-dell-assieme.md) | Scegliere la copia di un componente dalla cartella dell'assieme | accettata | +| [0006](0006-script-che-riusano-il-progetto.md) | Script standalone che referenziano il progetto principale | accettata | diff --git a/docs/glossario.md b/docs/glossario.md new file mode 100644 index 0000000..a915de8 --- /dev/null +++ b/docs/glossario.md @@ -0,0 +1,20 @@ +# Glossario + +| Termine | Significato | +|---|---| +| Assieme generale | l'assieme di primo livello di una distinta; il foglio del gestionale non lo elenca, l'app lo aggiunge come riga se il file è `DBP__` | +| `.asm` / `.prt` | file Creo/Pro-E di assieme e di parte; il nome è `..` | +| Cartella di lavoro | dove finiscono CSV, `raw/` e `step/`; di default la cartella di installazione | +| Codice | identificativo del componente, ricavato dal nome del file (può avere una lettera di revisione attaccata) | +| Creo Distributed Batch | strumento PTC che esporta in batch (qui in STEP); usato a mano, fuori dall'app | +| Distinta Base (DBP) | estrazione dal gestionale (Fata) dei componenti di un assieme | +| Family table | tabella di varianti di un modello; il nome dell'istanza sta nel parametro `ConfigName` | +| Indice | il DB `cad_index.db` con i metadati dei file della share | +| `members` | sezione del `.asm` con i componenti diretti dell'assieme | +| `raw/` | cartella con i file CAD da passare a Creo | +| Riferimento esterno | modello citato da un assembly feature o datum copiato; non è un membro, ma Creo lo carica con il suo albero | +| Share | disco di rete con i file CAD, `X:\` di default | +| Soppresso | componente tolto dalla distinta ma il cui file serve comunque ad aprire l'assieme | +| `step/` | cartella degli STEP esportati da Creo; non scritta dall'app | +| `temp_*` | cartelle personali della share, escluse dall'indice | +| Ultima versione | per ogni `(cartella_radice, codice, tipo_file)` la riga con versione più alta (vista `files_ultima_versione`) | diff --git a/docs/rilascio.md b/docs/rilascio.md new file mode 100644 index 0000000..3b45362 --- /dev/null +++ b/docs/rilascio.md @@ -0,0 +1,47 @@ +# Rilasciare una nuova versione + +Procedura per produrre il setup `PrepStep-Setup-.exe`, l'unico file da distribuire. +Il comportamento del setup (installazione per-utente, aggiornamento, `/VERYSILENT`) è descritto +in [`architettura.md`](architettura.md#installazione); qui ci sono solo i passi da eseguire. + +## Una volta sola + +Installa Inno Setup 6 per l'utente corrente (niente admin): + +```powershell +winget install --id JRSoftware.InnoSetup -e --scope user +``` + +## A ogni release + +1. Aggiorna `` in `PrepStep/PrepStep.csproj` (SemVer). +2. In [`CHANGELOG.md`](../CHANGELOG.md) trasforma "Non rilasciato" nella nuova versione con la + data e riscrivi sotto le modifiche (Aggiunto/Modificato/Corretto/Rimosso). +3. Controlla i doc: `dotnet run scripts/CheckDocs.cs` deve confermare che versione e CHANGELOG + coincidono. +4. Esegui i test: `dotnet test` in `PrepStep.Tests/`. +5. Chiudi l'app se è aperta (l'exe in uso blocca la build), poi dalla root del repo: + + ```powershell + dotnet publish PrepStep -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o PrepStep\publish + & "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" installer\PrepStep.iss + ``` + +6. Il setup è in `installer\Output\PrepStep-Setup-.exe`; la versione è letta + dall'exe, quindi coincide con ``. Provalo lanciandolo. +7. Commit con codice, doc e CHANGELOG insieme (regola in [`README.md`](README.md)). + +## Solo l'eseguibile, senza setup + +```powershell +cd PrepStep +dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish +``` + +Produce `PrepStep\publish\PrepStep.exe`, un file unico che gira su un Windows x64 senza .NET +installato. + +## Da non toccare + +`AppGuid` in `installer/PrepStep.iss`: è ciò che fa riconoscere a Windows le versioni nuove +come aggiornamento della stessa app. Cambiarlo produce un'installazione doppia. diff --git a/docs/risoluzione-problemi.md b/docs/risoluzione-problemi.md new file mode 100644 index 0000000..924613e --- /dev/null +++ b/docs/risoluzione-problemi.md @@ -0,0 +1,46 @@ +# Risoluzione dei problemi + +Sintomo → causa probabile → cosa fare. I messaggi di Creo non nominano il file né la causa, per +questo il sintomo è il punto di partenza. + +## Dentro l'app + +| Sintomo | Causa probabile | Cosa fare | +|---|---|---| +| Pallino rosso in fase 1, "Database non trovato" | non c'è `cad_index.db` nella cartella di installazione | **Aggiorna indice** (lo crea lì), oppure **Apri DB...** per usarne un altro solo per la sessione | +| "Disco non raggiungibile: X:\" | la share non è montata o non hai accesso | monta `X:\` e riprova; la radice è fissa in `Form1.RadiceDiscoDefault` | +| Componente rosso in "Verifica presenza" | il codice non è nell'indice: indice vecchio, oppure il nome su disco ha una lettera di revisione in più o in meno | **Aggiorna indice**; poi cerca il codice con `LIKE` (vedi sotto) | +| "Avanti" resta grigio in fase 2 | manca il passo **Verifica presenza** | premila: non basta l'import | +| Alcuni file non compaiono in `raw/` dopo fase 3/4 | la copia salta in silenzio i file irraggiungibili (share offline, permessi) | controlla l'accesso alla share e ripeti la fase | +| In fase 4 compaiono codici "non trovati" | disegni, componenti davvero assenti dalla share o falsi positivi della scansione | controllali a mano; non bloccano il lavoro | +| Risultati incoerenti in sviluppo | l'app sta usando il DB della cartella di build, non quello che credi | guarda il percorso mostrato in fase 1; apri il DB giusto con **Apri DB...** | + +Per capire se un codice è nell'indice: + +```powershell +dotnet run scripts/Query.cs -- cad_index.db "SELECT codice, tipo_file, percorso_completo FROM files_ultima_versione WHERE codice LIKE '925%' LIMIT 20" +``` + +Nessuna riga nemmeno con `LIKE` → il file non è indicizzato. Una riga con codice leggermente +diverso → revisione, maiuscole o spazi. + +## In Creo Distributed Batch + +| Sintomo | Causa probabile | Cosa fare | +|---|---|---| +| `[-4] Dati di input non completi` | nell'albero manca un file che Creo deve caricare, ma non dice quale | rifai la fase 4 e leggi l'elenco dei "non trovati"; poi vedi sotto | +| L'assieme non si apre, nessun messaggio chiaro | in `raw/` c'è una copia sbagliata di un componente (lo stesso codice esiste in più cartelle, 13% dei casi) | l'app sceglie la copia nella cartella dell'assieme: [`formato-asm-record.md`](formato-asm-record.md) §7.4 | +| Assieme con un componente soppresso che non si apre | il file del soppresso non era in `raw/` | §7.3; oggi la fase 4 lo copia | +| Assieme con feature di assembly o datum copiati che non si apre | manca il modello citato per riferimento esterno | §7.5; oggi la fase 4 lo copia con il suo albero | + +## Da sviluppatore + +| Sintomo | Causa | Cosa fare | +|---|---|---| +| `dotnet run` apre la GUI e si blocca | lanciato dentro `PrepStep/` | lanciare gli script dalla root del repo | +| `dotnet publish` fallisce con file in uso | l'app è aperta | chiudila e ripeti | +| `CheckDocs.cs` segnala un file non descritto | nuovo `.cs` o doc non documentato | aggiungilo a `architettura.md` o a `docs/README.md` | + +## Dopo la disinstallazione + +Restano `cad_index.db`, `raw/`, `step/`, i CSV e `%AppData%\PrepStep\`: vanno cancellati a mano. diff --git a/docs/sviluppo.md b/docs/sviluppo.md new file mode 100644 index 0000000..701841f --- /dev/null +++ b/docs/sviluppo.md @@ -0,0 +1,66 @@ +# Guida per chi sviluppa + +Come lavorare sul codice e sui doc. Per capire com'è fatto il programma parti da +[`architettura.md`](architettura.md); per rilasciare vedi [`rilascio.md`](rilascio.md). + +## Requisiti + +- Windows e [.NET 10 SDK](https://dotnet.microsoft.com/download). +- Accesso alla share `X:\` solo per indicizzare davvero; i test non ne hanno bisogno. +- Node.js (per `npx markdownlint-cli2`, solo per i doc). + +## Build e avvio + +```powershell +cd PrepStep +dotnet build -c Release +dotnet run -c Release +``` + +In sviluppo `cad_index.db` si cerca nella cartella di build, non nella radice del repo: vedi +[`db-cad-index.md`](db-cad-index.md#come-lapp-trova-il-db-allavvio). + +## Test + +```powershell +cd PrepStep.Tests +dotnet test +``` + +I test xUnit chiamano gli `internal` del progetto principale grazie a `InternalsVisibleTo`. I +`.asm` dei test si costruiscono byte per byte con piccoli helper invece di versionare file +binari: ogni caso strano trovato sulla share diventa un test minimale. Elenco dei file di test in +[`architettura.md`](architettura.md#test). + +## Script standalone + +Si lanciano dalla **root del repo**, mai da dentro `PrepStep/` (che è un `WinExe`: `dotnet run` +avvierebbe la GUI e resterebbe bloccato). Elenco in +[`architettura.md`](architettura.md#script-standalone-scripts). + +```powershell +dotnet run scripts/Query.cs -- cad_index.db "SELECT COUNT(*) FROM files" +``` + +## Documentazione come codice + +I doc stanno in git, si modificano nello stesso commit del codice e si verificano a mano: + +```powershell +dotnet run scripts/CheckDocs.cs # link, ancore, versione/CHANGELOG, copertura dei doc +npx markdownlint-cli2 # stile Markdown (.markdownlint-cli2.jsonc) +``` + +Regole di scrittura: + +- **Una sola fonte per ogni informazione**, altrove un link (tabella in [`README.md`](README.md)). +- **Ogni doc ha un solo scopo** (Diátaxis): non mescolare istruzioni, riferimento e motivazioni. +- Un cambiamento strutturale (fasi, formato `.asm`, schema DB, criterio di scelta di un file) + aggiorna i doc e il CHANGELOG **nello stesso commit**. +- Se la modifica nasce da un fallimento reale di Creo scrivi **il sintomo**: i messaggi di Creo + non nominano il file né la causa (esempio: §7.3-7.5 di + [`formato-asm-record.md`](formato-asm-record.md)). +- Una scelta con alternative reali non banali merita una voce in + [`decisioni/`](decisioni/README.md). +- Un nuovo `.cs` va descritto in `architettura.md` e un nuovo doc elencato in `docs/README.md`: + `CheckDocs.cs` lo verifica. diff --git a/docs/tutorial-primo-giro.md b/docs/tutorial-primo-giro.md new file mode 100644 index 0000000..b240a74 --- /dev/null +++ b/docs/tutorial-primo-giro.md @@ -0,0 +1,64 @@ +# Tutorial: il tuo primo giro con PrepStep + +Questo tutorial ti porta, con una distinta vera, dall'app appena installata ai file `raw/` pronti +per Creo. Alla fine saprai cosa produce ogni fase e come capire se è andata bene. Per il +dettaglio di ogni schermata e per l'esportazione in Creo vedi [`guida-utente.md`](guida-utente.md). + +**Ti serve:** il programma installato (setup in [`rilascio.md`](rilascio.md)), la share `X:\` +raggiungibile e un'estrazione "Distinta Base" `.xls` del gestionale, meglio se piccola (poche +decine di righe) per il primo giro. + +## 1. Avvia e carica l'indice + +Apri **PrepStep - Cevolani**. Accanto a "Database" c'è un pallino. + +- **Verde:** l'indice c'è già, vai al passo 2. +- **Rosso:** premi **Aggiorna indice**. La prima volta scansiona tutta la share e richiede + qualche minuto; vedi una barra di avanzamento con i conteggi. + +Risultato atteso: pallino verde, percorso di `cad_index.db` e data dell'aggiornamento. +Premi **Avanti**. + +## 2. Importa la distinta + +Trascina il `.xls` nella finestra. Risultato atteso: la griglia si riempie con +Componente/Descrizione e nella cartella di lavoro compare un CSV con lo stesso nome base. +Poi premi **Verifica presenza**: le righe diventano verdi (trovato nell'indice) o rosse (non +trovato). Solo ora **Avanti** si sblocca. + +Se vedi righe rosse non fermarti: nel primo giro è normale scoprire che l'indice è vecchio o +che un componente non è sulla share. Vedi [`risoluzione-problemi.md`](risoluzione-problemi.md). + +## 3. Crea la cartella raw + +Premi **Crea cartella raw**. Risultato atteso: nella cartella di lavoro nasce `raw/` con i file +`.prt`/`.asm` dei componenti trovati, e la griglia mostra il percorso CAD di ciascuno. + +## 4. Trova i componenti referenziati + +Premi **Trova componenti referenziati**. L'app legge i `.asm` in `raw/` e copia anche tutto +quello che serve a Creo per aprirli (sotto-assiemi, componenti soppressi, riferimenti esterni). +Risultato atteso: una finestra con l'elenco di quanto copiato e, in fondo, i codici "non +trovati". `raw/` ora contiene più file di prima: è giusto così, perché la lista dei file +necessari è più lunga di quella della distinta (il perché, in +[`decisioni/0003-parser-esatto-con-fallback.md`](decisioni/0003-parser-esatto-con-fallback.md)). + +## 5. Porta i `.asm` a Creo + +La fase 5 mostra i `.asm` della distinta come icone trascinabili. Trascinali su **Creo +Distributed Batch** (passi in +[`guida-utente.md`](guida-utente.md#5-verifica-completezza-e-creo-distributed-batch)): +i file STEP finiscono in `step/`. + +## Cosa hai imparato + +| Fase | Entra | Esce | +|---|---|---| +| 1 | share `X:\` | `cad_index.db` | +| 2 | `.xls` del gestionale | CSV + verifica sull'indice | +| 3 | CSV + indice | `raw/` con i componenti della distinta | +| 4 | `.asm` in `raw/` | `raw/` completa dell'albero | +| 5 | `raw/` | `.asm` da trascinare su Creo | + +Per un secondo giro con un'altra distinta usa **File → Nuovo** (Ctrl+N), e prima ripulisci +`raw/`/`step/` da **File → Pulisci raw e step...**. diff --git a/scripts/CheckDocs.cs b/scripts/CheckDocs.cs new file mode 100644 index 0000000..66c3581 --- /dev/null +++ b/scripts/CheckDocs.cs @@ -0,0 +1,59 @@ +// dotnet run scripts/CheckDocs.cs (dalla root del repo; exit 1 se i doc sono fuori allineamento) +// Controlla anche che ogni doc di docs/ e docs/decisioni/ sia nell'indice. +// Controlla: link relativi (file e #ancora), del csproj == ultima voce del CHANGELOG, +// ogni .cs di PrepStep/ e scripts/ citato in docs/architettura.md. +using System.Text.RegularExpressions; + +var errori = new List(); +var mdFiles = Directory.EnumerateFiles(".", "*.md", SearchOption.AllDirectories) + .Where(f => !f.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}") + && !f.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}") + && !f.Contains($"{Path.DirectorySeparatorChar}raw{Path.DirectorySeparatorChar}") + && !f.Contains($"{Path.DirectorySeparatorChar}step{Path.DirectorySeparatorChar}")).ToList(); + +// slug delle intestazioni come lo calcola GitHub/Gitea +static HashSet Ancore(string path) => + File.ReadLines(path).Where(l => l.StartsWith('#')) + .Select(l => Regex.Replace(Regex.Replace(l.TrimStart('#').Trim().ToLowerInvariant(), @"[`*]", ""), @"[^\p{L}\p{N}\- _]", "").Replace(' ', '-')) + .ToHashSet(); + +// 1. link relativi +foreach (var f in mdFiles) +{ + var testo = Regex.Replace(File.ReadAllText(f), @"```.*?```", "", RegexOptions.Singleline); + foreach (Match m in Regex.Matches(testo, @"\]\(([^)\s]+)\)")) + { + var url = m.Groups[1].Value; + if (Regex.IsMatch(url, @"^[a-z]+:")) continue; // http:, mailto:, ... + var parti = url.Split('#', 2); + var target = parti[0] == "" ? f : Path.GetFullPath(Path.Combine(Path.GetDirectoryName(f)!, parti[0])); + if (!File.Exists(target) && !Directory.Exists(target)) { errori.Add($"{f}: link rotto -> {url}"); continue; } + if (parti.Length == 2 && target.EndsWith(".md") && !Ancore(target).Contains(parti[1].ToLowerInvariant())) + errori.Add($"{f}: ancora inesistente -> {url}"); + } +} + +// 2. versione +var version = Regex.Match(File.ReadAllText("PrepStep/PrepStep.csproj"), @"(.+?)").Groups[1].Value; +var ultima = Regex.Match(File.ReadAllText("CHANGELOG.md"), @"^## \[(\d[^\]]*)\]", RegexOptions.Multiline).Groups[1].Value; +if (version != ultima) errori.Add($"CHANGELOG.md: ultima voce {ultima} != {version} del csproj"); + +// 3. ogni sorgente citato nella mappa file-per-file +var arch = File.ReadAllText("docs/architettura.md"); +foreach (var cs in Directory.EnumerateFiles("PrepStep", "*.cs").Concat(Directory.EnumerateFiles("scripts", "*.cs"))) +{ + var nome = Path.GetFileName(cs); + if (!arch.Contains(nome) && !nome.EndsWith(".Designer.cs")) errori.Add($"docs/architettura.md: {cs} non è descritto"); +} + +// 4. ogni doc in docs/ è raggiungibile dall'indice della sua cartella +foreach (var dir in new[] { "docs", Path.Combine("docs", "decisioni") }) +{ + var indice = File.ReadAllText(Path.Combine(dir, "README.md")) + (dir == "docs" ? File.ReadAllText(Path.Combine(dir, "decisioni", "README.md")) : ""); + foreach (var md in Directory.EnumerateFiles(dir, "*.md").Where(f => Path.GetFileName(f) != "README.md")) + if (!indice.Contains(Path.GetFileName(md))) errori.Add($"{dir}/README.md: {Path.GetFileName(md)} non è nell'indice"); +} + +errori.ForEach(Console.Error.WriteLine); +Console.WriteLine(errori.Count == 0 ? $"Doc OK ({mdFiles.Count} file md)" : $"{errori.Count} problemi"); +return errori.Count == 0 ? 0 : 1;