Add explicit working folder; rename project from CadSearchGui to PrepStep

Working folder (phase 1): CSV/raw/step/step_finali all used to live split
across two different implicit locations (CSV in the "repo root" found by
walking up for CLAUDE.md, raw/step next to the input distinta file) with
neither shown to the user. That repo-root lookup also silently breaks once
the app runs as a standalone published exe on a machine with no repo/
CLAUDE.md at all. Replaced both with one explicit "cartella di lavoro",
auto-suggested (last used, remembered the same way as the DB path) and
changeable from phase 1, with no dependency on any repo marker file.

Rename: CadSearchGui was the working name from before the app had a
product identity; renamed folders, csproj files, namespace and the
AppData settings folder to PrepStep throughout, matching the exe name
already in use. Product-facing name stays "Cevolani StepPrep".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5 committed 2026-09-21 11:18:43 +02:00
1 parent 6726dd2216
commit 13798ad47c
24 files changed
+211 -129

No files matched your search

+26 -20
View File
@@ -4,10 +4,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Cos'è questo progetto
`CadSearchGui` è il nome tecnico del progetto (cartella, namespace, csproj); il nome del
programma per l'utente finale è **Cevolani StepPrep** (versione in `<Version>` dentro
`CadSearchGui/CadSearchGui.csproj`, attualmente `0.1.0` — mostrata anche nel titolo della
finestra). È un'app Windows Forms (.NET 10, WinForms) che indicizza e permette di
`PrepStep` è il nome tecnico del progetto (cartella, namespace, csproj, eseguibile
`PrepStep.exe`); il nome mostrato all'utente finale è **Cevolani StepPrep** (`<Product>`/
`<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
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
@@ -16,7 +16,7 @@ pre-processo per quel passaggio, non lo sostituisce.
## Comandi
Tutti da eseguire dentro `CadSearchGui/`.
Tutti da eseguire dentro `PrepStep/`.
```powershell
dotnet build -c Release
@@ -29,16 +29,16 @@ 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
```
`CadSearchGui.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
via `ProjectReference` + `InternalsVisibleTo` (in `CadSearchGui.csproj`), così
via `ProjectReference` + `InternalsVisibleTo` (in `PrepStep.csproj`), così
i test possono chiamare direttamente `Indexer.ParseFilename` e
`AssemblyComponentScanner.EstraiCodiciReferenziati` senza esporli come API
pubblica.
## Architettura
Il programma è tutto in `CadSearchGui/` (nessun altro progetto nella solution — in
Il programma è tutto in `PrepStep/` (nessun altro progetto nella solution — in
passato esisteva anche una CLI separata e uno script Python di indicizzazione,
entrambi rimossi: ora l'unico programma è questo, con GUI e indicizzazione integrate).
@@ -50,19 +50,25 @@ entrambi rimossi: ora l'unico programma è questo, con GUI e indicizzazione inte
`Form1.Designer.cs` contiene solo lo scheletro minimo (`InitializeComponent`/`Dispose`)
generato dal template. Le fasi, in ordine:
1. **Database** — carica/aggiorna l'indice, mostra data ultimo aggiornamento; qui vive
anche "Pulisci raw e step" (manutenzione, non fa parte del flusso a valle).
anche la **cartella di lavoro** (`_cartellaLavoro`, mostrata in `_lblCartellaLavoro`,
cambiabile con "Cambia cartella..." → `ScegliCartellaLavoro`): destinazione di CSV,
`raw/`, `step/`, `step_finali/` per tutte le fasi successive. All'avvio
`InizializzaCartellaLavoro` propone l'ultima usata (ricordata in
`%AppData%/PrepStep/ultima_cartella_lavoro.txt`, stesso pattern di
`ultimo_db.txt` per il DB) se esiste ancora, altrimenti `AppContext.BaseDirectory`
— non dipende da un marker di repo (niente più `CLAUDE.md`/`TrovaRadiceRepo`),
quindi funziona identica anche sull'exe pubblicato standalone (`PrepStep.exe`)
copiato su un'altra macchina. Qui vive anche "Pulisci raw e step" (manutenzione,
non fa parte del flusso a valle).
2. **Importa Distinta Base** — `DistintaBaseImporter.ConvertiInCsv` genera il CSV dei
componenti; se il nome del file segue la convenzione del gestionale
(`DBP_<codice>_<timestamp>`), l'assieme generale viene aggiunto come riga in più nel
CSV stesso (il foglio elenca solo i componenti sotto l'assieme, non l'assieme in sé).
Da qui in avanti l'assieme generale è un componente come tutti gli altri, nessun
caso speciale nel resto del codice. Il CSV viene sempre scritto nella root del repo
(`Form1.TrovaRadiceRepo`, che risale da `AppContext.BaseDirectory` fino alla cartella
che contiene `CLAUDE.md`), indipendentemente da dove si trova il file `.xls`/`.xml`
di input scelto dall'utente.
componenti nella cartella di lavoro; se il nome del file segue la convenzione del
gestionale (`DBP_<codice>_<timestamp>`), l'assieme generale viene aggiunto come riga
in più nel CSV stesso (il foglio elenca solo i componenti sotto l'assieme, non
l'assieme in sé). Da qui in avanti l'assieme generale è un componente come tutti
gli altri, nessun caso speciale nel resto del codice.
3. **Crea cartella raw** — cerca nell'indice il percorso CAD di ogni componente del CSV
(`TrovaPercorsiCad`) e lo copia in `raw/` (`CopiaFileTrovati`, parallela — vedi
`docs/copia-raw-parallela.md`).
(`TrovaPercorsiCad`) e lo copia in `raw/` dentro la cartella di lavoro
(`CopiaFileTrovati`, parallela — vedi `docs/copia-raw-parallela.md`).
4. **Trova componenti referenziati** — legge euristicamente i metadati binari dei `.asm`
già in `raw/` (`AssemblyComponentScanner`) per risolvere ricorsivamente i
sotto-assiemi/parti che contengono (`RisolviAlberoComponenti`), copiandoli anch'essi
@@ -100,7 +106,7 @@ entrambi rimossi: ora l'unico programma è questo, con GUI e indicizzazione inte
in pixel, per evitare che il testo vada a capo e si tagli quando non ci sta) e
`Arrotonda` per gli angoli dei bottoni "Avanti"/"Indietro" del wizard.
- **app.ico** — icona dell'app (logo Cevolani + pezzo + STEP, sfondo trasparente),
referenziata da `<ApplicationIcon>` in `CadSearchGui.csproj` e riletta a runtime in
referenziata da `<ApplicationIcon>` in `PrepStep.csproj` e riletta a runtime in
`Form1.BuildUi()` via `Icon.ExtractAssociatedIcon(Application.ExecutablePath)` per
la titlebar (stessa icona dell'exe, nessun file duplicato nell'output).
@@ -1,6 +1,6 @@
using CadSearchGui;
using PrepStep;
namespace CadSearchGui.Tests;
namespace PrepStep.Tests;
public class AssemblyComponentScannerTests
{
@@ -1,7 +1,7 @@
using CadSearchGui;
using PrepStep;
using Microsoft.Data.Sqlite;
namespace CadSearchGui.Tests;
namespace PrepStep.Tests;
/// <summary>Ricrea lo schema reale del DB (stessa costante SQL usata da Indexer.ScanAsync) e
/// verifica, con dati inseriti a mano, i due comportamenti che in passato hanno causato bug
@@ -1,6 +1,6 @@
using CadSearchGui;
using PrepStep;
namespace CadSearchGui.Tests;
namespace PrepStep.Tests;
public class DistintaBaseImporterTests
{
@@ -1,6 +1,6 @@
using CadSearchGui;
using PrepStep;
namespace CadSearchGui.Tests;
namespace PrepStep.Tests;
public class IndexerParseFilenameTests
{
@@ -16,7 +16,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CadSearchGui\CadSearchGui.csproj" />
<ProjectReference Include="..\PrepStep\PrepStep.csproj" />
</ItemGroup>
<ItemGroup>
@@ -1,7 +1,7 @@
using System.Text;
using System.Text.RegularExpressions;
namespace CadSearchGui;
namespace PrepStep;
/// <summary>
/// Estrae i codici componente referenziati dentro un file .asm Pro-E/Creo.
@@ -2,7 +2,7 @@ using System.Text;
using System.Xml.Linq;
using ExcelDataReader;
namespace CadSearchGui;
namespace PrepStep;
/// <summary>
/// Converte il file "Distinta Base" esportato dal gestionale (SpreadsheetML
@@ -1,6 +1,6 @@
#nullable enable
namespace CadSearchGui;
namespace PrepStep;
partial class Form1
{
+105 -45
View File
@@ -1,7 +1,7 @@
using System.Diagnostics;
using Microsoft.Data.Sqlite;
namespace CadSearchGui;
namespace PrepStep;
public partial class Form1 : Form
{
@@ -17,6 +17,8 @@ public partial class Form1 : Form
private readonly Label _lblStatus = new();
private readonly Label _lblDb = new();
private readonly Label _lblUltimoAggiornamento = new();
private readonly Label _lblCartellaLavoro = new();
private readonly Button _btnCambiaCartella = new();
private readonly DataGridView _gridImport = new();
private readonly DataGridView _gridRaw = new();
private readonly Label _lblImportStatus = new();
@@ -42,20 +44,8 @@ public partial class Form1 : Form
private static readonly string[] TipiPredefiniti = { "prt", "asm" };
private const string RadiceDiscoDefault = @"X:\";
/// <summary>Risale da AppContext.BaseDirectory (variabile tra Debug/Release/publish) fino
/// alla cartella che contiene CLAUDE.md, cioè la root del repo; se non la trova (es. exe
/// pubblicato e spostato altrove) ripiega su AppContext.BaseDirectory.</summary>
private static string TrovaRadiceRepo()
{
for (var dir = new DirectoryInfo(AppContext.BaseDirectory); dir is not null; dir = dir.Parent)
{
if (File.Exists(Path.Combine(dir.FullName, "CLAUDE.md")))
return dir.FullName;
}
return AppContext.BaseDirectory;
}
private string? _dbPath;
private string? _cartellaLavoro;
private string? _percorsoInputCorrente;
private List<(string Componente, string Descrizione)>? _righeCorrenti;
private Dictionary<string, List<string>>? _ultimaVerifica;
@@ -65,7 +55,11 @@ public partial class Form1 : Form
{
InitializeComponent();
BuildUi();
Load += (_, _) => InizializzaDatabase();
Load += (_, _) =>
{
InizializzaCartellaLavoro();
InizializzaDatabase();
};
}
// Palette: blu primario per l'azione principale del flusso ("Crea cartella raw"),
@@ -77,7 +71,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)} — Distinta Base -> CSV con percorsi CAD";
Text = $"Cevolani StepPrep {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
@@ -221,7 +215,7 @@ public partial class Form1 : Form
/// <summary>Condizione per cui la fase indicata è "conclusa" e si può passare alla successiva.</summary>
private bool AvantiConsentito(int fase) => fase switch
{
0 => _dbPath is not null,
0 => _dbPath is not null && _cartellaLavoro is not null,
1 => _righeCorrenti is { Count: > 0 },
2 => _ultimaCartellaRaw is not null,
3 => true, // risoluzione ricorsiva opzionale: non tutti gli assiemi hanno sotto-componenti
@@ -282,14 +276,37 @@ public partial class Form1 : Form
contenuto.Controls.Add(bottoni);
var lblIstruzioniCartella = new Label
{
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 le "
+ "cartelle 'raw', 'step' e 'step_finali'. 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 root prima di un "
+ "nuovo giro (non tocca l'indice, solo le copie locali).",
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);
@@ -397,7 +414,7 @@ public partial class Form1 : Form
ForeColor = SystemColors.GrayText,
Margin = new Padding(0, 0, 0, 8),
Text = "Cerca nell'indice il percorso CAD di ogni componente importato e copia i "
+ "file trovati in una cartella 'raw' accanto al file di distinta originale.",
+ "file trovati in una cartella 'raw' dentro la cartella di lavoro (fase 1).",
};
contenuto.Controls.Add(lblIstruzioni);
@@ -578,7 +595,7 @@ public partial class Form1 : Form
{
try
{
var (csv, righe) = DistintaBaseImporter.ConvertiInCsv(percorso, TrovaRadiceRepo());
var (csv, righe) = DistintaBaseImporter.ConvertiInCsv(percorso, _cartellaLavoro!);
_percorsoInputCorrente = percorso;
_righeCorrenti = righe;
@@ -647,7 +664,7 @@ public partial class Form1 : Form
_gridRaw.Rows.Add(componente, percorsoCad);
}
var cartellaRaw = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(_percorsoInputCorrente))!, "raw");
var cartellaRaw = Path.Combine(_cartellaLavoro!, "raw");
int copiati = CopiaFileTrovati(percorsiTrovati.Values.SelectMany(p => p).Distinct(), cartellaRaw);
_ultimaCartellaRaw = cartellaRaw;
@@ -663,10 +680,10 @@ public partial class Form1 : Form
/// già stati realizzati e quali mancano ancora.</summary>
private void GeneraReportStep()
{
if (_percorsoInputCorrente is null || _righeCorrenti is null)
if (_righeCorrenti is null)
return;
string radice = Path.GetDirectoryName(Path.GetFullPath(_percorsoInputCorrente))!;
string radice = _cartellaLavoro!;
var cartellaStep = Path.Combine(radice, "step");
if (!Directory.Exists(cartellaStep))
{
@@ -722,26 +739,12 @@ public partial class Form1 : Form
dialog.ShowDialog(this);
}
/// <summary>Svuota in un colpo solo le cartelle "raw", "step" e "step_finali" nella root
/// (stessa cartella base del file distinta importato), previa un'unica conferma. Non tocca
/// l'indice CAD: solo le copie locali generate dalla pipeline.</summary>
/// <summary>Svuota in un colpo solo le cartelle "raw", "step" e "step_finali" nella cartella
/// di lavoro, previa un'unica conferma. Non tocca l'indice CAD: solo le copie locali
/// generate dalla pipeline.</summary>
private void PulisciRawEStep()
{
string radice;
if (_ultimaCartellaRaw is not null)
{
radice = Path.GetDirectoryName(_ultimaCartellaRaw)!;
}
else
{
// Non abbiamo ancora una cartella nota in questa sessione (es. app appena riaperta):
// si chiede all'utente di indicare la root invece di tenere il pulsante disabilitato.
using var dialog = new FolderBrowserDialog { Description = "Seleziona la cartella root (contiene 'raw' e 'step')" };
if (dialog.ShowDialog(this) != DialogResult.OK)
return;
radice = dialog.SelectedPath;
}
string radice = _cartellaLavoro!;
var cartelle = new[] { Path.Combine(radice, "raw"), Path.Combine(radice, "step"), Path.Combine(radice, "step_finali") }
.Where(Directory.Exists)
.ToList();
@@ -1058,8 +1061,65 @@ public partial class Form1 : Form
return risultato;
}
private static readonly string PercorsoImpostazioni = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CadSearchGui", "ultimo_db.txt");
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, la prima
/// volta, la cartella dell'eseguibile: non richiede mai un CLAUDE.md/repo (a differenza del
/// vecchio TrovaRadiceRepo), quindi funziona identica anche sull'exe pubblicato standalone
/// copiato su un'altra macchina. Sempre modificabile con "Cambia cartella..." in fase 1.</summary>
private void InizializzaCartellaLavoro()
{
var ultima = LeggiUltimaCartellaLavoro();
_cartellaLavoro = ultima is not null && Directory.Exists(ultima) ? ultima : AppContext.BaseDirectory;
AggiornaLabelCartellaLavoro();
}
private void ScegliCartellaLavoro()
{
using var dialog = new FolderBrowserDialog
{
Description = "Seleziona la cartella di lavoro (CSV, raw, step, step_finali)",
SelectedPath = _cartellaLavoro ?? string.Empty,
};
if (dialog.ShowDialog(this) != DialogResult.OK)
return;
string percorso = dialog.SelectedPath;
_cartellaLavoro = percorso;
AggiornaLabelCartellaLavoro();
SalvaUltimaCartellaLavoro(percorso);
AggiornaStatoAvanti();
}
private void AggiornaLabelCartellaLavoro() => _lblCartellaLavoro.Text = $"Cartella di lavoro: {_cartellaLavoro}";
private static string? LeggiUltimaCartellaLavoro()
{
try
{
return File.Exists(PercorsoImpostazioniCartella) ? File.ReadAllText(PercorsoImpostazioniCartella).Trim() : null;
}
catch (IOException)
{
return null;
}
}
private static void SalvaUltimaCartellaLavoro(string path)
{
try
{
Directory.CreateDirectory(CartellaImpostazioni);
File.WriteAllText(PercorsoImpostazioniCartella, path);
}
catch (IOException)
{
// ponytail: non critico se non si riesce a salvare, si ripropone il default al prossimo avvio
}
}
private void InizializzaDatabase()
{
@@ -1141,7 +1201,7 @@ public partial class Form1 : Form
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(PercorsoImpostazioni)!);
Directory.CreateDirectory(CartellaImpostazioni);
File.WriteAllText(PercorsoImpostazioni, path);
}
catch (IOException)
@@ -2,7 +2,7 @@ using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.Data.Sqlite;
namespace CadSearchGui;
namespace PrepStep;
public sealed record ScanProgress(
string Cartella,
@@ -8,6 +8,8 @@
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<ApplicationIcon>app.ico</ApplicationIcon>
<!-- Nome tecnico (cartella/csproj/namespace) e nome del file .exe pubblicato: PrepStep.
Il nome prodotto mostrato all'utente ("Cevolani StepPrep") resta separato, sotto. -->
<AssemblyTitle>Cevolani StepPrep</AssemblyTitle>
<Product>Cevolani StepPrep</Product>
<Company>Cevolani</Company>
@@ -22,7 +24,7 @@
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>CadSearchGui.Tests</_Parameter1>
<_Parameter1>PrepStep.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
@@ -1,4 +1,4 @@
namespace CadSearchGui;
namespace PrepStep;
static class Program
{
@@ -1,4 +1,4 @@
namespace CadSearchGui;
namespace PrepStep;
public sealed class ProgressForm : Form
{
@@ -1,4 +1,4 @@
namespace CadSearchGui;
namespace PrepStep;
public sealed class ProgressoComponentiForm : Form
{
@@ -1,6 +1,6 @@
using System.Diagnostics;
namespace CadSearchGui;
namespace PrepStep;
/// <summary>Mostra l'esito della risoluzione ricorsiva dell'albero componenti: i PRT e i
/// sotto-assiemi trovati nell'indice e già copiati in "raw" a partire dagli assiemi iniziali.</summary>
@@ -1,4 +1,4 @@
namespace CadSearchGui;
namespace PrepStep;
/// <summary>Mostra, per ogni assieme della distinta base, se tutti i suoi componenti
/// (ricorsivamente fino ai PRT) sono presenti nell'indice (v) o mancano (x).</summary>
@@ -1,6 +1,6 @@
using System.Diagnostics;
namespace CadSearchGui;
namespace PrepStep;
public enum EsitoStep
{
@@ -1,6 +1,6 @@
using System.Drawing.Drawing2D;
namespace CadSearchGui;
namespace PrepStep;
/// <summary>Stile bottoni condiviso tra la form principale e i dialog secondari: piatto con
/// bordo sottile e hover, per un aspetto coerente sia per l'azione primaria di ogni fase
File renamed without changes.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

+22 -17
View File
@@ -1,6 +1,6 @@
# Cevolani StepPrep
App Windows Forms (.NET 10, progetto `CadSearchGui`) 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
l'estrazione "Distinta Base" del gestionale (file `.xls`/`.xml`) in un CSV con il
percorso CAD di ogni componente, fino a raccogliere in `raw/` tutto il necessario per
@@ -13,17 +13,20 @@ passaggio, non lo sostituisce.
presente accanto all'eseguibile). 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.
livello che trova) e indicizza tutti i file. In questa fase si vede/cambia anche
la **cartella di lavoro**: dove finiscono il CSV e le cartelle `raw/`, `step/`,
`step_finali/`. Proposta automaticamente (ricordata da un avvio all'altro),
modificabile con **Cambia cartella...**.
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 con anteprima in griglia.
Se il file segue la convenzione di nome `DBP_<codice>_<timestamp>` del
gestionale, l'assieme generale dell'estrazione viene aggiunto come riga
in più nel CSV (il foglio elenca solo i componenti sotto l'assieme, non
l'assieme stesso).
Componente/Descrizione): viene generato un CSV nella cartella di lavoro, con
anteprima in griglia. Se il file segue la convenzione di nome
`DBP_<codice>_<timestamp>` del gestionale, l'assieme generale dell'estrazione
viene aggiunto come riga in più nel CSV (il foglio elenca solo i componenti
sotto l'assieme, non l'assieme stesso).
3. **Crea cartella raw** — cerca nell'indice il percorso CAD di ogni
componente importato (assieme generale incluso) e copia i file trovati
in una cartella `raw/` accanto al file di distinta originale.
in una cartella `raw/` dentro la cartella di lavoro.
4. **Trova componenti referenziati** — legge i metadati dei file `.asm` in
`raw/` per trovare ricorsivamente i sotto-assiemi/parti che li
compongono, copiandoli a loro volta in `raw/`.
@@ -42,7 +45,7 @@ passaggio, non lo sostituisce.
## Build
```powershell
cd CadSearchGui
cd PrepStep
dotnet build -c Release
```
@@ -51,15 +54,15 @@ dotnet build -c Release
Da sorgente:
```powershell
cd CadSearchGui
cd PrepStep
dotnet run -c Release
```
Oppure l'eseguibile già compilato:
```powershell
cd CadSearchGui\bin\Release\net10.0-windows
.\CadSearchGui.exe
cd PrepStep\bin\Release\net10.0-windows
.\PrepStep.exe
```
## Pubblicazione (eseguibile standalone)
@@ -68,15 +71,17 @@ Genera un `.exe` self-contained per Windows x64, senza bisogno del runtime
.NET installato sulla macchina di destinazione:
```powershell
cd CadSearchGui
cd PrepStep
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish
```
L'eseguibile risultante è in `CadSearchGui\publish\CadSearchGui.exe`.
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
Tutto il codice è in `CadSearchGui/` (unico progetto nella solution, GUI e
Tutto il codice è in `PrepStep/` (unico progetto nella solution, GUI e
indicizzazione integrate):
- `Program.cs` — entry point.
@@ -93,12 +98,12 @@ sono tracciati in git (vedi `.gitignore`).
## Test
`CadSearchGui.Tests/` contiene unit test (regex di estrazione dei componenti
`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
eseguire:
```powershell
cd CadSearchGui.Tests
cd PrepStep.Tests
dotnet test
```
+31 -22
View File
@@ -1,9 +1,9 @@
# Architettura di CadSearchGui (Cevolani StepPrep)
# Architettura di PrepStep (Cevolani StepPrep)
Panoramica di tutti i file sorgente e di come collaborano. `CadSearchGui` è il nome
tecnico del progetto; **Cevolani StepPrep** è il nome del programma per l'utente
finale (titolo finestra, icona, versione — vedi `<Version>`/`<Product>` in
`CadSearchGui.csproj`). Per il "come si usa" vedi il `README.md` in root; per il
Panoramica di tutti i file sorgente e di come collaborano. `PrepStep` è il nome
tecnico del progetto (cartella, namespace, csproj, eseguibile `PrepStep.exe`);
**Cevolani StepPrep** è il nome mostrato all'utente finale (titolo finestra, icona,
versione — vedi `<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`; per la parallelizzazione della
copia in `raw/` vedi `docs/copia-raw-parallela.md`.
@@ -40,14 +40,15 @@ Distinta (.xls/.xml) → CSV (componente;descrizione) → raw/ (CAD copiati
| # | Fase | Bottone → metodo | Cosa fa |
|---|---|---|---|
| 1 | Database | "Aggiorna indice" → `Indexer.ScanAsync` | Crea/aggiorna `cad_index.db` scansionando la share |
| 2 | Importa Distinta Base | selezione file → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella root del repo (`TrovaRadiceRepo`) |
| 3 | Crea cartella raw | "Crea cartella raw" → `CreaCartellaRaw` | `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela) → `raw/` |
| 1 | Database | "Aggiorna indice" → `Indexer.ScanAsync`; "Cambia cartella..." → `ScegliCartellaLavoro` | Crea/aggiorna `cad_index.db` scansionando la share; sceglie/mostra la cartella di lavoro |
| 2 | Importa Distinta Base | selezione file → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella cartella di lavoro |
| 3 | Crea cartella raw | "Crea cartella raw" → `CreaCartellaRaw` | `TrovaPercorsiCad` + `CopiaFileTrovati` (parallela) → `raw/` dentro la cartella di lavoro |
| 4 | Trova componenti referenziati | "Trova componenti referenziati" → `TrovaPrtReferenziati` → `RisolviAlberoComponenti` | Legge i `.asm` in `raw/` con `AssemblyComponentScanner`, risolve ricorsivamente sotto-assiemi/parti nell'indice, li copia anch'essi in `raw/` |
| 5 | Verifica completezza e STEP | "Verifica completezza assiemi" → `VerificaCompletezzaAssiemi`; "Crea cartella step_finali" → `GeneraReportStep` | Completezza ricorsiva sui percorsi in indice; confronto con `step/` → `step_finali/`. All'ingresso nella fase, `AggiornaAnteprimaAsm` mostra le icone trascinabili dei `.asm` della distinta già in `raw/` |
| 5 | Verifica completezza e STEP | "Verifica completezza assiemi" → `VerificaCompletezzaAssiemi`; "Crea cartella step_finali" → `GeneraReportStep` | Completezza ricorsiva sui percorsi in indice; confronto con `step/` → `step_finali/` (entrambe dentro la cartella di lavoro). All'ingresso nella fase, `AggiornaAnteprimaAsm` mostra le icone trascinabili dei `.asm` della distinta già in `raw/` |
Stato condiviso tra le fasi (campi privati di `Form1`): `_dbPath`, `_percorsoInputCorrente`,
`_righeCorrenti` (righe del CSV), `_ultimaVerifica` (cache percorsi CAD), `_ultimaCartellaRaw`.
Stato condiviso tra le fasi (campi privati di `Form1`): `_dbPath`, `_cartellaLavoro`,
`_percorsoInputCorrente`, `_righeCorrenti` (righe del CSV), `_ultimaVerifica` (cache percorsi
CAD), `_ultimaCartellaRaw`.
Altre responsabilità in Form1.cs:
- `TrovaPercorsiCad` / `CaricaCodiciNoti` — query sul DB (`files_ultima_versione`), la
@@ -55,10 +56,14 @@ Distinta (.xls/.xml) → CSV (componente;descrizione) → raw/ (CAD copiati
componenti sia da `AssemblyComponentScanner` per validare i token generici).
- `CopiaFileTrovati` — copia (in parallelo, grado 6) i file trovati in `raw/`, saltando
silenziosamente quelli irraggiungibili (share offline/permessi).
- `PulisciRawEStep` — svuota `raw/`, `step/`, `step_finali/` (manutenzione, richiede conferma).
- `InizializzaDatabase` / `ScegliDatabase` / `TrovaRadiceRepo` — localizzazione del DB e
della root del repo (quest'ultima risalendo da `AppContext.BaseDirectory` fino alla
cartella che contiene `CLAUDE.md`).
- `PulisciRawEStep` — svuota `raw/`, `step/`, `step_finali/` nella cartella di lavoro
(manutenzione, richiede conferma).
- `InizializzaDatabase` / `ScegliDatabase` — localizzazione del DB (ultimo usato, salvato in
`%AppData%/PrepStep/ultimo_db.txt`, o `cad_index.db` accanto all'eseguibile).
- `InizializzaCartellaLavoro` / `ScegliCartellaLavoro` — stesso pattern per la cartella di
lavoro (`ultima_cartella_lavoro.txt`), fallback `AppContext.BaseDirectory` se non c'è
ancora nulla di salvato: nessuna dipendenza da un marker di repo, funziona identica
sull'exe pubblicato standalone spostato su un'altra macchina.
## Indicizzazione
@@ -89,7 +94,8 @@ Distinta (.xls/.xml) → CSV (componente;descrizione) → raw/ (CAD copiati
file) nelle sole colonne Componente/Descrizione, foglio `"Distinta Base"`. Se il nome del
file segue la convenzione `DBP_<codice>_<timestamp>`, aggiunge l'assieme generale come riga
in più (il foglio elenca solo i componenti sotto l'assieme). Scrive il CSV nella cartella
passata da `Form1` (root del repo), non necessariamente accanto al file di input.
passata da `Form1` (la cartella di lavoro scelta in fase 1), non necessariamente accanto al
file di input.
## Finestre di dialogo (modali, mostrate a fine step)
@@ -130,9 +136,12 @@ Distinta (.xls/.xml) → CSV (componente;descrizione) → raw/ (CAD copiati
Percorso: cercato accanto all'eseguibile o nella working directory all'avvio; altrimenti
selezionabile con "Apri DB...". Radice disco di scansione di default `X:\`
(`Form1.RadiceDiscoDefault`, hardcoded).
- **File su disco**, tutti nella root del repo (non nella cartella dell'eseguibile): il CSV
della distinta, `raw/` (CAD dei componenti e sotto-componenti), `step/` (STEP esportati a
mano da Creo, input), `step_finali/` (STEP dei soli codici della distinta, output).
- **Cartella di lavoro** — salvata in `%AppData%/PrepStep/ultima_cartella_lavoro.txt`
(stesso meccanismo del DB sopra), proposta di default `AppContext.BaseDirectory` la prima
volta; selezionabile/cambiabile con "Cambia cartella..." in fase 1. Tutti i file generati a
valle ci finiscono dentro: il CSV della distinta, `raw/` (CAD dei componenti e
sotto-componenti), `step/` (STEP esportati a mano da Creo, input), `step_finali/` (STEP dei
soli codici della distinta, output).
## Script standalone (`scripts/`)
@@ -149,15 +158,15 @@ dipendenze extra con `#:package Nome@versione` in testa al file):
`AssemblyComponentScanner`.
- **Query.cs** — query SQL ad-hoc contro `cad_index.db` da riga di comando.
> ⚠️ Nota importante di sicurezza: se `dotnet run` viene lanciato dentro `CadSearchGui/`
> ⚠️ Nota importante di sicurezza: se `dotnet run` viene lanciato dentro `PrepStep/`
> (che contiene un `.csproj` di tipo `WinExe`), esegue l'app GUI vera invece dello script
> standalone e resta bloccato in attesa che una finestra si chiuda. Lanciare gli script da
> una cartella senza `.csproj` (es. la root del repo o `scripts/` stessa).
## Test
- **CadSearchGui.Tests/** (xUnit) — riferimento al progetto principale via `ProjectReference`
+ `InternalsVisibleTo` (in `CadSearchGui.csproj`), così i test chiamano direttamente
- **PrepStep.Tests/** (xUnit) — riferimento al progetto principale via `ProjectReference`
+ `InternalsVisibleTo` (in `PrepStep.csproj`), così i test chiamano direttamente
`Indexer.ParseFilename` e `AssemblyComponentScanner.EstraiCodiciReferenziati` senza esporli
come API pubblica. File: `IndexerParseFilenameTests.cs`,
`AssemblyComponentScannerTests.cs`, `DistintaBaseImporterTests.cs`,
+3 -3
View File
@@ -33,7 +33,7 @@ ASCII leggibili in diversi campi ricorrenti:
Un codice reale compare **più volte** nel file (una per feature/placement
diverso); rumore binario che assomiglia casualmente a un codice compare quasi
sempre una sola volta. Questa è la base del filtro in
[`AssemblyComponentScanner.cs`](../CadSearchGui/AssemblyComponentScanner.cs)
[`AssemblyComponentScanner.cs`](../PrepStep/AssemblyComponentScanner.cs)
e in [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs):
```
@@ -249,7 +249,7 @@ regex aggiuntiva: (?<=<)(?:UNI\d+|CUSCI_[A-Za-z0-9]+)(?=>)
```
Implementata sia in [`scripts/ExtractCreoStrings.cs`](../scripts/ExtractCreoStrings.cs) sia in
[`AssemblyComponentScanner.cs`](../CadSearchGui/AssemblyComponentScanner.cs) (fase "Trova componenti
[`AssemblyComponentScanner.cs`](../PrepStep/AssemblyComponentScanner.cs) (fase "Trova componenti
referenziati" dell'app). Il prefisso family table non è garantito essere solo `UNI`/`CUSCI`:
se ricompaiono famiglie diverse (altri componenti normalizzati), vanno aggiunte allo stesso modo,
verificando prima con `scripts/Analyze.cs` che il contenuto tra `<` e `>` corrisponda a un codice
@@ -271,7 +271,7 @@ 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`](../CadSearchGui/AssemblyComponentScanner.cs) + `CodiceRadice` — estrazione dei candidati da un file, usata dalla BFS in [`Form1.RisolviAlberoComponenti`](../CadSearchGui/Form1.cs) (fase "Trova componenti referenziati") e [`Form1.AssiemeCompleto`](../CadSearchGui/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 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`.
Nota: le query contro `files_ultima_versione` filtrano solo per
+1 -1
View File
@@ -3,7 +3,7 @@
//
// Estrae dal file .asm i codici dei componenti referenziati, espandendo ricorsivamente
// ogni sotto-assieme (.asm) trovato nell'indice, finché non restano solo PRT (foglie) o
// codici non presenti nell'indice. Stessa euristica di CadSearchGui/AssemblyComponentScanner.cs:
// codici non presenti nell'indice. Stessa euristica di PrepStep/AssemblyComponentScanner.cs:
// i codici veri compaiono più volte nel file, il rumore binario quasi sempre una sola volta;
// gli ID interni "ModelView#<numero>" sono esclusi via lookbehind - vedi scripts/Analyze.cs.
// Ogni codice trovato viene validato contro l'indice cad_index.db.