5 Commits
Author SHA1 Message Date
davideandClaude Sonnet 5.5 1eac3ccaa7 Documenta posizioni, fori e facce dei .prt (9239902104)
Matrici 4x3 di fori e feature (regola degli zeri corretta), foro a punti
di schizzo con facce piane di Estrusione 1, profilo a L, quote per feature
e tipi 928/1104/1052. Corregge l'ipotesi errata sul codice 12266.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 16:21:18 +02:00
davideandClaude Sonnet 5.5 5b11b3e8ca Documenta la decodifica dei .prt: albero delle feature, quote e geometria
Reverse engineering di test.prt.1, 9252000507.prt e 9239901907.prt contro
il model tree di Creo: contenitore e sezioni, ricostruzione dell'albero
(nomi per contatore, gruppi, sezioni), numeri compatti, quote, foro, smusso
e vincoli dei piani, con la tabella di cosa resta da decodificare.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 15:58:15 +02:00
davideandClaude Sonnet 5.5 d5fb20a34e Rilascia la 1.1.0
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 08:52:57 +02:00
davideandClaude Sonnet 5.5 6d1e36a2ae Aggiungi la lista personalizzata di codici nella fase 2
In alternativa al file del gestionale si possono digitare i codici uno alla
volta (Invio/Aggiungi, Canc per togliere). Tutto a valle legge solo
_righeCorrenti, quindi basta una nuova sorgente; la selezione della griglia
mantiene il colore verde/rosso della verifica.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 08:52:05 +02:00
davideandClaude Sonnet 5.5 ccfa00ff52 Aggiungi docs-as-code: indice Diátaxis, nuovi doc e controllo automatico
Nuovi doc: tutorial-primo-giro, risoluzione-problemi, sviluppo, rilascio,
glossario e registro delle decisioni (docs/decisioni, 6 ADR). docs/README.md
e' l'indice per categoria e la tabella delle fonti uniche; il README di
root rimanda a sviluppo/rilascio invece di duplicarli.

