diff --git a/CLAUDE.md b/CLAUDE.md index b034795..4eefae4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 `` 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** (``/ +`` in `PrepStep/PrepStep.csproj`, usato nel titolo della finestra insieme +alla versione, ``, 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__`), 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__`), 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 `` in `CadSearchGui.csproj` e riletta a runtime in + referenziata da `` 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). diff --git a/CadSearchGui.Tests/AssemblyComponentScannerTests.cs b/PrepStep.Tests/AssemblyComponentScannerTests.cs similarity index 99% rename from CadSearchGui.Tests/AssemblyComponentScannerTests.cs rename to PrepStep.Tests/AssemblyComponentScannerTests.cs index c8bcc9e..434eab8 100644 --- a/CadSearchGui.Tests/AssemblyComponentScannerTests.cs +++ b/PrepStep.Tests/AssemblyComponentScannerTests.cs @@ -1,6 +1,6 @@ -using CadSearchGui; +using PrepStep; -namespace CadSearchGui.Tests; +namespace PrepStep.Tests; public class AssemblyComponentScannerTests { diff --git a/CadSearchGui.Tests/CaseInsensitiveLookupIntegrationTests.cs b/PrepStep.Tests/CaseInsensitiveLookupIntegrationTests.cs similarity index 99% rename from CadSearchGui.Tests/CaseInsensitiveLookupIntegrationTests.cs rename to PrepStep.Tests/CaseInsensitiveLookupIntegrationTests.cs index f5c63d7..d96c129 100644 --- a/CadSearchGui.Tests/CaseInsensitiveLookupIntegrationTests.cs +++ b/PrepStep.Tests/CaseInsensitiveLookupIntegrationTests.cs @@ -1,7 +1,7 @@ -using CadSearchGui; +using PrepStep; using Microsoft.Data.Sqlite; -namespace CadSearchGui.Tests; +namespace PrepStep.Tests; /// 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 diff --git a/CadSearchGui.Tests/DistintaBaseImporterTests.cs b/PrepStep.Tests/DistintaBaseImporterTests.cs similarity index 98% rename from CadSearchGui.Tests/DistintaBaseImporterTests.cs rename to PrepStep.Tests/DistintaBaseImporterTests.cs index 75e895f..fb155eb 100644 --- a/CadSearchGui.Tests/DistintaBaseImporterTests.cs +++ b/PrepStep.Tests/DistintaBaseImporterTests.cs @@ -1,6 +1,6 @@ -using CadSearchGui; +using PrepStep; -namespace CadSearchGui.Tests; +namespace PrepStep.Tests; public class DistintaBaseImporterTests { diff --git a/CadSearchGui.Tests/IndexerParseFilenameTests.cs b/PrepStep.Tests/IndexerParseFilenameTests.cs similarity index 95% rename from CadSearchGui.Tests/IndexerParseFilenameTests.cs rename to PrepStep.Tests/IndexerParseFilenameTests.cs index b8fc5eb..367eb85 100644 --- a/CadSearchGui.Tests/IndexerParseFilenameTests.cs +++ b/PrepStep.Tests/IndexerParseFilenameTests.cs @@ -1,6 +1,6 @@ -using CadSearchGui; +using PrepStep; -namespace CadSearchGui.Tests; +namespace PrepStep.Tests; public class IndexerParseFilenameTests { diff --git a/CadSearchGui.Tests/CadSearchGui.Tests.csproj b/PrepStep.Tests/PrepStep.Tests.csproj similarity index 91% rename from CadSearchGui.Tests/CadSearchGui.Tests.csproj rename to PrepStep.Tests/PrepStep.Tests.csproj index bf8c38e..45931ee 100644 --- a/CadSearchGui.Tests/CadSearchGui.Tests.csproj +++ b/PrepStep.Tests/PrepStep.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/CadSearchGui/AssemblyComponentScanner.cs b/PrepStep/AssemblyComponentScanner.cs similarity index 99% rename from CadSearchGui/AssemblyComponentScanner.cs rename to PrepStep/AssemblyComponentScanner.cs index ab3619b..d56378d 100644 --- a/CadSearchGui/AssemblyComponentScanner.cs +++ b/PrepStep/AssemblyComponentScanner.cs @@ -1,7 +1,7 @@ using System.Text; using System.Text.RegularExpressions; -namespace CadSearchGui; +namespace PrepStep; /// /// Estrae i codici componente referenziati dentro un file .asm Pro-E/Creo. diff --git a/CadSearchGui/DistintaBaseImporter.cs b/PrepStep/DistintaBaseImporter.cs similarity index 99% rename from CadSearchGui/DistintaBaseImporter.cs rename to PrepStep/DistintaBaseImporter.cs index 1d90380..6fb13f2 100644 --- a/CadSearchGui/DistintaBaseImporter.cs +++ b/PrepStep/DistintaBaseImporter.cs @@ -2,7 +2,7 @@ using System.Text; using System.Xml.Linq; using ExcelDataReader; -namespace CadSearchGui; +namespace PrepStep; /// /// Converte il file "Distinta Base" esportato dal gestionale (SpreadsheetML diff --git a/CadSearchGui/Form1.Designer.cs b/PrepStep/Form1.Designer.cs similarity index 96% rename from CadSearchGui/Form1.Designer.cs rename to PrepStep/Form1.Designer.cs index 2819e0f..31fc8dc 100644 --- a/CadSearchGui/Form1.Designer.cs +++ b/PrepStep/Form1.Designer.cs @@ -1,6 +1,6 @@ #nullable enable -namespace CadSearchGui; +namespace PrepStep; partial class Form1 { diff --git a/CadSearchGui/Form1.cs b/PrepStep/Form1.cs similarity index 91% rename from CadSearchGui/Form1.cs rename to PrepStep/Form1.cs index 46b9110..a9e6fe3 100644 --- a/CadSearchGui/Form1.cs +++ b/PrepStep/Form1.cs @@ -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:\"; - /// 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. - 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>? _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 ( 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 /// Condizione per cui la fase indicata è "conclusa" e si può passare alla successiva. 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. 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); } - /// 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. + /// 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. 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"); + + /// 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. + 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) diff --git a/CadSearchGui/Indexer.cs b/PrepStep/Indexer.cs similarity index 99% rename from CadSearchGui/Indexer.cs rename to PrepStep/Indexer.cs index c7c74c6..860ff88 100644 --- a/CadSearchGui/Indexer.cs +++ b/PrepStep/Indexer.cs @@ -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, diff --git a/CadSearchGui/CadSearchGui.csproj b/PrepStep/PrepStep.csproj similarity index 80% rename from CadSearchGui/CadSearchGui.csproj rename to PrepStep/PrepStep.csproj index 229bac9..b894a74 100644 --- a/CadSearchGui/CadSearchGui.csproj +++ b/PrepStep/PrepStep.csproj @@ -8,6 +8,8 @@ enable PerMonitorV2 app.ico + Cevolani StepPrep Cevolani StepPrep Cevolani @@ -22,7 +24,7 @@ - <_Parameter1>CadSearchGui.Tests + <_Parameter1>PrepStep.Tests diff --git a/CadSearchGui/Program.cs b/PrepStep/Program.cs similarity index 94% rename from CadSearchGui/Program.cs rename to PrepStep/Program.cs index 84ac229..7b94b0f 100644 --- a/CadSearchGui/Program.cs +++ b/PrepStep/Program.cs @@ -1,4 +1,4 @@ -namespace CadSearchGui; +namespace PrepStep; static class Program { diff --git a/CadSearchGui/ProgressForm.cs b/PrepStep/ProgressForm.cs similarity index 99% rename from CadSearchGui/ProgressForm.cs rename to PrepStep/ProgressForm.cs index 9b2bc1a..85aca1b 100644 --- a/CadSearchGui/ProgressForm.cs +++ b/PrepStep/ProgressForm.cs @@ -1,4 +1,4 @@ -namespace CadSearchGui; +namespace PrepStep; public sealed class ProgressForm : Form { diff --git a/CadSearchGui/ProgressoComponentiForm.cs b/PrepStep/ProgressoComponentiForm.cs similarity index 97% rename from CadSearchGui/ProgressoComponentiForm.cs rename to PrepStep/ProgressoComponentiForm.cs index 90568f4..e79cf28 100644 --- a/CadSearchGui/ProgressoComponentiForm.cs +++ b/PrepStep/ProgressoComponentiForm.cs @@ -1,4 +1,4 @@ -namespace CadSearchGui; +namespace PrepStep; public sealed class ProgressoComponentiForm : Form { diff --git a/CadSearchGui/PrtMancantiForm.cs b/PrepStep/PrtMancantiForm.cs similarity index 99% rename from CadSearchGui/PrtMancantiForm.cs rename to PrepStep/PrtMancantiForm.cs index f8fbea0..8002fa3 100644 --- a/CadSearchGui/PrtMancantiForm.cs +++ b/PrepStep/PrtMancantiForm.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace CadSearchGui; +namespace PrepStep; /// 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. diff --git a/CadSearchGui/ReportCompletezzaForm.cs b/PrepStep/ReportCompletezzaForm.cs similarity index 99% rename from CadSearchGui/ReportCompletezzaForm.cs rename to PrepStep/ReportCompletezzaForm.cs index 81842a1..05a3bde 100644 --- a/CadSearchGui/ReportCompletezzaForm.cs +++ b/PrepStep/ReportCompletezzaForm.cs @@ -1,4 +1,4 @@ -namespace CadSearchGui; +namespace PrepStep; /// Mostra, per ogni assieme della distinta base, se tutti i suoi componenti /// (ricorsivamente fino ai PRT) sono presenti nell'indice (v) o mancano (x). diff --git a/CadSearchGui/ReportStepForm.cs b/PrepStep/ReportStepForm.cs similarity index 99% rename from CadSearchGui/ReportStepForm.cs rename to PrepStep/ReportStepForm.cs index fe76957..b5b301b 100644 --- a/CadSearchGui/ReportStepForm.cs +++ b/PrepStep/ReportStepForm.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace CadSearchGui; +namespace PrepStep; public enum EsitoStep { diff --git a/CadSearchGui/UiStile.cs b/PrepStep/UiStile.cs similarity index 99% rename from CadSearchGui/UiStile.cs rename to PrepStep/UiStile.cs index 402e14f..6397bf6 100644 --- a/CadSearchGui/UiStile.cs +++ b/PrepStep/UiStile.cs @@ -1,6 +1,6 @@ using System.Drawing.Drawing2D; -namespace CadSearchGui; +namespace PrepStep; /// 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 diff --git a/CadSearchGui/app.ico b/PrepStep/app.ico similarity index 100% rename from CadSearchGui/app.ico rename to PrepStep/app.ico diff --git a/README.md b/README.md index 43070e0..5f42b6b 100644 --- a/README.md +++ b/README.md @@ -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__` 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__` 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 ``` diff --git a/docs/architettura.md b/docs/architettura.md index c3025c1..050bafb 100644 --- a/docs/architettura.md +++ b/docs/architettura.md @@ -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 ``/`` 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 ``/`` 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__`, 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`, diff --git a/docs/formato-asm.md b/docs/formato-asm.md index 23e3a2e..b3e0d3f 100644 --- a/docs/formato-asm.md +++ b/docs/formato-asm.md @@ -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 diff --git a/scripts/ExtractCreoStrings.cs b/scripts/ExtractCreoStrings.cs index 70e1dea..f1e9980 100644 --- a/scripts/ExtractCreoStrings.cs +++ b/scripts/ExtractCreoStrings.cs @@ -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#" sono esclusi via lookbehind - vedi scripts/Analyze.cs. // Ogni codice trovato viene validato contro l'indice cad_index.db.