Merge e-planung-bruecke: stabile TRO-IDs, TRO-JSON v4, E-Planung-Bruecke
Uebernimmt Michael Stangls Zweig (tro_ids, Slots/jamAreas v4, e_planning --eplan, Konzept- und Datenquellen-Doku) in master, auf dem seitdem die SCL-Templates fuer alle 7 Typen mit Referenz-Aufrufstelle liegen. Konfliktloesung: - lib/scl_context.py: beide Seiten zusammengefuehrt - Michaels _separator (E-Planung-Namen) und Staubereiche (_entries/_exit_area, ein JamEntr je eigenem Staubereich) plus die Template-Felder (_scanner, _store, carriers/data, part_sensor). _jam() fuehrt jetzt auch jam_id. Die beim automatischen Zusammenfuehren verlorene Property SclJam.title wiederhergestellt. - Templates 1Sep, 1Sep2Swi, 1Sep_SSCC, Vario, Vario_workStation, PinStore_Auto auf v4 nachgezogen: Kommentar "Staubereich J.... von/vor ..." wie im Pilot; PinStore-TODO zaehlt Staubereiche statt Nachbarn (mubea: 2 Zulaeufe laufen in einen Eingang). - Golden-Files *_templates neu erzeugt; geaendert sind ausschliesslich Staubereichs-Zeilen (Kennung im Kommentar, ein Eingang statt einem je Vorgaenger). Generische Golden-Files = Michaels Stand. - Neuer Test: E-Planung-Namen kommen auch in der Template-Ausgabe an. - Doku: README Schema v4, SCL_Templates.md Staubereiche und TODO-Zahlen. Geprueft: 71 Tests gruen; tro_flow --json + scl_skeleton --templates auf allen 8 CSVs ohne Fehler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+152
-14
@@ -1,14 +1,16 @@
|
||||
# Python Scripts — CLI Tooling Overview
|
||||
|
||||
**As of:** 2026-10-09
|
||||
**As of:** 2026-10-10
|
||||
|
||||
`lib/` currently holds ten Python modules (five of them runnable CLI tools,
|
||||
`lib/` currently holds eleven Python modules (six of them runnable CLI tools,
|
||||
five supporting libraries), plus one documentation-generator script that lives
|
||||
under `doc/TRO_Katalog/tro_graphs/`. Together the CLI tools turn a CSV export
|
||||
of the mechanical layout (ILS 2.0) into a material-flow graph, a derived TRO
|
||||
list/diagram, a TRO-JSON (shared model `lib/model.py`), an `FB_Main` SCL
|
||||
skeleton generated from that JSON, and an annotated copy of the BricsCAD
|
||||
drawing for visual checking.
|
||||
drawing for visual checking. `e_planning.py` bridges to the electrical
|
||||
planning: it assigns the sensors/valves of the E-planning drawing to the
|
||||
layout parts, so the skeleton can carry their real names.
|
||||
|
||||
This document is the practical "what does each script do and which switches
|
||||
does it take" reference. For the underlying domain model (TRO types, JSON
|
||||
@@ -34,6 +36,11 @@ tro_flow.py ──► <name>_tro_flow.dot / .svg, _tro_doc.md, _tro_error
|
||||
│ scl_skeleton.py ──► <name>_FB_Main.scl
|
||||
│ (validates the JSON against model.py; one REGION per TRO;
|
||||
│ E-planning values left as TODO(E-Planung) gaps)
|
||||
│ ▲
|
||||
│ │ --eplan <name>_eplan.json (optional)
|
||||
│ e_planning.py ◄── <drawing>_positionsdraw.json (kabellaengen)
|
||||
│ (assigns BG/MB of the E-planning drawing to the layout
|
||||
│ parts by position + numbering rule; see §5c)
|
||||
│
|
||||
└── same derivation ──► tro_annotate.py ──► <name>_annotated.dxf (VIEWER ONLY)
|
||||
(also needs the BricsCAD DXF; uses dxf_registration.py for the CSV↔DXF
|
||||
@@ -66,6 +73,10 @@ Supporting libraries (no CLI of their own, imported by the tools above):
|
||||
- **`lib/dxf_registration.py`** — derives, verifies and persists the rigid
|
||||
(rotation + offset) transform between the CSV export's coordinate system and
|
||||
the DXF drawing's.
|
||||
- **`lib/tro_ids.py`** — the per-plant ID file `%SKEL_CFG%\<name>_tro_ids.ini`:
|
||||
reads it, finds each TRO again by the position of its separators, assigns
|
||||
new IDs, writes/extends the file. Used by `tro_flow.py` (read and write) and
|
||||
`tro_annotate.py` (read only). See §4, "TRO-IDs".
|
||||
- **`lib/tro_overrides.py`** — reads the hand-maintained
|
||||
`%SKEL_CFG%\tro_overrides.ini`; only `material_flow.py --connect` still uses
|
||||
it. See §6a.
|
||||
@@ -91,6 +102,8 @@ Each CLI tool is invoked through its `bin/<name>.bat` (Windows) / `bin/<name>.sh
|
||||
bin\material_flow.bat --file mubea.csv --tosvg --use-cords --doc
|
||||
bin\tro_flow.bat --file mubea.csv --tosvg --use-cords --doc --json
|
||||
bin\scl_skeleton.bat --json mubea_tro.json
|
||||
bin\e_planning.bat --file mubea.csv --eplan Mubea_positionsdraw.json
|
||||
bin\scl_skeleton.bat --json mubea_tro.json --eplan mubea_eplan.json
|
||||
bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend
|
||||
```
|
||||
|
||||
@@ -102,8 +115,9 @@ Conventions shared by the CLI tools:
|
||||
- `--file` / `--dxf` accept either a bare filename — resolved against
|
||||
`%SKEL_DATA%` — or a full path.
|
||||
- All generated output is written to `%SKEL_RESULTS%`.
|
||||
- `tro_annotate.py` additionally persists the verified CSV↔DXF coordinate
|
||||
offset to `%SKEL_CFG%\dxf_registration.json`.
|
||||
- `tro_annotate.py` and `e_planning.py` additionally persist the verified
|
||||
coordinate offset (CSV↔DXF, CSV↔E-planning drawing) to
|
||||
`%SKEL_CFG%\dxf_registration.json`.
|
||||
- Every run prints a `====` bordered report to stdout (counts, warnings,
|
||||
output file paths); non-fatal findings are printed as `?`/`!` lines and also
|
||||
do not stop the run.
|
||||
@@ -294,17 +308,45 @@ 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-v3`, 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-v4`, model `TroDocument` in `lib/model.py`): TROs with their `slots`, system boundaries (`SYSn`), connections with their kind, `jamAreas`, findings. Input for `scl_skeleton.py`. |
|
||||
| `--ids` | `DATEI` | `<name>_tro_ids.ini` | ID file of the plant (name in `%SKEL_CFG%`, or a path). Use it to run another export of the same plant against the same IDs, e.g. `--file mubea_export.csv --ids mubea_tro_ids.ini`. |
|
||||
| `--renumber` | — | off | Number all TROs afresh along the material flow and rewrite the `[tro]` section of the ID file (early project phase). Renamed IDs are lost; other sections of the file are kept. |
|
||||
| `--id-tolerance` | `MM` (float) | `100.0` | Maximum distance between an ID-file entry and a separator of a TRO for the TRO to count as found again. |
|
||||
|
||||
TROs, types and connections always come from the layout alone;
|
||||
`cfg/tro_overrides.ini` is not read. Errors in the derivation are fixed in
|
||||
`lib/tro_flow.py`, not in a correction file.
|
||||
|
||||
**TRO-IDs** (`lib/tro_ids.py`). The IDs belong to the SPS programmers and must
|
||||
survive a new layout export; the layout `TeileId` changes between exports, the
|
||||
position does not. So every run reads `%SKEL_CFG%\<name>_tro_ids.ini`:
|
||||
|
||||
```ini
|
||||
[tro]
|
||||
TRO01 = -400.0, 13920.0 ; 1Sep1Swi | Separator 0003 auf Kreisel2 Bahn L | Sep 0003
|
||||
K0302_1 = 10310.0, -440.1 ; renamed by hand
|
||||
```
|
||||
|
||||
- Each entry is `ID = x, y` (mm, CSV coordinates) of the TRO's anchor
|
||||
separator. An entry finds the TRO with a separator within `--id-tolerance`
|
||||
(closest first, one entry per TRO), and that TRO keeps the ID — renamed IDs
|
||||
included. An ID must be unique and a valid SCL identifier; `SYSn` is
|
||||
reserved for system boundaries.
|
||||
- No file yet: the TROs are numbered along the material flow (depth-first from
|
||||
the loading stations over the material-flow graph, on a carousel or Strecke in
|
||||
`TrackIds` order) and the file is created.
|
||||
- A TRO not found gets the next free `TRO<n>` and is appended to `[tro]`;
|
||||
an entry without a TRO is reported ("weggefallen", with the TRO that now
|
||||
holds its separator, e.g. after two TROs merged into a `2Sep2Swi`) and stays
|
||||
in the file; its ID is not reused.
|
||||
- The console report lists found / new / dropped IDs.
|
||||
|
||||
**Output** (in `%SKEL_RESULTS%`): `<name>_tro_flow.dot` (always, with `pos`
|
||||
attributes when `--use-cords` was used), `<name>_tro_flow.svg` (with
|
||||
`--tosvg`), `<name>_tro_doc.md` (with `--doc`), `<name>_tro.json` (with
|
||||
`--json`), `<name>_tro_errors.md` (only if TROs are unconnected or of an
|
||||
unknown type).
|
||||
unknown type). In `%SKEL_CFG%`: `<name>_tro_ids.ini` (created, extended or,
|
||||
with `--renumber`, rewritten — see above).
|
||||
|
||||
**Exit codes:** `0` ok · `1` input/CLI error · `2` SVG rendering failed
|
||||
(Graphviz) · `3` TRO validation errors found (error file written).
|
||||
@@ -355,6 +397,7 @@ overridden.
|
||||
| `--legend` | — | off | Draw a legend of the color groups. |
|
||||
| `--marker-size` | `MM` (float) | `300.0` | Marker radius in mm. |
|
||||
| `--text-height` | `MM` (float) | `220.0` | Text height in mm. |
|
||||
| `--ids` | `DATEI` | `<csv-stem>_tro_ids.ini` | ID file as for `tro_flow.py`, **read only**: the markers carry the same IDs as the TRO-JSON. Without the file the TROs are numbered along the material flow, as `tro_flow.py` would. |
|
||||
| `--offset` | `DX,DY` | — | Set the CSV↔DXF offset by hand instead of deriving/loading it. For a drawing built with dxfmakros (CSV exported from the same drawing) this is `0,0`. |
|
||||
| `--re-register` | — | off | Re-derive the coordinate offset even if a verified one is already cached in `%SKEL_CFG%`. |
|
||||
| `--check` | — | off | Dry run: only analyze and report, write no output file. |
|
||||
@@ -446,7 +489,8 @@ is missed.
|
||||
|---|---|
|
||||
| REGION order (material flow), instance list, FB type per TRO | sensor/actuator addresses (`cInBG`, `MB`, `MA`) |
|
||||
| number of separator / switch / scanner parameter blocks | separator and switch numbers, `cMainTro` |
|
||||
| `nTo1Destinations` when the TRO has exactly one switch | jam areas (`DB_JamArea…`) |
|
||||
| `nTo1Destinations` when the TRO has exactly one switch | jam-area DB names (`DB_JamArea…`) |
|
||||
| number of jam entries (one per own jam area, not per predecessor) and which jam area each exit leads to (`J<Separator>` in the comment) | |
|
||||
| destination list per switch exit (graph reachability) | times marked "pruefen" |
|
||||
| cross-reference to the layout parts (separator numbers, coordinate) | |
|
||||
|
||||
@@ -460,7 +504,7 @@ than a guess.
|
||||
|
||||
Switches: `--json NAME` (default `export_tro.json`, looked up in
|
||||
`%SKEL_RESULTS%`; written by `tro_flow.py --json`), `--out NAME` (default
|
||||
`<source>_FB_Main.scl`), `--start TRO`. The last one matters while the plant has
|
||||
`<source>_FB_Main.scl`), `--eplan NAME` (see below), `--start TRO`. The last one matters while the plant has
|
||||
no loading/unloading station: the flow is then a closed loop with no entry, so
|
||||
the order has to be broken somewhere arbitrary. The chosen start and the reason
|
||||
are recorded in the file header.
|
||||
@@ -494,6 +538,16 @@ by a PLC engineer (`doc/SCL_Templates.md` §6). `2Sep1Swi`, `EmptyCarrBuffer`,
|
||||
`LoadingBoom` and `2Sep2Swi` have no call site yet and stay generic. `--strict` stops (exit `1`, no SCL) if any FB type has no
|
||||
template. Without `--templates` the output is unchanged.
|
||||
|
||||
**Names from the E-planning (opt-in, `--eplan NAME`).** Reads the assignment
|
||||
`<csv>_eplan.json` written by `e_planning.py` (§5c; bare name looked up in
|
||||
`%SKEL_RESULTS%`). For every separator it assigns, the separator sensor
|
||||
(`stSenInSep := "DB_Inputs".Sensors["cInBG0642"]`) and the stopper valve
|
||||
(`xOutStopper<n> => "MB0643"`) are filled in and lose their `TODO(E-Planung)`
|
||||
marker (`SclName.todo = False`); everything else stays open. The header names
|
||||
the E-planning file. An assignment made for a different CSV (`csv_file` ≠ the
|
||||
TRO-JSON's `source`) stops the run with exit `1` — separator numbers are only
|
||||
unique within one export. Without `--eplan` the output is unchanged.
|
||||
|
||||
Tests: `tests/test_scl_context.py` (the derivations one by one),
|
||||
`tests/test_scl_templates.py` (each template rendered with the real values of
|
||||
its reference call site must equal that call site, comments and whitespace
|
||||
@@ -502,6 +556,86 @@ HundM05 byte for byte against `tests/fixtures/scl/*_FB_Main.scl` and
|
||||
`*_FB_Main_templates.scl`, timestamp masked; an intended change is written
|
||||
back with `SKEL_UPDATE_GOLDEN=1`).
|
||||
|
||||
## 5c. `e_planning.py` — assign the E-planning to the layout parts
|
||||
|
||||
The bridge between layout and electrical planning (background and findings:
|
||||
[`E-Planung_Bruecke.md`](E-Planung_Bruecke.md)). The layout knows which
|
||||
separators, switches and outfeed elements exist and where; the E-planning knows
|
||||
what their sensors and valves are called. The I/O list alone has no topology —
|
||||
but the E-planning is drawn as a layer over the hardware layout, every sensor or
|
||||
actuator symbol sits next to its part, and the cable-length tool
|
||||
(`kabellaengen/lib/getpositions.py`) writes those symbols with position and
|
||||
attributes to `<drawing>_positionsdraw.json`.
|
||||
|
||||
```
|
||||
<name>.csv (layout) + <drawing>_positionsdraw.json (E-planning)
|
||||
│
|
||||
▼
|
||||
e_planning.py ──► <name>_eplan.json (EplanDocument, schema sps_skel-eplan-v1)
|
||||
──► <name>_eplan.md (report: assignments, gaps, drives)
|
||||
│
|
||||
▼
|
||||
scl_skeleton.py --eplan <name>_eplan.json
|
||||
```
|
||||
|
||||
Steps:
|
||||
|
||||
1. **Read** `sensors` (field devices) and `schaltschrank_elemente` (cabinet) of
|
||||
the positionsdraw file into `EDevice` (BMK, `VERW`, `BEZEICHNUNG`,
|
||||
`KENNZEICHNUNG` → controller `UH01` / I/O station `KF107`, position). A BMK
|
||||
listed twice keeps the entry that has a `VERW`.
|
||||
2. **Registration** E-planning drawing → CSV with `dxf_registration.py`: the
|
||||
E symbols matching `[Registrierung] anker` (default `IN SEP`, the sensor that
|
||||
sits on the separator) against the CSV separators. Stored under the key
|
||||
`<csv>|<positionsdraw>` in `%SKEL_CFG%\dxf_registration.json` and re-verified
|
||||
on every run; `--re-register` forces a new derivation.
|
||||
3. **Rules** from `%SKEL_CFG%\e_planning.ini`, one section `<kind>.<role>` per
|
||||
rule, applied in file order; each E device is given out at most once, each
|
||||
role of a layout part filled at most once:
|
||||
|
||||
| key | meaning |
|
||||
|---|---|
|
||||
| `layout` | layout kind pattern (default: the part before the dot): `Separator`, `Ausschleus`, `Einschleus`, `Weiche`, `Scanner`, `Beladung`, `Entladung` |
|
||||
| `verw`, `praefix` | pattern(s) for the E attribute `VERW`, BMK prefix (`BG`, `MB`) |
|
||||
| `max_mm` | spatial: maximum distance after registration |
|
||||
| `soll_mm`, `toleranz_mm` | spatial: the symbol always sits this far from the part; candidates are ranked by their deviation from it, not by the smallest distance |
|
||||
| `nummer_von`, `nummer_versatz` | numbering rule: BMK number = number of the given role + offset (Mubea: valve = sensor + 1) — tried first, distance only for the rest |
|
||||
|
||||
Shipped rules (tuned on Mubea): `Separator.sensor`, `Separator.stopper`,
|
||||
`Ausschleus.stau`, `Ausschleus.ventil`, `Weiche.ventil`. `VERW` texts and the
|
||||
numbering rule are project-specific — in the reference SCL (UH01..UH05)
|
||||
sensor and stopper share the number (BG3242/MB3242), i.e. offset `0`.
|
||||
4. **Drives**: motor `MAnnnn` ↔ motor protection `FCnnnn` (same number), for the
|
||||
report.
|
||||
|
||||
Uncertain cases stay open on purpose: a gap is better than a plausible wrong
|
||||
name. The report lists, per role, the layout parts without a match, the field
|
||||
devices without a match, and distance and runner-up distance of every match
|
||||
(close values = check by hand).
|
||||
|
||||
Switches: `--file NAME` (CSV, default `export.csv`), `--eplan NAME`
|
||||
(positionsdraw JSON, required; bare name resolved against `%SKEL_DATA%`, or a
|
||||
full path), `--rules FILE` (default `%SKEL_CFG%\e_planning.ini`),
|
||||
`--re-register`. Exit `0` also when not everything was assigned; `1` on input
|
||||
errors or when the registration cannot be proven.
|
||||
|
||||
**Today only separator sensor and stopper reach the SCL.** Jam sensors and
|
||||
outfeed/switch valves are assigned in the JSON/report, but the TRO-JSON does not
|
||||
yet say which outfeed element or switch belongs to which TRO exit, so
|
||||
`scl_skeleton` cannot place them. The same holds for the storage lines of a
|
||||
`PinStore` TRO (`arInSep[n]`): their sensors are assigned but the generator does
|
||||
not emit per-line parameters yet.
|
||||
|
||||
Result on Mubea (`mubea.csv` + `Mubea_positionsdraw.json`): registration
|
||||
dx +50613.6 / dy +8555.8 mm; separator sensor 21/38, stopper 22/38 (all 20
|
||||
storage lines exact, 0–1 mm), jam sensor 20/23, outfeed valve 20/23, switch
|
||||
valve 1/1. The open separators are on the carousels K0102/K0104/K0105, where
|
||||
the E-planning drawing and the CSV apparently differ.
|
||||
|
||||
Tests: `tests/test_e_planning.py` (reading, rules, numbering vs. distance on a
|
||||
storage-line grid, target distance, uniqueness, registration, drives, the path
|
||||
into `scl_context` and `scl_skeleton --eplan`).
|
||||
|
||||
## 6. `tro_catalog.py` — TRO type catalog (library, no CLI)
|
||||
|
||||
The single source of truth for everything needed to generate or draw a TRO
|
||||
@@ -535,8 +669,8 @@ 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, TroCandidate,
|
||||
Analysis, TroDocument
|
||||
model.py instance level TroBase ─► DerivedTro, Slot, JamArea, Connection,
|
||||
TroCandidate, Analysis, TroDocument
|
||||
▲
|
||||
tro_flow.py (writes) · scl_skeleton.py (reads) · tro_annotate.py (draws)
|
||||
```
|
||||
@@ -544,11 +678,13 @@ tro_flow.py (writes) · scl_skeleton.py (reads) · tro_annotate.py (draws)
|
||||
| Class | What it is |
|
||||
|---|---|
|
||||
| `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`. |
|
||||
| `DerivedTro` | `TroBase` + what `tro_flow.py` knows from the derivation: `label`, `hosts`, `scanners`, `reason`, `colocated`, `positionFrom`, `positionPoints`, `slots`. Also used for the system boundaries `SYSn` (no slots). |
|
||||
| `Slot` | A place on a TRO where hardware belongs: `slot` (`separator1`, `switch1`, `scanner1`, `drive` — the stable key together with the TRO-ID), `layoutId` (TeileId in the current export, for tracing only; empty for switches, whose element `tro_flow` does not keep yet), `roles` the hardware planning must fill (`sensor`, `stopper`, `jamSensor?` …; `?` = optional). Built by `derive_slots()` from the catalog type. |
|
||||
| `JamArea` | A jam area = the queue in front of a separator, the entry of exactly one TRO: `id` (`J<Separator>`, e.g. `J0042`), `tro`, `entry` (1, 2 for 2Sep types), `separator`, `host`, `predecessors` (all TROs/stations feeding it — merges have several), `note`, `position`. Built by `derive_jam_areas()`; the SCL name `"DB_JamArea<NNNN>".stJam<NNNN>_<k>` needs the conveyor number from the hardware planning and is not stored. |
|
||||
| `Connection` | Directed edge `from` → `to` with its `kind` (transfer / umlauf / weiche / unbestimmt / normal / bahn). |
|
||||
| `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-v3`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `candidates`, `findings`. `from_analysis()`, `to_json()`, `from_json()`. |
|
||||
| `TroDocument` | The TRO-JSON: `schema` (`sps_skel-tro-v4`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `candidates`, `jamAreas`, `findings`. `from_analysis()` (derives slots and jam areas), `to_json()`, `from_json()`, `jam_areas_or_derived()` (a v3 file without `jamAreas` still renders). |
|
||||
|
||||
`TroBase` and `TroCandidate` share the `position` handling (JSON `{x,y,z}`,
|
||||
`x`/`y`/`z`, `describe_position()`) through the private mixin `_Positioned`.
|
||||
@@ -560,7 +696,9 @@ 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`, `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`.
|
||||
Schema v3 differs from v2 only in `candidates` replacing `openPoints`; v4 adds
|
||||
`slots` per TRO and `jamAreas`. A v3 file still reads (both default to empty;
|
||||
`scl_skeleton` then derives the jam areas while rendering).
|
||||
|
||||
Tests: `tests/test_model.py` (round trip, catalog-backed fields, rejection of
|
||||
wrong keys/items, SCL rendering from a document) — run with
|
||||
|
||||
Reference in New Issue
Block a user