From e56a3be5b4fe289e0ed3d404e026feef2d254b35 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Wed, 16 Sep 2026 11:40:34 +0200 Subject: [PATCH] Add recursive component resolution for assembly dependencies Assemblies reference other assemblies and parts internally; a flat single-level copy into raw/ left Creo unable to load models ("Dati di input non completi"). AssemblyComponentScanner heuristically extracts component codes embedded as readable strings inside the otherwise binary .asm files; each candidate is validated against the index (which filters out scan noise), copied into raw/, and if it's itself an assembly, queued for the same treatment until the whole tree resolves to PRT leaves. PrtMancantiForm reports what was found and copied. Also ignore Creo Distributed Batch job files (*.dxc). Co-Authored-By: Claude Sonnet 5 --- .gitignore | 1 + CadSearchGui/AssemblyComponentScanner.cs | 29 ++++++++ CadSearchGui/Form1.cs | 92 +++++++++++++++++++++++- CadSearchGui/PrtMancantiForm.cs | 68 ++++++++++++++++++ 4 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 CadSearchGui/AssemblyComponentScanner.cs create mode 100644 CadSearchGui/PrtMancantiForm.cs 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); + } +}