scripts/CheckDocs.cs verifica link e ancore, <Version> contro CHANGELOG,
copertura di architettura.md e presenza di ogni doc nell'indice;
.markdownlint-cli2.jsonc configura il lint Markdown.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 08:05:10 +02:00
25 changed files with 1243 additions and 98 deletions

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
{
// Regole base; disattivate quelle che non servono a prosa italiana con tabelle larghe
"config": {
"default": true,
"MD013": false, // lunghezza riga
"MD033": false, // HTML inline
"MD024": { "siblings_only": true }, // CHANGELOG ripete "Aggiunto/Modificato"
"MD036": false,
"MD040": false, // dump esadecimali/testuali senza linguaggio
"MD060": false, // stile colonne tabelle
"MD041": false
},
"globs": ["**/*.md"],
"ignores": ["node_modules", "raw", "step", "**/bin", "**/obj"]
}
+20
View File
@@ -5,6 +5,26 @@ Tutte le modifiche rilevanti di questo progetto sono documentate in questo file.
Il formato segue [Keep a Changelog](https://keepachangelog.com/it/1.1.0/), e il progetto
aderisce a [Semantic Versioning](https://semver.org/lang/it/).
## [Non rilasciato]
### Aggiunto
- `docs/formato-prt-feature.md`: decodifica (da reverse engineering) dell'albero delle feature di un `.prt`.
- `docs/formato-prt-geometria.md`: numeri, quote e geometria degli schizzi di un `.prt`, con la tabella di cosa resta da decodificare.
## [1.1.0] - 2026-10-07
### Aggiunto
- Fase 2: modalità "Lista personalizzata" per inserire i codici a mano, uno alla volta, in alternativa al file del gestionale.
- Docs-as-code: `scripts/CheckDocs.cs`, markdownlint, `docs/README.md` (indice Diátaxis e fonte unica).
- Nuovi doc: `tutorial-primo-giro`, `risoluzione-problemi`, `sviluppo`, `rilascio`, `glossario` e registro delle decisioni `docs/decisioni/` (6 ADR).
### Modificato
- Il README rimanda a `docs/sviluppo.md` e `docs/rilascio.md` per build, test e setup, invece di duplicarli.
- Fase 2: dopo "Verifica presenza" la riga selezionata mantiene il verde/rosso dell'esito invece di diventare blu.
## [1.0.0] - 2026-09-23
Prima versione distribuita agli utenti tramite setup di installazione.
+6
View File
@@ -63,8 +63,14 @@ Dove scrivere cosa:
| [`docs/formato-asm.md`](docs/formato-asm.md) | euristica testuale di fallback |
| [`docs/db-cad-index.md`](docs/db-cad-index.md) | schema e vista di `cad_index.db` |
| [`docs/copia-raw-parallela.md`](docs/copia-raw-parallela.md) | parallelizzazione della copia in `raw/` |
| [`docs/README.md`](docs/README.md) | indice Diátaxis di tutti i doc (tutorial, how-to, reference, decisioni) e regola di fonte unica |
| [`CHANGELOG.md`](CHANGELOG.md) | storico delle versioni (formato Keep a Changelog); ogni release che cambia `<Version>` nel csproj aggiunge qui la sua voce |
I doc sono trattati come codice: stanno in git e si verificano con
`dotnet run scripts/CheckDocs.cs` (link, versione/CHANGELOG, copertura di `architettura.md`) e
`npx markdownlint-cli2` (config `.markdownlint-cli2.jsonc`) — da lanciare prima di ogni commit che
tocca i doc. Organizzazione (Diátaxis) e regola di fonte unica: [`docs/README.md`](docs/README.md).
Quando il cambiamento nasce da un fallimento reale di Creo, scrivere anche **il sintomo**: i
messaggi di Creo non nominano il file o la causa, e senza quel ponte la prossima volta si
riparte da zero (i §7.3/§7.4/§7.5 di `formato-asm-record.md` sono l'esempio del formato
+125 -8
View File
@@ -12,6 +12,11 @@ public partial class Form1 : Form
private readonly Button _btnTrovaPrt = new();
private readonly Button _btnVerifica = new();
private readonly FlowLayoutPanel _rigaVerifica = new();
private readonly FlowLayoutPanel _rigaFile = new();
private readonly FlowLayoutPanel _rigaCustom = new();
private readonly RadioButton _rbFile = new() { Text = "Da file del gestionale", Checked = true, AutoSize = true };
private readonly RadioButton _rbCustom = new() { Text = "Lista personalizzata", AutoSize = true };
private readonly TextBox _txtCodice = new() { Width = 220, Margin = new Padding(0, 3, 8, 0) };
private readonly ListView _lstAnteprimaAsm = new();
private readonly Label _lblStatus = new();
private readonly Label _lblDb = new();
@@ -50,7 +55,7 @@ public partial class Form1 : Form
private string? _dbPath;
private string? _cartellaLavoro;
private string? _percorsoInputCorrente;
private bool _ripristinoModalita;
private List<(string Componente, string Descrizione)>? _righeCorrenti;
private Dictionary<string, List<string>>? _ultimaVerifica;
private string? _ultimaCartellaRaw;
@@ -266,7 +271,12 @@ public partial class Form1 : Form
return;
}
_percorsoInputCorrente = null;
AzzeraDistinta();
VaiAllaFase(1);
}
private void AzzeraDistinta()
{
_righeCorrenti = null;
_ultimaCartellaRaw = null;
_ultimaVerifica = null;
@@ -275,8 +285,68 @@ public partial class Form1 : Form
_lstAnteprimaAsm.Items.Clear();
ImpostaStatoImport("", Color.Black);
_rigaVerifica.Visible = false;
}
VaiAllaFase(1);
private void CambiaModalitaDistinta()
{
if (_ripristinoModalita)
return;
if (_gridImport.Rows.Count > 0 && MessageBox.Show(this,
"Cambiando modalità la distinta corrente viene azzerata. Continuare?", "Distinta",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
{
_ripristinoModalita = true;
(_rbCustom.Checked ? _rbFile : _rbCustom).Checked = true;
_ripristinoModalita = false;
return;
}
AzzeraDistinta();
_rigaFile.Visible = _rbFile.Checked;
_rigaCustom.Visible = _rbCustom.Checked;
AggiornaStatoAvanti();
}
private void AggiungiCodice()
{
string codice = _txtCodice.Text.Trim();
if (codice.Length == 0)
return;
_righeCorrenti ??= new();
if (_righeCorrenti.Any(r => string.Equals(r.Componente, codice, StringComparison.OrdinalIgnoreCase)))
{
_txtCodice.SelectAll();
return;
}
_righeCorrenti.Add((codice, ""));
_gridImport.Rows.Add(codice, "");
InvalidaVerifica();
_txtCodice.Clear();
_txtCodice.Focus();
}
private void RimuoviCodiceSelezionato()
{
if (_gridImport.CurrentRow is not { } riga || _righeCorrenti is null)
return;
_righeCorrenti.RemoveAt(riga.Index); // griglia e lista hanno lo stesso ordine
_gridImport.Rows.Remove(riga);
InvalidaVerifica();
}
/// <summary>La lista è cambiata: la verifica presenza e il raw già creato non valgono più.</summary>
private void InvalidaVerifica()
{
_ultimaVerifica = null;
_ultimaCartellaRaw = null;
_gridRaw.Rows.Clear();
foreach (DataGridViewRow r in _gridImport.Rows)
{
r.DefaultCellStyle.BackColor = Color.Empty;
r.DefaultCellStyle.SelectionBackColor = Color.Empty;
r.DefaultCellStyle.SelectionForeColor = Color.Empty;
}
_rigaVerifica.Visible = _righeCorrenti is { Count: > 0 };
AggiornaStatoAvanti();
}
private Panel BuildFaseDatabase()
@@ -400,12 +470,51 @@ public partial class Form1 : Form
};
contenuto.Controls.Add(lblIstruzioni);
var rigaImporta = new FlowLayoutPanel
var rigaModalita = new FlowLayoutPanel
{
AutoSize = true,
FlowDirection = FlowDirection.LeftToRight,
Margin = new Padding(0, 0, 0, 8),
};
rigaModalita.Controls.Add(_rbFile);
rigaModalita.Controls.Add(_rbCustom);
_rbCustom.CheckedChanged += (_, _) => CambiaModalitaDistinta();
contenuto.Controls.Add(rigaModalita);
_rigaCustom.AutoSize = true;
_rigaCustom.FlowDirection = FlowDirection.LeftToRight;
_rigaCustom.Margin = new Padding(0, 0, 0, 8);
_rigaCustom.Visible = false;
var btnAggiungi = new Button { Text = "Aggiungi", Margin = new Padding(0, 0, 8, 0) };
UiStile.Secondario(btnAggiungi);
btnAggiungi.Click += (_, _) => AggiungiCodice();
_txtCodice.KeyDown += (_, e) =>
{
if (e.KeyCode != Keys.Enter)
return;
e.SuppressKeyPress = true;
AggiungiCodice();
};
_rigaCustom.Controls.Add(_txtCodice);
_rigaCustom.Controls.Add(btnAggiungi);
_rigaCustom.Controls.Add(new Label
{
AutoSize = true,
ForeColor = SystemColors.GrayText,
Margin = new Padding(0, 9, 0, 0),
Text = "Digita un codice e premi Invio (Canc sulla riga per toglierlo)",
});
contenuto.Controls.Add(_rigaCustom);
_gridImport.KeyDown += (_, e) =>
{
if (e.KeyCode == Keys.Delete && _rbCustom.Checked)
RimuoviCodiceSelezionato();
};
var rigaImporta = _rigaFile;
rigaImporta.AutoSize = true;
rigaImporta.FlowDirection = FlowDirection.LeftToRight;
rigaImporta.Margin = new Padding(0, 0, 0, 8);
var btnSfoglia = new Button
{
Text = "Scegli file distinta...",
@@ -646,10 +755,15 @@ public partial class Form1 : Form
private void ImportaDistinta(string percorso)
{
if (_rbCustom.Checked)
{
_rbFile.Checked = true; // chiede conferma se c'è una lista; se rifiuta resta in custom
if (_rbCustom.Checked)
return;
}
try
{
var (_, righe) = DistintaBaseImporter.ConvertiInCsv(percorso, _cartellaLavoro!);
_percorsoInputCorrente = percorso;
_righeCorrenti = righe;
_gridImport.Rows.Clear();
@@ -666,7 +780,6 @@ public partial class Form1 : Form
}
catch (Exception ex)
{
_percorsoInputCorrente = null;
_righeCorrenti = null;
_gridImport.Rows.Clear();
ImpostaStatoImport($"Errore durante la conversione: {ex.Message}", Color.DarkRed);
@@ -694,16 +807,20 @@ public partial class Form1 : Form
foreach (DataGridViewRow riga in _gridImport.Rows)
{
string componente = (string)riga.Cells["componente"].Value!;
riga.DefaultCellStyle.BackColor = trovati.ContainsKey(componente)
var colore = trovati.ContainsKey(componente)
? Color.FromArgb(198, 239, 206)
: Color.FromArgb(255, 199, 206);
// la selezione mantiene il colore verde/rosso, altrimenti il blu nasconde l'esito
riga.DefaultCellStyle.BackColor = colore;
riga.DefaultCellStyle.SelectionBackColor = colore;
riga.DefaultCellStyle.SelectionForeColor = Color.Black;
}
AggiornaStatoAvanti();
}
private void CreaCartellaRaw()
{
if (_percorsoInputCorrente is null || _righeCorrenti is null)
if (_righeCorrenti is null)
return;
// L'assieme generale è già una riga di _righeCorrenti (aggiunta da
+4 -1
View File
@@ -104,7 +104,10 @@ public class GuidaForm : Form
[
"Carica la distinta: trascina il file .xls nella finestra, oppure premi " +
"\"Scegli file distinta...\". Le righe importate compaiono nella griglia " +
"(Componente/Descrizione) e viene scritto un CSV nella cartella di lavoro.",
"(Componente/Descrizione) e viene scritto un CSV nella cartella di lavoro. " +
"In alternativa scegli \"Lista personalizzata\" e digita i codici uno alla " +
"volta (Invio per aggiungere, Canc per togliere): nessun CSV, l'assieme " +
"generale va inserito a mano.",
"Verifica la presenza dei file sul server: compare solo dopo un import " +
"riuscito. Cerca ogni componente nel database — le righe della griglia si " +
"colorano di verde (trovato) o rosso (non trovato nell'indice).",
+1 -1
View File
@@ -12,7 +12,7 @@
<Product>PrepStep - Cevolani</Product>
<Company>Cevolani</Company>
<Copyright>Copyright (c) 2026 Davide Grilli</Copyright>
<Version>1.0.0</Version>
<Version>1.1.0</Version>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
+10 -83
View File
@@ -40,7 +40,8 @@ passaggio, non lo sostituisce.
manuale, fuori da quest'app): scrive in `step/`, dentro la cartella di
lavoro.
Guida utente passo-passo completa in [`docs/guida-utente.md`](docs/guida-utente.md)
Primo uso: [`docs/tutorial-primo-giro.md`](docs/tutorial-primo-giro.md). Guida utente completa in
[`docs/guida-utente.md`](docs/guida-utente.md)
(stessa sintesi anche in-app da **Aiuto → Guida**, F1).
## Requisiti
@@ -50,83 +51,20 @@ Guida utente passo-passo completa in [`docs/guida-utente.md`](docs/guida-utente.
- Accesso alla share di rete con i file CAD (`X:\` di default) per
l'indicizzazione
## Build
## Build, test e rilascio
```powershell
cd PrepStep
dotnet build -c Release
```
Build, avvio e test: [`docs/sviluppo.md`](docs/sviluppo.md). Pubblicazione dell'eseguibile e
generazione del setup di installazione: [`docs/rilascio.md`](docs/rilascio.md).
## Avvio
Da sorgente:
```powershell
cd PrepStep
dotnet run -c Release
```
Oppure l'eseguibile già compilato:
```powershell
cd PrepStep\bin\Release\net10.0-windows
.\PrepStep.exe
```
## Pubblicazione (eseguibile standalone)
Genera un `.exe` self-contained per Windows x64, senza bisogno del runtime
.NET installato sulla macchina di destinazione:
```powershell
cd PrepStep
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish
```
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.
## Setup di installazione
Per distribuire l'app agli utenti si genera un setup con
[Inno Setup 6](https://jrsoftware.org/isinfo.php).
Una volta sola, per installare Inno Setup (per l'utente corrente, senza admin):
```powershell
winget install --id JRSoftware.InnoSetup -e --scope user
```
A ogni release, dalla root del repo:
```powershell
# 1. aggiorna <Version> in PrepStep\PrepStep.csproj e la voce in CHANGELOG.md
# 2. chiudi l'app se è aperta (l'exe in uso blocca la build)
dotnet publish PrepStep -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o PrepStep\publish
& "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" installer\PrepStep.iss
```
Produce `installer\Output\PrepStep-Setup-<versione>.exe` (versione letta dall'exe, cioè
`<Version>` del csproj): è l'unico file da distribuire. Per provarlo sul proprio PC basta
lanciarlo (`.\installer\Output\PrepStep-Setup-<versione>.exe`). Il setup:
- installa **solo per l'utente corrente**, in `%LOCALAPPDATA%\Programs\PrepStep`, senza
chiedere i diritti di amministratore;
- crea nel menu Start i collegamenti al programma e al disinstallatore (quello sul desktop
è facoltativo);
- compare in "Impostazioni → App installate", da cui si disinstalla.
Se l'app è già installata, il setup chiede prima cosa fare: **Sì** aggiorna/reinstalla (DB e
impostazioni restano), **No** disinstalla, **Annulla** esce. Installazione silenziosa (aggiorna
senza chiedere) — es. per distribuirla da script:
`PrepStep-Setup-<versione>.exe /VERYSILENT /SUPPRESSMSGBOXES`.
Il setup installa **solo per l'utente corrente**, in `%LOCALAPPDATA%\Programs\PrepStep`, senza
diritti di amministratore; si disinstalla da "Impostazioni → App installate".
## Struttura del progetto
Il codice dell'app è tutto in `PrepStep/` (GUI e indicizzazione integrate); accanto ci sono
il progetto di test `PrepStep.Tests/`, gli script standalone `scripts/` e lo script del setup
`installer/`. Mappa file-per-file completa in [`docs/architettura.md`](docs/architettura.md).
`installer/`. Mappa file-per-file completa in [`docs/architettura.md`](docs/architettura.md); indice di tutti i doc in
[`docs/README.md`](docs/README.md).
- `Program.cs` — entry point.
- `Form1.cs` — finestra principale, wizard a 5 fasi e menu; UI costruita interamente in
@@ -145,18 +83,7 @@ Il database generato (`cad_index.db`), i file Excel/CSV della distinta
trascinati nella root e le cartelle `raw/`/`step/` sono rigenerabili e non
sono tracciati in git (vedi `.gitignore`).
## Test
`PrepStep.Tests/` (xUnit) contiene unit test (lettore esatto e euristica di
estrazione dei componenti dai `.asm`, parsing del nome file Pro-E, import della
distinta) e integration test (scansione su disco, schema/vista SQL reale con focus
sui bug di case-sensitivity/duplicati già risolti, pipeline end-to-end). Da
eseguire:
```powershell
cd PrepStep.Tests
dotnet test
```
Per i test vedi [`docs/sviluppo.md`](docs/sviluppo.md#test).
## Licenza
+58
View File
@@ -0,0 +1,58 @@
# Documentazione di PrepStep
Indice dei doc, organizzati secondo [Diátaxis](https://diataxis.fr/): ogni file ha **uno** scopo,
e cambia solo quando cambia il programma.
## Tutorial — imparare facendo
- [`tutorial-primo-giro.md`](tutorial-primo-giro.md) — il primo giro completo con una distinta vera.
## How-to — svolgere un compito
- [`guida-utente.md`](guida-utente.md) — i passi dell'utente fino a Creo Distributed Batch.
- [`risoluzione-problemi.md`](risoluzione-problemi.md) — sintomo → causa → cosa fare.
- [`sviluppo.md`](sviluppo.md) — build, test, script e regole per scrivere i doc.
- [`rilascio.md`](rilascio.md) — pubblicare e generare il setup di una nuova versione.
## Reference — consultare
- [`architettura.md`](architettura.md) — il processo delle 5 fasi e la mappa file-per-file.
- [`db-cad-index.md`](db-cad-index.md) — schema, vista e query di `cad_index.db`.
- [`formato-asm-record.md`](formato-asm-record.md) — parser esatto dei record `.asm`, casi limite (§7).
- [`formato-asm.md`](formato-asm.md) — euristica testuale di fallback.
- [`formato-prt-feature.md`](formato-prt-feature.md) — come leggere l'albero delle feature di un `.prt` (reverse engineering, non ancora nel codice).
- [`formato-prt-geometria.md`](formato-prt-geometria.md) — numeri, quote e geometria degli schizzi di un `.prt`, con la tabella di cosa manca per determinarlo per intero.
- [`glossario.md`](glossario.md) — i termini usati nei doc e nell'app.
- [`../CHANGELOG.md`](../CHANGELOG.md) — storico delle versioni.
## Explanation — capire il perché
- [`decisioni/`](decisioni/README.md) — registro delle scelte di progetto (ADR).
- [`copia-raw-parallela.md`](copia-raw-parallela.md) — perché e come la copia in `raw/` è parallela.
## Fonte unica (SSOT)
Ogni informazione vive in **un** posto; altrove si mette un link, non una copia.
| Informazione | Fonte |
|---|---|
| Versione e nome prodotto | `PrepStep/PrepStep.csproj` (`<Version>`, `<Product>`) |
| Schema e vista del DB | [`db-cad-index.md`](db-cad-index.md) |
| Formato dei record `.asm` e casi limite | [`formato-asm-record.md`](formato-asm-record.md) |
| Albero delle feature di un `.prt` | [`formato-prt-feature.md`](formato-prt-feature.md) |
| Quote, coordinate e geometria degli schizzi di un `.prt` | [`formato-prt-geometria.md`](formato-prt-geometria.md) |
| Processo delle 5 fasi, mappa dei file | [`architettura.md`](architettura.md) |
| Passi dell'utente | [`guida-utente.md`](guida-utente.md) |
| Build, test, regole sui doc | [`sviluppo.md`](sviluppo.md) |
| Procedura di rilascio e setup | [`rilascio.md`](rilascio.md) |
| Perché una scelta è fatta così | [`decisioni/`](decisioni/README.md) |
| Storico release | [`../CHANGELOG.md`](../CHANGELOG.md) |
`CLAUDE.md` e `README.md` riassumono soltanto e rimandano a questi file.
## Verifica
```powershell
dotnet run scripts/CheckDocs.cs
npx markdownlint-cli2
```
+7 -4
View File
@@ -3,7 +3,7 @@
Panoramica di tutti i file sorgente e di come collaborano. `PrepStep` è il nome
tecnico del progetto (cartella, namespace, csproj, eseguibile `PrepStep.exe`);
**PrepStep - Cevolani** è il nome mostrato all'utente finale (titolo finestra, icona,
versione — vedi `<Version>`/`<Product>` in `PrepStep.csproj`). Per il "come si usa" vedi il `README.md` in root; per il
versione — vedi `<Version>`/`<Product>` in `PrepStep.csproj`). Per il "come si usa" vedi il `README.md` in root e la [`guida-utente.md`](guida-utente.md); per il
formato binario `.asm` vedi `docs/formato-asm.md` (euristica testuale) e
`docs/formato-asm-record.md` (parser esatto dei record); per la parallelizzazione della
copia in `raw/` vedi `docs/copia-raw-parallela.md`.
@@ -94,7 +94,7 @@ trascinano i `.asm` su Creo Distributed Batch, che scrive in `step/`.
| # | Fase | Bottone → metodo | Cosa fa |
|---|---|---|---|
| 1 | Database | "Aggiorna indice" → `Indexer.ScanAsync` | Crea/aggiorna `cad_index.db` scansionando la share; un pallino verde/rosso accanto al titolo indica se un DB è caricato. La cartella di lavoro non è mostrata qui (cambiabile in qualunque momento da "File → Scegli cartella di lavoro...") |
| 2 | Importa Distinta Base | selezione file/drag&drop → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv`, poi "Verifica presenza" → `VerificaCodiciNelDb` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella cartella di lavoro; "Verifica presenza" (visibile solo dopo un import riuscito) cerca i componenti nell'indice e colora le righe della griglia — è quella che sblocca "Avanti", non il solo import |
| 2 | Importa Distinta Base | selezione file/drag&drop → `ImportaDistinta` → `DistintaBaseImporter.ConvertiInCsv`, poi "Verifica presenza" → `VerificaCodiciNelDb` | Xls/Xml gestionale → CSV componente;descrizione, scritto nella cartella di lavoro (oppure "Lista personalizzata": codici digitati uno alla volta con `AggiungiCodice`, solo in memoria, senza CSV; tutto a valle legge solo `_righeCorrenti`); "Verifica presenza" (visibile solo dopo un import riuscito) cerca i componenti nell'indice e colora le righe della griglia — è quella che sblocca "Avanti", non il solo import |
| 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/`, risolve ricorsivamente sotto-assiemi/parti nell'indice e li copia anch'essi in `raw/`, insieme ai componenti soppressi e ai modelli citati per riferimento esterno che servono a Creo per aprire l'assieme |
| 5 | Verifica completezza | (ingresso in fase) → `AggiornaAnteprimaAsm` | Mostra le icone trascinabili dei `.asm` della distinta già in `raw/`, da trascinare su Creo per l'esportazione STEP (Distributed Batch) |
@@ -282,7 +282,7 @@ l'`UninstallString`), perché Inno da solo aggiornerebbe sempre; con `/VERYSILEN
La disinstallazione rimuove solo
i file installati: `cad_index.db`, `raw/`/`step/`/CSV creati accanto all'exe e
`%AppData%/PrepStep/` restano.
Comandi di build del setup nel `README.md`.
Comandi di build del setup in [`rilascio.md`](rilascio.md).
## Script standalone (`scripts/`)
@@ -301,6 +301,9 @@ dipendenze extra con `#:package Nome@versione` in testa al file):
investigare a mano nuovi falsi positivi prima di toccare la regex di
`AssemblyComponentScanner`.
- **Query.cs** — query SQL ad-hoc contro `cad_index.db` da riga di comando.
- **CheckDocs.cs** — controllo dei doc (docs-as-code): link relativi e ancore, `<Version>` del csproj
uguale all'ultima voce del `CHANGELOG.md`, ogni `.cs` di `PrepStep/` e `scripts/` descritto qui.
Exit 1 se qualcosa non torna.
> ⚠️ 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
@@ -310,7 +313,7 @@ dipendenze extra con `#:package Nome@versione` in testa al file):
## Test
- **PrepStep.Tests/** (xUnit) — riferimento al progetto principale via `ProjectReference`
+ `InternalsVisibleTo` (in `PrepStep.csproj`), così i test chiamano direttamente
e `InternalsVisibleTo` (in `PrepStep.csproj`), così i test chiamano direttamente
`Indexer.ParseFilename` e `AssemblyComponentScanner.EstraiCodiciReferenziati` senza esporli
come API pubblica. File: `IndexerParseFilenameTests.cs`, `IndexerScanTests.cs`,
`AssemblyComponentScannerTests.cs`, `AsmRecordReaderTests.cs`,
@@ -0,0 +1,22 @@
# 0001 — Indice dei file in un DB SQLite locale
**Stato:** accettata
## Contesto
La share `X:\` contiene centinaia di migliaia di file (ordine di ~200k righe indicizzate).
Cercare un codice direttamente sul disco di rete a ogni distinta è lento: il costo è dominato
dalla latenza di rete.
## Decisione
Scansionare la share una volta in un DB SQLite locale (`cad_index.db`) e interrogare quello.
L'aggiornamento è incrementale (confronto su percorso, dimensione e data), la vista
`files_ultima_versione` usa `ROW_NUMBER()` e non un self-join con `GROUP BY`.
## Conseguenze
- Le ricerche sono istantanee e non richiedono la share (la copia sì).
- L'indice può essere vecchio: per questo la fase 2 ha "Verifica presenza".
- Il DB è rigenerabile, quindi non è tracciato in git.
- Schema e criteri di pareggio: [`../db-cad-index.md`](../db-cad-index.md).
@@ -0,0 +1,18 @@
# 0002 — UI WinForms costruita interamente in codice
**Stato:** accettata
## Contesto
L'app è un wizard a 5 fasi con stile condiviso (bottoni, card, barre) e stato comune tra fasi.
## Decisione
Nessun designer visuale: `Form1.BuildUi` e i `BuildFase*` costruiscono i controlli, `UiStile`
centralizza lo stile. `Form1.Designer.cs` resta lo scheletro del template.
## Conseguenze
- Layout e stile modificabili e rivedibili come codice normale (diff leggibili).
- Dimensioni con `AutoSize`+`Padding` invece di pixel fissi, per non tagliare il testo.
- `Form1.cs` è grande (~1400 righe) perché contiene wizard e logica delle fasi.
@@ -0,0 +1,29 @@
# 0003 — Parser esatto dei `.asm` con fallback euristico
**Stato:** accettata
## Contesto
Il `.asm` Creo è un binario proprietario non documentato. La prima soluzione, un'euristica
testuale (`AssemblyComponentScanner`), contava le occorrenze di stringhe candidate e produceva
falsi positivi (riferimenti fantasma, componenti soppressi con placement residuo).
Inoltre la lista dei componenti della distinta **non coincide** con i file necessari a Creo per
aprire l'assieme: ne servono di più (soppressi, riferimenti esterni). Se ne manca uno Creo
risponde solo `[-4] Dati di input non completi`.
## Decisione
Per reverse engineering si è decodificata la struttura a record (`AsmRecordReader`), validata
contro BOM esportate da Creo. È il metodo preferito. L'euristica resta come **fallback** quando
il parser lancia `InvalidDataException`, perché la validazione copre solo un campione di file.
La fase 4 tiene tre liste separate (distinta, soppressi, riferimenti esterni) con regole di
ricorsione diverse.
## Conseguenze
- Estrazione esatta sui file che rispettano il formato; nessun falso positivo da conteggio.
- Due implementazioni da mantenere, per ora; l'euristica serve anche a due script.
- Ogni caso strano diventa un test minimale costruito byte per byte.
- Dettagli: [`../formato-asm-record.md`](../formato-asm-record.md),
[`../formato-asm.md`](../formato-asm.md).
@@ -0,0 +1,27 @@
# 0004 — Setup per-utente con DB nella cartella di installazione
**Stato:** accettata
## Contesto
Gli utenti non hanno diritti di amministratore. L'app scrive `cad_index.db` e, di default, la
cartella di lavoro accanto all'eseguibile. In `C:\Program Files` non sarebbe scrivibile.
Prima il DB era scelto dall'utente e ricordato in `ultimo_db.txt`: si finiva con DB diversi tra
postazioni e ricerche incoerenti.
## Decisione
- Setup Inno Setup 6 con `PrivilegesRequired=lowest`: installa in
`%LOCALAPPDATA%\Programs\PrepStep`.
- Il DB è sempre `cad_index.db` nella cartella di installazione; "Apri DB..." vale solo per la
sessione e nulla viene ricordato.
- `AppId` fisso per riconoscere gli aggiornamenti; se l'app è già installata il setup chiede se
aggiornare o disinstallare.
## Conseguenze
- Nessun admin necessario; aggiornare non tocca DB e impostazioni.
- La disinstallazione lascia DB, `raw/`, `step/`, CSV e `%AppData%\PrepStep\`.
- In sviluppo il DB è nella cartella di build ([`../db-cad-index.md`](../db-cad-index.md)).
- Procedura: [`../rilascio.md`](../rilascio.md).
@@ -0,0 +1,22 @@
# 0005 — Scegliere la copia di un componente dalla cartella dell'assieme
**Stato:** accettata
## Contesto
Per il 13% dei codici esiste più di una copia sulla share, con contenuto diverso e stessa
versione. Il `.asm` non registra quale copia usa. Scegliere a caso, o per data, può mettere in
`raw/` un modello diverso da quello atteso: Creo lo carica, i vincoli non si risolvono e
l'assieme non si apre, con il solito `[-4]`.
## Decisione
`ScegliPerCartella` preferisce la copia nella stessa cartella dell'assieme che la referenzia
(replica la regola di Creo) e solo in mancanza ripiega su versione più alta e data più recente.
Nella vista `files_ultima_versione` il pareggio è deterministico (`data_modifica` e percorso
nell'`ORDER BY`), ma serve solo a non sbagliare a caso.
## Conseguenze
- La fase 4 legge `files` (tutte le copie) e non la vista.
- Dettaglio e casi reali: [`../formato-asm-record.md`](../formato-asm-record.md) §7.4.
@@ -0,0 +1,21 @@
# 0006 — Script standalone che referenziano il progetto principale
**Stato:** accettata
## Contesto
Servono strumenti da riga di comando (BOM, query, analisi di binari) senza duplicare la logica di
lettura dei `.asm`.
## Decisione
Gli script in `scripts/` sono C# file-based apps (.NET 10) che referenziano
`PrepStep/PrepStep.csproj` con `#:project`. La logica vive **solo** nel progetto principale: una
sola fonte di verità per GUI e script.
## Conseguenze
- Si lanciano dalla root del repo; da dentro `PrepStep/` partirebbe la GUI.
- Due famiglie non intercambiabili: euristica + DB (`ExtractCreoStrings`, `BomViaDb`) e parser
esatto + cartelle locali (`CreoBom`). Vedi [`../architettura.md`](../architettura.md).
- `CheckDocs.cs` segue lo stesso schema per i doc.
+14
View File
@@ -0,0 +1,14 @@
# Registro delle decisioni
Perché il programma è fatto così. Ogni voce spiega il contesto, la scelta e le conseguenze, così
chi ci rimette mano non rifà il ragionamento da zero. Se cambia una scelta non si riscrive la
vecchia voce: se ne aggiunge una nuova che la sostituisce.
| N. | Decisione | Stato |
|---|---|---|
| [0001](0001-indice-sqlite-locale.md) | Indice dei file in un DB SQLite locale | accettata |
| [0002](0002-ui-winforms-in-codice.md) | UI WinForms costruita interamente in codice | accettata |
| [0003](0003-parser-esatto-con-fallback.md) | Parser esatto dei `.asm` con fallback euristico | accettata |
| [0004](0004-installazione-per-utente.md) | Setup per-utente con DB nella cartella di installazione | accettata |
| [0005](0005-copia-nella-cartella-dell-assieme.md) | Scegliere la copia di un componente dalla cartella dell'assieme | accettata |
| [0006](0006-script-che-riusano-il-progetto.md) | Script standalone che referenziano il progetto principale | accettata |
Binary file not shown.
+535
View File
@@ -0,0 +1,535 @@
# Decodifica del `.prt` Creo: numeri, quote e geometria degli schizzi
Seguito di [`formato-prt-feature.md`](formato-prt-feature.md), che spiega come trovare le sezioni
del file e leggere l'**albero** delle feature (ordine, tipo, nome). Qui: come leggere i **numeri**
(quote, coordinate) e come ricostruire da quelli la forma di una feature.
**Stato, da leggere prima di fidarsi.** Questo doc contiene *tutto quello che è stato decodificato
e verificato*, e dice per ogni cosa quanto è provata. **Non basta ancora a determinare in modo
univoco com'è fatto un `.prt`**: manca il posizionamento delle feature nello spazio e il
significato certo di alcuni codici (§7 elenca cosa manca, dove sta nel file, e come finire il
lavoro). Nessun codice del progetto lo usa: è reverse engineering fatto con script di prova.
Livelli di prova, come negli altri doc:
1. **Contro Creo** (schermate del modello aperto): `9252000507.prt.11`, feature *Rivoluzione 1*
(§5). Quote e coordinate dello schizzo coincidono con quelle mostrate da Creo.
2. **Coerenza e plausibilità**: `test.prt.1` e le altre feature di `9252000507.prt.11`. I valori
sono numeri "tondi" e coerenti fra sezioni diverse, ma nessuno li ha confrontati con Creo.
## 1. Come sono scritti i numeri reali
Nelle sezioni a marcatori ([`formato-asm-record.md`](formato-asm-record.md) §2) i `double` sono
scritti in forma compatta. Si distinguono per il primo byte, `tag`:
| Tag (hex) | Byte dopo il tag | Valore | Prova |
|---|---|---|---|
| `18` | 0 | `0.0` | in ogni sezione |
| `0f` | 0 | `1.0` | diagonale delle matrici identità |
| `0e` | 0 | `0.5` | **livello 1**: la quota di *Smusso 1* (D = 0.500) è il solo byte `0e` |
| `10` | 0 | `-1.0` | ipotesi dal contesto |
| `2f` | 2 | `40 b1 b2 00 00 00 00 00` | **livello 1** (12,5; 69,5; 100; 139) e 2 (8, 20, 25, 75) |
| `2e` | 2 | `40 b1 b2 b2 b2 b2 b2 b2` (l'ultimo byte si ripete) | **livello 1**: `2e 1b 33` = 6,8, il diametro della punta mostrato da Creo |
| `2d` | 7 | `40 b1 .. b7` (precisione piena) | livello 2: `2d 39 7f ff ff ff ff ff` = 25,5 |
| `2a` | 2 | `3f b1 b2 00...` | livello 2: `2a f4 00` = 1,25, il passo di M8x1.25 |
| `48` | 2 | `c0 b1 b2 00...` (negativo) | **livello 1** (−12,5; −69,5) |
| `47` / `46` | 2 / 7 | stessa famiglia di `48`, con ripetizione / precisione piena | non isolati |
| `e4`-`e7` | 0 | `K − 3` zeri di seguito, `K` = byte − `e0` (solo dentro le matrici, §6.1) | **livello 1** (matrici di `9239902104`) |
| `ed`, `32`... | 8-9 | altri byte alti | non decodificato |
**La regola, ricavata dai dati** (non da una specifica): a ogni *byte alto* `T` del `double`
corrispondono **tre tag consecutivi**, e la base si sposta di 5 per ogni passo di `T`:
```
tag_zero(T) = 0x2f + 5 * (T - 0x40) per i positivi (negativi: 0x48 + 5 * (T - 0xc0))
tag_zero -> T, b1, b2 e 5 byte a zero (valori "tondi")
tag_zero - 1 -> T, b1, b2 e b2 ripetuto 5 volte (es. 6,8 = 40 1b 33 33 33 33 33 33)
tag_zero - 2 -> T e 7 byte pieni (precisione piena)
```
Verifica: `T = 0x40` dà `2f`, `2e`, `2d`; `T = 0x3f` dà `2a` (1,25 con `f4 00`); `T = 0xc0` dà
`48`. Funziona solo finché i tag non si sovrappongono: i byte alti `0x41`-`0x43` e `0xb8`-`0xbf`
non li ho incontrati e non so se la regola regge (i tag di `0x45` collidono con quelli dei
negativi). Per i valori "non tondi" con ripetizione (come 6,8 o 3,4) serve il tag `2e`: senza
quello si leggerebbe `6.7998`.
Come si ricostruisce un valore a 3 byte: si prende il `double` big-endian `40 b1 b2 00...` (per
`2f`) oppure `c0 b1 b2 00...` (per `48`). Esempi verificati (`struct.unpack('>d', ...)`):
| Byte | Valore | Dove |
|---|---|---|
| `2f 59 00` | `100.0` | quota |
| `2f 52 c0` | `75.0` | quota |
| `2f 51 60` | `69.5` | coordinata y dello schizzo di Rivoluzione 1 (= 139 / 2) |
| `2f 29 00` | `12.5` | coordinata x (= 25 / 2) |
| `48 29 00` | `-12.5` | idem, lato opposto all'asse |
| `48 51 60` | `-69.5` | idem |
| `2f 61 60` | `139.0` | quota (lunghezza) |
| `48 59 00` | `-100.0` | estremo della linea d'asse |
Le unità non sono scritte nel record. Nei campioni sono millimetri, ma il file ha una sezione
`UnitSystemDef` (nel contenitore compresso, §1.2 di `formato-prt-feature.md`) che non ho letto:
l'unità va presa da lì prima di dare un numero come "mm".
## 2. Le quote: `dim_array` in `BasFullData`
Ogni quota (dimensione guida) di ogni feature e di ogni schizzo è un record della sezione
`BasFullData`, array `dim_array`. Campi, nell'ordine in cui compaiono:
```
\xe1 nome della quota (qui sempre nullo: il nome "d12" si calcola altrove)
\x08 tipo del record
<dim_type> 1 byte: classe della quota (tabella sotto)
<feat_id> intero (formato asm-record §2.1): la feature a cui appartiene
<attributi> \xf8 \x02 ... flag
\xe3 <valore> <valore_di_backup> \xe3 ... due numeri uguali: value e bck_value
\xe3 \x09 \x01 ... <tolleranza> ... blocco della tolleranza (nominal_value, bound...)
```
Per leggere solo valore e feature basta ancorarsi a `\xe1 \x08 <dim_type> <feat_id>` e leggere il
primo numero che segue (§1). Il primo record dell'array è scritto con i nomi di campo per esteso
(layout diverso: non l'ho agganciato con lo stesso pattern, quindi **la prima quota dell'array può
mancare**).
`feat_id` è lo stesso id di `AllFeatur` e dello storico (`formato-prt-feature.md` §2 e §5): è il
modo per attribuire la quota a "Foro 1" o a "Estrusione 2".
### 2.1 Le quote lette sui due campioni
`test.prt.1` (id → feature come in `formato-prt-feature.md` §6):
| Feature | `dim_type` | Valore | Interpretazione |
|---|---|---|---|
| Estrusione 1 (79) | 62 | 75 | profondità (ipotesi) |
| Estrusione 1 (79) | 4 | 100 | diametro del cerchio dello schizzo: nello schizzo il raggio è 50 (`entity(circle)`, `radius`) |
| Foro 1 (100) | 4 | 19 | diametro (ipotesi) |
| Foro 1 (100) | 62 | 40 | profondità (ipotesi) |
| Arrotondamento 1 (125) | 3 | 3,5 | raggio (ipotesi) |
| Estrusione 2 (149) | 62 / 1 / 1 | 11 / 25 / 53 | profondità e due quote dello schizzo (ipotesi) |
| Smusso 1 (188) | 9 | 6 | distanza dello smusso (ipotesi) |
`9252000507.prt.11`:
| Feature | `dim_type` | Valore | Interpretazione |
|---|---|---|---|
| Rivoluzione 1 (73) | 2 | **25** | **diametro, confermato da Creo** (Ø25 h7) |
| Rivoluzione 1 (73) | 1 | **139** | **lunghezza, confermata da Creo** |
| Estrusione 1 / taglio (118) | 62 / 14 / 5 / 20 | 4 / 45 / 8 / 3 | non verificate |
| Foro 1 (157) | 4 / 9 / 2 / 10 / 2 | 8 / 20 / 6,8 / 118 / 25 | **confermate da Creo** (§6.4): 8 = diametro nominale, 20 = profondità filetto, 118 = angolo punta, 25 = profondità foro |
`9239902104.prt.1` (piastra con tre fori, §6.4.1):
| Feature | `dim_type` | Valore | Interpretazione |
|---|---|---|---|
| Sketch 1 / Estrusione 1 (84) | 1 / 5 / 1 | 21 / 54 / 125 (+ 46) | quote del profilo a L (§3.2) |
| Estrusione 1 (84) | 62 | 6 | profondità, coerente con y = 0 ... 6 delle facce |
| Sketch 2 / Foro 1 (124 / 131) | 8 / 2 / 2 / 7 | 38 / 8 / 8 / 30 | quote dei 4 punti (§6.4.1) |
| Foro 1 (131) | 4 | 5,5 | diametro |
| Sketch 3 / Foro 2 (1095 / 1099) | 2 / 2 | 4 / 5 | distanze del punto dai bordi: l'origine `(67, 6, -5)` = 71 − 4 e 5 |
| Foro 2 (1099) | 4 | 5 | diametro |
| Foro 3 (1132) | 4 / 9 / 2 / 2 | 3 / 6 / 2,5 / 137,5 | diametro nominale M3, profondità filetto, punta, lunghezza (distanza fino all'estremo, §6.1) |
| Sketch 4 / Estrusione 2 (1903 / 1911) | 1 / 62 | 3,5 / 3 | quota dello schizzo e profondità |
| Arrotondamento 1 (2777) | 3 | 3 | raggio, confermato dallo storico |
**Cosa è provato e cosa no.** I valori `25` e `139` di *Rivoluzione 1* sono confermati da Creo
(§5). Per tutte le altre righe il *numero* è decodificato in modo plausibile ma il *significato*
(profondità? diametro?) è dedotto dal codice `dim_type`, da cui non ho una tabella certa. I codici
visti: `1` e `2` quote lineari/diametrali di schizzo, `3` raggio, `4` diametro, `9` distanza
(smusso), `62` profondità/estensione, altri (`5`, `10`, `14`, `37`) non interpretati. Per
distinguere con certezza serve un confronto riga per riga con un modello noto, o leggere il
nome della quota (`d0`, `d1`... del Creo).
La *tolleranza* (es. `h7` di Ø25) sta nello stesso record, ma non l'ho decodificata: nel campo
`tol_class`/`tol_table_index` compaiono indici, non la stringa `h7`.
## 3. La geometria di uno schizzo: `p_saved_result`
Ogni schizzo (sezione) ha, dentro `BasFullData`, un blocco:
```
gsec_ptr ... name "Sezione 1" <- nome mostrato nel tree
gsec2d_ptr name "S2D0001"
segtab_ptr / dimtab_ptr / dim_ref <- segmenti, quote e a cosa si riferiscono
...
p_saved_result
entity(line) ... <- le linee dello schizzo con le coordinate
entity(circle) ... <- i cerchi (centro, raggio)
local_sys 4x3 <- sistema di coordinate dello schizzo
```
Le coordinate delle entità sono **nel sistema dello schizzo**, non assolute (§6).
Ogni `entity(line)` ha due estremi con tre coordinate ciascuno `(x, y, z)`; con z sempre `0` nello
schizzo. Le coordinate sono numeri come in §1.
### 3.1 Esempio verificato: *Sezione 1* di Rivoluzione 1
Decodificando i numeri del blocco `entity(line)` di `9252000507.prt.11` si ottengono quattro
linee chiuse e una linea d'asse:
| Linea | Da `(x, y)` | A `(x, y)` |
|---|---|---|
| 1 | (0, 69,5) | (−12,5, 69,5) |
| 2 | (−12,5, 69,5) | (−12,5, −69,5) |
| 3 | (−12,5, −69,5) | (0, −69,5) |
| 4 | (0, −69,5) | (0, 69,5) |
| asse | (0, −100) | (0, 100) |
È il rettangolo di larghezza 12,5 (= raggio) e altezza 139, centrato in y sul piano di
riferimento, con il lato destro sull'asse x = 0, e una linea d'asse più lunga della figura.
**Coincide con quello che Creo mostra** nella vista dello schizzo (rettangolo di 139 di altezza,
quotato 25 come diametro rispetto all'asse tratteggiato, simmetrico rispetto a `TOP`).
Nota: nel file la quota `25` è un **diametro** (Ø25), anche se in geometria il lato è 12,5. Per
questo non si può dedurre la forma dalle sole quote: servono anche le linee.
### 3.2 Secondo esempio: il profilo a L di *Estrusione 1* (`9239902104.prt.1`)
Le linee del profilo (`entity(line)`, estremi `(x, y)` nel sistema dello schizzo, `z = 0`):
| Linea | Da | A |
|---|---|---|
| 1 | (71, 0) | (−54, 0) |
| 2 | (−54, 0) | (−54, 46) |
| 3 | (−54, 46) | (0, 46) |
| 4 | (0, 46) | (0, 21) |
| 5 | (0, 21) | (71, 21) |
| 6 | (71, 21) | (71, 0) |
più una linea d'asse. Le quote 125 (= 54 + 71), 54, 46 e 21 sono tutte spiegate dal profilo. Qui le
linee stanno in `FeatDefs` (record della feature, sezione `S2D0006`), non in `BasFullData` come in
§3: la posizione del blocco `p_saved_result` dipende dal tipo di feature.
## 4. Da schizzo + quote alla forma: la rivoluzione
Con le due cose sopra, *Rivoluzione 1* è determinata così:
```
profilo (nel piano dello schizzo): x in [-12,5 ; 0], y in [-69,5 ; 69,5]
asse di rotazione: la linea x = 0 (la linea d'asse dello schizzo)
angolo: 360 (non decodificato, vedi §7)
risultato: cilindro Ø25 x 139, centrato sull'origine lungo y dello schizzo
```
Nell'albero di Creo il cilindro risulta con il fondo sul piano `DTM1` e l'asse come `A_1`: la
posizione assoluta (quale piano contiene lo schizzo, come è orientato `y`) **non è nello schizzo**
ma nella definizione della feature (§7).
## 5. Verifica di *Rivoluzione 1* contro Creo
`9252000507.prt` aperto in Creo (schermate del model tree e dello schizzo):
| Cosa | Creo | Letto dal file |
|---|---|---|
| Lunghezza dello schizzo | 139.000 | quota `dim_type 1` = 139 |
| Diametro | Ø25 (h7) | quota `dim_type 2` = 25 |
| Rettangolo, simmetrico rispetto a `TOP` | sì | y da −69,5 a 69,5 |
| Rettangolo a sinistra dell'asse | sì | x da −12,5 a 0 |
| Asse (linea di costruzione) | più lungo della figura | da y = −100 a 100 |
| Assi A_3, A_4, A_5, punto PNT0 | presenti nel tree | `AllFeatur` + storico (`formato-prt-feature.md`) |
## 6. Posizione nello spazio
### 6.1 Matrici 4×3
Piani di riferimento, assi, sistemi di coordinate, schizzi e feature hanno un `local_sys`: array
`f9 04 03` = 4 righe per 3 colonne = 12 numeri: **tre versori e, per ultima, l'origine**. Si leggono
con i numeri di §1 più un codice di ripetizione: un byte `e4`-`e7` vale **"altri `K − 3` zeri"**,
con `K` = byte − `e0` (`e4` = 1 zero, `e5` = 2, `e6` = 3, `e7` = 4). L'identità è quindi
`0f 18 e5 0f 18 e5 0f 18 e5` = `1,0,0,0, 1,0,0,0, 1,0,0,0`.
**Verificato su `9239902104.prt.1`** (livello 1: i valori coincidono con quelli mostrati da Creo,
sotto), con le 4 righe `(e1, e2, e3, origine)`:
| Feature | Matrice letta | Significato |
|---|---|---|
| Foro 1 | `(1,0,0) (0,0,-1) (0,1,0)`, origine `(-46, 6, -38)` | uno dei 4 punti dello schizzo `(-46, 38)`: x = x dello schizzo, z = **−**y dello schizzo, y = 6 = faccia superiore (la piastra è spessa 6) |
| Foro 2 | `(1,0,0) (0,0,-1) (0,1,0)`, origine `(67, 6, -5)` | 71 − 4 e 5 dal bordo: le due quote 4 e 5 della feature |
| Foro 3 | `(0,1,0) (-1,0,0) (0,0,1)`, origine `(-66.5, 3, -5)` | y = 3: l'**offset 3,00** dalla faccia superiore (y = 6) che Creo mostra; z = −5: allineato all'asse di Foro 2 (**"Allinea" con `A_7`**); x = −66,5 = 71 − 137,5, cioè il foro parte 137,5 prima dell'estremo destro (71) |
Quindi **la posizione di un foro si legge dall'origine della sua matrice**: le quote di
posizionamento (4, 5, 3,00) non servono, sono già il risultato. Nei tre fori l'`y` e la `z` tornano
esattamente con le quote e con i riferimenti mostrati da Creo.
Dalla stessa matrice: nel modello di questa piastra lo schizzo di Estrusione 1 sta nel piano `x–z`
(x dello schizzo = x, y dello schizzo = −z) e l'estrusione cresce lungo +y fino a 6.
**Resta incerta l'interpretazione dell'asse del foro.** Per Foro 1 e 2 il terzo versore è `(0,1,0)`
(perpendicolare alla piastra, come deve essere). Per Foro 3 è `(0,0,1)`, ma il foro in Creo entra
dalla faccia laterale: o la convenzione del versore dell'asse è diversa, o la direzione di foratura
sta in un altro campo. Non risolto.
### 6.2 Dove stanno
| Cosa | Dove nel file |
|---|---|
| Piani di riferimento (RIGHT, TOP, FRONT, DTM1) | `ActDatums` (`srf_prim_ptr(plane)` con `local_sys`), `feat_id` = id della feature; vincoli di posizionamento in `FeatDefsDtm` (§6.3) |
| Sistema di coordinate (`PRT_CSYS_DEF`) | `MdlRefInfo` (`ent_list(coord_sys)`) e `GeomDepen` |
| Assi, punti (A_3, PNT0...) | `ent_list(arc_z)`, `ent_list(point)` in `MdlRefInfo` |
| Piano che contiene lo schizzo | `gsec3d_ptr` -> `plane_id`, `ref_planes`; il piano è identificato da un id di geometria |
| Orientamento dello schizzo | `sec_orient_data`, `place_instruction_ptrs` (nella definizione di feature `feat_defs_<tipo>`) |
| Offset di un piano da un altro (DTM1) | quota di tipo "offset" nel `dim_array` della feature, se esiste |
### 6.3 Come è posizionato un piano: i vincoli di `FeatDefsDtm`
La finestra "Piano di riferimento → Posizionamento" di Creo (riferimenti con il loro vincolo,
es. *Tangente* a una superficie, *Parallelo* a un piano) **si legge dal file**. Sta nella sezione
`FeatDefsDtm`, record `feat_defs_923` (un record per piano; `feat_defs_926` assi, `feat_defs_931`
punti, `feat_defs_979` sistemi di assi). Ogni record ha:
```
id id della feature (come in AllFeatur)
local_sys matrice 4x3 del piano risultante (§6.1)
place_instruction_ptrs una istruzione per ogni riferimento della finestra:
type codice del vincolo (intero a 2 byte)
offset double, offset del vincolo
dim_id, ref_id quota dell'offset e riferimento, se esistono
geometry1_id id della geometria a cui ci si riferisce
geometry2_id id della geometria che si sta creando (il piano stesso)
```
Gli `id` di geometria (`geom_id`) si risolvono nelle sezioni `ActDatums` (piani e assi di
riferimento), `VisibGeom` e `NovisGeom` (superfici del solido, visibili e no). Ogni voce di
`srf_array` ha `geom_id`, `geom_type` (`"`, 0x22 = piano; `$`, 0x24 = cilindro) e **`feat_id`**: è il
legame con la feature che l'ha generata.
**Verifica sul caso che hai mostrato**: *DTM1* di `9252000507.prt` (feature 116), finestra di Creo
"Supfce:F5(RIVOLUZIONE_1) Tangente / FRONT:F3 Parallelo". Dal file:
| Istruzione | `type` | `geometry1_id` | Si risolve in | Corrisponde a |
|---|---|---|---|---|
| 1 | `81 fb` (507) | 88 | `VisibGeom`: cilindro (`$`), `feat_id` 73 = Rivoluzione 1 | "Supfce:F5(RIVOLUZIONE_1)", **Tangente** |
| 2 | `81 f9` (505) | 6 | `ActDatums`: piano (`"`), `feat_id` 5 = FRONT | "FRONT:F3", **Parallelo** |
`geometry2_id` è 117 in entrambe: `ActDatums` lo dà come piano con `feat_id` 116 = DTM1 (il piano
risultante). Nella sua matrice compare il valore `12.5` (`2f 29 00`): è il raggio del cilindro,
come deve essere per un piano tangente ad esso. Il campo `Traslazione` della finestra era vuoto.
Quindi **quali sono i riferimenti** (quale feature/superficie e quale piano) si legge con certezza.
Resta da fissare:
- **Il significato di `type`.** 507 ↔ Tangente e 505 ↔ Parallelo è dedotto da un solo esempio e
dall'ordine delle righe della finestra: non ho una tabella dei codici. Gli altri visti
(`82 01`, `82 02`, `82 03`, `81 fd`, `81 ff`) sono quelli dei piani di default e degli altri
piani del campione, non interpretati.
- **`Feature number` di Creo** (`F3`, `F5`): non è l'`id` ma la **posizione** nel tree
(RIGHT = F1, TOP = F2, FRONT = F3, PRT_CSYS_DEF = F4, Rivoluzione 1 = F5). I piani interni non
contano. Per tradurre un `id` in `F<n>` si numerano le feature visibili in ordine
(`formato-prt-feature.md` §3).
- **L'offset.** Se un vincolo ha un offset (quota di tipo offset) il valore sta nel `dim_array`
della feature (§2) con il `dim_id` dell'istruzione; non ho un esempio con offset non nullo.
### 6.4 Il foro: `feat_defs_911`
Riferimento: *Foro 1* di `9252000507.prt.11` (feature 157), confrontato con le schermate di Creo.
Creo mostra: tipo **Standard**, profilo **Diritto** con **Maschiato**, filetto ISO **M8x1.25**,
**profondità 25**, **Ø6,8**, **profondità del filetto 20**, **angolo della punta 118°**,
"Includi la superficie del filetto" attivo, posizionamento **Coassiale** con riferimenti
`Supfce:F5(RIVOLUZIONE_1)` e `A_1(ASSE):F5(RIVOLUZIONE_1)`.
La definizione del foro sta nella sezione `FeatDefs`, record `feat_defs_911`. Ha due parti.
**a) I parametri del filetto, in chiaro** (`data(ddt_plinfo_hole_fstn_param)`):
| Campo | Valore letto | In Creo |
|---|---|---|
| `name`, `series` | `ISO`, `ISO` | Tipo filetto: ISO |
| `scrsize` | `M8x1.25` | Dimensione vite: M8x1.25 |
| `scrsize_val` | `0x1d` (29) | indice di M8x1.25 nella tabella delle viti di Creo (nel segnaposto `M1x.25` vale 0) |
| `hole_class` | `H` | (classe del foro) |
| `drillnum` | `6.8` | Ø6,8 |
**b) Le istruzioni** (`place_instruction_ptrs`): una per ogni parametro numerico, nella forma
`type`, `offset` (un `double`, §1), `dim_id`, `ref_id`, `geometry1_id`, `geometry2_id`. Quelle
del foro filettato, con il valore atteso da Creo:
| `type` | `offset` letto | In Creo | Prova |
|---|---|---|---|
| 5104 (`0x13f0`) | 6,8 | Ø6,8 (anche nel foro semplice, vedi sotto) | **livello 1** |
| 5528 (`0x1598`) | 118 | angolo della punta | **livello 1** |
| 5538 (`0x15a2`) | 25 | profondità del foro | **livello 1** |
| 5536 (`0x15a0`) | 1,25 | passo del filetto (M8**x1.25**) | livello 2 |
| 6617 (`0x19d9`) | 11 | non corrisponde a nulla di mostrato | non interpretato |
| 5542, 10103, 5533, 5534 | 0 | non mostrati | non interpretati |
Il codice `type` identifica il parametro; il valore è `offset`. La profondità del filetto (20) e il
diametro nominale (8) **non** sono istruzioni ma quote del `dim_array` (§2), con `dim_type` 9 e 4
e `feat_id` 157. In tutto il `dim_array` ha per il foro cinque quote: 8 (`dim_type` 4), 20 (9),
Ø6,8 (2, scritto con il tag `2e`), 118 (10) e 25 (2). Tre di queste (6,8, 118, 25) compaiono
quindi due volte, come quota e come parametro di un'istruzione.
**Foro semplice e foro standard.** `test.prt.1` ha un foro **semplice**: il suo `feat_defs_911`
è lungo 862 byte e ha solo due istruzioni (`5104` con `dim_id` 7, e `1853`); i parametri del
filetto ci sono comunque, ma con i valori segnaposto di Creo (`M1x.25`, `drillnum 0.75`,
`scrsize_val 0`): **quindi le stringhe `scrsize`/`drillnum` da sole non dicono se il foro è
filettato**. Quello di Fata ha 3212 byte e 12 istruzioni, valori diversi dal segnaposto. Il segno
affidabile oggi è: **foro standard = presenza delle istruzioni 5536 (passo) e 5528 (angolo punta)**,
e il foro è filettato quando anche i valori del filetto non sono quelli segnaposto.
**Non individuato nel file** (per ognuno servono due salvataggi che differiscono solo per
quell'opzione):
- *Maschiato / Trapanato / Gioco*, *Diritto / Rastremato*: non so quale istruzione o flag le
codifichi. `plin_info_hole_flag.flags` vale `2, 2` in entrambi i fori.
- *Includi la superficie del filetto*: idem.
- Il tipo *Standard*/*Semplice* come campo esplicito: oggi si deduce solo dal numero di istruzioni.
**Posizionamento Coassiale.** Gli ingredienti ci sono: un blocco `data(plin_info_ref)` per ogni
riferimento, con gli id di geometria. Il piano del riferimento `Supfce:F5` dovrebbe essere la
faccia piana di *Rivoluzione 1* (feature 73, in `VisibGeom` con `feat_id` 73) e il secondo
riferimento l'asse `A_1`: non ho agganciato quegli id perché le coppie `id_type1`/`id_type2` non le
ho decifrate. La geometria generata dal foro (cilindro, cono, piani con `feat_id` 157) è invece
in `VisibGeom` e `NovisGeom`, e `geometry2_id` 175 delle istruzioni è un suo piano.
### 6.4.1 Foro a punti di schizzo e facce piane: `9239902104.prt.1`
*Foro 1* è definito da *Sketch 2* con **4 punti**. Cosa si legge, tutto da `local_sys` (§6.1):
**I punti.** Nello schizzo (`entity(point)`, campo `pnt`, un `[x, y, z]` per punto) sono
`(-46, 8)`, `(-8, 8)`, `(-8, 38)`, `(-46, 38)`, con quote 8, 8, 30 e 38. Nel modello ogni punto
diventa un'**istanza** del foro, e ogni istanza ha un piano proprio in `NovisGeom` (superficie
piana con `feat_id` 131, §6.3) con la matrice del punto: l'**origine** è la posizione assoluta.
| Piano (`geom_id`) | Origine nel modello |
|---|---|
| 135 | `(-46, 6, -8)` |
| 147 | `(-8, 6, -8)` |
| 159 | `(-8, 6, -38)` |
| 171 | `(-46, 6, -38)` |
Quindi `x` = `x` dello schizzo, `z` = **−**`y` dello schizzo (confermato dai quattro punti e da Foro
2, §6.1) e `y` = 6.
**La faccia.** La superficie su cui stanno è la faccia piana superiore di *Estrusione 1*: ha
`y = 6` e la stessa matrice del sistema di coordinate dello schizzo (origine `(0, 6, 0)`,
versori `(1,0,0)`, `(0,0,1)`, `(0,-1,0)`). Le facce piane di *Estrusione 1* (`feat_id` 84, in
`VisibGeom`) sono, con origine e normale (il terzo versore):
| `geom_id` | Origine | Normale | Faccia |
|---|---|---|---|
| 95 | `(0, 6, 0)` | `(0,-1,0)` | **superiore** (`y = 6`), dove sono i fori |
| 107 | `(71, 0, 0)` | `(-1,0,0)` | estremo destro (`x = 71`) |
| 113 | `(-54, 0, -46)` | `(1,0,0)` | estremo sinistro (`x = -54`) |
| 115 | `(-54, 0, 0)` | `(0,0,1)` | lato `z = 0` |
| 2999 | `(71, 0, -21)` | `(0,0,-1)` | lato del gradino (`z = -21`) |
| 3001 | `(0, 0, -21)` | `(-1,0,0)` | alzata del gradino (`x = 0`) |
| 3003 | `(0, 0, -46)` | `(0,0,-1)` | lato `z = -46` |
La piastra a L di Estrusione 1 (§3.2, profilo `x` da −54 a 71, `y` dello schizzo da 0 a 46, gradino
a 21) torna in coordinate modello con `z = −y` dello schizzo, e spessore 6 lungo `y`. Gli altri
tre fori cadono dentro il materiale (`x` −46 e −8, `z` −8 e −38).
**Non risolto:** quale `geom_id` il foro usi *formalmente* come faccia di riferimento (95, per
coincidenza di piano e di origine) non è scritto in un campo che io abbia identificato: è
un'identificazione per posizione, non una lettura.
### 6.5 Lo smusso: `feat_defs_914`
Riferimento: *Smusso 1* di `9252000507.prt.11` (feature 252), in modifica in Creo: modalità
**Insiemi**, schema quote **D x D** con **D = 0.500**, un insieme ("Insieme 1") con **due
riferimenti**, entrambi `Spigolo:F5(RIVOLUZIONE_1)`, e l'opzione "Superfici con offset".
| Cosa | Dove | Letto | Prova |
|---|---|---|---|
| Valore D | `dim_array`, `feat_id` 252, `dim_type` 9 | `0e` = **0,5** | **livello 1** |
| Valore in `test.prt.1` | `dim_array`, feat 188, `dim_type` 9 | `2f 18 00` = 6,0 | livello 2 |
| Superfici del bordo | `feat_defs_914`, istruzioni di tipo 4530 | `geometry1_id` = 88, 86, 106, 90 | vedi sotto |
| Superfici generate dallo smusso | `VisibGeom`, `feat_id` 252 | superfici coniche (`geom_id` 253, 258, 261, 266) | presenti |
Il `dim_type` 9 è lo stesso del foro (profondità del filetto): dice la *classe* della quota, non
cosa misura. Per lo smusso vale D, ma la stessa classe in un foro vale 20.
**I riferimenti.** Le istruzioni di tipo `4530` hanno nel campo `geometry1_id` gli id 88, 86, 106 e
90, ripetuti a coppie. Risolti con `VisibGeom`: **88** e **106** sono superfici cilindriche con
`feat_id` 73 (cioè *Rivoluzione 1*, F5), **86** è il piano di estremità della stessa rivoluzione
(visto in §3.1 e §6.3). Quindi i riferimenti `Spigolo:F5(RIVOLUZIONE_1)` sono spigoli ricavati dalle
superfici di Rivoluzione 1, come mostra Creo. Non so ancora quale coppia di id formi quale
spigolo, né a quale dei due "Spigolo" della lista corrisponda (le istruzioni non sono in un ordine
che io abbia decifrato), e **90** non l'ho risolto.
**Non decodificato:**
- lo schema quote (D x D, D1 x D2, angolo x D...): non ho trovato dove sta; compaiono vari tipi di
istruzione (`7350`, `7360`, `4549`, `4552`, `4557`, `4558`, `1125`, `7265`, `14741`) senza
corrispondenza con le voci mostrate;
- "Insieme 1" e la sua composizione: compare la struttura (`data(plin_info_chut_info)`, array di
riferimenti) ma non la ho letta;
- "Superfici con offset" e il tipo di transizione;
- l'istruzione `14741`, con un valore `11,476` in precisione piena: non corrisponde a niente di
mostrato.
**Foro filettato con posizionamento lineare** (*Foro 3* di `9239902104.prt.1`). Creo mostra: tipo
Standard, Diritto, Maschiato, ISO **M3x.5**, Ø2,50 (punta), campo profondità vuoto, posizionamento
**Lineare** con riferimento primario `Supfce:F6(ESTRUSIONE_1)` e due riferimenti di offset
(`Supfce:F6(ESTRUSIONE_1)` con offset 3,00 e `A_7(ASSE):F10(FORO 2)` allineato). Dal file:
| Informazione | Letto | Prova |
|---|---|---|
| Filetto | stringhe `ISO`, `M3x.5`, punta `2.5`; `scrsize_val` = 12 | concorda con Creo |
| Ø della punta | istruzione 5104, offset 2,5 | concorda con Creo (Ø2,50) |
| Quote del foro | 3 (`dim_type` 4), 6 (9), 2,5 (2), 137,5 (2) | numeri letti; 137,5 con campo "Profondità" vuoto in Creo: probabilmente un foro passante, valore non verificato |
| Posizione risultante (offset 3,00 e allineamento a `A_7`) | **letta dalla matrice** `local_sys` (§6.1): origine `(-66.5, 3, -5)`, con y = 3 e z = −5 uguali a quelli di Foro 2 | concorda con Creo; la quota 3,00 non è una quota a sé nel `dim_array` |
| Riferimenti `F6` e `A_7` | non risolti | gli id stanno in blocchi `plin_info_ref` come per DTM1 (§6.3) |
Correzione: il primo codice delle istruzioni è 5542 anche qui, come nel foro coassiale di
`9252000507`; il `12266` che avevo letto nella prima versione di questo doc veniva da un'istruzione
di un altro record. Il tipo di posizionamento (coassiale / lineare) **non** è quel codice. Quale sia
non lo so: la posizione finale si legge comunque dalla matrice (§6.1).
## 7. Cosa manca per determinare un `.prt` in modo univoco
Tabella di verità: ogni informazione necessaria, se l'ho decodificata e quanto è provata.
| Informazione | Stato | Dove si trova |
|---|---|---|
| Elenco e ordine delle feature | **fatto**, provato (`test.prt.1`, `9252000507.prt.11` contro Creo) | `AllFeatur` |
| Tipo di ogni feature | **fatto**; estrusione vs rivoluzione solo con lo storico | `AllFeatur`, storico |
| Nome di ogni feature | **fatto**, ma solo se esiste lo storico (10 file su 31 leggibili) | `MdlStatus` |
| Quote (valori numerici) | **fatto per i valori interi/semplici**, tag `2f`/`48` | `dim_array` |
| Significato di ogni quota | **parziale**: provato solo per Rivoluzione 1 | `dim_type` (§2.1) |
| Geometria dello schizzo (linee) | **fatto** per le linee, provato su Rivoluzione 1 | `p_saved_result` |
| Cerchi, archi, spline dello schizzo | non verificato (cerchio visto in `test.prt.1`, raggio 50) | `entity(circle)`, `entity(arc_z)`, `entity(spline)` |
| Valori non "tondi" | **non decodificato** (tag diversi da `2f`/`48`) | ovunque |
| Piano e orientamento dello schizzo | **non decodificato** | `gsec3d_ptr`, `sec_orient_data` |
| Posizione/rotazione di fori e feature | **fatto**: matrice 4×3 (§6.1) | `local_sys` |
| Posizione/rotazione di piani, assi, punti | stessa matrice, da verificare su un piano ruotato | `ActDatums`, `MdlRefInfo` |
| Vincoli e riferimenti di un piano (come DTM1) | **fatto per DTM1 di `9252000507`** (riferimenti sì, nome del vincolo da un esempio) | `FeatDefsDtm`, §6.3 |
| Offset di un piano da un altro | **non decodificato**: mai visto un offset non nullo | quota in `dim_array` (`dim_id`) |
| Direzione/lato e angolo di una rivoluzione, profondità e direzione di un'estrusione | **non decodificato** | `feat_defs_917`/`916` (`place_instruction_ptrs`, `plin_info_arr`) |
| Parametri del foro (misura, punta, passo, profondità, angolo) | **fatto**, provato contro Creo su `9252000507` | `feat_defs_911`, §6.4 |
| Opzioni del foro (maschiato, diritto, superficie del filetto) | **non individuato** | §6.4 |
| Posizione assoluta del foro (origine e versori) | **fatto**, provato contro i riferimenti di Creo su `9239902104` (§6.1) | `local_sys` del record |
| Riferimenti del posizionamento (quale faccia/asse) | **non decodificato** | `plin_info_ref` in `feat_defs_911` |
| Valore di raccordi e smussi | **fatto**, provato contro Creo su `9252000507` (D = 0,5) | `dim_array` |
| Spigoli a cui si applicano raccordi/smussi | **parziale**: si risolvono le superfici (feature di origine), non lo spigolo né l'insieme | `feat_defs_914`, §6.5 |
| Schema quote e insiemi dello smusso | **non decodificato** | `feat_defs_914` |
| Tolleranze (h7...) | **non decodificato** | blocco `tol_ptr` del `dim_array` |
| Unità di misura | **non letto** | `UnitSystemDef` (sezione compressa) |
| Feature nascoste / soppresse | **non decodificato** | non individuato nel file |
| Parametri e materiale | parziale, via `ND:0:Model_*` (XML) | sezione compressa `.Z` |
| File Wildfire (generazioni 1322/1375/1376) | **illeggibile** (sezioni di tipo `11`) | `formato-prt-feature.md` §1.3 |
**Conclusione onesta:** oggi si ricava con affidabilità *quali* feature ci sono, *che tipo* sono,
*come si chiamano* (se c'è lo storico), *quanto misurano* le quote semplici e *che forma ha lo
schizzo* se fatto di linee con numeri semplici. Non si ricava ancora *dove* stanno nello spazio né
*come* sono orientate, né si può ricostruire il solido completo.
## 8. Come finire il lavoro
Per ogni riga "non decodificato" della §7 il metodo è lo stesso, ed è quello che ha dato Rivoluzione
1:
1. Scegliere un `.prt` piccolo con **una sola feature** del tipo da decodificare, e annotare da
Creo i valori veri (quote, posizione dei piani, angolo, direzione).
2. Estrarre da `BasFullData`/`ActDatums` i record di quella feature e cercarvi quei numeri con il
decodificatore di §1: i valori noti ancorano i byte.
3. Variare **una cosa alla volta** nel modello (spostare il piano, cambiare l'angolo), risalvare e
confrontare i due file: i byte che cambiano sono il campo cercato.
4. Registrare in questo doc il campo, con il file di prova e il valore atteso, come in §5.
Tre modelli di prova che chiuderebbero la maggior parte della §7: un piano `DTM1` a offset non nullo
dal piano `TOP` (e con un vincolo *Attraverso*, *Normale*, *Offset*...); una estrusione di profondità nota su un piano ruotato; una rivoluzione di 90°.
Una volta chiusa la §7 il passo successivo è un `PrtModelReader` in C# accanto ad `AsmRecordReader`,
con le stesse regole: se un valore non si decodifica si solleva `InvalidDataException`, non si
restituisce un modello parziale in silenzio.
+20
View File
@@ -0,0 +1,20 @@
# Glossario
| Termine | Significato |
|---|---|
| Assieme generale | l'assieme di primo livello di una distinta; il foglio del gestionale non lo elenca, l'app lo aggiunge come riga se il file è `DBP_<codice>_<timestamp>` |
| `.asm` / `.prt` | file Creo/Pro-E di assieme e di parte; il nome è `<codice>.<tipo>.<versione>` |
| Cartella di lavoro | dove finiscono CSV, `raw/` e `step/`; di default la cartella di installazione |
| Codice | identificativo del componente, ricavato dal nome del file (può avere una lettera di revisione attaccata) |
| Creo Distributed Batch | strumento PTC che esporta in batch (qui in STEP); usato a mano, fuori dall'app |
| Distinta Base (DBP) | estrazione dal gestionale (Fata) dei componenti di un assieme |
| Family table | tabella di varianti di un modello; il nome dell'istanza sta nel parametro `ConfigName` |
| Indice | il DB `cad_index.db` con i metadati dei file della share |
| `members` | sezione del `.asm` con i componenti diretti dell'assieme |
| `raw/` | cartella con i file CAD da passare a Creo |
| Riferimento esterno | modello citato da un assembly feature o datum copiato; non è un membro, ma Creo lo carica con il suo albero |
| Share | disco di rete con i file CAD, `X:\` di default |
| Soppresso | componente tolto dalla distinta ma il cui file serve comunque ad aprire l'assieme |
| `step/` | cartella degli STEP esportati da Creo; non scritta dall'app |
| `temp_*` | cartelle personali della share, escluse dall'indice |
| Ultima versione | per ogni `(cartella_radice, codice, tipo_file)` la riga con versione più alta (vista `files_ultima_versione`) |
+7 -1
View File
@@ -76,7 +76,13 @@ Premi **Avanti** quando il database è caricato.
oppure premi **Scegli file distinta...** e selezionalo da Esplora risorse. Le righe importate
compaiono nella griglia (Componente/Descrizione) e viene scritto un CSV nella cartella
di lavoro.
- **2. Verifica la presenza dei file sul server**: compare solo dopo un import riuscito.
In alternativa scegli **Lista personalizzata**: digita un codice e premi **Invio** (o
**Aggiungi**) per inserirlo, ripeti per ogni codice; **Canc** sulla riga selezionata lo
toglie. La lista non scrive nessun CSV e, passando da una modalità all'altra, viene azzerata
(con conferma). Inserisci anche l'assieme generale, se serve: qui non viene aggiunto da solo.
- **2. Verifica la presenza dei file sul server**: compare solo dopo un import riuscito
(o dopo il primo codice aggiunto alla lista personalizzata; ogni modifica della lista
richiede di rifare la verifica).
Premila per cercare ogni componente nel database — le righe della griglia si colorano
di verde (trovato) o rosso (non trovato nell'indice).
+47
View File
@@ -0,0 +1,47 @@
# Rilasciare una nuova versione
Procedura per produrre il setup `PrepStep-Setup-<versione>.exe`, l'unico file da distribuire.
Il comportamento del setup (installazione per-utente, aggiornamento, `/VERYSILENT`) è descritto
in [`architettura.md`](architettura.md#installazione); qui ci sono solo i passi da eseguire.
## Una volta sola
Installa Inno Setup 6 per l'utente corrente (niente admin):
```powershell
winget install --id JRSoftware.InnoSetup -e --scope user
```
## A ogni release
1. Aggiorna `<Version>` in `PrepStep/PrepStep.csproj` (SemVer).
2. In [`CHANGELOG.md`](../CHANGELOG.md) trasforma "Non rilasciato" nella nuova versione con la
data e riscrivi sotto le modifiche (Aggiunto/Modificato/Corretto/Rimosso).
3. Controlla i doc: `dotnet run scripts/CheckDocs.cs` deve confermare che versione e CHANGELOG
coincidono.
4. Esegui i test: `dotnet test` in `PrepStep.Tests/`.
5. Chiudi l'app se è aperta (l'exe in uso blocca la build), poi dalla root del repo:
```powershell
dotnet publish PrepStep -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o PrepStep\publish
& "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe" installer\PrepStep.iss
```
6. Il setup è in `installer\Output\PrepStep-Setup-<versione>.exe`; la versione è letta
dall'exe, quindi coincide con `<Version>`. Provalo lanciandolo.
7. Commit con codice, doc e CHANGELOG insieme (regola in [`README.md`](README.md)).
## Solo l'eseguibile, senza setup
```powershell
cd PrepStep
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish
```
Produce `PrepStep\publish\PrepStep.exe`, un file unico che gira su un Windows x64 senza .NET
installato.
## Da non toccare
`AppGuid` in `installer/PrepStep.iss`: è ciò che fa riconoscere a Windows le versioni nuove
come aggiornamento della stessa app. Cambiarlo produce un'installazione doppia.
+46
View File
@@ -0,0 +1,46 @@
# Risoluzione dei problemi
Sintomo → causa probabile → cosa fare. I messaggi di Creo non nominano il file né la causa, per
questo il sintomo è il punto di partenza.
## Dentro l'app
| Sintomo | Causa probabile | Cosa fare |
|---|---|---|
| Pallino rosso in fase 1, "Database non trovato" | non c'è `cad_index.db` nella cartella di installazione | **Aggiorna indice** (lo crea lì), oppure **Apri DB...** per usarne un altro solo per la sessione |
| "Disco non raggiungibile: X:\" | la share non è montata o non hai accesso | monta `X:\` e riprova; la radice è fissa in `Form1.RadiceDiscoDefault` |
| Componente rosso in "Verifica presenza" | il codice non è nell'indice: indice vecchio, oppure il nome su disco ha una lettera di revisione in più o in meno | **Aggiorna indice**; poi cerca il codice con `LIKE` (vedi sotto) |
| "Avanti" resta grigio in fase 2 | manca il passo **Verifica presenza** | premila: non basta l'import |
| Alcuni file non compaiono in `raw/` dopo fase 3/4 | la copia salta in silenzio i file irraggiungibili (share offline, permessi) | controlla l'accesso alla share e ripeti la fase |
| In fase 4 compaiono codici "non trovati" | disegni, componenti davvero assenti dalla share o falsi positivi della scansione | controllali a mano; non bloccano il lavoro |
| Risultati incoerenti in sviluppo | l'app sta usando il DB della cartella di build, non quello che credi | guarda il percorso mostrato in fase 1; apri il DB giusto con **Apri DB...** |
Per capire se un codice è nell'indice:
```powershell
dotnet run scripts/Query.cs -- cad_index.db "SELECT codice, tipo_file, percorso_completo FROM files_ultima_versione WHERE codice LIKE '925%' LIMIT 20"
```
Nessuna riga nemmeno con `LIKE` → il file non è indicizzato. Una riga con codice leggermente
diverso → revisione, maiuscole o spazi.
## In Creo Distributed Batch
| Sintomo | Causa probabile | Cosa fare |
|---|---|---|
| `[-4] Dati di input non completi` | nell'albero manca un file che Creo deve caricare, ma non dice quale | rifai la fase 4 e leggi l'elenco dei "non trovati"; poi vedi sotto |
| L'assieme non si apre, nessun messaggio chiaro | in `raw/` c'è una copia sbagliata di un componente (lo stesso codice esiste in più cartelle, 13% dei casi) | l'app sceglie la copia nella cartella dell'assieme: [`formato-asm-record.md`](formato-asm-record.md) §7.4 |
| Assieme con un componente soppresso che non si apre | il file del soppresso non era in `raw/` | §7.3; oggi la fase 4 lo copia |
| Assieme con feature di assembly o datum copiati che non si apre | manca il modello citato per riferimento esterno | §7.5; oggi la fase 4 lo copia con il suo albero |
## Da sviluppatore
| Sintomo | Causa | Cosa fare |
|---|---|---|
| `dotnet run` apre la GUI e si blocca | lanciato dentro `PrepStep/` | lanciare gli script dalla root del repo |
| `dotnet publish` fallisce con file in uso | l'app è aperta | chiudila e ripeti |
| `CheckDocs.cs` segnala un file non descritto | nuovo `.cs` o doc non documentato | aggiungilo a `architettura.md` o a `docs/README.md` |
## Dopo la disinstallazione
Restano `cad_index.db`, `raw/`, `step/`, i CSV e `%AppData%\PrepStep\`: vanno cancellati a mano.
+66
View File
@@ -0,0 +1,66 @@
# Guida per chi sviluppa
Come lavorare sul codice e sui doc. Per capire com'è fatto il programma parti da
[`architettura.md`](architettura.md); per rilasciare vedi [`rilascio.md`](rilascio.md).
## Requisiti
- Windows e [.NET 10 SDK](https://dotnet.microsoft.com/download).
- Accesso alla share `X:\` solo per indicizzare davvero; i test non ne hanno bisogno.
- Node.js (per `npx markdownlint-cli2`, solo per i doc).
## Build e avvio
```powershell
cd PrepStep
dotnet build -c Release
dotnet run -c Release
```
In sviluppo `cad_index.db` si cerca nella cartella di build, non nella radice del repo: vedi
[`db-cad-index.md`](db-cad-index.md#come-lapp-trova-il-db-allavvio).
## Test
```powershell
cd PrepStep.Tests
dotnet test
```
I test xUnit chiamano gli `internal` del progetto principale grazie a `InternalsVisibleTo`. I
`.asm` dei test si costruiscono byte per byte con piccoli helper invece di versionare file
binari: ogni caso strano trovato sulla share diventa un test minimale. Elenco dei file di test in
[`architettura.md`](architettura.md#test).
## Script standalone
Si lanciano dalla **root del repo**, mai da dentro `PrepStep/` (che è un `WinExe`: `dotnet run`
avvierebbe la GUI e resterebbe bloccato). Elenco in
[`architettura.md`](architettura.md#script-standalone-scripts).
```powershell
dotnet run scripts/Query.cs -- cad_index.db "SELECT COUNT(*) FROM files"
```
## Documentazione come codice
I doc stanno in git, si modificano nello stesso commit del codice e si verificano a mano:
```powershell
dotnet run scripts/CheckDocs.cs # link, ancore, versione/CHANGELOG, copertura dei doc
npx markdownlint-cli2 # stile Markdown (.markdownlint-cli2.jsonc)
```
Regole di scrittura:
- **Una sola fonte per ogni informazione**, altrove un link (tabella in [`README.md`](README.md)).
- **Ogni doc ha un solo scopo** (Diátaxis): non mescolare istruzioni, riferimento e motivazioni.
- Un cambiamento strutturale (fasi, formato `.asm`, schema DB, criterio di scelta di un file)
aggiorna i doc e il CHANGELOG **nello stesso commit**.
- Se la modifica nasce da un fallimento reale di Creo scrivi **il sintomo**: i messaggi di Creo
non nominano il file né la causa (esempio: §7.3-7.5 di
[`formato-asm-record.md`](formato-asm-record.md)).
- Una scelta con alternative reali non banali merita una voce in
[`decisioni/`](decisioni/README.md).
- Un nuovo `.cs` va descritto in `architettura.md` e un nuovo doc elencato in `docs/README.md`:
`CheckDocs.cs` lo verifica.
+64
View File
@@ -0,0 +1,64 @@
# Tutorial: il tuo primo giro con PrepStep
Questo tutorial ti porta, con una distinta vera, dall'app appena installata ai file `raw/` pronti
per Creo. Alla fine saprai cosa produce ogni fase e come capire se è andata bene. Per il
dettaglio di ogni schermata e per l'esportazione in Creo vedi [`guida-utente.md`](guida-utente.md).
**Ti serve:** il programma installato (setup in [`rilascio.md`](rilascio.md)), la share `X:\`
raggiungibile e un'estrazione "Distinta Base" `.xls` del gestionale, meglio se piccola (poche
decine di righe) per il primo giro.
## 1. Avvia e carica l'indice
Apri **PrepStep - Cevolani**. Accanto a "Database" c'è un pallino.
- **Verde:** l'indice c'è già, vai al passo 2.
- **Rosso:** premi **Aggiorna indice**. La prima volta scansiona tutta la share e richiede
qualche minuto; vedi una barra di avanzamento con i conteggi.
Risultato atteso: pallino verde, percorso di `cad_index.db` e data dell'aggiornamento.
Premi **Avanti**.
## 2. Importa la distinta
Trascina il `.xls` nella finestra. Risultato atteso: la griglia si riempie con
Componente/Descrizione e nella cartella di lavoro compare un CSV con lo stesso nome base.
Poi premi **Verifica presenza**: le righe diventano verdi (trovato nell'indice) o rosse (non
trovato). Solo ora **Avanti** si sblocca.
Se vedi righe rosse non fermarti: nel primo giro è normale scoprire che l'indice è vecchio o
che un componente non è sulla share. Vedi [`risoluzione-problemi.md`](risoluzione-problemi.md).
## 3. Crea la cartella raw
Premi **Crea cartella raw**. Risultato atteso: nella cartella di lavoro nasce `raw/` con i file
`.prt`/`.asm` dei componenti trovati, e la griglia mostra il percorso CAD di ciascuno.
## 4. Trova i componenti referenziati
Premi **Trova componenti referenziati**. L'app legge i `.asm` in `raw/` e copia anche tutto
quello che serve a Creo per aprirli (sotto-assiemi, componenti soppressi, riferimenti esterni).
Risultato atteso: una finestra con l'elenco di quanto copiato e, in fondo, i codici "non
trovati". `raw/` ora contiene più file di prima: è giusto così, perché la lista dei file
necessari è più lunga di quella della distinta (il perché, in
[`decisioni/0003-parser-esatto-con-fallback.md`](decisioni/0003-parser-esatto-con-fallback.md)).
## 5. Porta i `.asm` a Creo
La fase 5 mostra i `.asm` della distinta come icone trascinabili. Trascinali su **Creo
Distributed Batch** (passi in
[`guida-utente.md`](guida-utente.md#5-verifica-completezza-e-creo-distributed-batch)):
i file STEP finiscono in `step/`.
## Cosa hai imparato
| Fase | Entra | Esce |
|---|---|---|
| 1 | share `X:\` | `cad_index.db` |
| 2 | `.xls` del gestionale | CSV + verifica sull'indice |
| 3 | CSV + indice | `raw/` con i componenti della distinta |
| 4 | `.asm` in `raw/` | `raw/` completa dell'albero |
| 5 | `raw/` | `.asm` da trascinare su Creo |
Per un secondo giro con un'altra distinta usa **File → Nuovo** (Ctrl+N), e prima ripulisci
`raw/`/`step/` da **File → Pulisci raw e step...**.
+59
View File
@@ -0,0 +1,59 @@
// dotnet run scripts/CheckDocs.cs (dalla root del repo; exit 1 se i doc sono fuori allineamento)
// Controlla anche che ogni doc di docs/ e docs/decisioni/ sia nell'indice.
// Controlla: link relativi (file e #ancora), <Version> del csproj == ultima voce del CHANGELOG,
// ogni .cs di PrepStep/ e scripts/ citato in docs/architettura.md.
using System.Text.RegularExpressions;
var errori = new List<string>();
var mdFiles = Directory.EnumerateFiles(".", "*.md", SearchOption.AllDirectories)
.Where(f => !f.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}")
&& !f.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}")
&& !f.Contains($"{Path.DirectorySeparatorChar}raw{Path.DirectorySeparatorChar}")
&& !f.Contains($"{Path.DirectorySeparatorChar}step{Path.DirectorySeparatorChar}")).ToList();
// slug delle intestazioni come lo calcola GitHub/Gitea
static HashSet<string> Ancore(string path) =>
File.ReadLines(path).Where(l => l.StartsWith('#'))
.Select(l => Regex.Replace(Regex.Replace(l.TrimStart('#').Trim().ToLowerInvariant(), @"[`*]", ""), @"[^\p{L}\p{N}\- _]", "").Replace(' ', '-'))
.ToHashSet();
// 1. link relativi
foreach (var f in mdFiles)
{
var testo = Regex.Replace(File.ReadAllText(f), @"```.*?```", "", RegexOptions.Singleline);
foreach (Match m in Regex.Matches(testo, @"\]\(([^)\s]+)\)"))
{
var url = m.Groups[1].Value;
if (Regex.IsMatch(url, @"^[a-z]+:")) continue; // http:, mailto:, ...
var parti = url.Split('#', 2);
var target = parti[0] == "" ? f : Path.GetFullPath(Path.Combine(Path.GetDirectoryName(f)!, parti[0]));
if (!File.Exists(target) && !Directory.Exists(target)) { errori.Add($"{f}: link rotto -> {url}"); continue; }
if (parti.Length == 2 && target.EndsWith(".md") && !Ancore(target).Contains(parti[1].ToLowerInvariant()))
errori.Add($"{f}: ancora inesistente -> {url}");
}
}
// 2. versione
var version = Regex.Match(File.ReadAllText("PrepStep/PrepStep.csproj"), @"<Version>(.+?)</Version>").Groups[1].Value;
var ultima = Regex.Match(File.ReadAllText("CHANGELOG.md"), @"^## \[(\d[^\]]*)\]", RegexOptions.Multiline).Groups[1].Value;
if (version != ultima) errori.Add($"CHANGELOG.md: ultima voce {ultima} != <Version> {version} del csproj");
// 3. ogni sorgente citato nella mappa file-per-file
var arch = File.ReadAllText("docs/architettura.md");
foreach (var cs in Directory.EnumerateFiles("PrepStep", "*.cs").Concat(Directory.EnumerateFiles("scripts", "*.cs")))
{
var nome = Path.GetFileName(cs);
if (!arch.Contains(nome) && !nome.EndsWith(".Designer.cs")) errori.Add($"docs/architettura.md: {cs} non è descritto");
}
// 4. ogni doc in docs/ è raggiungibile dall'indice della sua cartella
foreach (var dir in new[] { "docs", Path.Combine("docs", "decisioni") })
{
var indice = File.ReadAllText(Path.Combine(dir, "README.md")) + (dir == "docs" ? File.ReadAllText(Path.Combine(dir, "decisioni", "README.md")) : "");
foreach (var md in Directory.EnumerateFiles(dir, "*.md").Where(f => Path.GetFileName(f) != "README.md"))
if (!indice.Contains(Path.GetFileName(md))) errori.Add($"{dir}/README.md: {Path.GetFileName(md)} non è nell'indice");
}
errori.ForEach(Console.Error.WriteLine);
Console.WriteLine(errori.Count == 0 ? $"Doc OK ({mdFiles.Count} file md)" : $"{errori.Count} problemi");
return errori.Count == 0 ? 0 : 1;