diff --git a/.gitignore b/.gitignore
index 4183425..629568c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,4 @@ publish/
/*.csv
/raw/
/step/
+*.dxc
diff --git a/CadSearchGui/AssemblyComponentScanner.cs b/CadSearchGui/AssemblyComponentScanner.cs
new file mode 100644
index 0000000..8329a6f
--- /dev/null
+++ b/CadSearchGui/AssemblyComponentScanner.cs
@@ -0,0 +1,29 @@
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace CadSearchGui;
+
+///
+/// Estrae i codici componente referenziati dentro un file .asm Pro-E/Creo.
+/// Il formato è per lo più binario, ma i codici dei sotto-componenti compaiono
+/// come stringhe ASCII leggibili al suo interno: si scansiona il file come testo
+/// cercando pattern che assomigliano a un codice (stesso stile usato nei nomi file,
+/// vedi Indexer.FilenameRegex) e si lascia poi al chiamante il compito di verificarli
+/// contro l'indice DB, che scarta automaticamente i falsi positivi (numeri che non
+/// corrispondono a nessun codice reale).
+///
+static class AssemblyComponentScanner
+{
+ private static readonly Regex CodiceRegex = new(@"\b\d{8,12}[A-Za-z]{0,2}\b", RegexOptions.Compiled);
+
+ public static HashSet EstraiCodiciReferenziati(string percorsoAsm)
+ {
+ var testo = Encoding.Latin1.GetString(File.ReadAllBytes(percorsoAsm));
+ var codici = new HashSet();
+ foreach (Match m in CodiceRegex.Matches(testo))
+ {
+ codici.Add(m.Value);
+ }
+ return codici;
+ }
+}
diff --git a/CadSearchGui/Form1.cs b/CadSearchGui/Form1.cs
index d34e7a6..2bb444d 100644
--- a/CadSearchGui/Form1.cs
+++ b/CadSearchGui/Form1.cs
@@ -8,6 +8,7 @@ public partial class Form1 : Form
private readonly Button _btnApriDb = new();
private readonly Button _btnAggiorna = new();
private readonly Button _btnCreaRaw = new();
+ private readonly Button _btnTrovaPrt = new();
private readonly Label _lblStatus = new();
private readonly Label _lblDb = new();
private readonly DataGridView _gridImport = new();
@@ -20,6 +21,7 @@ public partial class Form1 : Form
private string? _dbPath;
private string? _percorsoInputCorrente;
private List<(string Componente, string Descrizione)>? _righeCorrenti;
+ private string? _ultimaCartellaRaw;
public Form1()
{
@@ -201,9 +203,27 @@ public partial class Form1 : Form
_btnCreaRaw.Click += (_, _) => CreaCartellaRaw();
contenuto.Controls.Add(_btnCreaRaw);
+ var lblIstruzioni3 = new Label
+ {
+ AutoSize = true,
+ ForeColor = SystemColors.GrayText,
+ Margin = new Padding(0, 12, 0, 0),
+ Text = "4. Gli assiemi referenziano altri PRT e sotto-assiemi al loro interno: premi "
+ + "qui per risolvere l'albero componenti (ricorsivamente) e copiare tutto ciò che "
+ + "viene trovato nell'indice dentro 'raw'.",
+ };
+ contenuto.Controls.Add(lblIstruzioni3);
+
+ _btnTrovaPrt.Text = "Trova componenti referenziati";
+ _btnTrovaPrt.Size = new Size(220, 32);
+ _btnTrovaPrt.Margin = new Padding(0, 8, 0, 0);
+ _btnTrovaPrt.Enabled = false;
+ _btnTrovaPrt.Click += (_, _) => TrovaPrtReferenziati();
+ contenuto.Controls.Add(_btnTrovaPrt);
+
return new GroupBox
{
- Text = "2-3. Importa Distinta Base e crea la cartella raw",
+ Text = "2-4. Importa Distinta Base, crea la cartella raw e risolvi i componenti referenziati",
Dock = DockStyle.Top,
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
@@ -276,6 +296,8 @@ public partial class Form1 : Form
var cartellaRaw = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(_percorsoInputCorrente))!, "raw");
int copiati = CopiaFileTrovati(percorsiTrovati.Values.SelectMany(p => p).Distinct(), cartellaRaw);
+ _ultimaCartellaRaw = cartellaRaw;
+ _btnTrovaPrt.Enabled = true;
ImpostaStatoImport(
$"{percorsiTrovati.Count} componenti con percorso CAD trovato su {_righeCorrenti.Count}. "
@@ -283,9 +305,75 @@ public partial class Form1 : Form
Color.DarkGreen);
}
+ /// Risolve ricorsivamente l'albero dei componenti a partire dagli .asm in "raw":
+ /// per ogni assieme legge i codici referenziati al suo interno, li cerca nell'indice, copia
+ /// in "raw" quelli trovati e, se sono a loro volta assiemi, li accoda per essere scansionati
+ /// finché non restano solo PRT (foglie) o codici non presenti nell'indice.
+ private void TrovaPrtReferenziati()
+ {
+ if (_ultimaCartellaRaw is null || !Directory.Exists(_ultimaCartellaRaw))
+ return;
+
+ var daScansionare = new Queue(
+ Directory.GetFiles(_ultimaCartellaRaw).Where(f => Path.GetFileName(f).Contains(".asm.")));
+ var codiciVisti = new HashSet(daScansionare.Select(f => Path.GetFileName(f).Split('.')[0]));
+
+ var trovati = new List<(string Codice, string Tipo, string Percorso)>();
+ int codiciNonTrovati = 0;
+
+ if (_dbPath is not null)
+ {
+ using var connection = new SqliteConnection($"Data Source={_dbPath};Mode=ReadOnly");
+ connection.Open();
+ using var command = connection.CreateCommand();
+ command.CommandText = "SELECT tipo_file, percorso_completo FROM files_ultima_versione "
+ + "WHERE codice = @codice AND tipo_file IN ('prt', 'asm')";
+ var pCodice = command.Parameters.Add("@codice", Microsoft.Data.Sqlite.SqliteType.Text);
+
+ while (daScansionare.Count > 0)
+ {
+ var asm = daScansionare.Dequeue();
+ foreach (var codice in AssemblyComponentScanner.EstraiCodiciReferenziati(asm))
+ {
+ if (!codiciVisti.Add(codice))
+ continue;
+
+ pCodice.Value = codice;
+ var componenti = new List<(string Tipo, string Percorso)>();
+ using (var reader = command.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ componenti.Add((reader.GetString(0), reader.GetString(1)));
+ }
+ }
+
+ if (componenti.Count == 0)
+ {
+ codiciNonTrovati++;
+ continue;
+ }
+
+ foreach (var (tipo, percorso) in componenti)
+ {
+ CopiaFileTrovati(new[] { percorso }, _ultimaCartellaRaw);
+ trovati.Add((codice, tipo, percorso));
+ if (tipo == "asm")
+ {
+ daScansionare.Enqueue(Path.Combine(_ultimaCartellaRaw, Path.GetFileName(percorso)));
+ }
+ }
+ }
+ }
+ }
+
+ using var dialog = new PrtMancantiForm(trovati, codiciNonTrovati, _ultimaCartellaRaw);
+ dialog.ShowDialog(this);
+ }
+
/// Copia i file trovati nella cartella "raw" indicata, creandola se necessario.
/// Ritorna il numero di file copiati con successo.
- private static int CopiaFileTrovati(IEnumerable percorsi, string cartellaRaw)
+ internal static int CopiaFileTrovati(IEnumerable percorsi, string cartellaRaw)
{
int copiati = 0;
foreach (var percorso in percorsi)
diff --git a/CadSearchGui/PrtMancantiForm.cs b/CadSearchGui/PrtMancantiForm.cs
new file mode 100644
index 0000000..0b2e0e8
--- /dev/null
+++ b/CadSearchGui/PrtMancantiForm.cs
@@ -0,0 +1,68 @@
+using System.Diagnostics;
+
+namespace CadSearchGui;
+
+/// 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.
+public class PrtMancantiForm : Form
+{
+ public PrtMancantiForm(List<(string Codice, string Tipo, string Percorso)> righe, int codiciNonTrovati, string cartellaRaw)
+ {
+ int numPrt = righe.Count(r => r.Tipo == "prt");
+ int numAsm = righe.Count(r => r.Tipo == "asm");
+
+ Text = "Componenti referenziati trovati";
+ ClientSize = new Size(720, 440);
+ StartPosition = FormStartPosition.CenterParent;
+ Font = new Font("Segoe UI", 9.5f);
+
+ var lblInfo = new Label
+ {
+ Dock = DockStyle.Top,
+ AutoSize = false,
+ Height = 44,
+ Padding = new Padding(12, 8, 12, 8),
+ Text = $"Copiati in \"{cartellaRaw}\": {numPrt} PRT e {numAsm} sotto-assiemi trovati nell'indice."
+ + (codiciNonTrovati > 0
+ ? $" {codiciNonTrovati} codici referenziati non corrispondono a nessun file indicizzato "
+ + "(probabilmente disegni, o falsi positivi della scansione)."
+ : ""),
+ };
+
+ var grid = new DataGridView
+ {
+ Dock = DockStyle.Fill,
+ ReadOnly = true,
+ AllowUserToAddRows = false,
+ AllowUserToDeleteRows = false,
+ RowHeadersVisible = false,
+ SelectionMode = DataGridViewSelectionMode.FullRowSelect,
+ AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
+ };
+ grid.Columns.Add("codice", "Codice");
+ grid.Columns.Add("tipo", "Tipo");
+ grid.Columns.Add("percorso", "Percorso CAD");
+ grid.Columns["codice"]!.FillWeight = 20;
+ grid.Columns["tipo"]!.FillWeight = 10;
+ grid.Columns["percorso"]!.FillWeight = 70;
+ foreach (var (codice, tipo, percorso) in righe.OrderBy(r => r.Tipo).ThenBy(r => r.Codice))
+ {
+ grid.Rows.Add(codice, tipo, percorso);
+ }
+
+ var btnApriCartella = new Button { Text = "Apri cartella raw", Dock = DockStyle.Bottom, Height = 34 };
+ btnApriCartella.Click += (_, _) =>
+ {
+ try { Process.Start("explorer.exe", cartellaRaw); }
+ catch (Exception ex)
+ {
+ MessageBox.Show(this, $"Impossibile aprire Esplora risorse:\n{ex.Message}", "Errore",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ };
+
+ Controls.Add(grid);
+ Controls.Add(lblInfo);
+ Controls.Add(btnApriCartella);
+ }
+}