diff --git a/CadSearchGui/AssemblyComponentScanner.cs b/CadSearchGui/AssemblyComponentScanner.cs index cdb2d70..41b90a9 100644 --- a/CadSearchGui/AssemblyComponentScanner.cs +++ b/CadSearchGui/AssemblyComponentScanner.cs @@ -36,12 +36,20 @@ namespace CadSearchGui; /// basta: può trattarsi del riferimento al nome di un altro assieme che lo contiene (non /// noto a priori, quindi non escludibile per confronto diretto come il proprio codice) o /// di un residuo di feature soppresse/superate (vedi docs/formato-asm.md). Per questi si -/// richiede anche un vero placement, in una delle due forme osservate nel binario: +/// richiede anche un vero placement, in una delle quattro forme osservate nel binario: /// "Componente ID N (<codice>...)"/"Component id N (<codice>...)" (la stringa -/// cambia con la localizzazione Creo del file), oppure il codice ripetuto due volte di -/// seguito separato da un singolo byte NUL ("<codice>\0<codice>\0", osservato per -/// componenti di family table non-UNI, es. cuscinetti "CUSCI_..."). Non si applica alle -/// varianti con suffisso o bracket UNI: in alcuni file il placement riporta solo il nome +/// cambia con la localizzazione Creo del file); il codice ripetuto due volte di seguito +/// separato da un singolo byte NUL ("<codice>\0<codice>\0", osservato per +/// componenti di family table non-UNI, es. cuscinetti "CUSCI_..."); il campo +/// "family_name\0<codice>\0" (osservato una sola volta per file, su un sotto-assieme +/// referenziato come family table generica invece che con un placement testuale esplicito); +/// oppure il codice seguito immediatamente da un bracket "<codice><GENERICO>" - +/// l'istanza di una family table non-UNI vista nel nome (es. "2510060303<CUSCI_ACBB7>", +/// un cuscinetto): il codice dell'istanza non è mai indicizzato a sé (solo il generico tra +/// "<" e ">" lo è), ma questo segnale lo fa comunque sopravvivere fino alla ricerca nel +/// DB invece di scomparire in silenzio come "nudo senza placement" - lì verrà comunque +/// scartato come "non in indice", ma in modo tracciabile. Non si applica alle varianti con +/// suffisso o bracket UNI: in alcuni file il placement riporta solo il nome /// istanza/generico senza il dettaglio del suffisso o del bracket, che vive altrove nei /// metadati - lì resta necessaria la sola euristica delle occorrenze ripetute. /// @@ -74,7 +82,9 @@ static class AssemblyComponentScanner bool HaPlacement(string codice) => Regex.IsMatch(testo, $@"Component(?:e)? id \d+ \([^()]*{Regex.Escape(codice)}[^()]*\)", RegexOptions.IgnoreCase) - || testo.Contains(codice + "\0" + codice + "\0"); + || testo.Contains(codice + "\0" + codice + "\0") + || testo.Contains("family_name\0" + codice + "\0") + || testo.Contains(codice + "<"); candidati.RemoveWhere(c => CodiceNudoRegex.IsMatch(c) && !HaPlacement(c)); return candidati; diff --git a/docs/formato-asm.md b/docs/formato-asm.md index 961d839..52fed2f 100644 --- a/docs/formato-asm.md +++ b/docs/formato-asm.md @@ -73,7 +73,7 @@ non essendo un componente. A differenza del proprio codice, non è noto a priori: dipende da dove/come il file è stato aperto o da chi lo referenzia, quindi non si può escludere per confronto diretto come per il caso precedente. La regola generale che discrimina questo caso: un componente -vero compare **sempre** anche dentro un vero placement, in una delle due +vero compare **sempre** anche dentro un vero placement, in una delle quattro forme osservate nel binario: - `Componente ID N (...)`/`Component id N (...)` (la stringa @@ -82,13 +82,32 @@ forme osservate nel binario: - il codice ripetuto due volte di seguito separato da un singolo byte NUL, `\0\0` (osservato per componenti di family table non-UNI, es. cuscinetti `CUSCI_...` - vedi sotto; il placement testuale - "Componente ID" non li copre, ma questa coppia doppia sì). + "Componente ID" non li copre, ma questa coppia doppia sì); +- il campo `family_name\0\0` (con `name` e `family_name` uguali, + entrambi pari al codice). Osservato una sola volta per file, su un + sotto-assieme referenziato come family table generica invece che con un + placement testuale esplicito - caso `9284202452` dentro + `9284208188_D400.asm`: il sotto-assieme (che contiene a sua volta + `9284202452_C01/_C02/_C03`) non ha nessun `Componente ID (...)` né coppia + NUL, solo questo campo `family_name`. Verificato su 5 file diversi + (`9284208188_D400/D600/D610/D620/D630.asm`): l'unica occorrenza di + `family_name\0\0` in ciascuno corrisponde sempre a un componente + reale confermato dalla BOM Creo, mai al riferimento fantasma verso l'alto; +- il codice seguito immediatamente da un bracket, `` + (senza NUL né spazio in mezzo) - l'istanza di una family table non-UNI + (es. `2510060303`, un cuscinetto, confermato aprendo l'assieme + in Creo: descrizione "CUSC.OBL.SFERE 2COR 17X40"). Il codice dell'istanza + non è mai indicizzato a sé sulla share (solo il generico tra `<` e `>` lo + è - stesso meccanismo di UNI, vedi sotto), quindi anche riconosciuto come + placement reale finisce comunque negli "scartati" alla ricerca nel DB - ma + in modo tracciabile, invece di sparire in silenzio prima ancora di essere + cercato. Un riferimento incidentale al nome di un altro assieme, anche se ripetuto più -volte, non compare mai in nessuna delle due forme. `scripts/ExtractCreoStrings.cs` +volte, non compare mai in nessuna delle quattro forme. `scripts/ExtractCreoStrings.cs` e `AssemblyComponentScanner.cs` applicano questa regola ai codici "nudi" (senza suffisso `_...` e senza bracket family table - vedi sotto perché limitata a -questi): un codice ripetuto ma mai trovato dentro un placement/coppia NUL +questi): un codice ripetuto ma mai trovato dentro uno dei quattro segnali viene scartato. Questo NON copre i casi fantasma/soppressi sotto, che hanno un placement sintatticamente identico a uno vero e restano quindi indistinguibili a livello testuale. diff --git a/scripts/ExtractCreoStrings.cs b/scripts/ExtractCreoStrings.cs index f1de61d..70e1dea 100644 --- a/scripts/ExtractCreoStrings.cs +++ b/scripts/ExtractCreoStrings.cs @@ -49,18 +49,28 @@ HashSet EstraiCodiciReferenziati(string percorsoAsm) // riferimenti al proprio assieme padre (nome sessione/file) o a codici superati/ // rinominati (caso "fantasma", docs/formato-asm.md) ripetono comunque il codice più // volte pur non essendo mai un componente vero. Per questi si richiede un vero - // placement, in una delle due forme osservate nel binario: "Componente ID N + // placement, in una delle quattro forme osservate nel binario: "Componente ID N // (...)"/"Component id N (...)" (la stringa cambia con la - // localizzazione Creo del file), oppure il codice ripetuto due volte di seguito - // separato da un singolo byte NUL ("\0\0", osservato per componenti di - // family table non-UNI, es. cuscinetti "CUSCI_..."). Non si applica alle varianti con - // suffisso "_..." né alle istanze family table UNI: in alcuni file il placement + // localizzazione Creo del file); il codice ripetuto due volte di seguito separato da + // un singolo byte NUL ("\0\0", osservato per componenti di family + // table non-UNI, es. cuscinetti "CUSCI_..."); il campo "family_name\0\0" + // (osservato una sola volta per file, su un sotto-assieme referenziato come family + // table generica invece che con un placement testuale esplicito); oppure il codice + // seguito immediatamente da un bracket "" - l'istanza di una family + // table non-UNI vista nel nome (es. "2510060303", un cuscinetto): il + // codice dell'istanza non è mai indicizzato a sé (solo il generico tra "<" e ">" lo è, + // vedi sotto), ma questo segnale lo fa comunque sopravvivere fino alla ricerca nel DB + // invece di scomparire in silenzio come "nudo senza placement" - lì verrà comunque + // scartato come "non in indice", ma in modo tracciabile. Non si applica alle varianti + // con suffisso "_..." né alle istanze family table UNI: in alcuni file il placement // riporta solo il nome istanza/generico senza il dettaglio del suffisso o del bracket // "<...>", che vive altrove nei metadati - lì l'occorrenza ripetuta resta l'unico // segnale disponibile. bool HaPlacement(string codice) => Regex.IsMatch(testo, $@"Component(?:e)? id \d+ \([^()]*{Regex.Escape(codice)}[^()]*\)", RegexOptions.IgnoreCase) - || testo.Contains(codice + "\0" + codice + "\0"); + || testo.Contains(codice + "\0" + codice + "\0") + || testo.Contains("family_name\0" + codice + "\0") + || testo.Contains(codice + "<"); candidati.RemoveWhere(c => Regex.IsMatch(c, @"^\d{8,12}[A-Za-z]{0,2}$") && !HaPlacement(c)); return candidati; @@ -84,8 +94,13 @@ Dictionary> CercaComponenti(IReadOn using var command = connection.CreateCommand(); var nomiParametri = codici.Select((_, i) => $"@c{i}").ToList(); + // Lo stesso codice+tipo può esistere in più cartelle radice (duplicati/copie storiche + // sulla share, es. un generico family table ripetuto in 5 progetti scollegati): ordinato + // per data_modifica DESC, si tiene solo il più recente per codice+tipo (stessa regola di + // Form1.RisolviAlberoComponenti, vedi docs/formato-asm.md). command.CommandText = "SELECT codice, tipo_file, percorso_completo FROM files_ultima_versione " - + $"WHERE LOWER(codice) IN ({string.Join(",", nomiParametri)}) AND LOWER(tipo_file) IN ('prt', 'asm')"; + + $"WHERE LOWER(codice) IN ({string.Join(",", nomiParametri)}) AND LOWER(tipo_file) IN ('prt', 'asm') " + + "ORDER BY codice, tipo_file, data_modifica DESC"; int i = 0; foreach (var codice in codici) command.Parameters.AddWithValue(nomiParametri[i++], codice.ToLowerInvariant());