4 Commits
Author SHA1 Message Date
davideandClaude Opus 5.5 5fac2edf52 Rilascia la 1.0.0: licenza, CHANGELOG e documentazione riallineata al codice
Versione portata a 1.0.0 (csproj, CLAUDE.md) e voce [1.0.0] nel
CHANGELOG con tutto il lavoro dalla 0.9.0: setup per-utente, nuovo
nome "PrepStep - Cevolani", DB fisso nella cartella di installazione,
"Elimina CSV delle distinte", guide aggiornate.

Nuova LICENSE proprietaria (Davide Grilli, tutti i diritti riservati),
riportata anche in <Copyright> dell'exe e AppCopyright del setup.
README: sequenza completa di comandi per generare il setup.

Documentazione riallineata al codice dopo un controllo file per file:
nomi delle fasi come nella UI, "File → Nuovo" che non cancella file,
mappa dei file e test nel README, riferimenti ad AssiemeCompleto (non
esiste più) e ruolo di CaricaCodiciNoti/CaricaCandidati in fase 4.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 08:53:35 +02:00
davideandClaude Opus 5.5 4ae3d0e087 Aggiungi "Elimina CSV delle distinte" e allinea le guide alla cartella di installazione
Nuova voce "File → Elimina CSV delle distinte..." (EliminaCsv): elimina
i *.csv al primo livello della cartella di lavoro, che altrimenti si
accumulano uno per distinta importata. Chiede conferma mostrando
l'elenco dei file; gli .xls del gestionale e raw/step non sono toccati.

Guida utente e guida in-app: nuova sezione "Dove finiscono i file"
(DB sempre nella cartella di installazione, CSV/raw/step nella cartella
di lavoro, comandi di pulizia, come aprire la cartella nascosta in
AppData), fase 1 allineata al DB fisso e ad "Apri DB..." solo per la
sessione. Tolta dalla guida utente la sezione di installazione: chi la
legge ha già il programma installato (resta nel README).

Doc aggiornati nello stesso commit: CLAUDE.md, docs/architettura.md,
docs/guida-utente.md, CHANGELOG.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 08:39:58 +02:00
davideandClaude Opus 5.5 64ff1d8770 Rinomina l'app in "PrepStep - Cevolani"
Nome mostrato all'utente cambiato da "Cevolani StepPrep" a "PrepStep -
Cevolani": titolo finestra, <Product>/<AssemblyTitle>, guida in-app,
setup (nome, collegamenti nel menu Start, voce in App installate).
Aggiornando, il setup rimuove i collegamenti col vecchio nome
([InstallDelete]) per non lasciarli doppi nel menu Start.

Doc aggiornati nello stesso commit: README.md, CLAUDE.md,
docs/architettura.md, docs/guida-utente.md, CHANGELOG.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 08:33:34 +02:00
davideandClaude Opus 5.5 57020f9964 Aggiungi il setup per-utente e fissa il DB nella cartella di installazione
Nuovo installer/PrepStep.iss (Inno Setup 6): installa in
%LOCALAPPDATA%\Programs\PrepStep senza diritti di amministratore, con
collegamenti nel menu Start al programma e al disinstallatore. Se l'app
è già installata chiede se aggiornarla o disinstallarla (Inno da solo
aggiornerebbe sempre); con /VERYSILENT aggiorna senza chiedere.

Il DB è ora sempre cad_index.db accanto all'eseguibile, cioè nella
cartella di installazione: non si ricorda più l'ultimo DB aperto
(ultimo_db.txt) e "Apri DB..." vale solo per la sessione. Senza una
cartella di lavoro salvata si propone la cartella di installazione
invece di quella del DB.

Doc aggiornati nello stesso commit: README.md, CLAUDE.md,
docs/architettura.md, docs/db-cad-index.md, docs/guida-utente.md,
CHANGELOG.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 08:29:03 +02:00
14 changed files with 507 additions and 163 deletions

No files matched your search

