Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5fb20a34e | ||
|
|
6d1e36a2ae | ||
|
|
ccfa00ff52 | ||
|
|
5fac2edf52 | ||
|
|
4ae3d0e087 | ||
|
|
64ff1d8770 | ||
|
|
57020f9964 | ||
|
|
206db85e12 | ||
|
|
d81fede4cc | ||
|
|
948937ba75 | ||
|
|
ff4e7a71e0 | ||
|
|
be4989fc8d | ||
|
|
e94331b69f | ||
|
|
3e9c78074c |
No files matched your search
@@ -37,3 +37,9 @@ publish/
|
|||||||
|
|
||||||
# Output di scripts/ExtractCreoStrings.cs
|
# Output di scripts/ExtractCreoStrings.cs
|
||||||
/BOM.txt
|
/BOM.txt
|
||||||
|
|
||||||
|
# Impostazioni locali dell'editor
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Setup generato da Inno Setup (installer/PrepStep.iss)
|
||||||
|
installer/Output/
|
||||||
@@ -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"]
|
||||||
|
}
|
||||||
@@ -5,6 +5,96 @@ 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
|
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/).
|
aderisce a [Semantic Versioning](https://semver.org/lang/it/).
|
||||||
|
|
||||||
|
## [Non rilasciato]
|
||||||
|
|
||||||
|
## [1.1.0] - 2026-10-07
|
||||||
|
|
||||||
|
### Aggiunto
|
||||||
|
|
||||||
|
- Fase 2: modalità "Lista personalizzata" per inserire i codici a mano, uno alla volta, in alternativa al file del gestionale.
|
||||||
|
- 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.
|
||||||
|
- Fase 2: dopo "Verifica presenza" la riga selezionata mantiene il verde/rosso dell'esito invece di diventare blu.
|
||||||
|
|
||||||
|
## [1.0.0] - 2026-09-23
|
||||||
|
|
||||||
|
Prima versione distribuita agli utenti tramite setup di installazione.
|
||||||
|
|
||||||
|
### Aggiunto
|
||||||
|
|
||||||
|
- Setup di installazione per-utente (`installer/PrepStep.iss`, Inno Setup 6):
|
||||||
|
`PrepStep-Setup-<versione>.exe` installa in `%LOCALAPPDATA%\Programs\PrepStep` senza diritti
|
||||||
|
di amministratore, crea nel menu Start i collegamenti al programma e al disinstallatore
|
||||||
|
(quello sul desktop è facoltativo) e registra l'app in "Impostazioni → App installate".
|
||||||
|
- Il setup, se l'app è già installata, chiede se aggiornarla/reinstallarla, disinstallarla o
|
||||||
|
uscire; con `/VERYSILENT` aggiorna senza chiedere. Aggiornando, database e impostazioni
|
||||||
|
restano.
|
||||||
|
- "File → Elimina CSV delle distinte...": elimina i CSV generati dall'import, che altrimenti si
|
||||||
|
accumulano nella cartella di lavoro (uno per distinta). Chiede conferma mostrando l'elenco
|
||||||
|
dei file; non tocca gli `.xls` del gestionale né `raw/`/`step/`.
|
||||||
|
- Guida utente e guida in-app: nuova sezione "Dove finiscono i file" (database, CSV, `raw/`,
|
||||||
|
`step/`, comandi di pulizia, come aprire la cartella nascosta in `AppData`, file che restano
|
||||||
|
dopo la disinstallazione).
|
||||||
|
- Licenza proprietaria (`LICENSE`, © 2026 Davide Grilli, tutti i diritti riservati), anche nei
|
||||||
|
metadati dell'exe e del setup.
|
||||||
|
- README: sequenza completa di comandi per generare il setup a ogni release.
|
||||||
|
|
||||||
|
### Modificato
|
||||||
|
|
||||||
|
- Nome dell'app mostrato all'utente: da "Cevolani StepPrep" a "PrepStep - Cevolani" (titolo
|
||||||
|
finestra, guida, setup, menu Start, "App installate").
|
||||||
|
- Il database è sempre `cad_index.db` nella cartella di installazione: all'avvio si carica
|
||||||
|
solo quello e "Aggiorna indice" lo crea lì se manca. "Apri DB..." carica un altro database
|
||||||
|
solo per la sessione corrente.
|
||||||
|
- La cartella di lavoro predefinita (se non ne è stata scelta una con "File → Scegli cartella
|
||||||
|
di lavoro...") è la cartella di installazione, invece della cartella del database.
|
||||||
|
- Guida utente: fase 1 riscritta per il database fisso; tolta la sezione di installazione (chi
|
||||||
|
legge la guida ha già il programma; le istruzioni restano nel `README.md`).
|
||||||
|
- Finestra Guida più grande (1000×680) e testo non più "da editor": niente cursore
|
||||||
|
lampeggiante, niente cursore a "I" e niente selezione col mouse.
|
||||||
|
- Documentazione riallineata al codice: nomi delle fasi come nella UI ("5. Verifica
|
||||||
|
completezza"), "File → Nuovo" che non cancella file su disco, mappa dei file e test nel
|
||||||
|
README, riferimenti a metodi non più esistenti (`AssiemeCompleto`) e ruolo di
|
||||||
|
`CaricaCodiciNoti`/`CaricaCandidati` in fase 4.
|
||||||
|
|
||||||
|
### Rimosso
|
||||||
|
|
||||||
|
- Memoria dell'ultimo database aperto (`%AppData%\PrepStep\ultimo_db.txt`) e ricerca di
|
||||||
|
`cad_index.db` nella cartella corrente.
|
||||||
|
|
||||||
|
## [0.9.0] - 2026-09-23
|
||||||
|
|
||||||
|
### Aggiunto
|
||||||
|
|
||||||
|
- Guida utente `docs/guida-utente.md`: tutti i passi, dalla distinta esportata dal gestionale
|
||||||
|
fino all'esportazione STEP con Creo Distributed Batch.
|
||||||
|
- Fase 2: bottone "Verifica presenza", visibile solo dopo un import riuscito; è lui (non il
|
||||||
|
solo import) a sbloccare "Avanti". Etichette numerate accanto ai bottoni.
|
||||||
|
- Dialogo "Componenti referenziati trovati": bottone "Chiudi" (anche con Esc) e icona dell'app.
|
||||||
|
|
||||||
|
### Modificato
|
||||||
|
|
||||||
|
- "Scegli cartella di lavoro..." e "Pulisci raw e step..." spostati dalla fase 1 al menu File,
|
||||||
|
raggiungibili in qualunque momento; "File → Nuova estrazione" rinominato in "Nuovo".
|
||||||
|
- Fase 1 ridisegnata in una card di stato "Database" con indicatore verde/rosso; rimossa la card
|
||||||
|
"Cartella di lavoro".
|
||||||
|
- Barra Avanti/Indietro trasformata in floatbar staccata dai bordi, con bottoni di dimensioni e
|
||||||
|
stile uniformi (riusata anche nel dialogo dei componenti referenziati).
|
||||||
|
- Finestra Guida ridisegnata: sezioni con titolo, passi numerati e callout "Nota"/"Attenzione"
|
||||||
|
colorati; sidebar con selezione nel blu dell'app.
|
||||||
|
- Testi delle fasi 3-4 semplificati.
|
||||||
|
|
||||||
|
### Corretto
|
||||||
|
|
||||||
|
- "Avanti" restava disabilitato in fase 1 all'avvio anche con DB e cartella di lavoro già
|
||||||
|
caricati.
|
||||||
|
- Guida in-app e README citavano ancora "Verifica completezza assiemi" e "Cambia cartella...",
|
||||||
|
rimossi da tempo.
|
||||||
|
|
||||||
## [0.1.0] - 2026-09-22
|
## [0.1.0] - 2026-09-22
|
||||||
|
|
||||||
### Aggiunto
|
### Aggiunto
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
## Cos'è questo progetto
|
## Cos'è questo progetto
|
||||||
|
|
||||||
`PrepStep` è il nome tecnico del progetto (cartella, namespace, csproj, eseguibile
|
`PrepStep` è il nome tecnico del progetto (cartella, namespace, csproj, eseguibile
|
||||||
`PrepStep.exe`); il nome mostrato all'utente finale è **Cevolani StepPrep** (`<Product>`/
|
`PrepStep.exe`); il nome mostrato all'utente finale è **PrepStep - Cevolani** (`<Product>`/
|
||||||
`<AssemblyTitle>` in `PrepStep/PrepStep.csproj`, usato nel titolo della finestra insieme
|
`<AssemblyTitle>` in `PrepStep/PrepStep.csproj`, usato nel titolo della finestra insieme
|
||||||
alla versione, `<Version>`, attualmente `0.1.0`). È un'app Windows Forms (.NET 10, WinForms) che indicizza e permette di
|
alla versione, `<Version>`, attualmente `1.0.0`). È un'app Windows Forms (.NET 10, WinForms) che indicizza e permette di
|
||||||
cercare i file CAD Pro-ENGINEER/Creo contenuti in tutte le cartelle di primo livello
|
cercare i file CAD Pro-ENGINEER/Creo contenuti in tutte le cartelle di primo livello
|
||||||
di un disco/share di rete (radice di default `X:\`), salvando i metadati in un DB
|
di un disco/share di rete (radice di default `X:\`), salvando i metadati in un DB
|
||||||
SQLite locale (`cad_index.db`). Il flusso a valle (fasi 2-5, vedi sotto) prepara i file
|
SQLite locale (`cad_index.db`). Il flusso a valle (fasi 2-5, vedi sotto) prepara i file
|
||||||
@@ -29,6 +29,13 @@ Pubblicazione come eseguibile standalone (single-file, self-contained, Windows x
|
|||||||
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish
|
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Setup di installazione per-utente (`%LOCALAPPDATA%\Programs\PrepStep`, niente admin), dalla
|
||||||
|
root del repo dopo il publish — dettagli in `README.md` e `docs/architettura.md`:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
& "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" installer\PrepStep.iss
|
||||||
|
```
|
||||||
|
|
||||||
`PrepStep.Tests/` (xUnit) contiene unit e integration test — vedi
|
`PrepStep.Tests/` (xUnit) contiene unit e integration test — vedi
|
||||||
`dotnet test` da dentro quella cartella. Riferimento al progetto principale
|
`dotnet test` da dentro quella cartella. Riferimento al progetto principale
|
||||||
via `ProjectReference` + `InternalsVisibleTo` (in `PrepStep.csproj`), così
|
via `ProjectReference` + `InternalsVisibleTo` (in `PrepStep.csproj`), così
|
||||||
@@ -51,12 +58,19 @@ Dove scrivere cosa:
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `CLAUDE.md` (questo file) | il flusso essenziale e i punti che si sbaglierebbero senza saperli |
|
| `CLAUDE.md` (questo file) | il flusso essenziale e i punti che si sbaglierebbero senza saperli |
|
||||||
| [`docs/architettura.md`](docs/architettura.md) | il processo delle 5 fasi e la mappa file-per-file |
|
| [`docs/architettura.md`](docs/architettura.md) | il processo delle 5 fasi e la mappa file-per-file |
|
||||||
|
| [`docs/guida-utente.md`](docs/guida-utente.md) | cosa clicca l'utente, passo per passo, fino a Creo Distributed Batch |
|
||||||
| [`docs/formato-asm-record.md`](docs/formato-asm-record.md) | parser esatto dei record `.asm`, con §7 per i casi limite |
|
| [`docs/formato-asm-record.md`](docs/formato-asm-record.md) | parser esatto dei record `.asm`, con §7 per i casi limite |
|
||||||
| [`docs/formato-asm.md`](docs/formato-asm.md) | euristica testuale di fallback |
|
| [`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/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/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 `<Version>` nel csproj aggiunge qui la sua voce |
|
| [`CHANGELOG.md`](CHANGELOG.md) | storico delle versioni (formato Keep a Changelog); ogni release che cambia `<Version>` 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
|
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
|
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
|
riparte da zero (i §7.3/§7.4/§7.5 di `formato-asm-record.md` sono l'esempio del formato
|
||||||
@@ -64,7 +78,8 @@ atteso).
|
|||||||
|
|
||||||
## Architettura
|
## Architettura
|
||||||
|
|
||||||
Il programma è tutto in `PrepStep/` (nessun altro progetto nella solution). Dettaglio
|
Il programma è tutto in `PrepStep/` (accanto ci sono solo i test `PrepStep.Tests/`, gli
|
||||||
|
script `scripts/` e il setup `installer/`). Dettaglio
|
||||||
completo file-per-file in [`docs/architettura.md`](docs/architettura.md); qui solo il flusso
|
completo file-per-file in [`docs/architettura.md`](docs/architettura.md); qui solo il flusso
|
||||||
che conta per capire il prodotto.
|
che conta per capire il prodotto.
|
||||||
|
|
||||||
@@ -73,15 +88,21 @@ che conta per capire il prodotto.
|
|||||||
designer visuale):
|
designer visuale):
|
||||||
|
|
||||||
1. **Database** — carica/aggiorna `cad_index.db` (`Indexer.ScanAsync`, radice disco di
|
1. **Database** — carica/aggiorna `cad_index.db` (`Indexer.ScanAsync`, radice disco di
|
||||||
default `X:\` hardcoded in `RadiceDiscoDefault`); qui vive anche la **cartella di lavoro**
|
default `X:\` hardcoded in `RadiceDiscoDefault`); un pallino verde/rosso accanto al titolo
|
||||||
(destinazione di CSV/`raw/`/`step/`), proposta dall'ultima usata o dalla cartella del DB se
|
indica se un DB è caricato. La **cartella di lavoro** (destinazione di CSV/`raw/`/`step/`)
|
||||||
non c'è ancora nulla di salvato — vedi `docs/db-cad-index.md` per il dettaglio di schema e
|
non è più mostrata qui — proposta dall'ultima usata o dalla cartella di installazione se
|
||||||
risoluzione del percorso DB.
|
non c'è ancora nulla di salvato, cambiabile in qualunque momento da "File → Scegli cartella di
|
||||||
|
lavoro..." — vedi `docs/db-cad-index.md` per il dettaglio di schema e percorso del DB
|
||||||
|
(sempre `cad_index.db` nella cartella di installazione; "Apri DB..." vale solo per la
|
||||||
|
sessione).
|
||||||
2. **Importa Distinta Base** — `DistintaBaseImporter.ConvertiInCsv` genera il CSV
|
2. **Importa Distinta Base** — `DistintaBaseImporter.ConvertiInCsv` genera il CSV
|
||||||
componente;descrizione nella cartella di lavoro; se il file segue la convenzione del
|
componente;descrizione nella cartella di lavoro (da file scelto o trascinato); se il file
|
||||||
gestionale (`DBP_<codice>_<timestamp>`), aggiunge l'assieme generale come riga in più (da
|
segue la convenzione del gestionale (`DBP_<codice>_<timestamp>`), aggiunge l'assieme
|
||||||
quel momento è un componente come tutti gli altri, nessun caso speciale nel resto del
|
generale come riga in più (da quel momento è un componente come tutti gli altri, nessun
|
||||||
codice).
|
caso speciale nel resto del codice). Il bottone "Verifica presenza" compare solo dopo un
|
||||||
|
import riuscito e cerca i componenti nell'indice (`VerificaCodiciNelDb`, righe colorate in
|
||||||
|
verde/rosso in griglia): è quello — non il solo import — a sbloccare "Avanti"
|
||||||
|
(`AvantiConsentito`).
|
||||||
3. **Crea cartella raw** — `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela, vedi
|
3. **Crea cartella raw** — `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela, vedi
|
||||||
`docs/copia-raw-parallela.md`) copiano in `raw/` i file CAD dei componenti del CSV trovati
|
`docs/copia-raw-parallela.md`) copiano in `raw/` i file CAD dei componenti del CSV trovati
|
||||||
nell'indice.
|
nell'indice.
|
||||||
@@ -103,9 +124,13 @@ designer visuale):
|
|||||||
5. **Verifica completezza** — mostra i `.asm` della distinta già in `raw/`, trascinabili su
|
5. **Verifica completezza** — mostra i `.asm` della distinta già in `raw/`, trascinabili su
|
||||||
Creo per l'esportazione STEP (Distributed Batch, manuale, fuori dall'app).
|
Creo per l'esportazione STEP (Distributed Batch, manuale, fuori dall'app).
|
||||||
|
|
||||||
Menu in cima alla finestra: "File → Nuova estrazione" (Ctrl+N) azzera lo stato delle fasi 2-5
|
Menu in cima alla finestra: "File → Nuovo" (Ctrl+N) azzera lo stato delle fasi 2-5
|
||||||
per ripartire con una nuova distinta, senza toccare DB/cartella di lavoro della fase 1;
|
per ripartire con una nuova distinta, senza toccare DB/cartella di lavoro della fase 1 (va
|
||||||
"Aiuto → Guida" (F1) apre una sintesi interattiva delle 5 fasi (`GuidaForm.cs`).
|
volutamente alla fase 2, non alla 1: quei dati restano quelli già caricati); "File → Scegli
|
||||||
|
cartella di lavoro..." cambia la cartella di CSV/`raw/`/`step/` in qualunque momento; "File →
|
||||||
|
Pulisci raw e step..." svuota le cartelle `raw/`/`step/` nella cartella di lavoro (non tocca
|
||||||
|
l'indice); "File → Elimina CSV delle distinte..." elimina i `*.csv` generati in fase 2, che
|
||||||
|
altrimenti si accumulano uno per distinta nella cartella di lavoro; "Aiuto → Guida" (F1) apre una sintesi interattiva delle 5 fasi (`GuidaForm.cs`).
|
||||||
|
|
||||||
## Script standalone (`scripts/`)
|
## Script standalone (`scripts/`)
|
||||||
|
|
||||||
@@ -122,5 +147,5 @@ intercambiabili:
|
|||||||
- `ExtractCreoStrings.cs`/`BomViaDb.cs` usano `AssemblyComponentScanner` (euristica testuale,
|
- `ExtractCreoStrings.cs`/`BomViaDb.cs` usano `AssemblyComponentScanner` (euristica testuale,
|
||||||
`docs/formato-asm.md`) e risolvono i sottoassiemi contro `cad_index.db`.
|
`docs/formato-asm.md`) e risolvono i sottoassiemi contro `cad_index.db`.
|
||||||
- `CreoBom.cs` usa `AsmRecordReader` (parser esatto dei record binari,
|
- `CreoBom.cs` usa `AsmRecordReader` (parser esatto dei record binari,
|
||||||
`docs/formato-asm-record.md`, condiviso con la GUI in fase 4/5) e risolve i sottoassiemi
|
`docs/formato-asm-record.md`, condiviso con la GUI in fase 4) e risolve i sottoassiemi
|
||||||
cercandoli in cartelle locali (`-p`), non contro `cad_index.db`.
|
cercandoli in cartelle locali (`-p`), non contro `cad_index.db`.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
Copyright (c) 2026 Davide Grilli. Tutti i diritti riservati.
|
||||||
|
|
||||||
|
Questo software, il codice sorgente, la documentazione e i file correlati ("il Software")
|
||||||
|
sono di proprietà esclusiva di Davide Grilli.
|
||||||
|
|
||||||
|
Senza previa autorizzazione scritta del titolare è vietato copiare, modificare, unire,
|
||||||
|
pubblicare, distribuire, concedere in sublicenza o vendere copie del Software, in tutto o
|
||||||
|
in parte, e ricavarne opere derivate.
|
||||||
|
|
||||||
|
L'uso del Software è consentito solo alle persone e alle organizzazioni espressamente
|
||||||
|
autorizzate dal titolare, e nei limiti di tale autorizzazione.
|
||||||
|
|
||||||
|
IL SOFTWARE È FORNITO "COSÌ COM'È", SENZA GARANZIE DI ALCUN TIPO, ESPRESSE O IMPLICITE,
|
||||||
|
INCLUSE, SENZA LIMITAZIONE, LE GARANZIE DI COMMERCIABILITÀ, IDONEITÀ A UNO SCOPO
|
||||||
|
PARTICOLARE E NON VIOLAZIONE. IN NESSUN CASO IL TITOLARE SARÀ RESPONSABILE PER QUALSIASI
|
||||||
|
RECLAMO, DANNO O ALTRA RESPONSABILITÀ DERIVANTE DALL'USO DEL SOFTWARE O IN RELAZIONE AD
|
||||||
|
ESSO.
|
||||||
+321
-160
@@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
|
|
||||||
namespace PrepStep;
|
namespace PrepStep;
|
||||||
@@ -10,19 +11,27 @@ public partial class Form1 : Form
|
|||||||
private readonly Button _btnCreaRaw = new();
|
private readonly Button _btnCreaRaw = new();
|
||||||
private readonly Button _btnTrovaPrt = new();
|
private readonly Button _btnTrovaPrt = new();
|
||||||
private readonly Button _btnVerifica = new();
|
private readonly Button _btnVerifica = new();
|
||||||
|
private readonly FlowLayoutPanel _rigaVerifica = new();
|
||||||
|
private readonly FlowLayoutPanel _rigaFile = new();
|
||||||
|
private readonly FlowLayoutPanel _rigaCustom = new();
|
||||||
|
private readonly RadioButton _rbFile = new() { Text = "Da file del gestionale", Checked = true, AutoSize = true };
|
||||||
|
private readonly RadioButton _rbCustom = new() { Text = "Lista personalizzata", AutoSize = true };
|
||||||
|
private readonly TextBox _txtCodice = new() { Width = 220, Margin = new Padding(0, 3, 8, 0) };
|
||||||
private readonly ListView _lstAnteprimaAsm = new();
|
private readonly ListView _lstAnteprimaAsm = new();
|
||||||
private readonly Button _btnPulisci = new();
|
|
||||||
private readonly Label _lblStatus = new();
|
private readonly Label _lblStatus = new();
|
||||||
private readonly Label _lblDb = new();
|
private readonly Label _lblDb = new();
|
||||||
private readonly Label _lblUltimoAggiornamento = new();
|
private readonly Label _lblUltimoAggiornamento = new();
|
||||||
private readonly Label _lblCartellaLavoro = new();
|
private readonly Panel _indicatoreDb = new();
|
||||||
private readonly Button _btnCambiaCartella = new();
|
private bool _dbCaricato;
|
||||||
private readonly DataGridView _gridImport = new();
|
private readonly DataGridView _gridImport = new();
|
||||||
private readonly DataGridView _gridRaw = new();
|
private readonly DataGridView _gridRaw = new();
|
||||||
private readonly Label _lblImportStatus = new();
|
private readonly Label _lblImportStatus = new();
|
||||||
private readonly ToolTip _tipImportStatus = new();
|
private readonly ToolTip _tipImportStatus = new();
|
||||||
private readonly MenuStrip _menu = new();
|
private readonly MenuStrip _menu = new();
|
||||||
private readonly ToolStripMenuItem _mnuNuovaEstrazione = new("Nuova estrazione");
|
private readonly ToolStripMenuItem _mnuNuovaEstrazione = new("Nuovo");
|
||||||
|
private readonly ToolStripMenuItem _mnuPulisci = new("Pulisci raw e step...");
|
||||||
|
private readonly ToolStripMenuItem _mnuEliminaCsv = new("Elimina CSV delle distinte...");
|
||||||
|
private readonly ToolStripMenuItem _mnuCambiaCartella = new("Scegli cartella di lavoro...");
|
||||||
|
|
||||||
// Wizard: una fase alla volta occupa tutta la finestra, si avanza solo quando quella
|
// Wizard: una fase alla volta occupa tutta la finestra, si avanza solo quando quella
|
||||||
// corrente è "pronta" (vedi AvantiConsentito). NomiFasi/_panelFasi sono paralleli per indice.
|
// corrente è "pronta" (vedi AvantiConsentito). NomiFasi/_panelFasi sono paralleli per indice.
|
||||||
@@ -46,7 +55,7 @@ public partial class Form1 : Form
|
|||||||
|
|
||||||
private string? _dbPath;
|
private string? _dbPath;
|
||||||
private string? _cartellaLavoro;
|
private string? _cartellaLavoro;
|
||||||
private string? _percorsoInputCorrente;
|
private bool _ripristinoModalita;
|
||||||
private List<(string Componente, string Descrizione)>? _righeCorrenti;
|
private List<(string Componente, string Descrizione)>? _righeCorrenti;
|
||||||
private Dictionary<string, List<string>>? _ultimaVerifica;
|
private Dictionary<string, List<string>>? _ultimaVerifica;
|
||||||
private string? _ultimaCartellaRaw;
|
private string? _ultimaCartellaRaw;
|
||||||
@@ -71,7 +80,7 @@ public partial class Form1 : Form
|
|||||||
AllowDrop = true;
|
AllowDrop = true;
|
||||||
// Versione letta dall'assembly (<Version> nel csproj) invece che duplicata qui a mano.
|
// Versione letta dall'assembly (<Version> nel csproj) invece che duplicata qui a mano.
|
||||||
var versione = typeof(Form1).Assembly.GetName().Version;
|
var versione = typeof(Form1).Assembly.GetName().Version;
|
||||||
Text = $"Cevolani StepPrep {versione?.ToString(3)}";
|
Text = $"PrepStep - Cevolani {versione?.ToString(3)}";
|
||||||
MinimumSize = new Size(760, 480);
|
MinimumSize = new Size(760, 480);
|
||||||
Font = new Font("Segoe UI", 9.5f);
|
Font = new Font("Segoe UI", 9.5f);
|
||||||
// Stessa icona di ApplicationIcon nel csproj: la si rilegge dall'exe compilato invece
|
// Stessa icona di ApplicationIcon nel csproj: la si rilegge dall'exe compilato invece
|
||||||
@@ -92,38 +101,27 @@ public partial class Form1 : Form
|
|||||||
_lblFase.Font = new Font(Font, FontStyle.Bold);
|
_lblFase.Font = new Font(Font, FontStyle.Bold);
|
||||||
headerPanel.Controls.Add(_lblFase);
|
headerPanel.Controls.Add(_lblFase);
|
||||||
|
|
||||||
// Barra di navigazione come footer separato dal contenuto: linea sottile in cima e
|
// Floatbar di navigazione: card staccata dai bordi della finestra invece di un footer
|
||||||
// sfondo bianco, invece dei bottoni appoggiati direttamente sul contenuto/sulla barra
|
// a tutta larghezza appoggiato direttamente sul contenuto/sulla barra di stato.
|
||||||
// di stato senza alcuna divisione visiva.
|
var (navContainer, navPanel) = UiStile.FloatBar();
|
||||||
// Padding destro = 16, lo stesso usato dai pannelli di ogni fase (Padding(16)): prima
|
|
||||||
// era 12, quindi "Avanti" sporgeva 4px oltre il margine destro del contenuto sopra.
|
|
||||||
var navContainer = new Panel { Dock = DockStyle.Bottom, Height = 64, BackColor = Color.White };
|
|
||||||
var navSeparatore = new Panel
|
|
||||||
{
|
|
||||||
Dock = DockStyle.Top,
|
|
||||||
Height = 1,
|
|
||||||
BackColor = Color.FromArgb(220, 220, 220),
|
|
||||||
};
|
|
||||||
var navPanel = new FlowLayoutPanel
|
|
||||||
{
|
|
||||||
Dock = DockStyle.Fill,
|
|
||||||
FlowDirection = FlowDirection.RightToLeft,
|
|
||||||
Padding = new Padding(16, 14, 16, 14),
|
|
||||||
};
|
|
||||||
_btnAvanti.Text = "Avanti ›";
|
_btnAvanti.Text = "Avanti ›";
|
||||||
UiStile.Primario(_btnAvanti);
|
UiStile.Primario(_btnAvanti);
|
||||||
UiStile.Arrotonda(_btnAvanti);
|
|
||||||
_btnAvanti.Click += (_, _) => VaiAllaFase(_faseAttuale + 1);
|
|
||||||
navPanel.Controls.Add(_btnAvanti);
|
|
||||||
|
|
||||||
_btnIndietro.Text = "‹ Indietro";
|
_btnIndietro.Text = "‹ Indietro";
|
||||||
_btnIndietro.Margin = new Padding(8, 0, 0, 0);
|
_btnIndietro.Margin = new Padding(8, 0, 0, 0);
|
||||||
UiStile.Secondario(_btnIndietro);
|
UiStile.SecondarioPieno(_btnIndietro);
|
||||||
UiStile.Arrotonda(_btnIndietro);
|
|
||||||
|
// Dimensioni uguali decise PRIMA di arrotondare: se si arrotonda un bottone e solo dopo
|
||||||
|
// lo si ridimensiona, la Region calcolata sulla dimensione vecchia lascia un bordo
|
||||||
|
// "fantasma" finché non arriva un repaint completo della finestra.
|
||||||
|
UiStile.LarghezzaUguale(_btnAvanti, _btnIndietro);
|
||||||
|
UiStile.Arrotonda(_btnAvanti, 10);
|
||||||
|
UiStile.Arrotonda(_btnIndietro, 10);
|
||||||
|
|
||||||
|
_btnAvanti.Click += (_, _) => VaiAllaFase(_faseAttuale + 1);
|
||||||
|
navPanel.Controls.Add(_btnAvanti);
|
||||||
_btnIndietro.Click += (_, _) => VaiAllaFase(_faseAttuale - 1);
|
_btnIndietro.Click += (_, _) => VaiAllaFase(_faseAttuale - 1);
|
||||||
navPanel.Controls.Add(_btnIndietro);
|
navPanel.Controls.Add(_btnIndietro);
|
||||||
navContainer.Controls.Add(navPanel);
|
|
||||||
navContainer.Controls.Add(navSeparatore);
|
|
||||||
|
|
||||||
var statusPanel = new Panel { Dock = DockStyle.Bottom, Height = 28, BackColor = SystemColors.ControlLight };
|
var statusPanel = new Panel { Dock = DockStyle.Bottom, Height = 28, BackColor = SystemColors.ControlLight };
|
||||||
_lblStatus.Dock = DockStyle.Fill;
|
_lblStatus.Dock = DockStyle.Fill;
|
||||||
@@ -152,6 +150,15 @@ public partial class Form1 : Form
|
|||||||
_mnuNuovaEstrazione.Click += (_, _) => NuovaEstrazione();
|
_mnuNuovaEstrazione.Click += (_, _) => NuovaEstrazione();
|
||||||
mnuFile.DropDownItems.Add(_mnuNuovaEstrazione);
|
mnuFile.DropDownItems.Add(_mnuNuovaEstrazione);
|
||||||
|
|
||||||
|
_mnuCambiaCartella.Click += (_, _) => ScegliCartellaLavoro();
|
||||||
|
mnuFile.DropDownItems.Add(_mnuCambiaCartella);
|
||||||
|
|
||||||
|
_mnuPulisci.Click += (_, _) => PulisciRawEStep();
|
||||||
|
mnuFile.DropDownItems.Add(_mnuPulisci);
|
||||||
|
|
||||||
|
_mnuEliminaCsv.Click += (_, _) => EliminaCsv();
|
||||||
|
mnuFile.DropDownItems.Add(_mnuEliminaCsv);
|
||||||
|
|
||||||
var mnuAiuto = new ToolStripMenuItem("Aiuto");
|
var mnuAiuto = new ToolStripMenuItem("Aiuto");
|
||||||
var mnuGuida = new ToolStripMenuItem("Guida") { ShortcutKeys = Keys.F1 };
|
var mnuGuida = new ToolStripMenuItem("Guida") { ShortcutKeys = Keys.F1 };
|
||||||
mnuGuida.Click += (_, _) => new GuidaForm().ShowDialog(this);
|
mnuGuida.Click += (_, _) => new GuidaForm().ShowDialog(this);
|
||||||
@@ -244,7 +251,7 @@ public partial class Form1 : Form
|
|||||||
private bool AvantiConsentito(int fase) => fase switch
|
private bool AvantiConsentito(int fase) => fase switch
|
||||||
{
|
{
|
||||||
0 => _dbPath is not null && _cartellaLavoro is not null,
|
0 => _dbPath is not null && _cartellaLavoro is not null,
|
||||||
1 => _righeCorrenti is { Count: > 0 },
|
1 => _righeCorrenti is { Count: > 0 } && _ultimaVerifica is not null,
|
||||||
2 => _ultimaCartellaRaw is not null,
|
2 => _ultimaCartellaRaw is not null,
|
||||||
3 => true, // risoluzione ricorsiva opzionale: non tutti gli assiemi hanno sotto-componenti
|
3 => true, // risoluzione ricorsiva opzionale: non tutti gli assiemi hanno sotto-componenti
|
||||||
_ => true,
|
_ => true,
|
||||||
@@ -259,12 +266,17 @@ public partial class Form1 : Form
|
|||||||
{
|
{
|
||||||
if (_dbPath is null || _cartellaLavoro is null)
|
if (_dbPath is null || _cartellaLavoro is null)
|
||||||
{
|
{
|
||||||
MessageBox.Show(this, "Carica prima il database in fase 1.", "Nuova estrazione",
|
MessageBox.Show(this, "Carica prima il database in fase 1.", "Nuovo",
|
||||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_percorsoInputCorrente = null;
|
AzzeraDistinta();
|
||||||
|
VaiAllaFase(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AzzeraDistinta()
|
||||||
|
{
|
||||||
_righeCorrenti = null;
|
_righeCorrenti = null;
|
||||||
_ultimaCartellaRaw = null;
|
_ultimaCartellaRaw = null;
|
||||||
_ultimaVerifica = null;
|
_ultimaVerifica = null;
|
||||||
@@ -272,8 +284,69 @@ public partial class Form1 : Form
|
|||||||
_gridRaw.Rows.Clear();
|
_gridRaw.Rows.Clear();
|
||||||
_lstAnteprimaAsm.Items.Clear();
|
_lstAnteprimaAsm.Items.Clear();
|
||||||
ImpostaStatoImport("", Color.Black);
|
ImpostaStatoImport("", Color.Black);
|
||||||
|
_rigaVerifica.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
VaiAllaFase(1);
|
private void CambiaModalitaDistinta()
|
||||||
|
{
|
||||||
|
if (_ripristinoModalita)
|
||||||
|
return;
|
||||||
|
if (_gridImport.Rows.Count > 0 && MessageBox.Show(this,
|
||||||
|
"Cambiando modalità la distinta corrente viene azzerata. Continuare?", "Distinta",
|
||||||
|
MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
|
||||||
|
{
|
||||||
|
_ripristinoModalita = true;
|
||||||
|
(_rbCustom.Checked ? _rbFile : _rbCustom).Checked = true;
|
||||||
|
_ripristinoModalita = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AzzeraDistinta();
|
||||||
|
_rigaFile.Visible = _rbFile.Checked;
|
||||||
|
_rigaCustom.Visible = _rbCustom.Checked;
|
||||||
|
AggiornaStatoAvanti();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AggiungiCodice()
|
||||||
|
{
|
||||||
|
string codice = _txtCodice.Text.Trim();
|
||||||
|
if (codice.Length == 0)
|
||||||
|
return;
|
||||||
|
_righeCorrenti ??= new();
|
||||||
|
if (_righeCorrenti.Any(r => string.Equals(r.Componente, codice, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
_txtCodice.SelectAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_righeCorrenti.Add((codice, ""));
|
||||||
|
_gridImport.Rows.Add(codice, "");
|
||||||
|
InvalidaVerifica();
|
||||||
|
_txtCodice.Clear();
|
||||||
|
_txtCodice.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RimuoviCodiceSelezionato()
|
||||||
|
{
|
||||||
|
if (_gridImport.CurrentRow is not { } riga || _righeCorrenti is null)
|
||||||
|
return;
|
||||||
|
_righeCorrenti.RemoveAt(riga.Index); // griglia e lista hanno lo stesso ordine
|
||||||
|
_gridImport.Rows.Remove(riga);
|
||||||
|
InvalidaVerifica();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>La lista è cambiata: la verifica presenza e il raw già creato non valgono più.</summary>
|
||||||
|
private void InvalidaVerifica()
|
||||||
|
{
|
||||||
|
_ultimaVerifica = null;
|
||||||
|
_ultimaCartellaRaw = null;
|
||||||
|
_gridRaw.Rows.Clear();
|
||||||
|
foreach (DataGridViewRow r in _gridImport.Rows)
|
||||||
|
{
|
||||||
|
r.DefaultCellStyle.BackColor = Color.Empty;
|
||||||
|
r.DefaultCellStyle.SelectionBackColor = Color.Empty;
|
||||||
|
r.DefaultCellStyle.SelectionForeColor = Color.Empty;
|
||||||
|
}
|
||||||
|
_rigaVerifica.Visible = _righeCorrenti is { Count: > 0 };
|
||||||
|
AggiornaStatoAvanti();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Panel BuildFaseDatabase()
|
private Panel BuildFaseDatabase()
|
||||||
@@ -295,78 +368,78 @@ public partial class Form1 : Form
|
|||||||
Dock = DockStyle.Top,
|
Dock = DockStyle.Top,
|
||||||
AutoSize = true,
|
AutoSize = true,
|
||||||
ForeColor = SystemColors.GrayText,
|
ForeColor = SystemColors.GrayText,
|
||||||
Margin = new Padding(0, 0, 0, 12),
|
Margin = new Padding(0, 0, 0, 16),
|
||||||
Text = "L'indice CAD viene caricato automaticamente all'avvio, se già presente. "
|
Text = "L'indice CAD viene caricato automaticamente all'avvio, se già presente.",
|
||||||
+ "Usa 'Apri DB...' per selezionarne un altro, oppure 'Aggiorna indice' per "
|
|
||||||
+ "scansionare di nuovo il disco (X:\\).",
|
|
||||||
};
|
};
|
||||||
contenuto.Controls.Add(lblIstruzioni);
|
contenuto.Controls.Add(lblIstruzioni);
|
||||||
|
|
||||||
|
var cardDb = UiStile.Card();
|
||||||
|
var contenutoCardDb = new TableLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Top,
|
||||||
|
AutoSize = true,
|
||||||
|
AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
||||||
|
ColumnCount = 1,
|
||||||
|
};
|
||||||
|
contenutoCardDb.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
|
||||||
|
|
||||||
|
var rigaTitoloDb = new FlowLayoutPanel
|
||||||
|
{
|
||||||
|
AutoSize = true,
|
||||||
|
FlowDirection = FlowDirection.LeftToRight,
|
||||||
|
Margin = new Padding(0, 0, 0, 6),
|
||||||
|
};
|
||||||
|
|
||||||
|
_indicatoreDb.Size = new Size(10, 10);
|
||||||
|
_indicatoreDb.Margin = new Padding(0, 5, 8, 0);
|
||||||
|
_indicatoreDb.Paint += (_, e) =>
|
||||||
|
{
|
||||||
|
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||||
|
var colore = _dbCaricato ? Color.FromArgb(30, 160, 90) : Color.FromArgb(200, 60, 60);
|
||||||
|
using var pennello = new SolidBrush(colore);
|
||||||
|
e.Graphics.FillEllipse(pennello, 0, 0, _indicatoreDb.Width - 1, _indicatoreDb.Height - 1);
|
||||||
|
};
|
||||||
|
rigaTitoloDb.Controls.Add(_indicatoreDb);
|
||||||
|
|
||||||
|
var lblTitoloDb = new Label
|
||||||
|
{
|
||||||
|
AutoSize = true,
|
||||||
|
Font = new Font(Font, FontStyle.Bold),
|
||||||
|
Text = "Database",
|
||||||
|
};
|
||||||
|
rigaTitoloDb.Controls.Add(lblTitoloDb);
|
||||||
|
|
||||||
|
contenutoCardDb.Controls.Add(rigaTitoloDb);
|
||||||
|
|
||||||
_lblDb.Dock = DockStyle.Top;
|
_lblDb.Dock = DockStyle.Top;
|
||||||
_lblDb.AutoSize = true;
|
_lblDb.AutoSize = true;
|
||||||
_lblDb.ForeColor = SystemColors.GrayText;
|
_lblDb.ForeColor = SystemColors.ControlText;
|
||||||
_lblDb.Margin = new Padding(0, 0, 0, 4);
|
_lblDb.Margin = new Padding(0, 0, 0, 2);
|
||||||
contenuto.Controls.Add(_lblDb);
|
contenutoCardDb.Controls.Add(_lblDb);
|
||||||
|
|
||||||
_lblUltimoAggiornamento.AutoSize = true;
|
_lblUltimoAggiornamento.AutoSize = true;
|
||||||
_lblUltimoAggiornamento.ForeColor = SystemColors.GrayText;
|
_lblUltimoAggiornamento.ForeColor = SystemColors.GrayText;
|
||||||
_lblUltimoAggiornamento.Margin = new Padding(0, 0, 0, 12);
|
_lblUltimoAggiornamento.Margin = new Padding(0, 0, 0, 14);
|
||||||
contenuto.Controls.Add(_lblUltimoAggiornamento);
|
contenutoCardDb.Controls.Add(_lblUltimoAggiornamento);
|
||||||
|
|
||||||
var bottoni = new FlowLayoutPanel { AutoSize = true, FlowDirection = FlowDirection.LeftToRight };
|
var bottoniDb = new FlowLayoutPanel { AutoSize = true, FlowDirection = FlowDirection.LeftToRight };
|
||||||
|
|
||||||
_btnApriDb.Text = "Apri DB...";
|
_btnApriDb.Text = "Apri DB...";
|
||||||
_btnApriDb.Margin = new Padding(0, 0, 8, 0);
|
_btnApriDb.Margin = new Padding(0, 0, 8, 0);
|
||||||
UiStile.Secondario(_btnApriDb);
|
UiStile.Secondario(_btnApriDb);
|
||||||
|
UiStile.Arrotonda(_btnApriDb);
|
||||||
_btnApriDb.Click += (_, _) => ScegliDatabase();
|
_btnApriDb.Click += (_, _) => ScegliDatabase();
|
||||||
bottoni.Controls.Add(_btnApriDb);
|
bottoniDb.Controls.Add(_btnApriDb);
|
||||||
|
|
||||||
_btnAggiorna.Text = "Aggiorna indice";
|
_btnAggiorna.Text = "Aggiorna indice";
|
||||||
UiStile.Secondario(_btnAggiorna);
|
UiStile.Secondario(_btnAggiorna);
|
||||||
|
UiStile.Arrotonda(_btnAggiorna);
|
||||||
_btnAggiorna.Click += (_, _) => AggiornaIndice();
|
_btnAggiorna.Click += (_, _) => AggiornaIndice();
|
||||||
bottoni.Controls.Add(_btnAggiorna);
|
bottoniDb.Controls.Add(_btnAggiorna);
|
||||||
|
|
||||||
contenuto.Controls.Add(bottoni);
|
contenutoCardDb.Controls.Add(bottoniDb);
|
||||||
|
cardDb.Controls.Add(contenutoCardDb);
|
||||||
var lblIstruzioniCartella = new Label
|
contenuto.Controls.Add(cardDb);
|
||||||
{
|
|
||||||
Dock = DockStyle.Top,
|
|
||||||
AutoSize = true,
|
|
||||||
ForeColor = SystemColors.GrayText,
|
|
||||||
Margin = new Padding(0, 20, 0, 4),
|
|
||||||
Text = "Cartella di lavoro: qui vengono scritti il CSV della distinta e la "
|
|
||||||
+ "cartella 'raw' (gli STEP esportati con Creo vanno invece in 'step', "
|
|
||||||
+ "dentro la stessa cartella). Proposta automaticamente (ricordata da un "
|
|
||||||
+ "avvio all'altro), modificabile con 'Cambia cartella...'.",
|
|
||||||
};
|
|
||||||
contenuto.Controls.Add(lblIstruzioniCartella);
|
|
||||||
|
|
||||||
_lblCartellaLavoro.Dock = DockStyle.Top;
|
|
||||||
_lblCartellaLavoro.AutoSize = true;
|
|
||||||
_lblCartellaLavoro.ForeColor = SystemColors.GrayText;
|
|
||||||
_lblCartellaLavoro.Margin = new Padding(0, 0, 0, 4);
|
|
||||||
contenuto.Controls.Add(_lblCartellaLavoro);
|
|
||||||
|
|
||||||
_btnCambiaCartella.Text = "Cambia cartella...";
|
|
||||||
UiStile.Secondario(_btnCambiaCartella);
|
|
||||||
_btnCambiaCartella.Click += (_, _) => ScegliCartellaLavoro();
|
|
||||||
contenuto.Controls.Add(_btnCambiaCartella);
|
|
||||||
|
|
||||||
var lblManutenzione = new Label
|
|
||||||
{
|
|
||||||
Dock = DockStyle.Top,
|
|
||||||
AutoSize = true,
|
|
||||||
ForeColor = SystemColors.GrayText,
|
|
||||||
Margin = new Padding(0, 20, 0, 8),
|
|
||||||
Text = "Manutenzione: svuota le cartelle 'raw' e 'step' nella cartella di lavoro "
|
|
||||||
+ "prima di un nuovo giro (non tocca l'indice, solo le copie locali).",
|
|
||||||
};
|
|
||||||
contenuto.Controls.Add(lblManutenzione);
|
|
||||||
|
|
||||||
_btnPulisci.Text = "Pulisci raw e step";
|
|
||||||
UiStile.Secondario(_btnPulisci);
|
|
||||||
_btnPulisci.Click += (_, _) => PulisciRawEStep();
|
|
||||||
contenuto.Controls.Add(_btnPulisci);
|
|
||||||
|
|
||||||
var panel = new Panel();
|
var panel = new Panel();
|
||||||
panel.Controls.Add(contenuto);
|
panel.Controls.Add(contenuto);
|
||||||
@@ -397,20 +470,86 @@ public partial class Form1 : Form
|
|||||||
};
|
};
|
||||||
contenuto.Controls.Add(lblIstruzioni);
|
contenuto.Controls.Add(lblIstruzioni);
|
||||||
|
|
||||||
|
var rigaModalita = new FlowLayoutPanel
|
||||||
|
{
|
||||||
|
AutoSize = true,
|
||||||
|
FlowDirection = FlowDirection.LeftToRight,
|
||||||
|
Margin = new Padding(0, 0, 0, 8),
|
||||||
|
};
|
||||||
|
rigaModalita.Controls.Add(_rbFile);
|
||||||
|
rigaModalita.Controls.Add(_rbCustom);
|
||||||
|
_rbCustom.CheckedChanged += (_, _) => CambiaModalitaDistinta();
|
||||||
|
contenuto.Controls.Add(rigaModalita);
|
||||||
|
|
||||||
|
_rigaCustom.AutoSize = true;
|
||||||
|
_rigaCustom.FlowDirection = FlowDirection.LeftToRight;
|
||||||
|
_rigaCustom.Margin = new Padding(0, 0, 0, 8);
|
||||||
|
_rigaCustom.Visible = false;
|
||||||
|
var btnAggiungi = new Button { Text = "Aggiungi", Margin = new Padding(0, 0, 8, 0) };
|
||||||
|
UiStile.Secondario(btnAggiungi);
|
||||||
|
btnAggiungi.Click += (_, _) => AggiungiCodice();
|
||||||
|
_txtCodice.KeyDown += (_, e) =>
|
||||||
|
{
|
||||||
|
if (e.KeyCode != Keys.Enter)
|
||||||
|
return;
|
||||||
|
e.SuppressKeyPress = true;
|
||||||
|
AggiungiCodice();
|
||||||
|
};
|
||||||
|
_rigaCustom.Controls.Add(_txtCodice);
|
||||||
|
_rigaCustom.Controls.Add(btnAggiungi);
|
||||||
|
_rigaCustom.Controls.Add(new Label
|
||||||
|
{
|
||||||
|
AutoSize = true,
|
||||||
|
ForeColor = SystemColors.GrayText,
|
||||||
|
Margin = new Padding(0, 9, 0, 0),
|
||||||
|
Text = "Digita un codice e premi Invio (Canc sulla riga per toglierlo)",
|
||||||
|
});
|
||||||
|
contenuto.Controls.Add(_rigaCustom);
|
||||||
|
_gridImport.KeyDown += (_, e) =>
|
||||||
|
{
|
||||||
|
if (e.KeyCode == Keys.Delete && _rbCustom.Checked)
|
||||||
|
RimuoviCodiceSelezionato();
|
||||||
|
};
|
||||||
|
|
||||||
|
var rigaImporta = _rigaFile;
|
||||||
|
rigaImporta.AutoSize = true;
|
||||||
|
rigaImporta.FlowDirection = FlowDirection.LeftToRight;
|
||||||
|
rigaImporta.Margin = new Padding(0, 0, 0, 8);
|
||||||
var btnSfoglia = new Button
|
var btnSfoglia = new Button
|
||||||
{
|
{
|
||||||
Text = "Scegli file distinta...",
|
Text = "Scegli file distinta...",
|
||||||
Margin = new Padding(0, 0, 0, 8),
|
Margin = new Padding(0, 0, 8, 0),
|
||||||
};
|
};
|
||||||
UiStile.Secondario(btnSfoglia);
|
UiStile.Secondario(btnSfoglia);
|
||||||
btnSfoglia.Click += (_, _) => ScegliFileDistinta();
|
btnSfoglia.Click += (_, _) => ScegliFileDistinta();
|
||||||
contenuto.Controls.Add(btnSfoglia);
|
rigaImporta.Controls.Add(btnSfoglia);
|
||||||
|
rigaImporta.Controls.Add(new Label
|
||||||
|
{
|
||||||
|
AutoSize = true,
|
||||||
|
ForeColor = SystemColors.GrayText,
|
||||||
|
Margin = new Padding(0, 9, 0, 0),
|
||||||
|
Text = "1. Carica la distinta",
|
||||||
|
});
|
||||||
|
contenuto.Controls.Add(rigaImporta);
|
||||||
|
|
||||||
|
_rigaVerifica.AutoSize = true;
|
||||||
|
_rigaVerifica.FlowDirection = FlowDirection.LeftToRight;
|
||||||
|
_rigaVerifica.Margin = new Padding(0, 0, 0, 8);
|
||||||
|
_rigaVerifica.Visible = false; // compare solo dopo un import riuscito (file o drag&drop)
|
||||||
|
|
||||||
_btnVerifica.Text = "Verifica presenza";
|
_btnVerifica.Text = "Verifica presenza";
|
||||||
_btnVerifica.Margin = new Padding(0, 0, 0, 8);
|
_btnVerifica.Margin = new Padding(0, 0, 8, 0);
|
||||||
UiStile.Secondario(_btnVerifica);
|
UiStile.Secondario(_btnVerifica);
|
||||||
_btnVerifica.Click += (_, _) => VerificaCodiciNelDb();
|
_btnVerifica.Click += (_, _) => VerificaCodiciNelDb();
|
||||||
contenuto.Controls.Add(_btnVerifica);
|
_rigaVerifica.Controls.Add(_btnVerifica);
|
||||||
|
_rigaVerifica.Controls.Add(new Label
|
||||||
|
{
|
||||||
|
AutoSize = true,
|
||||||
|
ForeColor = SystemColors.GrayText,
|
||||||
|
Margin = new Padding(0, 9, 0, 0),
|
||||||
|
Text = "2. Verifica la presenza dei file sul server",
|
||||||
|
});
|
||||||
|
contenuto.Controls.Add(_rigaVerifica);
|
||||||
|
|
||||||
_lblImportStatus.Dock = DockStyle.Top;
|
_lblImportStatus.Dock = DockStyle.Top;
|
||||||
_lblImportStatus.AutoSize = false;
|
_lblImportStatus.AutoSize = false;
|
||||||
@@ -460,22 +599,25 @@ public partial class Form1 : Form
|
|||||||
};
|
};
|
||||||
contenuto.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
|
contenuto.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
|
||||||
|
|
||||||
var lblIstruzioni = new Label
|
var rigaCreaRaw = new FlowLayoutPanel
|
||||||
{
|
{
|
||||||
Dock = DockStyle.Top,
|
|
||||||
AutoSize = true,
|
AutoSize = true,
|
||||||
ForeColor = SystemColors.GrayText,
|
FlowDirection = FlowDirection.LeftToRight,
|
||||||
Margin = new Padding(0, 0, 0, 8),
|
Margin = new Padding(0, 0, 0, 12),
|
||||||
Text = "Cerca nell'indice il percorso CAD di ogni componente importato e copia i "
|
|
||||||
+ "file trovati in una cartella 'raw' dentro la cartella di lavoro (fase 1).",
|
|
||||||
};
|
};
|
||||||
contenuto.Controls.Add(lblIstruzioni);
|
|
||||||
|
|
||||||
_btnCreaRaw.Text = "Crea cartella raw";
|
_btnCreaRaw.Text = "Crea cartella raw";
|
||||||
_btnCreaRaw.Margin = new Padding(0, 0, 0, 12);
|
_btnCreaRaw.Margin = new Padding(0, 0, 8, 0);
|
||||||
UiStile.Primario(_btnCreaRaw);
|
UiStile.Primario(_btnCreaRaw);
|
||||||
_btnCreaRaw.Click += (_, _) => CreaCartellaRaw();
|
_btnCreaRaw.Click += (_, _) => CreaCartellaRaw();
|
||||||
contenuto.Controls.Add(_btnCreaRaw);
|
rigaCreaRaw.Controls.Add(_btnCreaRaw);
|
||||||
|
rigaCreaRaw.Controls.Add(new Label
|
||||||
|
{
|
||||||
|
AutoSize = true,
|
||||||
|
ForeColor = SystemColors.GrayText,
|
||||||
|
Margin = new Padding(0, 9, 0, 0),
|
||||||
|
Text = "Copia i file dal server nella cartella 'raw'.",
|
||||||
|
});
|
||||||
|
contenuto.Controls.Add(rigaCreaRaw);
|
||||||
|
|
||||||
_gridRaw.Dock = DockStyle.Fill;
|
_gridRaw.Dock = DockStyle.Fill;
|
||||||
_gridRaw.ReadOnly = true;
|
_gridRaw.ReadOnly = true;
|
||||||
@@ -526,8 +668,7 @@ public partial class Form1 : Form
|
|||||||
Margin = new Padding(0, 0, 0, 12),
|
Margin = new Padding(0, 0, 0, 12),
|
||||||
Text = "Gli assiemi referenziano altri PRT e sotto-assiemi al loro interno: premi "
|
Text = "Gli assiemi referenziano altri PRT e sotto-assiemi al loro interno: premi "
|
||||||
+ "qui per risolvere l'albero componenti (ricorsivamente) e copiare tutto ciò che "
|
+ "qui per risolvere l'albero componenti (ricorsivamente) e copiare tutto ciò che "
|
||||||
+ "viene trovato nell'indice dentro 'raw'. Passo opzionale: se la distinta contiene "
|
+ "viene trovato nell'indice dentro 'raw'.",
|
||||||
+ "solo parti singole puoi anche passare direttamente alla fase successiva.",
|
|
||||||
};
|
};
|
||||||
contenuto.Controls.Add(lblIstruzioni);
|
contenuto.Controls.Add(lblIstruzioni);
|
||||||
|
|
||||||
@@ -614,10 +755,15 @@ public partial class Form1 : Form
|
|||||||
|
|
||||||
private void ImportaDistinta(string percorso)
|
private void ImportaDistinta(string percorso)
|
||||||
{
|
{
|
||||||
|
if (_rbCustom.Checked)
|
||||||
|
{
|
||||||
|
_rbFile.Checked = true; // chiede conferma se c'è una lista; se rifiuta resta in custom
|
||||||
|
if (_rbCustom.Checked)
|
||||||
|
return;
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (csv, righe) = DistintaBaseImporter.ConvertiInCsv(percorso, _cartellaLavoro!);
|
var (_, righe) = DistintaBaseImporter.ConvertiInCsv(percorso, _cartellaLavoro!);
|
||||||
_percorsoInputCorrente = percorso;
|
|
||||||
_righeCorrenti = righe;
|
_righeCorrenti = righe;
|
||||||
|
|
||||||
_gridImport.Rows.Clear();
|
_gridImport.Rows.Clear();
|
||||||
@@ -629,14 +775,15 @@ public partial class Form1 : Form
|
|||||||
_ultimaCartellaRaw = null;
|
_ultimaCartellaRaw = null;
|
||||||
_ultimaVerifica = null;
|
_ultimaVerifica = null;
|
||||||
|
|
||||||
ImpostaStatoImport($"CSV generato ({righe.Count} righe): {csv}", Color.DarkGreen);
|
ImpostaStatoImport("", Color.Black);
|
||||||
|
_rigaVerifica.Visible = true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_percorsoInputCorrente = null;
|
|
||||||
_righeCorrenti = null;
|
_righeCorrenti = null;
|
||||||
_gridImport.Rows.Clear();
|
_gridImport.Rows.Clear();
|
||||||
ImpostaStatoImport($"Errore durante la conversione: {ex.Message}", Color.DarkRed);
|
ImpostaStatoImport($"Errore durante la conversione: {ex.Message}", Color.DarkRed);
|
||||||
|
_rigaVerifica.Visible = false;
|
||||||
}
|
}
|
||||||
AggiornaStatoAvanti();
|
AggiornaStatoAvanti();
|
||||||
}
|
}
|
||||||
@@ -660,15 +807,20 @@ public partial class Form1 : Form
|
|||||||
foreach (DataGridViewRow riga in _gridImport.Rows)
|
foreach (DataGridViewRow riga in _gridImport.Rows)
|
||||||
{
|
{
|
||||||
string componente = (string)riga.Cells["componente"].Value!;
|
string componente = (string)riga.Cells["componente"].Value!;
|
||||||
riga.DefaultCellStyle.BackColor = trovati.ContainsKey(componente)
|
var colore = trovati.ContainsKey(componente)
|
||||||
? Color.FromArgb(198, 239, 206)
|
? Color.FromArgb(198, 239, 206)
|
||||||
: Color.FromArgb(255, 199, 206);
|
: Color.FromArgb(255, 199, 206);
|
||||||
|
// la selezione mantiene il colore verde/rosso, altrimenti il blu nasconde l'esito
|
||||||
|
riga.DefaultCellStyle.BackColor = colore;
|
||||||
|
riga.DefaultCellStyle.SelectionBackColor = colore;
|
||||||
|
riga.DefaultCellStyle.SelectionForeColor = Color.Black;
|
||||||
}
|
}
|
||||||
|
AggiornaStatoAvanti();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreaCartellaRaw()
|
private void CreaCartellaRaw()
|
||||||
{
|
{
|
||||||
if (_percorsoInputCorrente is null || _righeCorrenti is null)
|
if (_righeCorrenti is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// L'assieme generale è già una riga di _righeCorrenti (aggiunta da
|
// L'assieme generale è già una riga di _righeCorrenti (aggiunta da
|
||||||
@@ -707,7 +859,8 @@ public partial class Form1 : Form
|
|||||||
.ToList();
|
.ToList();
|
||||||
if (cartelle.Count == 0)
|
if (cartelle.Count == 0)
|
||||||
{
|
{
|
||||||
ImpostaStatoImport($"Nessuna cartella 'raw' o 'step' trovata in {radice}.", Color.DarkRed);
|
MessageBox.Show(this, $"Nessuna cartella 'raw' o 'step' trovata in {radice}.",
|
||||||
|
"Pulisci raw e step", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,7 +887,49 @@ public partial class Form1 : Form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImpostaStatoImport($"{eliminati} file eliminati da {string.Join(" e ", cartelle)}.", Color.DarkGreen);
|
MessageBox.Show(this, $"{eliminati} file eliminati da {string.Join(" e ", cartelle)}.",
|
||||||
|
"Pulisci raw e step", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Elimina i CSV generati dall'import delle distinte (fase 2), che altrimenti si
|
||||||
|
/// accumulano uno per distinta nella cartella di lavoro. Solo i *.csv direttamente nella
|
||||||
|
/// cartella di lavoro, non in sottocartelle; i file originali del gestionale (.xls) restano.</summary>
|
||||||
|
private void EliminaCsv()
|
||||||
|
{
|
||||||
|
string radice = _cartellaLavoro!;
|
||||||
|
var files = Directory.Exists(radice) ? Directory.GetFiles(radice, "*.csv") : [];
|
||||||
|
if (files.Length == 0)
|
||||||
|
{
|
||||||
|
MessageBox.Show(this, $"Nessun file CSV trovato in {radice}.",
|
||||||
|
"Elimina CSV", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nomi = files.Select(Path.GetFileName).Take(15).ToList();
|
||||||
|
if (files.Length > nomi.Count)
|
||||||
|
nomi.Add($"... e altri {files.Length - nomi.Count}");
|
||||||
|
var conferma = MessageBox.Show(this,
|
||||||
|
$"Eliminare {files.Length} file CSV da {radice}?\n\n{string.Join("\n", nomi)}\n\nL'operazione non è reversibile.",
|
||||||
|
"Elimina CSV", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
||||||
|
if (conferma != DialogResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int eliminati = 0;
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
eliminati++;
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
// ponytail: file in uso (es. aperto in Excel) -> si salta, non blocca gli altri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBox.Show(this, $"{eliminati} file CSV eliminati da {radice}.",
|
||||||
|
"Elimina CSV", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Tutti i codici prt/asm noti all'indice, con i relativi percorsi (ultima
|
/// <summary>Tutti i codici prt/asm noti all'indice, con i relativi percorsi (ultima
|
||||||
@@ -1097,21 +1292,19 @@ public partial class Form1 : Form
|
|||||||
|
|
||||||
private static readonly string CartellaImpostazioni = Path.Combine(
|
private static readonly string CartellaImpostazioni = Path.Combine(
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "PrepStep");
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "PrepStep");
|
||||||
private static readonly string PercorsoImpostazioni = Path.Combine(CartellaImpostazioni, "ultimo_db.txt");
|
|
||||||
private static readonly string PercorsoImpostazioniCartella = Path.Combine(CartellaImpostazioni, "ultima_cartella_lavoro.txt");
|
private static readonly string PercorsoImpostazioniCartella = Path.Combine(CartellaImpostazioni, "ultima_cartella_lavoro.txt");
|
||||||
|
|
||||||
/// <summary>Propone l'ultima cartella di lavoro usata (se ancora esiste su disco) o, se non
|
/// <summary>Propone l'ultima cartella di lavoro usata (se ancora esiste su disco) o, se non
|
||||||
/// c'è ancora una preferenza salvata (o punta a una cartella cancellata), la cartella dove si
|
/// c'è ancora una preferenza salvata (o punta a una cartella cancellata), la cartella di
|
||||||
/// trova il DB (più sensata dell'eseguibile: quella è dove l'utente tiene il proprio lavoro,
|
/// installazione (%LOCALAPPDATA%\Programs\PrepStep col setup). Sempre modificabile con
|
||||||
/// non dov'è installata la app). Richiede quindi che InizializzaDatabase sia già stata
|
/// "File → Scegli cartella di lavoro...".</summary>
|
||||||
/// chiamata. Sempre modificabile con "Cambia cartella..." in fase 1.</summary>
|
|
||||||
private void InizializzaCartellaLavoro()
|
private void InizializzaCartellaLavoro()
|
||||||
{
|
{
|
||||||
var ultima = LeggiUltimaCartellaLavoro();
|
var ultima = LeggiUltimaCartellaLavoro();
|
||||||
_cartellaLavoro = ultima is not null && Directory.Exists(ultima)
|
_cartellaLavoro = ultima is not null && Directory.Exists(ultima)
|
||||||
? ultima
|
? ultima
|
||||||
: (_dbPath is not null ? Path.GetDirectoryName(_dbPath) : null) ?? AppContext.BaseDirectory;
|
: AppContext.BaseDirectory;
|
||||||
AggiornaLabelCartellaLavoro();
|
AggiornaStatoAvanti();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScegliCartellaLavoro()
|
private void ScegliCartellaLavoro()
|
||||||
@@ -1126,13 +1319,10 @@ public partial class Form1 : Form
|
|||||||
|
|
||||||
string percorso = dialog.SelectedPath;
|
string percorso = dialog.SelectedPath;
|
||||||
_cartellaLavoro = percorso;
|
_cartellaLavoro = percorso;
|
||||||
AggiornaLabelCartellaLavoro();
|
|
||||||
SalvaUltimaCartellaLavoro(percorso);
|
SalvaUltimaCartellaLavoro(percorso);
|
||||||
AggiornaStatoAvanti();
|
AggiornaStatoAvanti();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AggiornaLabelCartellaLavoro() => _lblCartellaLavoro.Text = $"Cartella di lavoro: {_cartellaLavoro}";
|
|
||||||
|
|
||||||
private static string? LeggiUltimaCartellaLavoro()
|
private static string? LeggiUltimaCartellaLavoro()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -1160,24 +1350,21 @@ public partial class Form1 : Form
|
|||||||
|
|
||||||
private void InizializzaDatabase()
|
private void InizializzaDatabase()
|
||||||
{
|
{
|
||||||
var candidati = new[]
|
// Il DB sta sempre nella cartella di installazione; "Apri DB..." ne carica un altro
|
||||||
{
|
// solo per la sessione corrente, senza ricordarlo al prossimo avvio.
|
||||||
LeggiUltimoDbUsato(),
|
string predefinito = Path.Combine(AppContext.BaseDirectory, "cad_index.db");
|
||||||
Path.Combine(AppContext.BaseDirectory, "cad_index.db"),
|
if (!File.Exists(predefinito))
|
||||||
Path.Combine(Directory.GetCurrentDirectory(), "cad_index.db"),
|
|
||||||
};
|
|
||||||
_dbPath = candidati.FirstOrDefault(p => p is not null && File.Exists(p));
|
|
||||||
|
|
||||||
if (_dbPath is null)
|
|
||||||
{
|
{
|
||||||
_lblStatus.Text = "Database non trovato. Usa 'Apri DB...' per selezionarlo oppure 'Aggiorna indice' per crearlo.";
|
_lblStatus.Text = "Database non trovato. Usa 'Apri DB...' per selezionarlo oppure 'Aggiorna indice' per crearlo.";
|
||||||
_lblDb.Text = "Nessun database caricato. Premi 'Aggiorna indice' per crearlo da X:\\, oppure 'Apri DB...' se ne hai gi\u00e0 uno.";
|
_lblDb.Text = "Nessun database caricato. Premi 'Aggiorna indice' per crearlo da X:\\, oppure 'Apri DB...' se ne hai gi\u00e0 uno.";
|
||||||
_lblUltimoAggiornamento.Text = "";
|
_lblUltimoAggiornamento.Text = "";
|
||||||
|
_dbCaricato = false;
|
||||||
|
_indicatoreDb.Invalidate();
|
||||||
AggiornaStatoAvanti();
|
AggiornaStatoAvanti();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CaricaDatabase(_dbPath);
|
CaricaDatabase(predefinito);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScegliDatabase()
|
private void ScegliDatabase()
|
||||||
@@ -1210,43 +1397,17 @@ public partial class Form1 : Form
|
|||||||
? $"Ultimo aggiornamento indice: {ultimaScansione.ToLocalTime():dd/MM/yyyy HH:mm}"
|
? $"Ultimo aggiornamento indice: {ultimaScansione.ToLocalTime():dd/MM/yyyy HH:mm}"
|
||||||
: "Ultimo aggiornamento indice: sconosciuto";
|
: "Ultimo aggiornamento indice: sconosciuto";
|
||||||
_lblStatus.Text = "Pronto.";
|
_lblStatus.Text = "Pronto.";
|
||||||
SalvaUltimoDbUsato(path);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
MessageBox.Show(this, $"Impossibile aprire il database:\n{ex.Message}", "Errore",
|
MessageBox.Show(this, $"Impossibile aprire il database:\n{ex.Message}", "Errore",
|
||||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
|
_dbCaricato = _dbPath is not null;
|
||||||
|
_indicatoreDb.Invalidate();
|
||||||
AggiornaStatoAvanti();
|
AggiornaStatoAvanti();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Ricorda l'ultimo DB caricato con successo, così al prossimo avvio si
|
|
||||||
/// ricarica automaticamente anche se l'eseguibile gira da una cartella diversa.</summary>
|
|
||||||
private static string? LeggiUltimoDbUsato()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return File.Exists(PercorsoImpostazioni) ? File.ReadAllText(PercorsoImpostazioni).Trim() : null;
|
|
||||||
}
|
|
||||||
catch (IOException)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SalvaUltimoDbUsato(string path)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(CartellaImpostazioni);
|
|
||||||
File.WriteAllText(PercorsoImpostazioni, path);
|
|
||||||
}
|
|
||||||
catch (IOException)
|
|
||||||
{
|
|
||||||
// ponytail: non critico se non si riesce a salvare, si torna a chiedere "Apri DB..." la prossima volta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void AggiornaIndice()
|
private async void AggiornaIndice()
|
||||||
{
|
{
|
||||||
string dbPath = _dbPath ?? Path.Combine(AppContext.BaseDirectory, "cad_index.db");
|
string dbPath = _dbPath ?? Path.Combine(AppContext.BaseDirectory, "cad_index.db");
|
||||||
|
|||||||
+310
-49
@@ -1,70 +1,239 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace PrepStep;
|
namespace PrepStep;
|
||||||
|
|
||||||
/// <summary>Guida interattiva richiamabile da "Aiuto ▸ Guida" (F1): sintesi delle 5 fasi del
|
/// <summary>Guida interattiva richiamabile da "Aiuto ▸ Guida" (F1): stessa guida completa di
|
||||||
/// wizard, per chi apre l'app senza aver letto i docs. Il dettaglio completo (formato .asm,
|
/// docs/guida-utente.md (passo-passo fino a Creo Distributed Batch), pensata per chi apre
|
||||||
/// schema DB, ecc.) resta solo in docs/architettura.md e affini, per non duplicarlo qui.</summary>
|
/// l'app senza aver letto i docs e senza esperienza pregressa. Il dettaglio tecnico (formato
|
||||||
|
/// .asm, schema DB, ecc.) resta solo in docs/architettura.md e affini, per non duplicarlo qui.
|
||||||
|
///
|
||||||
|
/// Gerarchia tipografica nel pannello di lettura (vedi ScriviSezione): titolo sezione in blu
|
||||||
|
/// primario/grassetto/14pt, corpo in grigio quasi-nero/10pt, passi numerati con il numero in
|
||||||
|
/// blu grassetto per farli scorrere a colpo d'occhio, callout con sfondo tinta piatta (nessun
|
||||||
|
/// bordo/ombra: RichTextBox non li supporta) — blu tenue per le note informative, ambra tenue
|
||||||
|
/// per gli avvisi importanti, con etichetta ("Nota"/"Attenzione") in grassetto a inizio blocco
|
||||||
|
/// invece di un colore isolato, così restano leggibili anche senza vedere i colori.</summary>
|
||||||
public class GuidaForm : Form
|
public class GuidaForm : Form
|
||||||
{
|
{
|
||||||
private static readonly (string Titolo, string Testo)[] Sezioni =
|
private sealed record Sezione(
|
||||||
{
|
string Titolo,
|
||||||
("Panoramica", """
|
string Intro,
|
||||||
Cevolani StepPrep prepara i file CAD Pro-ENGINEER/Creo da passare a Creo
|
string[]? Passi = null,
|
||||||
Distributed Batch per l'esportazione STEP: indicizza i file di una share di
|
string? NotaTesto = null,
|
||||||
rete e guida attraverso 5 fasi, una alla volta, con "Avanti"/"Indietro".
|
bool NotaAttenzione = false);
|
||||||
|
|
||||||
"File ▸ Nuova estrazione" (Ctrl+N) azzera distinta/raw/verifica per
|
// Palette: blu primario dell'app per titoli/passi, ambra tenue solo per gli avvisi che
|
||||||
ripartire con una nuova distinta base, senza dover ricaricare il database.
|
// possono far perdere tempo se ignorati (es. l'errore Creo [-4]) — il rosso resta riservato
|
||||||
"""),
|
// ai messaggi di errore veri e propri altrove nell'app.
|
||||||
("1. Database", """
|
private static readonly Color ColoreTesto = Color.FromArgb(40, 40, 40);
|
||||||
Carica o aggiorna l'indice "cad_index.db" a partire dalle cartelle di primo
|
private static readonly Color ColoreNotaInfoSfondo = Color.FromArgb(232, 240, 250);
|
||||||
livello del disco/share (radice di default X:\). Qui si sceglie anche la
|
private static readonly Color ColoreNotaInfoTesto = Color.FromArgb(15, 65, 120);
|
||||||
cartella di lavoro, dove finiscono CSV, "raw" e "step".
|
private static readonly Color ColoreNotaAttenzioneSfondo = Color.FromArgb(253, 240, 219);
|
||||||
"""),
|
private static readonly Color ColoreNotaAttenzioneTesto = Color.FromArgb(140, 90, 10);
|
||||||
("2. Importa Distinta Base", """
|
|
||||||
Converte il file della distinta (trascinabile nella finestra) in un CSV
|
private static readonly Sezione[] Sezioni =
|
||||||
componente;descrizione nella cartella di lavoro. Se il file segue la
|
{
|
||||||
convenzione del gestionale, aggiunge anche l'assieme generale come riga.
|
new("Panoramica",
|
||||||
"""),
|
"PrepStep - Cevolani prepara i file CAD Pro-ENGINEER/Creo da passare a Creo " +
|
||||||
("3. Crea cartella raw", """
|
"Distributed Batch per l'esportazione STEP: indicizza i file di una share di rete " +
|
||||||
Cerca nell'indice i file CAD dei componenti del CSV e li copia nella
|
"e guida attraverso 5 fasi, una alla volta, con \"Avanti\"/\"Indietro\".",
|
||||||
sottocartella "raw" della cartella di lavoro.
|
NotaTesto: "\"File ▸ Nuovo\" (Ctrl+N) azzera lo stato di distinta/raw/verifica per ripartire " +
|
||||||
"""),
|
"con una nuova distinta base, senza dover ricaricare il database."),
|
||||||
("4. Trova componenti referenziati", """
|
|
||||||
Legge i file .asm già in "raw" per risolvere ricorsivamente sotto-assiemi e
|
new("Dove finiscono i file",
|
||||||
parti citate, copiando anche quelli in "raw": componenti soppressi (non in
|
"Tutto sta nella cartella di lavoro, che di default è la cartella di " +
|
||||||
distinta ma necessari per aprire l'assieme) e riferimenti esterni (assembly
|
"installazione (%LOCALAPPDATA%\\Programs\\PrepStep):",
|
||||||
feature, datum copiati) inclusi.
|
Passi:
|
||||||
"""),
|
[
|
||||||
("5. Verifica completezza", """
|
"Il database cad_index.db sta sempre nella cartella di installazione.",
|
||||||
Mostra i file .asm della distinta già in "raw", trascinabili su Creo per
|
"Il CSV di ogni distinta importata, \"raw\" (file CAD delle fasi 3-4) e " +
|
||||||
l'esportazione STEP (passo manuale, fuori da quest'app). "Verifica
|
"\"step\" (STEP esportati da Creo, cartella che indichi tu a Creo) stanno " +
|
||||||
completezza assiemi" controlla che tutti i sotto-componenti risultino
|
"nella cartella di lavoro. Il file .xls del gestionale resta dove l'hai " +
|
||||||
presenti nell'indice.
|
"preso.",
|
||||||
"""),
|
"Per cambiare la cartella di lavoro: \"File ▸ Scegli cartella di lavoro...\" " +
|
||||||
|
"(viene ricordata agli avvii successivi).",
|
||||||
|
"Per fare pulizia: \"File ▸ Pulisci raw e step...\" svuota \"raw\" e " +
|
||||||
|
"\"step\"; \"File ▸ Elimina CSV delle distinte...\" elimina i CSV, che " +
|
||||||
|
"altrimenti si accumulano uno per distinta. Nessuno dei due tocca il " +
|
||||||
|
"database.",
|
||||||
|
],
|
||||||
|
NotaTesto: "AppData è una cartella nascosta in Esplora file: per aprirla scrivi " +
|
||||||
|
"%LOCALAPPDATA%\\Programs\\PrepStep nella barra degli indirizzi (vale anche " +
|
||||||
|
"per scegliere \"step\" in Creo). Disinstallando il programma questi file " +
|
||||||
|
"restano: cancellali a mano se non servono più."),
|
||||||
|
|
||||||
|
new("Prima di iniziare",
|
||||||
|
"Prima di importare la prima distinta:",
|
||||||
|
Passi:
|
||||||
|
[
|
||||||
|
"Serve accesso alla share di rete con i file CAD (di default X:\\).",
|
||||||
|
"Serve l'estrazione \"Distinta Base\" da Fata (il gestionale): il file .xls " +
|
||||||
|
"così come Fata lo esporta, senza risalvarlo o convertirlo in altri " +
|
||||||
|
"formati — l'app riconosce da sola se sotto l'estensione .xls c'è XML o " +
|
||||||
|
"Excel binario vero.",
|
||||||
|
"Se il file segue la convenzione di nome DBP_<codice>_<timestamp>, l'app " +
|
||||||
|
"aggiunge da sola l'assieme generale come componente in più: non serve " +
|
||||||
|
"fare nulla di diverso.",
|
||||||
|
"Se non vuoi usare la cartella di installazione, scegli la cartella di " +
|
||||||
|
"lavoro (\"File ▸ Scegli cartella di lavoro...\") prima di importare la " +
|
||||||
|
"distinta: è lì che finiscono CSV, \"raw\" e, dopo l'esportazione con " +
|
||||||
|
"Creo, \"step\".",
|
||||||
|
"Se la cartella di lavoro è già stata usata per un'altra distinta, conviene " +
|
||||||
|
"pulire \"raw\" e \"step\" prima di ripartire (\"File ▸ Pulisci raw e " +
|
||||||
|
"step...\"): \"File ▸ Nuovo\" azzera solo lo stato dell'app, non i file " +
|
||||||
|
"già copiati su disco.",
|
||||||
|
]),
|
||||||
|
|
||||||
|
new("1. Database",
|
||||||
|
"All'avvio l'app carica da sola \"cad_index.db\" dalla cartella di installazione. " +
|
||||||
|
"Un pallino verde/rosso indica se un database è caricato.",
|
||||||
|
Passi:
|
||||||
|
[
|
||||||
|
"Pallino rosso (nessun database): premi \"Aggiorna indice\" per scansionare " +
|
||||||
|
"le cartelle di primo livello della share (X:\\) e crearlo. La prima volta " +
|
||||||
|
"richiede qualche minuto, i giri successivi aggiornano solo quello che è " +
|
||||||
|
"cambiato.",
|
||||||
|
"Database da un'altra postazione: \"Apri DB...\" lo carica solo finché non " +
|
||||||
|
"chiudi il programma. Per usarlo sempre, copialo nella cartella di " +
|
||||||
|
"installazione come cad_index.db.",
|
||||||
|
]),
|
||||||
|
|
||||||
|
new("2. Importa Distinta Base",
|
||||||
|
"Due passi in sequenza:",
|
||||||
|
Passi:
|
||||||
|
[
|
||||||
|
"Carica la distinta: trascina il file .xls nella finestra, oppure premi " +
|
||||||
|
"\"Scegli file distinta...\". Le righe importate compaiono nella griglia " +
|
||||||
|
"(Componente/Descrizione) e viene scritto un CSV nella cartella di lavoro. " +
|
||||||
|
"In alternativa scegli \"Lista personalizzata\" e digita i codici uno alla " +
|
||||||
|
"volta (Invio per aggiungere, Canc per togliere): nessun CSV, l'assieme " +
|
||||||
|
"generale va inserito a mano.",
|
||||||
|
"Verifica la presenza dei file sul server: compare solo dopo un import " +
|
||||||
|
"riuscito. Cerca ogni componente nel database — le righe della griglia si " +
|
||||||
|
"colorano di verde (trovato) o rosso (non trovato nell'indice).",
|
||||||
|
],
|
||||||
|
NotaTesto: "\"Avanti\" si sblocca solo dopo il passo 2, non basta il solo import: " +
|
||||||
|
"è il modo per accorgersi subito se il database è disallineato rispetto alla " +
|
||||||
|
"share prima di andare avanti.",
|
||||||
|
NotaAttenzione: true),
|
||||||
|
|
||||||
|
new("3. Crea cartella raw",
|
||||||
|
"Premi \"Crea cartella raw\": per ogni componente della distinta trovato " +
|
||||||
|
"nell'indice, l'app ne copia il file CAD dalla share nella cartella \"raw\" " +
|
||||||
|
"(dentro la cartella di lavoro). La griglia mostra il percorso CAD copiato per " +
|
||||||
|
"ogni componente."),
|
||||||
|
|
||||||
|
new("4. Trova componenti referenziati",
|
||||||
|
"Gli assiemi (.asm) referenziano altri file (sotto-assiemi, parti, componenti " +
|
||||||
|
"soppressi, riferimenti esterni) che Creo deve poter caricare insieme " +
|
||||||
|
"all'assieme principale, anche se non compaiono nella distinta del gestionale. " +
|
||||||
|
"Premi \"Trova componenti referenziati\": l'app legge i file .asm già in \"raw\", " +
|
||||||
|
"risolve ricorsivamente tutto l'albero e copia anche quei file in \"raw\".\n\n" +
|
||||||
|
"Si apre una finestra con l'elenco di quanto trovato e copiato, con in fondo " +
|
||||||
|
"eventuali codici non corrispondenti a nessun file indicizzato (possono essere " +
|
||||||
|
"disegni, componenti davvero mancanti, o falsi positivi — vale la pena " +
|
||||||
|
"controllarli). Da lì si può anche aprire direttamente la cartella \"raw\".",
|
||||||
|
NotaTesto: "Passo opzionale se la distinta contiene solo parti singole senza " +
|
||||||
|
"sotto-assiemi: in quel caso si può passare direttamente alla fase successiva."),
|
||||||
|
|
||||||
|
new("5. Verifica completezza",
|
||||||
|
"Mostra i file .asm della distinta già in \"raw\", trascinabili. Da qui il " +
|
||||||
|
"passaggio a Creo è manuale, fuori da quest'app:",
|
||||||
|
Passi:
|
||||||
|
[
|
||||||
|
"Apri Creo Distributed Batch (dal menu Start di Windows, o dal file " +
|
||||||
|
"ptcdbatch.bat nella cartella bin dell'installazione Creo).",
|
||||||
|
"Crea (o riusa, se già presente in azienda) un gruppo di attività per " +
|
||||||
|
"l'esportazione STEP: menu Actions ▸ New Task Group.",
|
||||||
|
"Aggiungi i file: trascina i .asm dalla finestra dell'app (o dalla cartella " +
|
||||||
|
"\"raw\" in Esplora risorse) sul gruppo di attività, oppure sfoglia e " +
|
||||||
|
"selezionali a mano.",
|
||||||
|
"Verifica che il gruppo di attività punti al config.pro/profilo di export " +
|
||||||
|
"STEP giusto (sezione Configuration Files) — di norma è già impostato " +
|
||||||
|
"una volta sola per tutti.",
|
||||||
|
"Avvia l'elaborazione (Start/Run): Distributed Batch legge ogni .asm, con " +
|
||||||
|
"tutto l'albero di sotto-componenti già raccolto nelle fasi 3-4, ed " +
|
||||||
|
"esporta i file STEP nella cartella \"step\" (dentro la cartella di " +
|
||||||
|
"lavoro, accanto a \"raw\").",
|
||||||
|
],
|
||||||
|
NotaTesto: "Se Distributed Batch fallisce con \"[-4] Dati di input non " +
|
||||||
|
"completi\", quasi sempre manca un file nell'albero dei componenti — Creo " +
|
||||||
|
"non dice quale. Torna alla fase 4 e controlla l'elenco dei \"non trovati\": " +
|
||||||
|
"è il punto di partenza per capire cosa recuperare prima di riprovare.",
|
||||||
|
NotaAttenzione: true),
|
||||||
|
|
||||||
|
new("Ripartire da capo",
|
||||||
|
"Quando devi processare un'altra distinta, usa \"File ▸ Nuovo\" (Ctrl+N): svuota " +
|
||||||
|
"le griglie e la verifica delle fasi 2-5 e riporta al passo 2, senza dover " +
|
||||||
|
"ricaricare il database né cambiare la cartella di lavoro. Non cancella nessun " +
|
||||||
|
"file su disco: CSV, \"raw\" e \"step\" restano.",
|
||||||
|
NotaTesto: "Per ripulire le cartelle \"raw\" e \"step\" della cartella di lavoro " +
|
||||||
|
"prima di un nuovo giro (senza toccare l'indice), usa \"File ▸ Pulisci raw e " +
|
||||||
|
"step...\"; per eliminare i CSV che ogni distinta importata lascia nella " +
|
||||||
|
"cartella di lavoro, usa \"File ▸ Elimina CSV delle distinte...\"."),
|
||||||
};
|
};
|
||||||
|
|
||||||
public GuidaForm()
|
public GuidaForm()
|
||||||
{
|
{
|
||||||
Text = "Guida";
|
Text = "Guida";
|
||||||
ClientSize = new Size(640, 420);
|
ClientSize = new Size(1000, 680);
|
||||||
MinimumSize = new Size(480, 320);
|
MinimumSize = new Size(640, 440);
|
||||||
StartPosition = FormStartPosition.CenterParent;
|
StartPosition = FormStartPosition.CenterParent;
|
||||||
Font = new Font("Segoe UI", 9.5f);
|
Font = new Font("Segoe UI", 9.5f);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is IOException or ArgumentException)
|
||||||
|
{
|
||||||
|
// ponytail: nessuna icona nell'exe (es. lanciato da dotnet run) -> resta quella di default
|
||||||
|
}
|
||||||
|
|
||||||
|
var headerPanel = new Panel { Dock = DockStyle.Top, Height = 40, BackColor = SystemColors.ControlLight };
|
||||||
|
var lblHeader = new Label
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
Text = "Guida",
|
||||||
|
TextAlign = ContentAlignment.MiddleLeft,
|
||||||
|
Padding = new Padding(16, 0, 0, 0),
|
||||||
|
Font = new Font(Font, FontStyle.Bold),
|
||||||
|
};
|
||||||
|
headerPanel.Controls.Add(lblHeader);
|
||||||
|
|
||||||
|
// Sidebar disegnata a mano (OwnerDrawFixed) invece della selezione blu di sistema:
|
||||||
|
// stessa identità visiva del resto dell'app (UiStile.ColorePrimario) invece del colore
|
||||||
|
// di selezione standard di Windows, che varia con il tema dell'utente.
|
||||||
var lista = new ListBox
|
var lista = new ListBox
|
||||||
{
|
{
|
||||||
Dock = DockStyle.Left,
|
Dock = DockStyle.Left,
|
||||||
Width = 200,
|
Width = 230,
|
||||||
IntegralHeight = false,
|
IntegralHeight = false,
|
||||||
BorderStyle = BorderStyle.None,
|
BorderStyle = BorderStyle.None,
|
||||||
|
DrawMode = DrawMode.OwnerDrawFixed,
|
||||||
|
ItemHeight = 34,
|
||||||
|
BackColor = UiStile.ColoreSfondoCard,
|
||||||
|
};
|
||||||
|
foreach (var sezione in Sezioni)
|
||||||
|
lista.Items.Add(sezione.Titolo);
|
||||||
|
lista.DrawItem += (_, e) =>
|
||||||
|
{
|
||||||
|
if (e.Index < 0)
|
||||||
|
return;
|
||||||
|
bool selezionato = (e.State & DrawItemState.Selected) != 0;
|
||||||
|
using var sfondo = new SolidBrush(selezionato ? UiStile.ColorePrimario : UiStile.ColoreSfondoCard);
|
||||||
|
e.Graphics.FillRectangle(sfondo, e.Bounds);
|
||||||
|
var colore = selezionato ? Color.White : ColoreTesto;
|
||||||
|
using var testoFont = new Font(lista.Font, selezionato ? FontStyle.Bold : FontStyle.Regular);
|
||||||
|
var rettangoloTesto = new Rectangle(e.Bounds.X + 16, e.Bounds.Y, e.Bounds.Width - 20, e.Bounds.Height);
|
||||||
|
TextRenderer.DrawText(e.Graphics, lista.Items[e.Index].ToString(), testoFont, rettangoloTesto,
|
||||||
|
colore, TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis);
|
||||||
};
|
};
|
||||||
foreach (var (titolo, _) in Sezioni)
|
|
||||||
lista.Items.Add(titolo);
|
|
||||||
|
|
||||||
var testo = new RichTextBox
|
var testo = new TestoSolaLettura
|
||||||
{
|
{
|
||||||
Dock = DockStyle.Fill,
|
Dock = DockStyle.Fill,
|
||||||
ReadOnly = true,
|
ReadOnly = true,
|
||||||
|
TabStop = false,
|
||||||
BorderStyle = BorderStyle.None,
|
BorderStyle = BorderStyle.None,
|
||||||
BackColor = Color.White,
|
BackColor = Color.White,
|
||||||
Font = new Font(Font.FontFamily, 10f),
|
Font = new Font(Font.FontFamily, 10f),
|
||||||
@@ -73,19 +242,111 @@ public class GuidaForm : Form
|
|||||||
lista.SelectedIndexChanged += (_, _) =>
|
lista.SelectedIndexChanged += (_, _) =>
|
||||||
{
|
{
|
||||||
if (lista.SelectedIndex >= 0)
|
if (lista.SelectedIndex >= 0)
|
||||||
testo.Text = Sezioni[lista.SelectedIndex].Testo.Trim();
|
ScriviSezione(testo, Sezioni[lista.SelectedIndex]);
|
||||||
};
|
};
|
||||||
|
|
||||||
var btnChiudi = new Button { Text = "Chiudi", Dock = DockStyle.Bottom, Height = 34 };
|
var (barraChiudi, azioniChiudi) = UiStile.FloatBar();
|
||||||
UiStile.Secondario(btnChiudi);
|
var btnChiudi = new Button { Text = "Chiudi" };
|
||||||
|
UiStile.Primario(btnChiudi);
|
||||||
|
UiStile.Arrotonda(btnChiudi, 10);
|
||||||
btnChiudi.Click += (_, _) => Close();
|
btnChiudi.Click += (_, _) => Close();
|
||||||
|
azioniChiudi.Controls.Add(btnChiudi);
|
||||||
|
CancelButton = btnChiudi;
|
||||||
|
|
||||||
var contenuto = new Panel { Dock = DockStyle.Fill, Padding = new Padding(16) };
|
var contenuto = new Panel { Dock = DockStyle.Fill, Padding = new Padding(24, 20, 24, 20), BackColor = Color.White };
|
||||||
contenuto.Controls.Add(testo);
|
contenuto.Controls.Add(testo);
|
||||||
|
|
||||||
Controls.Add(contenuto);
|
Controls.Add(contenuto);
|
||||||
Controls.Add(lista);
|
Controls.Add(lista);
|
||||||
Controls.Add(btnChiudi);
|
Controls.Add(barraChiudi);
|
||||||
|
Controls.Add(headerPanel);
|
||||||
|
|
||||||
lista.SelectedIndex = 0;
|
lista.SelectedIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Costruisce il pannello di lettura con gerarchia tipografica esplicita: titolo
|
||||||
|
/// (blu primario, grassetto, 14pt), corpo (grigio quasi-nero, 10pt), passi numerati (numero
|
||||||
|
/// in blu grassetto, indentati) e callout finale con sfondo a tinta piatta ed etichetta in
|
||||||
|
/// grassetto — informativo (blu tenue) o di attenzione (ambra tenue). Applica la
|
||||||
|
/// formattazione DOPO aver scritto il testo, selezionando il range appena aggiunto: è il
|
||||||
|
/// solo modo affidabile di formattare i paragrafi in un RichTextBox (impostare
|
||||||
|
/// SelectionFont/Color su una selezione vuota prima di AppendText non è garantito che
|
||||||
|
/// "si attacchi" al testo che segue).</summary>
|
||||||
|
private static void ScriviSezione(RichTextBox box, Sezione sezione)
|
||||||
|
{
|
||||||
|
box.Clear();
|
||||||
|
var titoloFont = new Font(box.Font.FontFamily, 14f, FontStyle.Bold);
|
||||||
|
var corpoFont = new Font(box.Font.FontFamily, 10f, FontStyle.Regular);
|
||||||
|
var corpoGrassetto = new Font(box.Font.FontFamily, 10f, FontStyle.Bold);
|
||||||
|
|
||||||
|
void Scrivi(string testo, Font font, Color colore, int indent = 0, Color? sfondo = null)
|
||||||
|
{
|
||||||
|
int inizio = box.TextLength;
|
||||||
|
box.AppendText(testo);
|
||||||
|
box.Select(inizio, box.TextLength - inizio);
|
||||||
|
box.SelectionFont = font;
|
||||||
|
box.SelectionColor = colore;
|
||||||
|
box.SelectionBackColor = sfondo ?? box.BackColor;
|
||||||
|
box.SelectionIndent = indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scrivi(sezione.Titolo + "\n\n", titoloFont, UiStile.ColorePrimario);
|
||||||
|
Scrivi(sezione.Intro + "\n", corpoFont, ColoreTesto);
|
||||||
|
|
||||||
|
if (sezione.Passi is { Length: > 0 })
|
||||||
|
{
|
||||||
|
Scrivi("\n", corpoFont, ColoreTesto);
|
||||||
|
for (int i = 0; i < sezione.Passi.Length; i++)
|
||||||
|
{
|
||||||
|
Scrivi($"{i + 1}. ", corpoGrassetto, UiStile.ColorePrimario, indent: 8);
|
||||||
|
Scrivi(sezione.Passi[i] + "\n\n", corpoFont, ColoreTesto, indent: 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sezione.NotaTesto is not null)
|
||||||
|
{
|
||||||
|
var sfondo = sezione.NotaAttenzione ? ColoreNotaAttenzioneSfondo : ColoreNotaInfoSfondo;
|
||||||
|
var coloreEtichetta = sezione.NotaAttenzione ? ColoreNotaAttenzioneTesto : ColoreNotaInfoTesto;
|
||||||
|
string etichetta = sezione.NotaAttenzione ? "Attenzione" : "Nota";
|
||||||
|
|
||||||
|
Scrivi("\n", corpoFont, ColoreTesto);
|
||||||
|
Scrivi(" " + etichetta + "\n", corpoGrassetto, coloreEtichetta, indent: 12, sfondo: sfondo);
|
||||||
|
Scrivi(" " + sezione.NotaTesto + " \n", corpoFont, ColoreTesto, indent: 12, sfondo: sfondo);
|
||||||
|
}
|
||||||
|
|
||||||
|
box.SelectionStart = 0;
|
||||||
|
box.SelectionLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>RichTextBox da sola lettura che non si comporta come un editor: niente cursore
|
||||||
|
/// lampeggiante, niente cursore a "I" e niente selezione col mouse. I clic nell'area di testo
|
||||||
|
/// vengono ignorati (così non prende il fuoco e non mostra il caret); la barra di scorrimento
|
||||||
|
/// sta nell'area non-client e resta utilizzabile, la rotella scorre al passaggio del mouse.</summary>
|
||||||
|
private sealed class TestoSolaLettura : RichTextBox
|
||||||
|
{
|
||||||
|
private const int WM_SETFOCUS = 0x0007;
|
||||||
|
private const int WM_SETCURSOR = 0x0020;
|
||||||
|
private const int WM_LBUTTONDOWN = 0x0201;
|
||||||
|
private const int WM_LBUTTONDBLCLK = 0x0203;
|
||||||
|
private const int WM_RBUTTONDOWN = 0x0204;
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool HideCaret(IntPtr hWnd);
|
||||||
|
|
||||||
|
protected override void WndProc(ref Message m)
|
||||||
|
{
|
||||||
|
switch (m.Msg)
|
||||||
|
{
|
||||||
|
case WM_LBUTTONDOWN or WM_LBUTTONDBLCLK or WM_RBUTTONDOWN:
|
||||||
|
return;
|
||||||
|
case WM_SETCURSOR:
|
||||||
|
Cursor.Current = Cursors.Arrow;
|
||||||
|
m.Result = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
base.WndProc(ref m);
|
||||||
|
if (m.Msg == WM_SETFOCUS)
|
||||||
|
HideCaret(Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,10 +8,11 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
|
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
|
||||||
<ApplicationIcon>app.ico</ApplicationIcon>
|
<ApplicationIcon>app.ico</ApplicationIcon>
|
||||||
<AssemblyTitle>Cevolani StepPrep</AssemblyTitle>
|
<AssemblyTitle>PrepStep - Cevolani</AssemblyTitle>
|
||||||
<Product>Cevolani StepPrep</Product>
|
<Product>PrepStep - Cevolani</Product>
|
||||||
<Company>Cevolani</Company>
|
<Company>Cevolani</Company>
|
||||||
<Version>0.1.0</Version>
|
<Copyright>Copyright (c) 2026 Davide Grilli</Copyright>
|
||||||
|
<Version>1.1.0</Version>
|
||||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ public class PrtMancantiForm : Form
|
|||||||
ClientSize = new Size(720, 440);
|
ClientSize = new Size(720, 440);
|
||||||
StartPosition = FormStartPosition.CenterParent;
|
StartPosition = FormStartPosition.CenterParent;
|
||||||
Font = new Font("Segoe UI", 9.5f);
|
Font = new Font("Segoe UI", 9.5f);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is IOException or ArgumentException)
|
||||||
|
{
|
||||||
|
// ponytail: nessuna icona nell'exe (es. lanciato da dotnet run) -> resta quella di default
|
||||||
|
}
|
||||||
|
|
||||||
var lblInfo = new Label
|
var lblInfo = new Label
|
||||||
{
|
{
|
||||||
@@ -56,8 +64,17 @@ public class PrtMancantiForm : Form
|
|||||||
grid.Rows[i].DefaultCellStyle.ForeColor = Color.DarkRed;
|
grid.Rows[i].DefaultCellStyle.ForeColor = Color.DarkRed;
|
||||||
}
|
}
|
||||||
|
|
||||||
var btnApriCartella = new Button { Text = "Apri cartella raw", Dock = DockStyle.Bottom, Height = 34 };
|
var (pannelloAzioni, flowAzioni) = UiStile.FloatBar();
|
||||||
UiStile.Secondario(btnApriCartella);
|
|
||||||
|
var btnChiudi = new Button { Text = "Chiudi" };
|
||||||
|
UiStile.Primario(btnChiudi);
|
||||||
|
UiStile.Arrotonda(btnChiudi, 10);
|
||||||
|
btnChiudi.Click += (_, _) => Close();
|
||||||
|
flowAzioni.Controls.Add(btnChiudi);
|
||||||
|
|
||||||
|
var btnApriCartella = new Button { Text = "Apri cartella raw", Margin = new Padding(8, 0, 0, 0) };
|
||||||
|
UiStile.SecondarioPieno(btnApriCartella);
|
||||||
|
UiStile.Arrotonda(btnApriCartella, 10);
|
||||||
btnApriCartella.Click += (_, _) =>
|
btnApriCartella.Click += (_, _) =>
|
||||||
{
|
{
|
||||||
try { Process.Start("explorer.exe", cartellaRaw); }
|
try { Process.Start("explorer.exe", cartellaRaw); }
|
||||||
@@ -67,10 +84,12 @@ public class PrtMancantiForm : Form
|
|||||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
flowAzioni.Controls.Add(btnApriCartella);
|
||||||
|
|
||||||
Controls.Add(grid);
|
Controls.Add(grid);
|
||||||
Controls.Add(lblInfo);
|
Controls.Add(lblInfo);
|
||||||
Controls.Add(btnApriCartella);
|
Controls.Add(pannelloAzioni);
|
||||||
|
CancelButton = btnChiudi;
|
||||||
|
|
||||||
Resize += (_, _) => lblInfo.MaximumSize = new Size(ClientSize.Width - 24, 0);
|
Resize += (_, _) => lblInfo.MaximumSize = new Size(ClientSize.Width - 24, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
+109
-11
@@ -12,6 +12,12 @@ internal static class UiStile
|
|||||||
/// l'RGB.</summary>
|
/// l'RGB.</summary>
|
||||||
public static readonly Color ColorePrimario = Color.FromArgb(0, 99, 177);
|
public static readonly Color ColorePrimario = Color.FromArgb(0, 99, 177);
|
||||||
|
|
||||||
|
/// <summary>Sfondo/bordo delle card di stato (es. riepilogo Database/cartella di lavoro in
|
||||||
|
/// fase 1): un grigio-azzurro appena percettibile rispetto al bianco del resto della
|
||||||
|
/// pagina, sufficiente a separare "stato attuale" da "istruzioni" senza ombre/gradienti.</summary>
|
||||||
|
public static readonly Color ColoreSfondoCard = Color.FromArgb(247, 249, 251);
|
||||||
|
public static readonly Color ColoreBordoCard = Color.FromArgb(226, 231, 236);
|
||||||
|
|
||||||
// AutoSize+Padding invece di una Size fissa in pixel: il bottone si dimensiona sempre
|
// AutoSize+Padding invece di una Size fissa in pixel: il bottone si dimensiona sempre
|
||||||
// al proprio testo (a qualunque lunghezza/font), niente più testo che va a capo e viene
|
// al proprio testo (a qualunque lunghezza/font), niente più testo che va a capo e viene
|
||||||
// tagliato in verticale quando non ci sta nella larghezza indovinata a mano.
|
// tagliato in verticale quando non ci sta nella larghezza indovinata a mano.
|
||||||
@@ -48,22 +54,38 @@ internal static class UiStile
|
|||||||
b.Cursor = Cursors.Hand;
|
b.Cursor = Cursors.Hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Arrotonda gli angoli del bottone. Si aggancia a SizeChanged invece di calcolare
|
/// <summary>Come Secondario, ma pieno invece che a bordo: sulle card bianche delle floatbar
|
||||||
/// la Region una volta sola: con AutoSize la dimensione finale si conosce solo a layout
|
/// (vedi FloatBar) il bordo sottile di Secondario è quasi invisibile su sfondo bianco, e il
|
||||||
/// avvenuto (dopo Text/Padding), non subito dopo la creazione del controllo.</summary>
|
/// bottone risulterebbe visivamente diverso dal Primario pieno accanto — stessa costruzione
|
||||||
public static void Arrotonda(Button b, int raggio = 6)
|
/// (nessun bordo, riempimento pieno), solo grigio chiaro invece che blu.</summary>
|
||||||
|
public static void SecondarioPieno(Button b)
|
||||||
{
|
{
|
||||||
b.SizeChanged += (_, _) => AggiornaRegione(b, raggio);
|
DimensionamentoAutomatico(b);
|
||||||
AggiornaRegione(b, raggio);
|
b.FlatStyle = FlatStyle.Flat;
|
||||||
|
b.FlatAppearance.BorderSize = 0;
|
||||||
|
b.FlatAppearance.MouseOverBackColor = Color.FromArgb(230, 233, 237);
|
||||||
|
b.FlatAppearance.MouseDownBackColor = Color.FromArgb(213, 217, 222);
|
||||||
|
b.BackColor = Color.FromArgb(241, 243, 245);
|
||||||
|
b.Cursor = Cursors.Hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AggiornaRegione(Button b, int raggio)
|
/// <summary>Arrotonda gli angoli del controllo (bottone o card). Si aggancia a SizeChanged
|
||||||
|
/// invece di calcolare la Region una volta sola: con AutoSize la dimensione finale si
|
||||||
|
/// conosce solo a layout avvenuto (dopo Text/Padding), non subito dopo la creazione del
|
||||||
|
/// controllo.</summary>
|
||||||
|
public static void Arrotonda(Control c, int raggio = 6)
|
||||||
{
|
{
|
||||||
if (b.Width <= 0 || b.Height <= 0)
|
c.SizeChanged += (_, _) => AggiornaRegione(c, raggio);
|
||||||
|
AggiornaRegione(c, raggio);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AggiornaRegione(Control c, int raggio)
|
||||||
|
{
|
||||||
|
if (c.Width <= 0 || c.Height <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int d = raggio * 2;
|
int d = raggio * 2;
|
||||||
var rect = new Rectangle(Point.Empty, b.Size);
|
var rect = new Rectangle(Point.Empty, c.Size);
|
||||||
using var percorso = new GraphicsPath();
|
using var percorso = new GraphicsPath();
|
||||||
percorso.AddArc(rect.X, rect.Y, d, d, 180, 90);
|
percorso.AddArc(rect.X, rect.Y, d, d, 180, 90);
|
||||||
percorso.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);
|
percorso.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);
|
||||||
@@ -71,7 +93,83 @@ internal static class UiStile
|
|||||||
percorso.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);
|
percorso.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);
|
||||||
percorso.CloseFigure();
|
percorso.CloseFigure();
|
||||||
|
|
||||||
b.Region?.Dispose();
|
c.Region?.Dispose();
|
||||||
b.Region = new Region(percorso);
|
c.Region = new Region(percorso);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Card di stato: sfondo/bordo leggermente distinti dalla pagina, angoli arrotondati,
|
||||||
|
/// padding interno. Il bordo va disegnato a mano (Paint) perché Region ritaglia il
|
||||||
|
/// controllo ma non lascia spazio a un Border WinForms nativo sugli angoli arrotondati.</summary>
|
||||||
|
public static Panel Card()
|
||||||
|
{
|
||||||
|
var card = new Panel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Top,
|
||||||
|
AutoSize = true,
|
||||||
|
AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
||||||
|
BackColor = ColoreSfondoCard,
|
||||||
|
Padding = new Padding(18, 16, 18, 16),
|
||||||
|
Margin = new Padding(0, 0, 0, 16),
|
||||||
|
};
|
||||||
|
card.Paint += (_, e) =>
|
||||||
|
{
|
||||||
|
using var penna = new Pen(ColoreBordoCard);
|
||||||
|
e.Graphics.DrawRectangle(penna, 0, 0, card.Width - 1, card.Height - 1);
|
||||||
|
};
|
||||||
|
Arrotonda(card, 8);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Barra di bottoni "flottante" (Avanti/Indietro, Apri cartella raw/Chiudi, ecc.):
|
||||||
|
/// una card bianca staccata su tutti e 4 i lati dal bordo della finestra — non un footer a
|
||||||
|
/// tutta larghezza — con angoli arrotondati e bordo sottile, sospesa sopra lo sfondo grigio
|
||||||
|
/// della pagina invece che ancorata al bordo. Lo sfondo del contenitore esterno deve
|
||||||
|
/// combaciare con quello di default della Form (nessun BackColor impostato altrove:
|
||||||
|
/// SystemColors.Control) perché il "distacco" si veda solo nel margine attorno alla card,
|
||||||
|
/// non in una fascia di colore diverso.</summary>
|
||||||
|
public static (Panel Contenitore, FlowLayoutPanel Bottoni) FloatBar()
|
||||||
|
{
|
||||||
|
// Altezza minima per non tagliare il contenuto: bottone (MinimumSize.Height = 32) +
|
||||||
|
// padding verticale della riga bottoni (12+12) + padding verticale del contenitore
|
||||||
|
// esterno (10+16) = 82, con un po' di margine in più.
|
||||||
|
var contenitore = new Panel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Bottom,
|
||||||
|
Height = 88,
|
||||||
|
BackColor = SystemColors.Control,
|
||||||
|
Padding = new Padding(16, 10, 16, 16),
|
||||||
|
};
|
||||||
|
|
||||||
|
var card = new Panel { Dock = DockStyle.Fill, BackColor = Color.White };
|
||||||
|
card.Paint += (_, e) =>
|
||||||
|
{
|
||||||
|
using var penna = new Pen(ColoreBordoCard);
|
||||||
|
e.Graphics.DrawRectangle(penna, 0, 0, card.Width - 1, card.Height - 1);
|
||||||
|
};
|
||||||
|
Arrotonda(card, 14);
|
||||||
|
|
||||||
|
var bottoni = new FlowLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
FlowDirection = FlowDirection.RightToLeft,
|
||||||
|
Padding = new Padding(16, 12, 16, 12),
|
||||||
|
};
|
||||||
|
|
||||||
|
card.Controls.Add(bottoni);
|
||||||
|
contenitore.Controls.Add(card);
|
||||||
|
return (contenitore, bottoni);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Forza le stesse dimensioni per due bottoni della stessa barra (es. Avanti/
|
||||||
|
/// Indietro): AutoSize li dimensiona in base al proprio testo, e con testi di lunghezza
|
||||||
|
/// diversa (o solo uno dei due in grassetto, come Primario/Secondario) risulterebbero
|
||||||
|
/// disallineati sia in larghezza che in altezza — un dettaglio che un designer non
|
||||||
|
/// lascerebbe passare. Va chiamato dopo aver impostato Text/Font su entrambi.</summary>
|
||||||
|
public static void LarghezzaUguale(params Button[] bottoni)
|
||||||
|
{
|
||||||
|
int larghezza = bottoni.Max(b => b.PreferredSize.Width);
|
||||||
|
int altezza = bottoni.Max(b => b.PreferredSize.Height);
|
||||||
|
foreach (var b in bottoni)
|
||||||
|
b.MinimumSize = new Size(larghezza, altezza);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Cevolani StepPrep
|
# PrepStep - Cevolani
|
||||||
|
|
||||||
App Windows Forms (.NET 10, progetto `PrepStep`) per indicizzare, cercare e
|
App Windows Forms (.NET 10, progetto `PrepStep`) per indicizzare, cercare e
|
||||||
recuperare i file CAD Pro-ENGINEER/Creo di un disco/share di rete, e per trasformare
|
recuperare i file CAD Pro-ENGINEER/Creo di un disco/share di rete, e per trasformare
|
||||||
@@ -9,32 +9,40 @@ passaggio, non lo sostituisce.
|
|||||||
|
|
||||||
## Funzionalità
|
## Funzionalità
|
||||||
|
|
||||||
1. **Indice CAD** — all'avvio carica automaticamente `cad_index.db` (se
|
1. **Indice CAD** — all'avvio carica automaticamente `cad_index.db` dalla
|
||||||
presente accanto all'eseguibile). Se manca, si può selezionare un DB
|
cartella di installazione (`%LOCALAPPDATA%\Programs\PrepStep`). Se manca, lo si
|
||||||
esistente con **Apri DB...** o generarne uno nuovo con **Aggiorna
|
crea con **Aggiorna indice**, oppure si apre un altro DB per la sola sessione
|
||||||
indice**, che scansiona il disco (di default `X:\`, tutte le cartelle di primo
|
con **Apri DB...**. **Aggiorna indice** scansiona il disco (di default `X:\`,
|
||||||
livello che trova) e indicizza tutti i file. In questa fase si vede/cambia anche
|
tutte le cartelle di primo livello che trova) e indicizza tutti i file. La
|
||||||
la **cartella di lavoro**: dove finiscono il CSV e la cartella `raw/` (gli STEP
|
**cartella di lavoro** (dove finiscono il CSV e la cartella `raw/`; gli STEP
|
||||||
esportati con Creo vanno in `step/`, dentro la stessa cartella). Proposta
|
esportati con Creo vanno in `step/`, dentro la stessa cartella) è di default la
|
||||||
automaticamente (ricordata da un avvio all'altro), modificabile con
|
cartella di installazione, modificabile in qualunque momento da **File → Scegli
|
||||||
**Cambia cartella...**.
|
cartella di lavoro...** (la scelta viene ricordata). Dal menu **File** si
|
||||||
|
ripuliscono anche `raw/`/`step/` e i CSV accumulati.
|
||||||
2. **Importa Distinta Base** — trascina (o scegli da file) l'estrazione
|
2. **Importa Distinta Base** — trascina (o scegli da file) l'estrazione
|
||||||
`.xls`/`.xml` del gestionale (foglio "Distinta Base", colonne
|
`.xls`/`.xml` del gestionale (foglio "Distinta Base", colonne
|
||||||
Componente/Descrizione): viene generato un CSV nella cartella di lavoro, con
|
Componente/Descrizione): viene generato un CSV nella cartella di lavoro, con
|
||||||
anteprima in griglia. Se il file segue la convenzione di nome
|
anteprima in griglia. Se il file segue la convenzione di nome
|
||||||
`DBP_<codice>_<timestamp>` del gestionale, l'assieme generale dell'estrazione
|
`DBP_<codice>_<timestamp>` del gestionale, l'assieme generale dell'estrazione
|
||||||
viene aggiunto come riga in più nel CSV (il foglio elenca solo i componenti
|
viene aggiunto come riga in più nel CSV (il foglio elenca solo i componenti
|
||||||
sotto l'assieme, non l'assieme stesso).
|
sotto l'assieme, non l'assieme stesso). Il bottone **Verifica presenza**
|
||||||
|
(compare solo dopo un import riuscito) cerca i componenti nel database — è
|
||||||
|
questo passo, non il solo import, a sbloccare la fase successiva.
|
||||||
3. **Crea cartella raw** — cerca nell'indice il percorso CAD di ogni
|
3. **Crea cartella raw** — cerca nell'indice il percorso CAD di ogni
|
||||||
componente importato (assieme generale incluso) e copia i file trovati
|
componente importato (assieme generale incluso) e copia i file trovati
|
||||||
in una cartella `raw/` dentro la cartella di lavoro.
|
in una cartella `raw/` dentro la cartella di lavoro.
|
||||||
4. **Trova componenti referenziati** — legge i metadati dei file `.asm` in
|
4. **Trova componenti referenziati** — legge i metadati dei file `.asm` in
|
||||||
`raw/` per trovare ricorsivamente i sotto-assiemi/parti che li
|
`raw/` per trovare ricorsivamente i sotto-assiemi/parti che li
|
||||||
compongono, copiandoli a loro volta in `raw/`.
|
compongono, copiandoli a loro volta in `raw/` (passo opzionale se la
|
||||||
5. **Verifica completezza assiemi** — confronta la distinta con l'indice per
|
distinta contiene solo parti singole).
|
||||||
capire se manca qualche sotto-componente, e mostra i file `.asm` della
|
5. **Verifica completezza** — mostra i file `.asm` della distinta già in `raw/`,
|
||||||
distinta trascinabili su Creo/Esplora risorse per l'esportazione STEP con
|
trascinabili su Creo Distributed Batch per l'esportazione STEP (passo
|
||||||
Creo Distributed Batch.
|
manuale, fuori da quest'app): scrive in `step/`, dentro la cartella di
|
||||||
|
lavoro.
|
||||||
|
|
||||||
|
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
|
## Requisiti
|
||||||
|
|
||||||
@@ -43,68 +51,41 @@ passaggio, non lo sostituisce.
|
|||||||
- Accesso alla share di rete con i file CAD (`X:\` di default) per
|
- Accesso alla share di rete con i file CAD (`X:\` di default) per
|
||||||
l'indicizzazione
|
l'indicizzazione
|
||||||
|
|
||||||
## Build
|
## Build, test e rilascio
|
||||||
|
|
||||||
```powershell
|
Build, avvio e test: [`docs/sviluppo.md`](docs/sviluppo.md). Pubblicazione dell'eseguibile e
|
||||||
cd PrepStep
|
generazione del setup di installazione: [`docs/rilascio.md`](docs/rilascio.md).
|
||||||
dotnet build -c Release
|
|
||||||
```
|
|
||||||
|
|
||||||
## Avvio
|
Il setup installa **solo per l'utente corrente**, in `%LOCALAPPDATA%\Programs\PrepStep`, senza
|
||||||
|
diritti di amministratore; si disinstalla da "Impostazioni → App installate".
|
||||||
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.
|
|
||||||
|
|
||||||
## Struttura del progetto
|
## Struttura del progetto
|
||||||
|
|
||||||
Tutto il codice è in `PrepStep/` (unico progetto nella solution, GUI e
|
Il codice dell'app è tutto in `PrepStep/` (GUI e indicizzazione integrate); accanto ci sono
|
||||||
indicizzazione integrate):
|
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); indice di tutti i doc in
|
||||||
|
[`docs/README.md`](docs/README.md).
|
||||||
|
|
||||||
- `Program.cs` — entry point.
|
- `Program.cs` — entry point.
|
||||||
- `Form1.cs` — finestra principale (indice CAD, import Distinta Base, griglia
|
- `Form1.cs` — finestra principale, wizard a 5 fasi e menu; UI costruita interamente in
|
||||||
risultati); UI costruita interamente in codice, nessun designer visuale.
|
codice (`Form1.Designer.cs` è solo lo scheletro del template).
|
||||||
- `Indexer.cs` — scansione del disco e scrittura/aggiornamento del DB SQLite.
|
- `Indexer.cs` — scansione del disco e scrittura/aggiornamento del DB SQLite.
|
||||||
- `DistintaBaseImporter.cs` — parsing del file Distinta Base (SpreadsheetML
|
- `DistintaBaseImporter.cs` — parsing del file Distinta Base (SpreadsheetML
|
||||||
XML o Excel binario/OpenXML tramite ExcelDataReader) e generazione CSV.
|
XML o Excel binario/OpenXML tramite ExcelDataReader) e generazione CSV.
|
||||||
- `ProgressForm.cs` — dialog di avanzamento durante l'aggiornamento indice.
|
- `AsmRecordReader.cs` — lettura esatta dei record dei `.asm` (fase 4).
|
||||||
|
- `AssemblyComponentScanner.cs` — euristica testuale di fallback per i `.asm` fuori formato.
|
||||||
|
- `ProgressForm.cs`, `ProgressoComponentiForm.cs` — avanzamento di scansione e fase 4.
|
||||||
|
- `PrtMancantiForm.cs` — esito di "Trova componenti referenziati".
|
||||||
|
- `GuidaForm.cs` — guida in-app (Aiuto → Guida, F1).
|
||||||
|
- `UiStile.cs` — stile condiviso di bottoni, card e barre.
|
||||||
|
|
||||||
Il database generato (`cad_index.db`), i file Excel/CSV della distinta
|
Il database generato (`cad_index.db`), i file Excel/CSV della distinta
|
||||||
trascinati nella root e le cartelle `raw/`/`step/` sono rigenerabili e non
|
trascinati nella root e le cartelle `raw/`/`step/` sono rigenerabili e non
|
||||||
sono tracciati in git (vedi `.gitignore`).
|
sono tracciati in git (vedi `.gitignore`).
|
||||||
|
|
||||||
## Test
|
Per i test vedi [`docs/sviluppo.md`](docs/sviluppo.md#test).
|
||||||
|
|
||||||
`PrepStep.Tests/` contiene unit test (regex di estrazione dei componenti
|
## Licenza
|
||||||
dai `.asm`, parsing del nome file Pro-E) e integration test (schema/vista SQL
|
|
||||||
reale, con focus sui bug di case-sensitivity/duplicati già risolti). Da
|
|
||||||
eseguire:
|
|
||||||
|
|
||||||
```powershell
|
Software proprietario, © 2026 Davide Grilli, tutti i diritti riservati: vedi
|
||||||
cd PrepStep.Tests
|
[`LICENSE`](LICENSE).
|
||||||
dotnet test
|
|
||||||
```
|
|
||||||
@@ -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` (`<Version>`, `<Product>`) |
|
||||||
|
| 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
|
||||||
|
```
|
||||||
+86
-34
@@ -1,9 +1,9 @@
|
|||||||
# Architettura di PrepStep (Cevolani StepPrep)
|
# Architettura di PrepStep
|
||||||
|
|
||||||
Panoramica di tutti i file sorgente e di come collaborano. `PrepStep` è il nome
|
Panoramica di tutti i file sorgente e di come collaborano. `PrepStep` è il nome
|
||||||
tecnico del progetto (cartella, namespace, csproj, eseguibile `PrepStep.exe`);
|
tecnico del progetto (cartella, namespace, csproj, eseguibile `PrepStep.exe`);
|
||||||
**Cevolani StepPrep** è il nome mostrato all'utente finale (titolo finestra, icona,
|
**PrepStep - Cevolani** è 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
|
versione — vedi `<Version>`/`<Product>` 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
|
formato binario `.asm` vedi `docs/formato-asm.md` (euristica testuale) e
|
||||||
`docs/formato-asm-record.md` (parser esatto dei record); per la parallelizzazione della
|
`docs/formato-asm-record.md` (parser esatto dei record); per la parallelizzazione della
|
||||||
copia in `raw/` vedi `docs/copia-raw-parallela.md`.
|
copia in `raw/` vedi `docs/copia-raw-parallela.md`.
|
||||||
@@ -35,7 +35,8 @@ successiva.
|
|||||||
**1. Indice della share → `cad_index.db`.** `Indexer.ScanAsync` cammina tutte le cartelle di
|
**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
|
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
|
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.
|
delta. La **cartella di lavoro**, che da qui in poi ospita tutto il resto, non si sceglie qui
|
||||||
|
ma da "File → Scegli cartella di lavoro..." (default: cartella di installazione).
|
||||||
|
|
||||||
**2. Distinta del gestionale → CSV.** `DistintaBaseImporter.ConvertiInCsv` riduce l'export a
|
**2. Distinta del gestionale → CSV.** `DistintaBaseImporter.ConvertiInCsv` riduce l'export a
|
||||||
due colonne `componente;descrizione`. Se il nome del file segue la convenzione
|
due colonne `componente;descrizione`. Se il nome del file segue la convenzione
|
||||||
@@ -92,8 +93,8 @@ trascinano i `.asm` su Creo Distributed Batch, che scrive in `step/`.
|
|||||||
|
|
||||||
| # | Fase | Bottone → metodo | Cosa fa |
|
| # | 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 |
|
| 1 | Database | "Aggiorna indice" → `Indexer.ScanAsync` | Crea/aggiorna `cad_index.db` scansionando la share; un pallino verde/rosso accanto al titolo indica se un DB è caricato. La cartella di lavoro non è mostrata qui (cambiabile in qualunque momento da "File → Scegli cartella di lavoro...") |
|
||||||
| 2 | Importa Distinta Base | selezione file → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella cartella di lavoro |
|
| 2 | Importa Distinta Base | selezione file/drag&drop → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv`, poi "Verifica presenza" → `VerificaCodiciNelDb` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella cartella di lavoro (oppure "Lista personalizzata": codici digitati uno alla volta con `AggiungiCodice`, solo in memoria, senza CSV; tutto a valle legge solo `_righeCorrenti`); "Verifica presenza" (visibile solo dopo un import riuscito) cerca i componenti nell'indice e colora le righe della griglia — è quella che sblocca "Avanti", non il solo import |
|
||||||
| 3 | Crea cartella raw | "Crea cartella raw" → `CreaCartellaRaw` | `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela) → `raw/` dentro la 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 |
|
| 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) |
|
| 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) |
|
||||||
@@ -102,21 +103,32 @@ trascinano i `.asm` su Creo Distributed Batch, che scrive in `step/`.
|
|||||||
`_percorsoInputCorrente`, `_righeCorrenti` (righe del CSV), `_ultimaVerifica` (cache percorsi
|
`_percorsoInputCorrente`, `_righeCorrenti` (righe del CSV), `_ultimaVerifica` (cache percorsi
|
||||||
CAD), `_ultimaCartellaRaw`.
|
CAD), `_ultimaCartellaRaw`.
|
||||||
|
|
||||||
Menu (`MenuStrip`, in cima alla finestra): "File → Nuova estrazione" (Ctrl+N) azzera lo
|
Menu (`MenuStrip`, in cima alla finestra): "File → Nuovo" (Ctrl+N) azzera lo
|
||||||
stato delle fasi 2-5 (`NuovaEstrazione`) per ripartire con una nuova distinta senza
|
stato delle fasi 2-5 (`NuovaEstrazione`) e va direttamente alla fase 2, senza passare dalla
|
||||||
ricaricare il database/la cartella di lavoro (fase 1, che resta invariata); "Aiuto → Guida"
|
fase 1: database e cartella di lavoro restano quelli già caricati, non serve rifare lo scan;
|
||||||
(F1) apre `GuidaForm`, sintesi interattiva delle 5 fasi per chi non ha letto questi docs.
|
"File → Scegli cartella di lavoro..." → `ScegliCartellaLavoro`, cambia in qualunque momento
|
||||||
|
la destinazione di CSV/`raw/`/`step/`; "File → Pulisci raw e step..." svuota le cartelle
|
||||||
|
`raw/`/`step/` nella cartella di lavoro (`PulisciRawEStep`, non tocca l'indice); "File → Elimina CSV delle
|
||||||
|
distinte..." elimina i `*.csv` direttamente nella cartella di lavoro (`EliminaCsv`); "Aiuto →
|
||||||
|
Guida" (F1) apre `GuidaForm`, sintesi interattiva delle 5 fasi per chi non ha letto questi
|
||||||
|
docs.
|
||||||
|
|
||||||
Altre responsabilità in Form1.cs:
|
Altre responsabilità in Form1.cs:
|
||||||
- `TrovaPercorsiCad` / `CaricaCodiciNoti` — query sul DB (`files_ultima_versione`), la
|
- `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
|
prima risolve i componenti del CSV (fase 3), la seconda carica tutti i codici prt/asm noti
|
||||||
componenti sia da `AssemblyComponentScanner` per validare i token generici).
|
in un colpo solo, usata in fase 4 solo per validare i token generici di
|
||||||
|
`AssemblyComponentScanner`.
|
||||||
|
- `CaricaCandidati` — in fase 4, tutte le copie di ogni codice dalla tabella `files` (non
|
||||||
|
dalla vista), da cui `ScegliPerCartella` sceglie quella da copiare in `raw/`.
|
||||||
- `CopiaFileTrovati` — copia (in parallelo, grado 6) i file trovati in `raw/`, saltando
|
- `CopiaFileTrovati` — copia (in parallelo, grado 6) i file trovati in `raw/`, saltando
|
||||||
silenziosamente quelli irraggiungibili (share offline/permessi).
|
silenziosamente quelli irraggiungibili (share offline/permessi).
|
||||||
- `PulisciRawEStep` — svuota `raw/` e `step/` nella cartella di lavoro (manutenzione,
|
- `PulisciRawEStep` — svuota `raw/` e `step/` nella cartella di lavoro (manutenzione,
|
||||||
richiede conferma).
|
richiede conferma).
|
||||||
- `InizializzaDatabase` / `ScegliDatabase` — localizzazione del DB (ultimo usato, salvato in
|
- `EliminaCsv` — elimina i `*.csv` generati dall'import (uno per distinta) nella cartella di
|
||||||
`%AppData%/PrepStep/ultimo_db.txt`, o `cad_index.db` accanto all'eseguibile).
|
lavoro, solo al primo livello; gli `.xls` del gestionale restano (richiede conferma, con
|
||||||
|
l'elenco dei file).
|
||||||
|
- `InizializzaDatabase` / `ScegliDatabase` — localizzazione del DB (sempre `cad_index.db` accanto
|
||||||
|
all'eseguibile; "Apri DB..." ne carica un altro solo per la sessione).
|
||||||
- `InizializzaCartellaLavoro` / `ScegliCartellaLavoro` — stesso pattern per la cartella di
|
- `InizializzaCartellaLavoro` / `ScegliCartellaLavoro` — stesso pattern per la cartella di
|
||||||
lavoro (`ultima_cartella_lavoro.txt`), fallback `AppContext.BaseDirectory` se non c'è
|
lavoro (`ultima_cartella_lavoro.txt`), fallback `AppContext.BaseDirectory` se non c'è
|
||||||
ancora nulla di salvato: nessuna dipendenza da un marker di repo, funziona identica
|
ancora nulla di salvato: nessuna dipendenza da un marker di repo, funziona identica
|
||||||
@@ -189,8 +201,8 @@ completi".
|
|||||||
file) nelle sole colonne Componente/Descrizione, foglio `"Distinta Base"`. Se il nome 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
|
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
|
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
|
passata da `Form1` (la cartella di lavoro, impostabile da "File → Scegli cartella di lavoro..."),
|
||||||
file di input.
|
non necessariamente accanto al file di input.
|
||||||
|
|
||||||
## Finestre di dialogo (modali, mostrate a fine step)
|
## Finestre di dialogo (modali, mostrate a fine step)
|
||||||
|
|
||||||
@@ -207,9 +219,22 @@ completi".
|
|||||||
per i secondari, blu pieno per l'azione principale di ogni fase), entrambi con
|
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
|
`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
|
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
|
larghezza scelta). `SecondarioPieno` è la variante senza bordo (sfondo grigio chiaro
|
||||||
`SizeChanged` (non una volta sola: con `AutoSize` la dimensione finale del bottone si
|
pieno) per i bottoni secondari sopra le card bianche di `Card`/`FloatBar`, dove il
|
||||||
conosce solo a layout avvenuto), usata sui bottoni "Avanti"/"Indietro" del wizard.
|
bordo sottile di `Secondario` è troppo poco visibile su sfondo bianco. `Arrotonda`
|
||||||
|
applica angoli arrotondati a un `Control` qualsiasi (bottone o card) via `Region`
|
||||||
|
calcolata su `SizeChanged`, non una volta sola: con `AutoSize` la dimensione finale si
|
||||||
|
conosce solo a layout avvenuto — va chiamata dopo aver fissato le dimensioni finali del
|
||||||
|
controllo (es. dopo `LarghezzaUguale`), altrimenti la `Region` vecchia resta visibile
|
||||||
|
come bordo residuo finché non arriva un repaint completo. `Card` costruisce una card di
|
||||||
|
stato (sfondo/bordo distinti, angoli arrotondati), usata in fase 1 per il riepilogo
|
||||||
|
Database. `FloatBar` costruisce la barra di navigazione "flottante" in fondo alle
|
||||||
|
finestre (card bianca staccata su tutti e 4 i lati, non un footer a tutta larghezza):
|
||||||
|
usata per Avanti/Indietro nel wizard e per Chiudi/Apri cartella raw in
|
||||||
|
`PrtMancantiForm`. `LarghezzaUguale` forza la stessa larghezza e altezza tra due
|
||||||
|
bottoni della stessa barra (es. Avanti/Indietro, che altrimenti avrebbero dimensioni
|
||||||
|
diverse: testi di lunghezza diversa, e il font in grassetto di `Primario` richiede più
|
||||||
|
altezza di quello normale di `Secondario`/`SecondarioPieno`).
|
||||||
- **app.ico** — icona dell'app, referenziata da `<ApplicationIcon>` nel csproj (icona
|
- **app.ico** — icona dell'app, referenziata da `<ApplicationIcon>` nel csproj (icona
|
||||||
dell'exe in Esplora risorse/taskbar) e riletta esplicitamente in `Form1.BuildUi()`
|
dell'exe in Esplora risorse/taskbar) e riletta esplicitamente in `Form1.BuildUi()`
|
||||||
con `Icon.ExtractAssociatedIcon(Application.ExecutablePath)` per la titlebar, senza
|
con `Icon.ExtractAssociatedIcon(Application.ExecutablePath)` per la titlebar, senza
|
||||||
@@ -219,21 +244,45 @@ completi".
|
|||||||
sotto-assiemi trovati e copiati in `raw/`, più i codici referenziati che non corrispondono a
|
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
|
nessun file indicizzato (possono essere disegni, componenti davvero mancanti, o falsi
|
||||||
positivi della scansione `.asm`).
|
positivi della scansione `.asm`).
|
||||||
- **GuidaForm.cs** — guida interattiva ("Aiuto → Guida"/F1): lista delle 5 fasi a sinistra,
|
- **GuidaForm.cs** — guida interattiva ("Aiuto → Guida"/F1): elenco delle sezioni a sinistra
|
||||||
testo a destra; sintesi, il dettaglio resta solo in questi docs.
|
(Panoramica, Dove finiscono i file, Prima di iniziare, le 5 fasi, Ripartire da capo), testo
|
||||||
|
a destra; stessa guida di `docs/guida-utente.md`, il dettaglio tecnico resta solo in questi
|
||||||
|
docs.
|
||||||
|
|
||||||
## Persistenza
|
## Persistenza
|
||||||
|
|
||||||
- **DB** (`cad_index.db`, SQLite) — vedi tabella `files`/vista `files_ultima_versione` sopra.
|
- **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
|
Sempre `cad_index.db` accanto all'eseguibile (`AppContext.BaseDirectory`, cioè la cartella
|
||||||
selezionabile con "Apri DB...". Radice disco di scansione di default `X:\`
|
di installazione): `InizializzaDatabase` carica solo quello, e "Aggiorna indice" lo crea lì
|
||||||
(`Form1.RadiceDiscoDefault`, hardcoded).
|
se manca. "Apri DB..." carica un altro file solo per la sessione corrente, senza
|
||||||
- **Cartella di lavoro** — salvata in `%AppData%/PrepStep/ultima_cartella_lavoro.txt`
|
ricordarlo. Radice disco di scansione
|
||||||
(stesso meccanismo del DB sopra), proposta di default `AppContext.BaseDirectory` la prima
|
di default `X:\` (`Form1.RadiceDiscoDefault`, hardcoded).
|
||||||
volta; selezionabile/cambiabile con "Cambia cartella..." in fase 1. Tutti i file generati a
|
- **Cartella di lavoro** — ricordata tra un avvio e l'altro in
|
||||||
valle ci finiscono dentro: il CSV della distinta, `raw/` (CAD dei componenti e
|
`%AppData%/PrepStep/ultima_cartella_lavoro.txt`; se non c'è una preferenza salvata (o punta
|
||||||
sotto-componenti); `step/` non è scritta dall'app ma è dove l'utente esporta gli STEP a
|
a una cartella cancellata) si propone la cartella di installazione
|
||||||
mano con Creo Distributed Batch, a partire dai file trascinati da `raw/` in fase 5.
|
(`AppContext.BaseDirectory`); cambiabile in qualunque momento da "File → Scegli cartella di
|
||||||
|
lavoro...". 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.
|
||||||
|
|
||||||
|
## Installazione
|
||||||
|
|
||||||
|
`installer/PrepStep.iss` (Inno Setup 6) impacchetta `PrepStep/publish/PrepStep.exe` in un
|
||||||
|
setup **per-utente** (`PrivilegesRequired=lowest`, quindi `{autopf}` =
|
||||||
|
`%LOCALAPPDATA%\Programs`, cartella `PrepStep`): niente admin, e la cartella resta scrivibile —
|
||||||
|
cosa che serve, perché `cad_index.db` e (di default) la cartella di lavoro con `raw/`
|
||||||
|
stanno accanto all'eseguibile (`AppContext.BaseDirectory`, vedi Persistenza). In `C:\Program Files` fallirebbe per un utente
|
||||||
|
normale. Versione del setup letta dall'`exe` (ProductVersion, tolto il `+<commit>`), quindi
|
||||||
|
basta cambiare `<Version>` nel csproj. L'`AppId` fisso fa riconoscere a Windows le versioni
|
||||||
|
nuove come aggiornamento della stessa app: non va cambiato
|
||||||
|
(`AppGuid` nello script). Se l'app risulta già installata (chiave `…\Uninstall\{AppGuid}_is1`
|
||||||
|
in HKCU), `InitializeSetup` nella sezione `[Code]` chiede se aggiornare o disinstallare (lancia
|
||||||
|
l'`UninstallString`), perché Inno da solo aggiornerebbe sempre; con `/VERYSILENT` non chiede.
|
||||||
|
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 in [`rilascio.md`](rilascio.md).
|
||||||
|
|
||||||
## Script standalone (`scripts/`)
|
## Script standalone (`scripts/`)
|
||||||
|
|
||||||
@@ -241,17 +290,20 @@ C# file-based apps (.NET 10, `dotnet run scripts/Nome.cs -- arg1 arg2`, nessun `
|
|||||||
dipendenze extra con `#:package Nome@versione` in testa al file):
|
dipendenze extra con `#:package Nome@versione` in testa al file):
|
||||||
|
|
||||||
- **ExtractCreoStrings.cs** — BOM di un `.asm` risolvendo ricorsivamente i sotto-assiemi
|
- **ExtractCreoStrings.cs** — BOM di un `.asm` risolvendo ricorsivamente i sotto-assiemi
|
||||||
contro `cad_index.db` (stessa logica di `AssiemeCompleto` in Form1, solo codici numerici
|
contro `cad_index.db` con `AssemblyComponentScanner` (via `#:project`, solo codici numerici
|
||||||
Pro-E).
|
Pro-E).
|
||||||
- **BomViaDb.cs** — come sopra ma con la tokenizzazione generica validata contro il DB, per
|
- **BomViaDb.cs** — come sopra ma con la tokenizzazione generica validata contro il DB, per
|
||||||
intercettare anche i nomi alfabetici delle parti commerciali.
|
intercettare anche i nomi alfabetici delle parti commerciali.
|
||||||
- **CreoBom.cs** — usa `AsmRecordReader` (stesso lettore esatto della GUI, fase 4/5, via
|
- **CreoBom.cs** — usa `AsmRecordReader` (stesso lettore esatto della GUI, fase 4, via
|
||||||
`#:project`) invece dell'euristica testuale. Risolve i sottoassiemi cercandoli in cartelle
|
`#:project`) invece dell'euristica testuale. Risolve i sottoassiemi cercandoli in cartelle
|
||||||
locali (`-p`), non contro `cad_index.db` come `ExtractCreoStrings.cs`/`BomViaDb.cs`.
|
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
|
- **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
|
investigare a mano nuovi falsi positivi prima di toccare la regex di
|
||||||
`AssemblyComponentScanner`.
|
`AssemblyComponentScanner`.
|
||||||
- **Query.cs** — query SQL ad-hoc contro `cad_index.db` da riga di comando.
|
- **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, `<Version>` 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/`
|
> ⚠️ 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
|
> (che contiene un `.csproj` di tipo `WinExe`), esegue l'app GUI vera invece dello script
|
||||||
@@ -261,7 +313,7 @@ dipendenze extra con `#:package Nome@versione` in testa al file):
|
|||||||
## Test
|
## Test
|
||||||
|
|
||||||
- **PrepStep.Tests/** (xUnit) — riferimento al progetto principale via `ProjectReference`
|
- **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
|
`Indexer.ParseFilename` e `AssemblyComponentScanner.EstraiCodiciReferenziati` senza esporli
|
||||||
come API pubblica. File: `IndexerParseFilenameTests.cs`, `IndexerScanTests.cs`,
|
come API pubblica. File: `IndexerParseFilenameTests.cs`, `IndexerScanTests.cs`,
|
||||||
`AssemblyComponentScannerTests.cs`, `AsmRecordReaderTests.cs`,
|
`AssemblyComponentScannerTests.cs`, `AsmRecordReaderTests.cs`,
|
||||||
|
|||||||
+25
-25
@@ -29,10 +29,12 @@ componenti e nel filtro per cartella).
|
|||||||
|
|
||||||
Vista `files_ultima_versione`: stessa struttura di `files`, filtrata alla sola riga con
|
Vista `files_ultima_versione`: stessa struttura di `files`, filtrata alla sola riga con
|
||||||
`versione` più alta per ogni `(cartella_radice, codice, tipo_file)`, tramite `ROW_NUMBER()
|
`versione` più alta per ogni `(cartella_radice, codice, tipo_file)`, tramite `ROW_NUMBER()
|
||||||
OVER (PARTITION BY ... ORDER BY versione DESC, data_modifica DESC, percorso_completo)`. Tutte
|
OVER (PARTITION BY ... ORDER BY versione DESC, data_modifica DESC, percorso_completo)`. Le
|
||||||
le ricerche di componenti (`Form1.TrovaPercorsiCad`, `Form1.CaricaCodiciNoti`) interrogano
|
ricerche per codice (`Form1.TrovaPercorsiCad` in fase 3, `Form1.CaricaCodiciNoti`)
|
||||||
questa vista, non `files` direttamente, così un componente con più versioni sulla share
|
interrogano questa vista, non `files` direttamente, così un componente con più versioni sulla
|
||||||
risolve sempre all'ultima. Deliberatamente una `ROW_NUMBER()` invece di un self-join con
|
share risolve sempre all'ultima. Fa eccezione la fase 4 (`Form1.CaricaCandidati`), che legge
|
||||||
|
`files` per avere tutte le copie di un codice in cartelle diverse e scegliere quella giusta
|
||||||
|
(`ScegliPerCartella`). Deliberatamente una `ROW_NUMBER()` invece di un self-join con
|
||||||
`GROUP BY`: su ~200k righe un self-join degenera in tempi quasi quadratici, `ROW_NUMBER()`
|
`GROUP BY`: su ~200k righe un self-join degenera in tempi quasi quadratici, `ROW_NUMBER()`
|
||||||
resta lineare.
|
resta lineare.
|
||||||
|
|
||||||
@@ -60,8 +62,9 @@ ciascuna — non una sola in assoluto. `TrovaPercorsiCad` gestisce questo caso d
|
|||||||
|
|
||||||
Chiamato da "Aggiorna indice" (fase 1) e riutilizza lo stesso `dbPath` già caricato.
|
Chiamato da "Aggiorna indice" (fase 1) e riutilizza lo stesso `dbPath` già caricato.
|
||||||
|
|
||||||
1. Crea lo schema se non esiste (`CREATE TABLE/VIEW IF NOT EXISTS`) — quindi funziona sia su
|
1. Crea lo schema se non esiste (`CREATE TABLE/INDEX IF NOT EXISTS`; la vista invece è
|
||||||
un DB vuoto (prima esecuzione) sia su uno esistente (aggiornamento incrementale).
|
`DROP`+`CREATE` a ogni scan) — quindi funziona sia su un DB vuoto (prima esecuzione) sia su
|
||||||
|
uno esistente (aggiornamento incrementale).
|
||||||
2. Cancella subito tutte le righe con `cartella_radice LIKE 'temp\_%'`: le cartelle
|
2. Cancella subito tutte le righe con `cartella_radice LIKE 'temp\_%'`: le cartelle
|
||||||
`temp_paris`, `temp_davide`, `temp_studi esterni`, ecc. sono lavori personali/temporanei che
|
`temp_paris`, `temp_davide`, `temp_studi esterni`, ecc. sono lavori personali/temporanei che
|
||||||
possono contenere copie datate con lo stesso codice numerico di file "ufficiali" altrove
|
possono contenere copie datate con lo stesso codice numerico di file "ufficiali" altrove
|
||||||
@@ -109,24 +112,20 @@ sulla share, non tipicamente cercato dalla UI).
|
|||||||
|
|
||||||
## Come l'app trova il DB all'avvio
|
## Come l'app trova il DB all'avvio
|
||||||
|
|
||||||
`Form1.InizializzaDatabase` prova, in ordine, i seguenti candidati e usa il primo che esiste
|
`Form1.InizializzaDatabase` carica sempre e solo `cad_index.db` accanto all'eseguibile
|
||||||
su disco:
|
(`AppContext.BaseDirectory`: con il setup è `%LOCALAPPDATA%\Programs\PrepStep`). Nessun
|
||||||
|
percorso viene ricordato fra un avvio e l'altro.
|
||||||
|
|
||||||
1. l'ultimo DB caricato con successo, salvato in
|
Se non esiste, la fase 1 resta bloccata su "Database non trovato" finché l'utente non preme
|
||||||
`%AppData%/PrepStep/ultimo_db.txt` (`Form1.LeggiUltimoDbUsato`/`SalvaUltimoDbUsato`)
|
"Aggiorna indice" (`Form1.AggiornaIndice`, che crea `cad_index.db` da zero accanto
|
||||||
2. `cad_index.db` accanto all'eseguibile (`AppContext.BaseDirectory`)
|
all'eseguibile) o "Apri DB..." (`Form1.ScegliDatabase`, file picker libero: il DB scelto vale
|
||||||
3. `cad_index.db` nella working directory corrente
|
solo per la sessione corrente, e "Aggiorna indice" aggiorna quello).
|
||||||
|
|
||||||
Se nessuno dei tre esiste, la fase 1 resta bloccata su "Database non trovato" finché l'utente
|
|
||||||
non preme "Apri DB..." (`Form1.ScegliDatabase`, file picker libero) o "Aggiorna indice"
|
|
||||||
(`Form1.AggiornaIndice`, che crea `cad_index.db` da zero accanto all'eseguibile se non c'è
|
|
||||||
ancora un `_dbPath`).
|
|
||||||
|
|
||||||
**Attenzione in sviluppo (dotnet run / IDE):** `AppContext.BaseDirectory` è la cartella di
|
**Attenzione in sviluppo (dotnet run / IDE):** `AppContext.BaseDirectory` è la cartella di
|
||||||
build (`PrepStep/bin/Release/net10.0-windows` o `Debug/...`), non la radice del repo. Se in
|
build (`PrepStep/bin/Release/net10.0-windows` o `Debug/...`), non la radice del repo. L'app usa
|
||||||
quella cartella finisce per errore una copia di `cad_index.db` (es. creata lì da una vecchia
|
quindi la copia di `cad_index.db` in quella cartella (creandola lì al primo "Aggiorna
|
||||||
sessione), l'app la userà invece di quella nella radice del repo, e le due copie divergeranno
|
indice"), non quella nella radice del repo né quella installata: per usare un altro DB in
|
||||||
a ogni "Aggiorna indice" fatto nell'uno o nell'altro ambiente. Se i risultati di una ricerca
|
sviluppo va aperto con "Apri DB..." a ogni avvio. Se i risultati di una ricerca
|
||||||
sembrano incoerenti con quello che ti aspetti di vedere sul DB "giusto", controlla con "Apri
|
sembrano incoerenti con quello che ti aspetti di vedere sul DB "giusto", controlla con "Apri
|
||||||
DB..." quale file è effettivamente caricato (mostrato in fase 1 accanto al conteggio dei file
|
DB..." quale file è effettivamente caricato (mostrato in fase 1 accanto al conteggio dei file
|
||||||
indicizzati) prima di sospettare un bug nella ricerca.
|
indicizzati) prima di sospettare un bug nella ricerca.
|
||||||
@@ -142,10 +141,11 @@ indicizzati) prima di sospettare un bug nella ricerca.
|
|||||||
riga con quell'esatto `codice`. In presenza di duplicati su più `cartella_radice`, tiene solo
|
riga con quell'esatto `codice`. In presenza di duplicati su più `cartella_radice`, tiene solo
|
||||||
il primo per `tipo_file` (il più recente, dato l'`ORDER BY data_modifica DESC`) e scarta gli
|
il primo per `tipo_file` (il più recente, dato l'`ORDER BY data_modifica DESC`) e scarta gli
|
||||||
altri.
|
altri.
|
||||||
- **Risoluzione codici noti per lo scanner assiemi** (`Form1.CaricaCodiciNoti`, usata sia dalla
|
- **Codici noti per lo scanner assiemi** (`Form1.CaricaCodiciNoti`, fase 4;
|
||||||
GUI in fase 4 sia da `scripts/BomViaDb.cs`): carica in un'unica query tutti i codici
|
`scripts/BomViaDb.cs` fa una query equivalente per conto suo): carica in un'unica query tutti
|
||||||
prt/asm noti con i relativi percorsi, invece di una query per codice — usata per validare i
|
i codici prt/asm noti, invece di una query per codice — usata solo per validare i token
|
||||||
token generici trovati nei binari `.asm` (vedi `docs/formato-asm.md`).
|
generici trovati nei binari `.asm` (vedi `docs/formato-asm.md`). I percorsi da copiare
|
||||||
|
vengono invece da `Form1.CaricaCandidati` (tabella `files`, vedi sopra).
|
||||||
|
|
||||||
## Debug ad-hoc
|
## Debug ad-hoc
|
||||||
|
|
||||||
|
|||||||
@@ -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).
|
||||||
@@ -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.
|
||||||
@@ -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).
|
||||||
@@ -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).
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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 |
|
||||||
@@ -8,8 +8,8 @@ l'euristica testuale ("codice che compare almeno due volte") descritta in
|
|||||||
struttura effettiva dei record.
|
struttura effettiva dei record.
|
||||||
|
|
||||||
Implementazione: lettura in [`PrepStep/AsmRecordReader.cs`](../PrepStep/AsmRecordReader.cs)
|
Implementazione: lettura in [`PrepStep/AsmRecordReader.cs`](../PrepStep/AsmRecordReader.cs)
|
||||||
(condivisa con la GUI, `Form1.EstraiCodiciReferenziati` in fase "Trova componenti
|
(condivisa con la GUI, `Form1.EstraiCodiciReferenziati` in fase 4 "Trova componenti
|
||||||
referenziati"/"Verifica completezza" - con fallback su `AssemblyComponentScanner` per i file
|
referenziati" - con fallback su `AssemblyComponentScanner` per i file
|
||||||
che non rispettano il formato validato), resa in formato BOM e ricorsione sui sottoassiemi
|
che non rispettano il formato validato), resa in formato BOM e ricorsione sui sottoassiemi
|
||||||
in [`scripts/CreoBom.cs`](../scripts/CreoBom.cs) (via `#:project`).
|
in [`scripts/CreoBom.cs`](../scripts/CreoBom.cs) (via `#:project`).
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ conteggio dei membri: con la lettura a 1 byte fisso i campioni grandi si
|
|||||||
disallineavano. Non è confermata da una specifica; regge sui campioni
|
disallineavano. Non è confermata da una specifica; regge sui campioni
|
||||||
disponibili al momento del reverse engineering.
|
disponibili al momento del reverse engineering.
|
||||||
|
|
||||||
In `CreoBom.cs` è `AsmRecordReader.AsInt`, applicata al testo decodificato Latin1 (ogni `char`
|
Nel codice è `AsmRecordReader.AsInt` (privata, in `PrepStep/AsmRecordReader.cs`), applicata al testo decodificato Latin1 (ogni `char`
|
||||||
della stringa corrisponde 1:1 a un byte, quindi indicizzare la stringa o l'array di byte
|
della stringa corrisponde 1:1 a un byte, quindi indicizzare la stringa o l'array di byte
|
||||||
originale dà lo stesso offset).
|
originale dà lo stesso offset).
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -12,8 +12,8 @@ Un reverse engineering successivo ha decodificato la **struttura a record** del
|
|||||||
solo pattern testuali): legge esattamente la sezione `members` di un assieme, senza contare
|
solo pattern testuali): legge esattamente la sezione `members` di un assieme, senza contare
|
||||||
occorrenze né rischiare i falsi positivi descritti in questo documento. Implementato in
|
occorrenze né rischiare i falsi positivi descritti in questo documento. Implementato in
|
||||||
`PrepStep/AsmRecordReader.cs`, documentato in [`formato-asm-record.md`](formato-asm-record.md).
|
`PrepStep/AsmRecordReader.cs`, documentato in [`formato-asm-record.md`](formato-asm-record.md).
|
||||||
È il metodo **preferito** in GUI (fase "Trova componenti referenziati"/"Verifica
|
È il metodo **preferito** in GUI (fase 4 "Trova componenti referenziati",
|
||||||
completezza", `Form1.EstraiCodiciReferenziati`) e nello script `CreoBom.cs`:
|
`Form1.EstraiCodiciReferenziati`) e nello script `CreoBom.cs`:
|
||||||
`AssemblyComponentScanner` (l'euristica di questo documento) resta come **fallback**, usato
|
`AssemblyComponentScanner` (l'euristica di questo documento) resta come **fallback**, usato
|
||||||
solo quando `AsmRecordReader` lancia `InvalidDataException` (file che non rispetta il
|
solo quando `AsmRecordReader` lancia `InvalidDataException` (file che non rispetta il
|
||||||
formato validato dal reverse engineering — copertura limitata agli 8 campioni disponibili al
|
formato validato dal reverse engineering — copertura limitata agli 8 campioni disponibili al
|
||||||
@@ -55,8 +55,9 @@ regex: \b\d{8,12}(?:_[A-Za-z]\d{2,4}|[A-Za-z]{0,2})\b
|
|||||||
occorrenze: >= 2
|
occorrenze: >= 2
|
||||||
```
|
```
|
||||||
|
|
||||||
`scripts/ExtractCreoStrings.cs` ha una versione più raffinata della regex e un filtro
|
La regex effettiva in `AssemblyComponentScanner.cs` (usata anche da
|
||||||
aggiuntivo (vedi sotto), non ancora portati in `AssemblyComponentScanner.cs`:
|
`scripts/ExtractCreoStrings.cs` via `#:project`) è una versione più raffinata, con in più il
|
||||||
|
filtro sui falsi positivi descritto sotto:
|
||||||
|
|
||||||
```
|
```
|
||||||
regex: \b\d{8,12}[A-Za-z]{0,2}(?:_[A-Za-z]?\d{2,4})?\b
|
regex: \b\d{8,12}[A-Za-z]{0,2}(?:_[A-Za-z]?\d{2,4})?\b
|
||||||
@@ -285,8 +286,8 @@ livello) serve una BFS:
|
|||||||
Stessa logica di estrazione candidati ed euristica del codice radice (sopra),
|
Stessa logica di estrazione candidati ed euristica del codice radice (sopra),
|
||||||
duplicata in due punti del codice per scopi diversi:
|
duplicata in due punti del codice per scopi diversi:
|
||||||
|
|
||||||
- [`AssemblyComponentScanner.EstraiCodiciReferenziati`](../PrepStep/AssemblyComponentScanner.cs) + `CodiceRadice` — estrazione dei candidati da un file, usata dalla BFS in [`Form1.RisolviAlberoComponenti`](../PrepStep/Form1.cs) (fase "Trova componenti referenziati") e [`Form1.AssiemeCompleto`](../PrepStep/Form1.cs) (fase "Verifica completezza"). Entrambe seminano l'insieme dei codici visitati con codice + radice di ogni file di partenza, e caricano una volta sola tutti i codici noti all'indice (`Form1.CaricaCodiciNoti`) sia per validare i token generici delle parti commerciali sia per risolvere i percorsi dei componenti trovati, senza una query SQL per candidato.
|
- [`AssemblyComponentScanner.EstraiCodiciReferenziati`](../PrepStep/AssemblyComponentScanner.cs) + `CodiceRadice` — estrazione dei candidati da un file, usata come fallback dalla BFS in [`Form1.RisolviAlberoComponenti`](../PrepStep/Form1.cs) (fase 4 "Trova componenti referenziati"). La BFS semina l'insieme dei codici visitati con codice + radice di ogni file di partenza e carica una volta sola i codici noti all'indice (`Form1.CaricaCodiciNoti`, per validare i token generici delle parti commerciali) e tutte le copie di ogni codice (`Form1.CaricaCandidati`, per scegliere il percorso da copiare), senza una query SQL per candidato.
|
||||||
- [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs) e [`scripts/BomViaDb.cs`](../scripts/BomViaDb.cs) — stessa BFS ricorsiva standalone da riga di comando (copia indipendente della stessa euristica, nessun `ProjectReference` verso la GUI). Usati per confrontare l'euristica contro l'albero Creo di un singolo file durante il reverse engineering, senza dover passare dalla UI/dal DB `raw`.
|
- [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs) e [`scripts/BomViaDb.cs`](../scripts/BomViaDb.cs) — stessa BFS ricorsiva standalone da riga di comando, che riusa `AssemblyComponentScanner` del progetto principale via `#:project` (nessuna copia dell'euristica). Usati per confrontare l'euristica contro l'albero Creo di un singolo file durante il reverse engineering, senza dover passare dalla UI/dal DB `raw`.
|
||||||
|
|
||||||
Nota: le query contro `files_ultima_versione` filtrano solo per
|
Nota: le query contro `files_ultima_versione` filtrano solo per
|
||||||
`(cartella_radice, codice, tipo_file)` - se lo stesso codice esiste in più
|
`(cartella_radice, codice, tipo_file)` - se lo stesso codice esiste in più
|
||||||
|
|||||||
@@ -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_<codice>_<timestamp>` |
|
||||||
|
| `.asm` / `.prt` | file Creo/Pro-E di assieme e di parte; il nome è `<codice>.<tipo>.<versione>` |
|
||||||
|
| 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`) |
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
# Guida utente
|
||||||
|
|
||||||
|
Questa guida descrive, in ordine, tutti i passi da fare in **PrepStep - Cevolani** per
|
||||||
|
arrivare dalla distinta base esportata dal gestionale ai file STEP pronti, passando per
|
||||||
|
**Creo Distributed Batch**. Per il dettaglio tecnico di come funziona internamente ogni
|
||||||
|
fase vedi [`architettura.md`](architettura.md); questa guida si ferma a cosa clicca e
|
||||||
|
cosa vede l'utente. La stessa sintesi è disponibile anche dentro l'app da **Aiuto → Guida**
|
||||||
|
(F1).
|
||||||
|
|
||||||
|
## Dove finiscono i file
|
||||||
|
|
||||||
|
Tutto sta nella **cartella di lavoro**, che di default è la cartella di installazione
|
||||||
|
`%LOCALAPPDATA%\Programs\PrepStep`:
|
||||||
|
|
||||||
|
| Cosa | Dove |
|
||||||
|
|---|---|
|
||||||
|
| database `cad_index.db` | sempre nella cartella di installazione |
|
||||||
|
| CSV di ogni distinta importata | cartella di lavoro |
|
||||||
|
| `raw/` — file CAD copiati nelle fasi 3-4 | cartella di lavoro |
|
||||||
|
| `step/` — file STEP esportati da Creo | cartella di lavoro (la indichi tu a Creo) |
|
||||||
|
|
||||||
|
Il file `.xls` del gestionale resta dove l'hai preso: l'app lo legge soltanto.
|
||||||
|
|
||||||
|
- **Cambiare la cartella di lavoro**: **File → Scegli cartella di lavoro...** (viene ricordata
|
||||||
|
agli avvii successivi). Il database resta comunque nella cartella di installazione.
|
||||||
|
- **Aprire la cartella**: `AppData` è nascosta in Esplora file; scrivi
|
||||||
|
`%LOCALAPPDATA%\Programs\PrepStep` nella barra degli indirizzi (vale anche per scegliere
|
||||||
|
`step/` in Creo). Dalla fase 4 si apre `raw/` con **Apri cartella raw**.
|
||||||
|
- **Fare pulizia** (menu **File**): **Pulisci raw e step...** svuota `raw/` e `step/`;
|
||||||
|
**Elimina CSV delle distinte...** elimina i CSV, che altrimenti si accumulano uno per
|
||||||
|
distinta. Nessuno dei due tocca il database. Entrambi chiedono conferma.
|
||||||
|
- **Disinstallando** il programma questi file restano: cancellali a mano se non servono più.
|
||||||
|
|
||||||
|
## Prima di iniziare
|
||||||
|
|
||||||
|
- Serve accesso alla share di rete con i file CAD (di default `X:\`).
|
||||||
|
- Serve l'estrazione "Distinta Base" dal gestionale (Fata): il file `.xls` così come
|
||||||
|
Fata lo salva, con le colonne Componente/Descrizione (foglio `"Distinta Base"`) — va
|
||||||
|
lasciato nel formato/estensione in cui viene esportato, senza risalvarlo o convertirlo
|
||||||
|
in altri formati prima di usarlo nell'app. Sotto l'estensione `.xls` il contenuto può
|
||||||
|
essere XML (SpreadsheetML) o Excel binario vero: l'app riconosce da sola quale dei due
|
||||||
|
è, non serve fare nulla di diverso a seconda del caso.
|
||||||
|
- Se il file segue la convenzione di nome del gestionale (`DBP_<codice>_<timestamp>`),
|
||||||
|
l'app aggiunge da sola l'assieme generale come componente in più — non serve fare
|
||||||
|
nulla di diverso.
|
||||||
|
- Se non vuoi usare la cartella di installazione, scegli la **cartella di lavoro**
|
||||||
|
("File → Scegli cartella di lavoro...") prima di importare la distinta: è lì che
|
||||||
|
finiscono CSV, `raw/` e, dopo l'esportazione con Creo, `step/`.
|
||||||
|
- Se la cartella di lavoro è già stata usata per un'altra distinta, conviene **pulire
|
||||||
|
`raw/` e `step/`** prima di ripartire ("File → Pulisci raw e step..."): evita di
|
||||||
|
confondere i file CAD/STEP della copia precedente con quelli della nuova distinta (il
|
||||||
|
"File → Nuovo" descritto più sotto azzera solo lo stato di CSV/griglie dell'app, non
|
||||||
|
tocca i file già copiati su disco).
|
||||||
|
|
||||||
|
## 1. Database
|
||||||
|
|
||||||
|
Se nella cartella di installazione c'è già un database (`cad_index.db`), l'app lo carica
|
||||||
|
da sola all'avvio: si vede il pallino verde accanto a "Database", il percorso del
|
||||||
|
file e la data dell'ultimo aggiornamento.
|
||||||
|
|
||||||
|
- **Se il pallino è rosso** (nessun database trovato): premi **Aggiorna indice** per
|
||||||
|
scansionare la share (`X:\`) e crearne uno nuovo — l'operazione può richiedere qualche
|
||||||
|
minuto la prima volta, i giri successivi sono più veloci perché aggiornano solo quello
|
||||||
|
che è cambiato.
|
||||||
|
- **Se hai già un database da un'altra postazione o cartella**: premi **Apri DB...** e
|
||||||
|
selezionalo a mano. Vale solo finché non chiudi il programma: al prossimo avvio si riparte
|
||||||
|
dal database nella cartella di installazione. Per usarlo sempre, copialo in
|
||||||
|
`%LOCALAPPDATA%\Programs\PrepStep` come `cad_index.db`.
|
||||||
|
- La **cartella di lavoro** non si imposta qui: vedi [Dove finiscono i file](#dove-finiscono-i-file).
|
||||||
|
|
||||||
|
Premi **Avanti** quando il database è caricato.
|
||||||
|
|
||||||
|
## 2. Importa Distinta Base
|
||||||
|
|
||||||
|
- **1. Carica la distinta**: trascina il file `.xls` esportato da Fata nella finestra,
|
||||||
|
oppure premi **Scegli file distinta...** e selezionalo da Esplora risorse. Le righe importate
|
||||||
|
compaiono nella griglia (Componente/Descrizione) e viene scritto un CSV nella cartella
|
||||||
|
di lavoro.
|
||||||
|
In alternativa scegli **Lista personalizzata**: digita un codice e premi **Invio** (o
|
||||||
|
**Aggiungi**) per inserirlo, ripeti per ogni codice; **Canc** sulla riga selezionata lo
|
||||||
|
toglie. La lista non scrive nessun CSV e, passando da una modalità all'altra, viene azzerata
|
||||||
|
(con conferma). Inserisci anche l'assieme generale, se serve: qui non viene aggiunto da solo.
|
||||||
|
- **2. Verifica la presenza dei file sul server**: compare solo dopo un import riuscito
|
||||||
|
(o dopo il primo codice aggiunto alla lista personalizzata; ogni modifica della lista
|
||||||
|
richiede di rifare la verifica).
|
||||||
|
Premila per cercare ogni componente nel database — le righe della griglia si colorano
|
||||||
|
di verde (trovato) o rosso (non trovato nell'indice).
|
||||||
|
|
||||||
|
**Avanti** si sblocca solo dopo aver premuto **Verifica presenza**, non basta il solo
|
||||||
|
import: è il modo per accorgersi subito se il database è disallineato
|
||||||
|
rispetto alla share (es. indice non aggiornato) prima di andare avanti.
|
||||||
|
|
||||||
|
## 3. Crea cartella raw
|
||||||
|
|
||||||
|
Premi **Crea cartella raw**: per ogni componente della distinta trovato nell'indice,
|
||||||
|
l'app ne copia il file CAD dalla share nella cartella `raw/` (dentro la cartella di
|
||||||
|
lavoro). La griglia mostra il percorso CAD copiato per ogni componente.
|
||||||
|
|
||||||
|
## 4. Trova componenti referenziati
|
||||||
|
|
||||||
|
Gli assiemi (`.asm`) referenziano altri file (sotto-assiemi, parti, componenti soppressi,
|
||||||
|
riferimenti esterni) che Creo deve poter caricare insieme all'assieme principale, anche
|
||||||
|
se non compaiono nella distinta del gestionale. Premi **Trova componenti referenziati**:
|
||||||
|
l'app legge i file `.asm` già in `raw/`, risolve ricorsivamente tutto l'albero e copia
|
||||||
|
anche quei file in `raw/`.
|
||||||
|
|
||||||
|
Si apre una finestra con l'elenco di quanto trovato e copiato (PRT e sotto-assiemi), con
|
||||||
|
in fondo eventuali codici referenziati che non corrispondono a nessun file indicizzato
|
||||||
|
(possono essere disegni, componenti davvero mancanti, o falsi positivi della scansione —
|
||||||
|
non bloccano il lavoro, ma vale la pena controllarli). Da qui puoi anche aprire
|
||||||
|
direttamente la cartella `raw/` con **Apri cartella raw**.
|
||||||
|
|
||||||
|
Questo passo è opzionale se la distinta contiene solo parti singole senza sotto-assiemi:
|
||||||
|
in quel caso si può passare direttamente alla fase successiva.
|
||||||
|
|
||||||
|
## 5. Verifica completezza e Creo Distributed Batch
|
||||||
|
|
||||||
|
L'ultima fase mostra l'elenco dei file `.asm` della distinta già presenti in `raw/`,
|
||||||
|
trascinabili. Da qui il passaggio a Creo è manuale, fuori dall'app:
|
||||||
|
|
||||||
|
1. Apri **Creo Distributed Batch** (dal menu Start di Windows, o dal file
|
||||||
|
`ptcdbatch.bat` nella cartella `bin` dell'installazione Creo).
|
||||||
|
2. Crea un nuovo gruppo di attività per l'esportazione STEP (menu **Actions → New Task
|
||||||
|
Group**), scegliendo il tipo di attività per l'export STEP — se in azienda è già
|
||||||
|
presente un profilo/gruppo predefinito per l'export STEP, riusa quello invece di
|
||||||
|
crearne uno nuovo.
|
||||||
|
3. Aggiungi i file: trascina i `.asm` dalla finestra dell'app (o direttamente da Esplora
|
||||||
|
risorse, dalla cartella `raw/`) sul gruppo di attività, oppure sfoglia e selezionali a
|
||||||
|
mano da Distributed Batch.
|
||||||
|
4. Verifica che il gruppo di attività punti al `config.pro`/profilo di export STEP giusto
|
||||||
|
(sezione **Configuration Files** del gruppo) — di norma è già impostato una volta sola
|
||||||
|
per tutti, non richiede modifiche a ogni giro.
|
||||||
|
5. Avvia l'elaborazione (**Start**/**Run**): Distributed Batch legge ogni `.asm`, con
|
||||||
|
tutto l'albero di sotto-componenti già raccolto in `raw/` nelle fasi 3-4, ed esporta i
|
||||||
|
file STEP nella cartella `step/` (dentro la cartella di lavoro, accanto a `raw/`).
|
||||||
|
|
||||||
|
I nomi esatti dei menu possono variare leggermente a seconda della versione di Creo e di
|
||||||
|
come Distributed Batch è già configurato in azienda — questi passi descrivono il flusso
|
||||||
|
generale (fonte: [documentazione e community PTC](https://www.ptc.com/en/support/article/CS28241));
|
||||||
|
in caso di dubbio su profili/config già pronti, chiedi a chi ha impostato Distributed
|
||||||
|
Batch la prima volta.
|
||||||
|
|
||||||
|
**Se Distributed Batch fallisce con `[-4] Dati di input non completi`**, quasi sempre
|
||||||
|
manca un file nell'albero dei componenti — Creo non dice quale. Torna alla fase 4 e
|
||||||
|
controlla l'elenco dei "non trovati": è il punto di partenza per capire cosa recuperare
|
||||||
|
prima di riprovare.
|
||||||
|
|
||||||
|
## Ripartire con una nuova distinta
|
||||||
|
|
||||||
|
Quando devi processare un'altra distinta, usa **File → Nuovo** (Ctrl+N): svuota le griglie e
|
||||||
|
la verifica delle fasi 2-5 e riporta al passo 2, senza dover ricaricare il database né
|
||||||
|
cambiare la cartella di lavoro. Non cancella nessun file su disco: CSV, `raw/` e `step/`
|
||||||
|
restano (vedi sotto per ripulirli).
|
||||||
|
|
||||||
|
Per ripulire le cartelle `raw/` e `step/` della cartella di lavoro prima di un nuovo giro
|
||||||
|
(senza toccare l'indice), usa **File → Pulisci raw e step...**.
|
||||||
|
|
||||||
|
Ogni distinta importata lascia un CSV nella cartella di lavoro: per eliminarli tutti usa
|
||||||
|
**File → Elimina CSV delle distinte...** (i file `.xls` del gestionale non vengono toccati).
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Rilasciare una nuova versione
|
||||||
|
|
||||||
|
Procedura per produrre il setup `PrepStep-Setup-<versione>.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 `<Version>` 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-<versione>.exe`; la versione è letta
|
||||||
|
dall'exe, quindi coincide con `<Version>`. 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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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...**.
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
; Setup per-utente di PrepStep - Cevolani: installa in %LOCALAPPDATA%\Programs\PrepStep
|
||||||
|
; senza richiedere diritti di amministratore.
|
||||||
|
; Prima: dotnet publish (vedi README), poi: iscc installer\PrepStep.iss
|
||||||
|
|
||||||
|
#define Exe "..\PrepStep\publish\PrepStep.exe"
|
||||||
|
; Versione letta dall'exe (<Version> nel csproj), tolto l'eventuale "+<commit>" di .NET.
|
||||||
|
#define AppVersion GetStringFileInfo(Exe, "ProductVersion")
|
||||||
|
#if Pos("+", AppVersion) > 0
|
||||||
|
#define AppVersion Copy(AppVersion, 1, Pos("+", AppVersion) - 1)
|
||||||
|
#endif
|
||||||
|
; Non cambiare AppGuid: è quello che fa riconoscere a Windows gli aggiornamenti come la stessa app.
|
||||||
|
#define AppGuid "FAC1B760-4735-422F-86F0-888484CF2830"
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
AppId={{{#AppGuid}}
|
||||||
|
AppName=PrepStep - Cevolani
|
||||||
|
AppVersion={#AppVersion}
|
||||||
|
AppPublisher=Cevolani
|
||||||
|
AppCopyright=Copyright (c) 2026 Davide Grilli
|
||||||
|
; Con PrivilegesRequired=lowest {autopf} diventa %LOCALAPPDATA%\Programs.
|
||||||
|
PrivilegesRequired=lowest
|
||||||
|
DefaultDirName={autopf}\PrepStep
|
||||||
|
DisableDirPage=yes
|
||||||
|
DisableProgramGroupPage=yes
|
||||||
|
ArchitecturesAllowed=x64compatible
|
||||||
|
ArchitecturesInstallIn64BitMode=x64compatible
|
||||||
|
SetupIconFile=..\PrepStep\app.ico
|
||||||
|
UninstallDisplayIcon={app}\PrepStep.exe
|
||||||
|
UninstallDisplayName=PrepStep - Cevolani
|
||||||
|
OutputDir=Output
|
||||||
|
OutputBaseFilename=PrepStep-Setup-{#AppVersion}
|
||||||
|
Compression=lzma2
|
||||||
|
SolidCompression=yes
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
Name: "it"; MessagesFile: "compiler:Languages\Italian.isl"
|
||||||
|
|
||||||
|
[Tasks]
|
||||||
|
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
Source: "{#Exe}"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|
||||||
|
[InstallDelete]
|
||||||
|
; Collegamenti col vecchio nome dell'app (fino a 0.9.0), altrimenti restano nel menu Start.
|
||||||
|
Type: files; Name: "{autoprograms}\Cevolani StepPrep.lnk"
|
||||||
|
Type: files; Name: "{autoprograms}\Disinstalla Cevolani StepPrep.lnk"
|
||||||
|
Type: files; Name: "{autodesktop}\Cevolani StepPrep.lnk"
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Name: "{autoprograms}\PrepStep - Cevolani"; Filename: "{app}\PrepStep.exe"
|
||||||
|
Name: "{autodesktop}\PrepStep - Cevolani"; Filename: "{app}\PrepStep.exe"; Tasks: desktopicon
|
||||||
|
Name: "{autoprograms}\Disinstalla PrepStep - Cevolani"; Filename: "{uninstallexe}"
|
||||||
|
|
||||||
|
[Run]
|
||||||
|
Filename: "{app}\PrepStep.exe"; Description: "{cm:LaunchProgram,PrepStep - Cevolani}"; Flags: nowait postinstall skipifsilent
|
||||||
|
|
||||||
|
[Code]
|
||||||
|
{ Se l'app è già installata, chiede se aggiornarla o disinstallarla (Inno da solo aggiornerebbe
|
||||||
|
sempre, senza offrire la disinstallazione). In modalità silenziosa aggiorna senza chiedere. }
|
||||||
|
function InitializeSetup(): Boolean;
|
||||||
|
var
|
||||||
|
Disinstallatore: String;
|
||||||
|
Codice: Integer;
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
if WizardSilent then
|
||||||
|
Exit;
|
||||||
|
if not RegQueryStringValue(HKCU,
|
||||||
|
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{' + '{#AppGuid}' + '}_is1',
|
||||||
|
'UninstallString', Disinstallatore) then
|
||||||
|
Exit;
|
||||||
|
case MsgBox('PrepStep - Cevolani è già installato.' + #13#10#13#10 +
|
||||||
|
'Sì: aggiorna / reinstalla' + #13#10 +
|
||||||
|
'No: disinstalla' + #13#10 +
|
||||||
|
'Annulla: esci senza fare nulla', mbConfirmation, MB_YESNOCANCEL) of
|
||||||
|
IDNO:
|
||||||
|
begin
|
||||||
|
Exec(RemoveQuotes(Disinstallatore), '', '', SW_SHOW, ewWaitUntilTerminated, Codice);
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
IDCANCEL:
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
@@ -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), <Version> 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<string>();
|
||||||
|
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<string> 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"), @"<Version>(.+?)</Version>").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> {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;
|
||||||
Reference in new issue
Block a user