model: TroCandidate statt OpenPoint, tro_overrides nutzt model-Klassen (Schema v3)

- OpenPoint -> model.TroCandidate (type, position, belongsTo, note);
  TroDocument.candidates ersetzt openPoints, Schema sps_skel-tro-v3
- Positionsbehandlung von TroBase und TroCandidate im Mixin _Positioned
- tro_overrides.py: eigene ConnectionSpec/OpenPoint entfallen, stattdessen
  model.Connection / model.TroCandidate; einheitliche Feldnamen
  (tro_id/separator, hosts als Liste)
- Fixtures, Beispiel-JSON und Doku auf v3 nachgezogen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-10-10 11:31:05 +02:00
co-authored by Claude Opus 5.5
parent de8ecb9460
commit 29077caa81
10 changed files with 153 additions and 134 deletions
+21 -8
View File
@@ -60,7 +60,7 @@ Supporting libraries (no CLI of their own, imported by the tools above):
name, Siemens FB block, sub-components and counts, color group, CAD marker
shape.
- **`lib/model.py`** — the shared pydantic model of TRO *instances* and of the
TRO-JSON (`TroBase`, `DerivedTro`, `Connection`, `OpenPoint`, `Analysis`,
TRO-JSON (`TroBase`, `DerivedTro`, `Connection`, `TroCandidate`, `Analysis`,
`TroDocument`). Written by `tro_flow.py --json`, read and validated by
`scl_skeleton.py`. See §6b.
- **`lib/dxf_registration.py`** — derives, verifies and persists the rigid
@@ -294,7 +294,7 @@ colors all come from `lib/tro_catalog.py` — nothing is hard-coded here.
| `--doc` | — | off | Also write a Markdown report (`<name>_tro_doc.md`) with the TRO list, connections, findings and the full type catalog. |
| `--connect` | — | off | Also write the TRO topology as `<name>_tro_connect.ini` (nodes, connections, system boundaries, predecessors/successors per TRO). Pure output, never read back. |
| `--true-scale` | — | off | Only with `--use-cords`: draw to scale. By default empty stretches longer than 1500 mm between nodes are shortened (order and neighbourhood are kept). |
| `--json` | — | off | Also write the TRO-JSON `<name>_tro.json` (schema `sps_skel-tro-v2`, model `TroDocument` in `lib/model.py`): TROs, system boundaries (`SYSn`), connections with their kind, findings. Input for `scl_skeleton.py`. |
| `--json` | — | off | Also write the TRO-JSON `<name>_tro.json` (schema `sps_skel-tro-v3`, model `TroDocument` in `lib/model.py`): TROs, system boundaries (`SYSn`), connections with their kind, findings. Input for `scl_skeleton.py`. |
TROs, types and connections always come from the layout alone;
`cfg/tro_overrides.ini` is not read. Errors in the derivation are fixed in
@@ -533,7 +533,7 @@ TRO in a plant, and the whole TRO-JSON, look like. It imports only the catalog.
```
tro_catalog.py type level TroDefinition (items, FB block, color, symbol)
▲ get_tro()
model.py instance level TroBase ─► DerivedTro, Connection, OpenPoint,
model.py instance level TroBase ─► DerivedTro, Connection, TroCandidate,
Analysis, TroDocument
▲
tro_flow.py (writes) · scl_skeleton.py (reads) · tro_annotate.py (draws)
@@ -544,14 +544,21 @@ tro_flow.py (writes) · scl_skeleton.py (reads) · tro_annotate.py (draws)
| `TroBase` | Core of every TRO: `id`, `type`, `confidence`, `separators`, `predecessors`, `successors`, `position` (`{x,y,z}` in JSON). `definition()`, `fb_block` (JSON `fbType`) and `items` are **read from the catalog** on every access, never stored — so they follow a type change and cannot go stale. |
| `DerivedTro` | `TroBase` + what `tro_flow.py` knows from the derivation: `label`, `hosts`, `scanners`, `reason`, `colocated`, `positionFrom`, `positionPoints`. Also used for the system boundaries `SYSn`. |
| `Connection` | Directed edge `from` → `to` with its `kind` (transfer / umlauf / weiche / unbestimmt / normal / bahn). |
| `OpenPoint` | A reported spot that is not a TRO yet (empty in the JSON for now; meant to replace the class of the same name in `tro_overrides.py`). |
| `TroCandidate` | A spot reported in a review where a TRO is **expected** but not derived (formerly `OpenPoint`): `name`, `type` (the expected catalog type), `position`, `belongsTo` (the TRO that holds the part today), `note`. Not a TRO — no separators, no connections, no SCL. Built by `tro_overrides.py` from `[<csv>.openpoint.<Name>]`; empty in the TRO-JSON for now. |
| `Analysis` | Working result of `tro_flow.analyse_tros` (moved here from `tro_flow.py`). |
| `TroDocument` | The TRO-JSON: `schema` (`sps_skel-tro-v2`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `openPoints`, `findings`. `from_analysis()`, `to_json()`, `from_json()`. |
| `TroDocument` | The TRO-JSON: `schema` (`sps_skel-tro-v3`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `candidates`, `findings`. `from_analysis()`, `to_json()`, `from_json()`. |
`TroBase` and `TroCandidate` share the `position` handling (JSON `{x,y,z}`,
`x`/`y`/`z`, `describe_position()`) through the private mixin `_Positioned`.
The same data has the same field name in every class, including
`tro_overrides.py`: `tro_id`, `type_name`, `separators`, `scanners`, `hosts`,
`label`, `reason`, `confidence`, `position`.
All models validate on creation and assignment and reject unknown keys
(`extra="forbid"`). JSON keys keep the camelCase names of the TRO-JSON (`id`,
`type`, `fbType`, `openPoints` …); Python code uses the field names
(`tro_id`, `type_name`, `fb_block`, `open_points`) — both are accepted on input.
`type`, `fbType`, `belongsTo` …); Python code uses the field names
(`tro_id`, `type_name`, `fb_block`, `belongs_to`) — both are accepted on input.
Schema v3 differs from v2 only in `candidates` replacing `openPoints`.
Tests: `tests/test_model.py` (round trip, catalog-backed fields, rejection of
wrong keys/items, SCL rendering from a document) — run with
@@ -634,7 +641,13 @@ Three kinds of entry, deliberately different in weight:
flow node is primary, the rest stay attached so the linking still sees both
sides of a transition. Like splits, this renumbers.
- **`openpoint`** — a spot reported in a review that is **not** turned into a
TRO. The structure stays untouched.
TRO. The structure stays untouched. Read as `model.TroCandidate` (`expect` →
`type_name`, `x`/`y`/`z` → `position`, missing `z` = 0).
In Python the entries are `TroOverride` (`tro_id` *or* `separator`, from
`TRO<n>` / `SEP<n>`), `SplitSpec`/`MergeSpec` (`hosts` is a list; the INI key
stays `host`, comma-separated), `model.Connection` for `[<csv>.connections]`,
and `model.TroCandidate` — field names as in `model.py`.
Consumer: `material_flow.py --connect` (writes the `[<plant>.manual]` section
into `connect.ini`). Keep in mind: **TRO IDs are running numbers of one run and
+2 -2
View File
@@ -12,7 +12,7 @@ It is written so that it fits the existing main path unchanged:
```
export.csv ─► tro_flow --json ─► <name>_tro.json ─► scl_skeleton ─► <name>_FB_Main.scl
(dxfmakros (derive) (TroDocument, (render) (skeleton with
EXPORTCSV) sps_skel-tro-v2) TODO(E-Planung))
EXPORTCSV) sps_skel-tro-v3) TODO(E-Planung))
```
Nothing upstream of `scl_skeleton` changes. The TRO-JSON stays the only input, and the
@@ -451,7 +451,7 @@ bin\scl_skeleton.bat --json <name>_tro.json [--templates [DIR]] [--strict] [--na
| File | Where | Who writes it |
|---|---|---|
| `<name>.csv` | `%SKEL_DATA%` | dxfmakros `EXPORTCSV` |
| `<name>_tro.json` | `%SKEL_RESULTS%` | `tro_flow --json` (schema `sps_skel-tro-v2`) |
| `<name>_tro.json` | `%SKEL_RESULTS%` | `tro_flow --json` (schema `sps_skel-tro-v3`) |
| `scl_naming.ini`, `<name>_scl_naming.ini` | `%SKEL_CFG%` | you, once per project (optional) |
| `lib/scl_templates/*.scl.j2` | repo | maintained with the PLC engineers |
+1 -1
View File
@@ -477,5 +477,5 @@
"to": "TRO05"
}
],
"openPoints": []
"candidates": []
}