+3
View File
@@ -40,3 +40,6 @@ publish/
# Impostazioni locali dell'editor
.vscode/
# Setup generato da Inno Setup (installer/PrepStep.iss)
installer/Output/
+46
View File
@@ -5,6 +5,52 @@ Tutte le modifiche rilevanti di questo progetto sono documentate in questo file.
Il formato segue [Keep a Changelog](https://keepachangelog.com/it/1.1.0/), e il progetto
aderisce a [Semantic Versioning](https://semver.org/lang/it/).
## [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
+19 -9
View File
@@ -5,9 +5,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Cos'è questo progetto
`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
alla versione, `<Version>`, attualmente `0.9.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
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
@@ -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
```
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
`dotnet test` da dentro quella cartella. Riferimento al progetto principale
via `ProjectReference` + `InternalsVisibleTo` (in `PrepStep.csproj`), così
@@ -65,7 +72,8 @@ atteso).
## 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
che conta per capire il prodotto.
@@ -76,10 +84,11 @@ designer visuale):
1. **Database** — carica/aggiorna `cad_index.db` (`Indexer.ScanAsync`, radice disco di
default `X:\` hardcoded in `RadiceDiscoDefault`); un pallino verde/rosso accanto al titolo
indica se un DB è caricato. La **cartella di lavoro** (destinazione di CSV/`raw/`/`step/`)
non è più mostrata qui — proposta dall'ultima usata o dalla cartella del DB se 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 risoluzione del
percorso DB.
non è più mostrata qui — proposta dall'ultima usata o dalla cartella di installazione se
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
componente;descrizione nella cartella di lavoro (da file scelto o trascinato); se il file
segue la convenzione del gestionale (`DBP_<codice>_<timestamp>`), aggiunge l'assieme
@@ -114,7 +123,8 @@ per ripartire con una nuova distinta, senza toccare DB/cartella di lavoro della
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); "Aiuto → Guida" (F1) apre una sintesi interattiva delle 5 fasi (`GuidaForm.cs`).
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/`)
@@ -131,5 +141,5 @@ intercambiabili:
- `ExtractCreoStrings.cs`/`BomViaDb.cs` usano `AssemblyComponentScanner` (euristica testuale,
`docs/formato-asm.md`) e risolvono i sottoassiemi contro `cad_index.db`.
- `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`.
+17
View File
@@ -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.
+55 -45
View File
@@ -25,6 +25,7 @@ public partial class Form1 : Form
private readonly MenuStrip _menu = new();
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
@@ -74,7 +75,7 @@ public partial class Form1 : Form
AllowDrop = true;
// Versione letta dall'assembly (<Version> nel csproj) invece che duplicata qui a mano.
var versione = typeof(Form1).Assembly.GetName().Version;
Text = $"Cevolani StepPrep {versione?.ToString(3)}";
Text = $"PrepStep - Cevolani {versione?.ToString(3)}";
MinimumSize = new Size(760, 480);
Font = new Font("Segoe UI", 9.5f);
// Stessa icona di ApplicationIcon nel csproj: la si rilegge dall'exe compilato invece
@@ -150,6 +151,9 @@ public partial class Form1 : Form
_mnuPulisci.Click += (_, _) => PulisciRawEStep();
mnuFile.DropDownItems.Add(_mnuPulisci);
_mnuEliminaCsv.Click += (_, _) => EliminaCsv();
mnuFile.DropDownItems.Add(_mnuEliminaCsv);
var mnuAiuto = new ToolStripMenuItem("Aiuto");
var mnuGuida = new ToolStripMenuItem("Guida") { ShortcutKeys = Keys.F1 };
mnuGuida.Click += (_, _) => new GuidaForm().ShowDialog(this);
@@ -770,6 +774,47 @@ public partial class Form1 : Form
"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
/// versione, un solo file per tipo - stesso criterio di dedup per data_modifica DESC
/// usato ovunque in questa classe). Caricato in un'unica query invece che una per
@@ -1130,20 +1175,18 @@ public partial class Form1 : Form
private static readonly string CartellaImpostazioni = Path.Combine(
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");
/// <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
/// trova il DB (più sensata dell'eseguibile: quella è dove l'utente tiene il proprio lavoro,
/// non dov'è installata la app). Richiede quindi che InizializzaDatabase sia già stata
/// chiamata. Sempre modificabile con "File → Scegli cartella di lavoro...".</summary>
/// c'è ancora una preferenza salvata (o punta a una cartella cancellata), la cartella di
/// installazione (%LOCALAPPDATA%\Programs\PrepStep col setup). Sempre modificabile con
/// "File → Scegli cartella di lavoro...".</summary>
private void InizializzaCartellaLavoro()
{
var ultima = LeggiUltimaCartellaLavoro();
_cartellaLavoro = ultima is not null && Directory.Exists(ultima)
? ultima
: (_dbPath is not null ? Path.GetDirectoryName(_dbPath) : null) ?? AppContext.BaseDirectory;
: AppContext.BaseDirectory;
AggiornaStatoAvanti();
}
@@ -1190,15 +1233,10 @@ public partial class Form1 : Form
private void InizializzaDatabase()
{
var candidati = new[]
{
LeggiUltimoDbUsato(),
Path.Combine(AppContext.BaseDirectory, "cad_index.db"),
Path.Combine(Directory.GetCurrentDirectory(), "cad_index.db"),
};
_dbPath = candidati.FirstOrDefault(p => p is not null && File.Exists(p));
if (_dbPath is null)
// Il DB sta sempre nella cartella di installazione; "Apri DB..." ne carica un altro
// solo per la sessione corrente, senza ricordarlo al prossimo avvio.
string predefinito = Path.Combine(AppContext.BaseDirectory, "cad_index.db");
if (!File.Exists(predefinito))
{
_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.";
@@ -1209,7 +1247,7 @@ public partial class Form1 : Form
return;
}
CaricaDatabase(_dbPath);
CaricaDatabase(predefinito);
}
private void ScegliDatabase()
@@ -1242,7 +1280,6 @@ public partial class Form1 : Form
? $"Ultimo aggiornamento indice: {ultimaScansione.ToLocalTime():dd/MM/yyyy HH:mm}"
: "Ultimo aggiornamento indice: sconosciuto";
_lblStatus.Text = "Pronto.";
SalvaUltimoDbUsato(path);
}
catch (Exception ex)
{
@@ -1254,33 +1291,6 @@ public partial class Form1 : Form
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()
{
string dbPath = _dbPath ?? Path.Combine(AppContext.BaseDirectory, "cad_index.db");
+84 -16
View File
@@ -1,3 +1,5 @@
using System.Runtime.InteropServices;
namespace PrepStep;
/// <summary>Guida interattiva richiamabile da "Aiuto ▸ Guida" (F1): stessa guida completa di
@@ -32,12 +34,34 @@ public class GuidaForm : Form
private static readonly Sezione[] Sezioni =
{
new("Panoramica",
"Cevolani StepPrep prepara i file CAD Pro-ENGINEER/Creo da passare a Creo " +
"PrepStep - Cevolani prepara i file CAD Pro-ENGINEER/Creo da passare a Creo " +
"Distributed Batch per l'esportazione STEP: indicizza i file di una share di rete " +
"e guida attraverso 5 fasi, una alla volta, con \"Avanti\"/\"Indietro\".",
NotaTesto: "\"File ▸ Nuovo\" (Ctrl+N) azzera distinta/raw/verifica per ripartire " +
NotaTesto: "\"File ▸ Nuovo\" (Ctrl+N) azzera lo stato di distinta/raw/verifica per ripartire " +
"con una nuova distinta base, senza dover ricaricare il database."),
new("Dove finiscono i file",
"Tutto sta nella cartella di lavoro, che di default è la cartella di " +
"installazione (%LOCALAPPDATA%\\Programs\\PrepStep):",
Passi:
[
"Il database cad_index.db sta sempre nella cartella di installazione.",
"Il CSV di ogni distinta importata, \"raw\" (file CAD delle fasi 3-4) e " +
"\"step\" (STEP esportati da Creo, cartella che indichi tu a Creo) stanno " +
"nella cartella di lavoro. Il file .xls del gestionale resta dove l'hai " +
"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:
@@ -50,9 +74,10 @@ public class GuidaForm : Form
"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.",
"Conviene impostare la cartella di lavoro giusta (fase 1, o \"File ▸ Scegli " +
"cartella di lavoro...\") prima di importare la distinta: è lì che " +
"finiscono CSV, \"raw\" e, dopo l'esportazione con Creo, \"step\".",
"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 " +
@@ -60,10 +85,18 @@ public class GuidaForm : Form
]),
new("1. Database",
"Carica o aggiorna l'indice \"cad_index.db\" a partire dalle cartelle di primo " +
"livello del disco/share (radice di default X:\\). Un pallino verde/rosso indica " +
"se un database è caricato. La cartella di lavoro, dove finiscono CSV, \"raw\" e " +
"\"step\", si cambia da \"File ▸ Scegli cartella di lavoro...\"."),
"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:",
@@ -127,19 +160,21 @@ public class GuidaForm : Form
NotaAttenzione: true),
new("Ripartire da capo",
"Quando devi processare un'altra distinta, usa \"File ▸ Nuovo\" (Ctrl+N): azzera " +
"CSV, raw e verifica delle fasi 2-5 e riporta al passo 2, senza dover ricaricare " +
"il database o la cartella di lavoro (restano quelli già impostati in fase 1).",
"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...\"."),
"step...\"; per eliminare i CSV che ogni distinta importata lascia nella " +
"cartella di lavoro, usa \"File ▸ Elimina CSV delle distinte...\"."),
};
public GuidaForm()
{
Text = "Guida";
ClientSize = new Size(780, 520);
MinimumSize = new Size(580, 400);
ClientSize = new Size(1000, 680);
MinimumSize = new Size(640, 440);
StartPosition = FormStartPosition.CenterParent;
Font = new Font("Segoe UI", 9.5f);
try
@@ -191,10 +226,11 @@ public class GuidaForm : Form
colore, TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis);
};
var testo = new RichTextBox
var testo = new TestoSolaLettura
{
Dock = DockStyle.Fill,
ReadOnly = true,
TabStop = false,
BorderStyle = BorderStyle.None,
BackColor = Color.White,
Font = new Font(Font.FontFamily, 10f),
@@ -278,4 +314,36 @@ public class GuidaForm : Form
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);
}
}
}
+4 -3
View File
@@ -8,10 +8,11 @@
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<ApplicationIcon>app.ico</ApplicationIcon>
<AssemblyTitle>Cevolani StepPrep</AssemblyTitle>
<Product>Cevolani StepPrep</Product>
<AssemblyTitle>PrepStep - Cevolani</AssemblyTitle>
<Product>PrepStep - Cevolani</Product>
<Company>Cevolani</Company>
<Version>0.9.0</Version>
<Copyright>Copyright (c) 2026 Davide Grilli</Copyright>
<Version>1.0.0</Version>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
+67 -19
View File
@@ -1,4 +1,4 @@
# Cevolani StepPrep
# PrepStep - Cevolani
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
@@ -9,15 +9,16 @@ passaggio, non lo sostituisce.
## Funzionalità
1. **Indice CAD** — all'avvio carica automaticamente l'ultimo `cad_index.db`
usato (ricordato tra un avvio e l'altro). Se manca, si può selezionare un DB
esistente con **Apri DB...** o generarne uno nuovo con **Aggiorna
indice**, che scansiona il disco (di default `X:\`, tutte le cartelle di primo
livello che trova) e indicizza tutti i file. La **cartella di lavoro** (dove
finiscono il CSV e la cartella `raw/`; gli STEP esportati con Creo vanno in
`step/`, dentro la stessa cartella) è proposta automaticamente e ricordata tra
un avvio e l'altro, modificabile in qualunque momento da **File → Scegli
cartella di lavoro...**.
1. **Indice CAD** — all'avvio carica automaticamente `cad_index.db` dalla
cartella di installazione (`%LOCALAPPDATA%\Programs\PrepStep`). Se manca, lo si
crea con **Aggiorna indice**, oppure si apre un altro DB per la sola sessione
con **Apri DB...**. **Aggiorna indice** scansiona il disco (di default `X:\`,
tutte le cartelle di primo livello che trova) e indicizza tutti i file. La
**cartella di lavoro** (dove finiscono il CSV e la cartella `raw/`; gli STEP
esportati con Creo vanno in `step/`, dentro la stessa cartella) è di default la
cartella di installazione, modificabile in qualunque momento da **File → Scegli
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
`.xls`/`.xml` del gestionale (foglio "Distinta Base", colonne
Componente/Descrizione): viene generato un CSV nella cartella di lavoro, con
@@ -34,7 +35,7 @@ passaggio, non lo sostituisce.
`raw/` per trovare ricorsivamente i sotto-assiemi/parti che li
compongono, copiandoli a loro volta in `raw/` (passo opzionale se la
distinta contiene solo parti singole).
5. **Trascina su Creo** — mostra i file `.asm` della distinta già in `raw/`,
5. **Verifica completezza** — mostra i file `.asm` della distinta già in `raw/`,
trascinabili su Creo Distributed Batch per l'esportazione STEP (passo
manuale, fuori da quest'app): scrive in `step/`, dentro la cartella di
lavoro.
@@ -86,18 +87,59 @@ L'eseguibile risultante è in `PrepStep\publish\PrepStep.exe` — un unico
file, si copia e si lancia su un'altra macchina Windows senza bisogno di .NET
installato.
## Setup di installazione
Per distribuire l'app agli utenti si genera un setup con
[Inno Setup 6](https://jrsoftware.org/isinfo.php).
Una volta sola, per installare Inno Setup (per l'utente corrente, senza admin):
```powershell
winget install --id JRSoftware.InnoSetup -e --scope user
```
A ogni release, dalla root del repo:
```powershell
# 1. aggiorna <Version> in PrepStep\PrepStep.csproj e la voce in CHANGELOG.md
# 2. chiudi l'app se è aperta (l'exe in uso blocca la build)
dotnet publish PrepStep -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o PrepStep\publish
& "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" installer\PrepStep.iss
```
Produce `installer\Output\PrepStep-Setup-<versione>.exe` (versione letta dall'exe, cioè
`<Version>` del csproj): è l'unico file da distribuire. Per provarlo sul proprio PC basta
lanciarlo (`.\installer\Output\PrepStep-Setup-<versione>.exe`). Il setup:
- installa **solo per l'utente corrente**, in `%LOCALAPPDATA%\Programs\PrepStep`, senza
chiedere i diritti di amministratore;
- crea nel menu Start i collegamenti al programma e al disinstallatore (quello sul desktop
è facoltativo);
- compare in "Impostazioni → App installate", da cui si disinstalla.
Se l'app è già installata, il setup chiede prima cosa fare: **Sì** aggiorna/reinstalla (DB e
impostazioni restano), **No** disinstalla, **Annulla** esce. Installazione silenziosa (aggiorna
senza chiedere) — es. per distribuirla da script:
`PrepStep-Setup-<versione>.exe /VERYSILENT /SUPPRESSMSGBOXES`.
## Struttura del progetto
Tutto il codice è in `PrepStep/` (unico progetto nella solution, GUI e
indicizzazione integrate):
Il codice dell'app è tutto in `PrepStep/` (GUI e indicizzazione integrate); accanto ci sono
il progetto di test `PrepStep.Tests/`, gli script standalone `scripts/` e lo script del setup
`installer/`. Mappa file-per-file completa in [`docs/architettura.md`](docs/architettura.md).
- `Program.cs` — entry point.
- `Form1.cs` — finestra principale (indice CAD, import Distinta Base, griglia
risultati); UI costruita interamente in codice, nessun designer visuale.
- `Form1.cs` — finestra principale, wizard a 5 fasi e menu; UI costruita interamente in
codice (`Form1.Designer.cs` è solo lo scheletro del template).
- `Indexer.cs` — scansione del disco e scrittura/aggiornamento del DB SQLite.
- `DistintaBaseImporter.cs` — parsing del file Distinta Base (SpreadsheetML
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
trascinati nella root e le cartelle `raw/`/`step/` sono rigenerabili e non
@@ -105,12 +147,18 @@ sono tracciati in git (vedi `.gitignore`).
## Test
`PrepStep.Tests/` contiene unit test (regex di estrazione dei componenti
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
`PrepStep.Tests/` (xUnit) contiene unit test (lettore esatto e euristica di
estrazione dei componenti dai `.asm`, parsing del nome file Pro-E, import della
distinta) e integration test (scansione su disco, schema/vista SQL reale con focus
sui bug di case-sensitivity/duplicati già risolti, pipeline end-to-end). Da
eseguire:
```powershell
cd PrepStep.Tests
dotnet test
```
## Licenza
Software proprietario, © 2026 Davide Grilli, tutti i diritti riservati: vedi
[`LICENSE`](LICENSE).
+48 -21
View File
@@ -1,8 +1,8 @@
# Architettura di PrepStep (Cevolani StepPrep)
# Architettura di PrepStep
Panoramica di tutti i file sorgente e di come collaborano. `PrepStep` è il nome
tecnico del progetto (cartella, namespace, csproj, eseguibile `PrepStep.exe`);
**Cevolani StepPrep** è il nome mostrato all'utente finale (titolo finestra, icona,
**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
formato binario `.asm` vedi `docs/formato-asm.md` (euristica testuale) e
`docs/formato-asm-record.md` (parser esatto dei record); per la parallelizzazione della
@@ -35,7 +35,8 @@ successiva.
**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
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
due colonne `componente;descrizione`. Se il nome del file segue la convenzione
@@ -107,20 +108,27 @@ trascinano i `.asm` su Creo Distributed Batch, che scrive in `step/`.
fase 1: database e cartella di lavoro restano quelli già caricati, non serve rifare lo scan;
"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); "Aiuto →
`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:
- `TrovaPercorsiCad` / `CaricaCodiciNoti` — query sul DB (`files_ultima_versione`), la
seconda carica tutti i codici prt/asm noti in un colpo solo (usata sia per risolvere i
componenti sia da `AssemblyComponentScanner` per validare i token generici).
- `TrovaPercorsiCad` / `CaricaCodiciNoti` — query sul DB (`files_ultima_versione`): la
prima risolve i componenti del CSV (fase 3), la seconda carica tutti i codici prt/asm noti
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
silenziosamente quelli irraggiungibili (share offline/permessi).
- `PulisciRawEStep` — svuota `raw/` e `step/` nella cartella di lavoro (manutenzione,
richiede conferma).
- `InizializzaDatabase` / `ScegliDatabase` — localizzazione del DB (ultimo usato, salvato in
`%AppData%/PrepStep/ultimo_db.txt`, o `cad_index.db` accanto all'eseguibile).
- `EliminaCsv` — elimina i `*.csv` generati dall'import (uno per distinta) nella cartella di
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
lavoro (`ultima_cartella_lavoro.txt`), fallback `AppContext.BaseDirectory` se non c'è
ancora nulla di salvato: nessuna dipendenza da un marker di repo, funziona identica
@@ -236,38 +244,57 @@ completi".
sotto-assiemi trovati e copiati in `raw/`, più i codici referenziati che non corrispondono a
nessun file indicizzato (possono essere disegni, componenti davvero mancanti, o falsi
positivi della scansione `.asm`).
- **GuidaForm.cs** — guida interattiva ("Aiuto → Guida"/F1): lista delle 5 fasi a sinistra,
testo a destra; sintesi, il dettaglio resta solo in questi docs.
- **GuidaForm.cs** — guida interattiva ("Aiuto → Guida"/F1): elenco delle sezioni a sinistra
(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
- **DB** (`cad_index.db`, SQLite) — vedi tabella `files`/vista `files_ultima_versione` sopra.
Percorso ricordato tra un avvio e l'altro in `%AppData%/PrepStep/ultimo_db.txt`
(`SalvaUltimoDbUsato`/`LeggiUltimoDbUsato`), scritto ogni volta che si carica un DB
(all'avvio se già noto, oppure da "Apri DB..." o al termine di "Aggiorna indice"); se il
file non c'è o punta a un percorso cancellato, `InizializzaDatabase` ripiega su
`cad_index.db` accanto all'eseguibile o nella working directory. Radice disco di scansione
Sempre `cad_index.db` accanto all'eseguibile (`AppContext.BaseDirectory`, cioè la cartella
di installazione): `InizializzaDatabase` carica solo quello, e "Aggiorna indice" lo crea lì
se manca. "Apri DB..." carica un altro file solo per la sessione corrente, senza
ricordarlo. Radice disco di scansione
di default `X:\` (`Form1.RadiceDiscoDefault`, hardcoded).
- **Cartella di lavoro** — stesso meccanismo del DB sopra, salvata in
- **Cartella di lavoro** — ricordata tra un avvio e l'altro in
`%AppData%/PrepStep/ultima_cartella_lavoro.txt`; se non c'è una preferenza salvata (o punta
a una cartella cancellata) si propone la cartella del DB caricato, o in ultima istanza
`AppContext.BaseDirectory`; cambiabile in qualunque momento da "File → Scegli cartella di
a una cartella cancellata) si propone la cartella di installazione
(`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 nel `README.md`.
## Script standalone (`scripts/`)
C# file-based apps (.NET 10, `dotnet run scripts/Nome.cs -- arg1 arg2`, nessun `.csproj`,
dipendenze extra con `#:package Nome@versione` in testa al file):
- **ExtractCreoStrings.cs** — BOM di un `.asm` risolvendo ricorsivamente i sotto-assiemi
contro `cad_index.db` (stessa logica di `AssiemeCompleto` in Form1, solo codici numerici
contro `cad_index.db` con `AssemblyComponentScanner` (via `#:project`, solo codici numerici
Pro-E).
- **BomViaDb.cs** — come sopra ma con la tokenizzazione generica validata contro il DB, per
intercettare anche i nomi alfabetici delle parti commerciali.
- **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
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
+25 -25
View File
@@ -29,10 +29,12 @@ componenti e nel filtro per cartella).
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()
OVER (PARTITION BY ... ORDER BY versione DESC, data_modifica DESC, percorso_completo)`. Tutte
le ricerche di componenti (`Form1.TrovaPercorsiCad`, `Form1.CaricaCodiciNoti`) interrogano
questa vista, non `files` direttamente, così un componente con più versioni sulla share
risolve sempre all'ultima. Deliberatamente una `ROW_NUMBER()` invece di un self-join con
OVER (PARTITION BY ... ORDER BY versione DESC, data_modifica DESC, percorso_completo)`. Le
ricerche per codice (`Form1.TrovaPercorsiCad` in fase 3, `Form1.CaricaCodiciNoti`)
interrogano questa vista, non `files` direttamente, così un componente con più versioni sulla
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()`
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.
1. Crea lo schema se non esiste (`CREATE TABLE/VIEW IF NOT EXISTS`) — quindi funziona sia su
un DB vuoto (prima esecuzione) sia su uno esistente (aggiornamento incrementale).
1. Crea lo schema se non esiste (`CREATE TABLE/INDEX IF NOT EXISTS`; la vista invece è
`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
`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
@@ -109,24 +112,20 @@ sulla share, non tipicamente cercato dalla UI).
## Come l'app trova il DB all'avvio
`Form1.InizializzaDatabase` prova, in ordine, i seguenti candidati e usa il primo che esiste
su disco:
`Form1.InizializzaDatabase` carica sempre e solo `cad_index.db` accanto all'eseguibile
(`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
`%AppData%/PrepStep/ultimo_db.txt` (`Form1.LeggiUltimoDbUsato`/`SalvaUltimoDbUsato`)
2. `cad_index.db` accanto all'eseguibile (`AppContext.BaseDirectory`)
3. `cad_index.db` nella working directory corrente
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`).
Se non esiste, la fase 1 resta bloccata su "Database non trovato" finché l'utente non preme
"Aggiorna indice" (`Form1.AggiornaIndice`, che crea `cad_index.db` da zero accanto
all'eseguibile) o "Apri DB..." (`Form1.ScegliDatabase`, file picker libero: il DB scelto vale
solo per la sessione corrente, e "Aggiorna indice" aggiorna quello).
**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
quella cartella finisce per errore una copia di `cad_index.db` (es. creata lì da una vecchia
sessione), l'app la userà invece di quella nella radice del repo, e le due copie divergeranno
a ogni "Aggiorna indice" fatto nell'uno o nell'altro ambiente. Se i risultati di una ricerca
build (`PrepStep/bin/Release/net10.0-windows` o `Debug/...`), non la radice del repo. L'app usa
quindi la copia di `cad_index.db` in quella cartella (creandola lì al primo "Aggiorna
indice"), non quella nella radice del repo né quella installata: per usare un altro DB in
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
DB..." quale file è effettivamente caricato (mostrato in fase 1 accanto al conteggio dei file
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
il primo per `tipo_file` (il più recente, dato l'`ORDER BY data_modifica DESC`) e scarta gli
altri.
- **Risoluzione codici noti per lo scanner assiemi** (`Form1.CaricaCodiciNoti`, usata sia dalla
GUI in fase 4 sia da `scripts/BomViaDb.cs`): carica in un'unica query tutti i codici
prt/asm noti con i relativi percorsi, invece di una query per codice — usata per validare i
token generici trovati nei binari `.asm` (vedi `docs/formato-asm.md`).
- **Codici noti per lo scanner assiemi** (`Form1.CaricaCodiciNoti`, fase 4;
`scripts/BomViaDb.cs` fa una query equivalente per conto suo): carica in un'unica query tutti
i codici prt/asm noti, invece di una query per codice — usata solo per validare i token
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
+3 -3
View File
@@ -8,8 +8,8 @@ l'euristica testuale ("codice che compare almeno due volte") descritta in
struttura effettiva dei record.
Implementazione: lettura in [`PrepStep/AsmRecordReader.cs`](../PrepStep/AsmRecordReader.cs)
(condivisa con la GUI, `Form1.EstraiCodiciReferenziati` in fase "Trova componenti
referenziati"/"Verifica completezza" - con fallback su `AssemblyComponentScanner` per i file
(condivisa con la GUI, `Form1.EstraiCodiciReferenziati` in fase 4 "Trova componenti
referenziati" - con fallback su `AssemblyComponentScanner` per i file
che non rispettano il formato validato), resa in formato BOM e ricorsione sui sottoassiemi
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
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
originale dà lo stesso offset).
+7 -6
View File
@@ -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
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).
È il metodo **preferito** in GUI (fase "Trova componenti referenziati"/"Verifica
completezza", `Form1.EstraiCodiciReferenziati`) e nello script `CreoBom.cs`:
È il metodo **preferito** in GUI (fase 4 "Trova componenti referenziati",
`Form1.EstraiCodiciReferenziati`) e nello script `CreoBom.cs`:
`AssemblyComponentScanner` (l'euristica di questo documento) resta come **fallback**, usato
solo quando `AsmRecordReader` lancia `InvalidDataException` (file che non rispetta il
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
```
`scripts/ExtractCreoStrings.cs` ha una versione più raffinata della regex e un filtro
aggiuntivo (vedi sotto), non ancora portati in `AssemblyComponentScanner.cs`:
La regex effettiva in `AssemblyComponentScanner.cs` (usata anche da
`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
@@ -285,8 +286,8 @@ livello) serve una BFS:
Stessa logica di estrazione candidati ed euristica del codice radice (sopra),
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.
- [`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`.
- [`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, 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
`(cartella_radice, codice, tipo_file)` - se lo stesso codice esiste in più
+44 -16
View File
@@ -1,12 +1,36 @@
# Guida utente
Questa guida descrive, in ordine, tutti i passi da fare in **Cevolani StepPrep** per
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:\`).
@@ -19,9 +43,9 @@ cosa vede l'utente. La stessa sintesi è disponibile anche dentro l'app da **Aiu
- 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.
- Conviene definire la **cartella di lavoro** giusta (fase 1, o "File → Scegli cartella
di lavoro...") prima di importare la distinta: è lì che finiscono CSV, `raw/` e, dopo
l'esportazione con Creo, `step/`.
- 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
@@ -30,8 +54,8 @@ cosa vede l'utente. La stessa sintesi è disponibile anche dentro l'app da **Aiu
## 1. Database
Alla prima apertura, se un database (`cad_index.db`) è già stato creato in precedenza,
l'app lo ricarica da sola: si vede il pallino verde accanto a "Database", il percorso del
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
@@ -39,10 +63,10 @@ file e la data dell'ultimo aggiornamento.
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.
- La **cartella di lavoro** (dove l'app scrive il CSV della distinta e la cartella
`raw/`) è scelta automaticamente e ricordata tra un avvio e l'altro; per cambiarla usa
**File → Scegli cartella di lavoro...**.
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.
@@ -56,8 +80,8 @@ Premi **Avanti** quando il database è caricato.
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 la presenza dei file sul server",
non basta il solo import: è il modo per accorgersi subito se il database è disallineato
**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
@@ -83,7 +107,7 @@ 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. Trascina su Creo Distributed Batch
## 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:
@@ -117,9 +141,13 @@ prima di riprovare.
## Ripartire con una nuova distinta
Quando devi processare un'altra distinta, usa **File → Nuovo** (Ctrl+N): azzera CSV, raw
e verifica delle fasi 2-5 e riporta al passo 2, senza dover ricaricare il database o
la cartella di lavoro (restano quelli già impostati in fase 1).
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).
+85
View File
@@ -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;