Aggiungi menu Nuovo estrazione/Guida, rimuovi verifica completezza assiemi

"File -> Nuova estrazione" (Ctrl+N) azzera distinta/raw/verifica per ripartire
senza ricaricare DB e cartella di lavoro; "Aiuto -> Guida" (F1) apre una sintesi
interattiva delle 5 fasi. Rimossa "Verifica completezza assiemi": bottone,
ReportCompletezzaForm e VerificaCompletezzaAssiemi non più necessari.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5 committed 2026-09-22 14:24:57 +02:00
1 parent d9f16f2844
commit 28aa16837b
5 files changed
+149 -195

No files matched your search

+41 -118
View File
@@ -10,7 +10,6 @@ public partial class Form1 : Form
private readonly Button _btnCreaRaw = new();
private readonly Button _btnTrovaPrt = new();
private readonly Button _btnVerifica = new();
private readonly Button _btnCompletezza = new();
private readonly ListView _lstAnteprimaAsm = new();
private readonly Button _btnPulisci = new();
private readonly Label _lblStatus = new();
@@ -22,6 +21,8 @@ public partial class Form1 : Form
private readonly DataGridView _gridRaw = new();
private readonly Label _lblImportStatus = new();
private readonly ToolTip _tipImportStatus = new();
private readonly MenuStrip _menu = new();
private readonly ToolStripMenuItem _mnuNuovaEstrazione = new("Nuova estrazione");
// Wizard: una fase alla volta occupa tutta la finestra, si avanza solo quando quella
// corrente è "pronta" (vedi AvantiConsentito). NomiFasi/_panelFasi sono paralleli per indice.
@@ -146,10 +147,25 @@ public partial class Form1 : Form
// Ordine dei Dock.Bottom: l'ultimo aggiunto è il più esterno (il vero bordo inferiore
// della finestra). La barra di stato va tra contenuto e azioni; i bottoni Avanti/Indietro
// restano il footer più esterno, come in ogni wizard/dialog standard di Windows.
var mnuFile = new ToolStripMenuItem("File");
_mnuNuovaEstrazione.ShortcutKeys = Keys.Control | Keys.N;
_mnuNuovaEstrazione.Click += (_, _) => NuovaEstrazione();
mnuFile.DropDownItems.Add(_mnuNuovaEstrazione);
var mnuAiuto = new ToolStripMenuItem("Aiuto");
var mnuGuida = new ToolStripMenuItem("Guida") { ShortcutKeys = Keys.F1 };
mnuGuida.Click += (_, _) => new GuidaForm().ShowDialog(this);
mnuAiuto.DropDownItems.Add(mnuGuida);
_menu.Items.Add(mnuFile);
_menu.Items.Add(mnuAiuto);
MainMenuStrip = _menu;
Controls.Add(_faseContainer);
Controls.Add(statusPanel);
Controls.Add(navContainer);
Controls.Add(headerPanel);
Controls.Add(_menu);
DragEnter += (_, e) =>
e.Effect = e.Data?.GetDataPresent(DataFormats.FileDrop) == true
@@ -236,6 +252,30 @@ public partial class Form1 : Form
private void AggiornaStatoAvanti() => _btnAvanti.Enabled = AvantiConsentito(_faseAttuale);
/// <summary>Azzera distinta/raw/verifica (fasi 2-5) per ripartire con una nuova distinta;
/// DB indicizzato e cartella di lavoro (fase 1) restano quelli già caricati, non serve
/// rifare lo scan del disco per ogni estrazione.</summary>
private void NuovaEstrazione()
{
if (_dbPath is null || _cartellaLavoro is null)
{
MessageBox.Show(this, "Carica prima il database in fase 1.", "Nuova estrazione",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
_percorsoInputCorrente = null;
_righeCorrenti = null;
_ultimaCartellaRaw = null;
_ultimaVerifica = null;
_gridImport.Rows.Clear();
_gridRaw.Rows.Clear();
_lstAnteprimaAsm.Items.Clear();
ImpostaStatoImport("", Color.Black);
VaiAllaFase(1);
}
private Panel BuildFaseDatabase()
{
var contenuto = new TableLayoutPanel
@@ -516,27 +556,6 @@ public partial class Form1 : Form
};
top.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
var lblIstruzioniCompletezza = new Label
{
Dock = DockStyle.Top,
AutoSize = true,
ForeColor = SystemColors.GrayText,
Margin = new Padding(0, 0, 0, 8),
Text = "Per ogni assieme della distinta, verifica se tutti i suoi componenti "
+ "(ricorsivamente fino ai PRT) sono nell'indice. Nota: la scansione dei .asm "
+ "è euristica, quindi qualche falso 'incompleto' è possibile.",
};
top.Controls.Add(lblIstruzioniCompletezza);
// Primario: è l'azione principale di questa fase, non una scelta secondaria — prima
// era uno stile grigio che si confondeva con lo sfondo.
_btnCompletezza.Text = "Verifica completezza assiemi";
_btnCompletezza.Margin = new Padding(0, 0, 0, 12);
UiStile.Primario(_btnCompletezza);
UiStile.Arrotonda(_btnCompletezza);
_btnCompletezza.Click += (_, _) => VerificaCompletezzaAssiemi();
top.Controls.Add(_btnCompletezza);
var lblAnteprimaAsm = new Label
{
AutoSize = true,
@@ -1007,102 +1026,6 @@ public partial class Form1 : Form
dialog.ShowDialog(this);
}
/// <summary>Per ogni assieme della distinta, verifica ricorsivamente (leggendo i .asm dai
/// percorsi originali sulla share) se tutti i componenti fino ai PRT sono nell'indice.
/// I risultati di ogni file/codice vengono in cache e condivisi tra gli assiemi controllati
/// nella stessa passata, per non rileggere più volte gli stessi sotto-componenti.</summary>
private void VerificaCompletezzaAssiemi()
{
if (_righeCorrenti is null)
return;
if (_dbPath is null)
{
ImpostaStatoImport("Nessun database caricato: impossibile verificare la completezza.", Color.DarkRed);
return;
}
var codiciNoti = CaricaCodiciNoti(_dbPath);
var cacheCodiciFile = new Dictionary<string, HashSet<string>>();
List<(string Tipo, string Percorso)> CercaComponenti(string codice) =>
codiciNoti.TryGetValue(codice, out var trovati) ? trovati : new List<(string, string)>();
bool AssiemeCompleto(string percorsoAsmIniziale)
{
var daVisitare = new Queue<string>();
daVisitare.Enqueue(percorsoAsmIniziale);
var fileVisitati = new HashSet<string> { percorsoAsmIniziale };
var codiceIniziale = Path.GetFileName(percorsoAsmIniziale).Split('.')[0];
var codiciVisti = new HashSet<string>
{
codiceIniziale,
AssemblyComponentScanner.CodiceRadice(codiceIniziale),
};
while (daVisitare.Count > 0)
{
var file = daVisitare.Dequeue();
if (!cacheCodiciFile.TryGetValue(file, out var codiciReferenziati))
{
try
{
codiciReferenziati = EstraiCodiciReferenziati(file, codiciNoti.Keys).ToHashSet(StringComparer.OrdinalIgnoreCase);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
{
return false; // file irraggiungibile (share offline) -> non verificabile, per prudenza "incompleto"
}
cacheCodiciFile[file] = codiciReferenziati;
}
foreach (var codice in codiciReferenziati)
{
if (!codiciVisti.Add(codice))
continue;
var componenti = CercaComponenti(codice);
if (componenti.Count == 0)
return false;
foreach (var (tipo, percorso) in componenti)
{
if (tipo == "asm" && fileVisitati.Add(percorso))
{
daVisitare.Enqueue(percorso);
}
}
}
}
return true;
}
// L'assieme generale è già una riga di _righeCorrenti (vedi DistintaBaseImporter), quindi
// la sua completezza viene verificata insieme a quella di tutti gli altri componenti.
var righeReport = new List<(string Componente, string Descrizione, bool? Completo)>();
foreach (var (componente, descrizione) in _righeCorrenti)
{
var componenti = CercaComponenti(componente);
string? percorsoAsm = componenti.Where(c => c.Tipo == "asm").Select(c => c.Percorso).FirstOrDefault();
bool? completo = componenti.Count == 0 ? null
: percorsoAsm is null ? true // solo prt trovato: nessuna sotto-struttura da verificare
: AssiemeCompleto(percorsoAsm);
righeReport.Add((componente, descrizione, completo));
}
int completi = righeReport.Count(r => r.Completo == true);
int incompleti = righeReport.Count(r => r.Completo == false);
int nonTrovati = righeReport.Count(r => r.Completo is null);
ImpostaStatoImport(
$"Completezza assiemi: {completi} completi, {incompleti} incompleti, "
+ $"{nonTrovati} non trovati nell'indice.",
Color.DarkGreen);
using var dialogCompletezza = new ReportCompletezzaForm(righeReport);
dialogCompletezza.ShowDialog(this);
}
/// <summary>Copia i file trovati nella cartella "raw" indicata, creandola se necessario.
/// Ritorna il numero di file copiati con successo. Parallelizzata (grado limitato): il
/// collo di bottiglia è la latenza di rete verso la share, non la CPU, quindi poche copie
+91
View File
@@ -0,0 +1,91 @@
namespace PrepStep;
/// <summary>Guida interattiva richiamabile da "Aiuto ▸ Guida" (F1): sintesi delle 5 fasi del
/// wizard, per chi apre l'app senza aver letto i docs. Il dettaglio completo (formato .asm,
/// schema DB, ecc.) resta solo in docs/architettura.md e affini, per non duplicarlo qui.</summary>
public class GuidaForm : Form
{
private static readonly (string Titolo, string Testo)[] Sezioni =
{
("Panoramica", """
Cevolani StepPrep 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".
"File ▸ Nuova estrazione" (Ctrl+N) azzera distinta/raw/verifica per
ripartire con una nuova distinta base, senza dover ricaricare il database.
"""),
("1. Database", """
Carica o aggiorna l'indice "cad_index.db" a partire dalle cartelle di primo
livello del disco/share (radice di default X:\). Qui si sceglie anche la
cartella di lavoro, dove finiscono CSV, "raw" e "step".
"""),
("2. Importa Distinta Base", """
Converte il file della distinta (trascinabile nella finestra) in un CSV
componente;descrizione nella cartella di lavoro. Se il file segue la
convenzione del gestionale, aggiunge anche l'assieme generale come riga.
"""),
("3. Crea cartella raw", """
Cerca nell'indice i file CAD dei componenti del CSV e li copia nella
sottocartella "raw" della cartella di lavoro.
"""),
("4. Trova componenti referenziati", """
Legge i file .asm già in "raw" per risolvere ricorsivamente sotto-assiemi e
parti citate, copiando anche quelli in "raw": componenti soppressi (non in
distinta ma necessari per aprire l'assieme) e riferimenti esterni (assembly
feature, datum copiati) inclusi.
"""),
("5. Verifica completezza", """
Mostra i file .asm della distinta già in "raw", trascinabili su Creo per
l'esportazione STEP (passo manuale, fuori da quest'app). "Verifica
completezza assiemi" controlla che tutti i sotto-componenti risultino
presenti nell'indice.
"""),
};
public GuidaForm()
{
Text = "Guida";
ClientSize = new Size(640, 420);
MinimumSize = new Size(480, 320);
StartPosition = FormStartPosition.CenterParent;
Font = new Font("Segoe UI", 9.5f);
var lista = new ListBox
{
Dock = DockStyle.Left,
Width = 200,
IntegralHeight = false,
BorderStyle = BorderStyle.None,
};
foreach (var (titolo, _) in Sezioni)
lista.Items.Add(titolo);
var testo = new RichTextBox
{
Dock = DockStyle.Fill,
ReadOnly = true,
BorderStyle = BorderStyle.None,
BackColor = Color.White,
Font = new Font(Font.FontFamily, 10f),
};
lista.SelectedIndexChanged += (_, _) =>
{
if (lista.SelectedIndex >= 0)
testo.Text = Sezioni[lista.SelectedIndex].Testo.Trim();
};
var btnChiudi = new Button { Text = "Chiudi", Dock = DockStyle.Bottom, Height = 34 };
UiStile.Secondario(btnChiudi);
btnChiudi.Click += (_, _) => Close();
var contenuto = new Panel { Dock = DockStyle.Fill, Padding = new Padding(16) };
contenuto.Controls.Add(testo);
Controls.Add(contenuto);
Controls.Add(lista);
Controls.Add(btnChiudi);
lista.SelectedIndex = 0;
}
}
-61
View File
@@ -1,61 +0,0 @@
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>
public class ReportCompletezzaForm : Form
{
public ReportCompletezzaForm(List<(string Componente, string Descrizione, bool? Completo)> righe)
{
int completi = righe.Count(r => r.Completo == true);
int incompleti = righe.Count(r => r.Completo == false);
int nonTrovati = righe.Count(r => r.Completo is null);
Text = "Completezza assiemi";
ClientSize = new Size(720, 440);
StartPosition = FormStartPosition.CenterParent;
Font = new Font("Segoe UI", 9.5f);
var lblInfo = new Label
{
Dock = DockStyle.Top,
AutoSize = true,
Padding = new Padding(12, 8, 12, 8),
Text = $"{completi} completi, {incompleti} incompleti, {nonTrovati} non trovati nell'indice "
+ $"su {righe.Count} componenti. Nota: la scansione dei .asm è euristica, qualche "
+ "falso 'incompleto' è possibile.",
};
var grid = new DataGridView
{
Dock = DockStyle.Fill,
ReadOnly = true,
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
RowHeadersVisible = false,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
};
grid.Columns.Add("componente", "Componente");
grid.Columns.Add("descrizione", "Descrizione");
grid.Columns.Add("completo", "Completo");
grid.Columns["componente"]!.FillWeight = 20;
grid.Columns["descrizione"]!.FillWeight = 55;
grid.Columns["completo"]!.FillWeight = 25;
foreach (var (componente, descrizione, completo) in righe.OrderBy(r => r.Completo).ThenBy(r => r.Componente))
{
string testo = completo switch { true => "✓", false => "✗", null => "?" };
int i = grid.Rows.Add(componente, descrizione, testo);
grid.Rows[i].DefaultCellStyle.ForeColor = completo switch
{
true => Color.DarkGreen,
false => Color.DarkRed,
null => Color.Gray,
};
grid.Rows[i].Cells["completo"].Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
Controls.Add(grid);
Controls.Add(lblInfo);
}
}