tro_annotate --anlage: Staubereiche in der Zeichnung, --read-jams zurueck

Mit --anlage <csv>_anlage.json zeichnet tro_annotate je Staubereich einen
Block JAM_AREA (Layer TRO_JAM): Rechteck mit dem Namen Jam<NNNN>_<k> wie in
der SCL, jam_versatz vor dem Separator Richtung Vorgaenger. Attribute
CONVEYOR, NUMBER, TRO + ENTRY (stabiler Schluessel), AREA, AUTO. Doppelklick
oeffnet den Dialog JAM_EDIT (dxfmakros).

--read-jams uebernimmt im Dialog geaenderte Nummern aus der beschrifteten
Zeichnung nach cfg/<csv>_jam_names.ini (nur Abweichungen von AUTO);
scl_skeleton wendet die Datei mit der Anlagen-JSON an (scl_context:
jam_numbers + Korrekturen, load/save_jam_overrides).

Dematic: 25 Staubereiche gezeichnet; Rundreise TRO20 -> Jam0054_9 bis in die
SCL geprueft. Gemeinsame Zeichnung mit --offset 0,0 beschriften (automatischer
Lagebezug waehlte dy +360 mm).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 12:47:11 +02:00
parent 404d500271
commit e5d1fce41f
7 changed files with 540 additions and 17 deletions
+96 -8
View File
@@ -33,9 +33,11 @@ Wert ersetzen, Merker loeschen.
from __future__ import annotations
import configparser
import re
from collections import deque
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING
from pydantic import BaseModel, ConfigDict, Field
@@ -64,7 +66,13 @@ __all__ = [
"HwSource",
"build_context",
"flow_order",
"JAM_NAMES_SUFFIX",
"jam_key",
"jam_label",
"jam_names",
"jam_numbers",
"load_jam_overrides",
"save_jam_overrides",
"reachable_via",
]
@@ -631,10 +639,88 @@ def _jam_db(area: JamArea | None, hw: HwSource,
return (f'"DB_JamArea{nr}".stJam{nr}_____' if nr else _JAM_DB), False
def jam_names(document: TroDocument, geordnet: list[TroBase],
hw: HwSource) -> dict[str, str]:
JAM_NAMES_SUFFIX = "_jam_names.ini"
# Wert in der Korrekturdatei: Foerderer und laufende Nummer, "0054_3"
_JAM_WERT = re.compile(r"^(\d{4})_(\d+)$")
def jam_key(area: JamArea) -> str:
"""Stabiler Schluessel eines Staubereichs: TRO-ID + Eingang ("TRO17.1").
Die Kennung J<Separator> folgt der TeileId und aendert sich mit jedem
CSV-Export; die TRO-ID bleibt (cfg/<csv>_tro_ids.ini).
"""
return f"{area.tro}.{area.entry}"
def jam_label(nr: str, k: int) -> str:
"""Kurzname fuer Zeichnung und Dialog: Jam0054_3."""
return f"Jam{nr}_{k}"
def load_jam_overrides(path: Path) -> dict[str, tuple[str, int]]:
"""Korrekturen der Staubereichs-Nummern aus ``<csv>_jam_names.ini``.
Abschnitt [jam], je Zeile ``<TRO-ID>.<Eingang> = <NNNN>_<k>``
(``TRO17.1 = 0054_3``). Geschrieben von tro_annotate --read-jams aus den
im Dialog JAM_EDIT geaenderten Staubereichen; von Hand pflegbar.
Fehlt die Datei, gibt es keine Korrekturen.
"""
if not path.is_file():
return {}
parser = configparser.ConfigParser(inline_comment_prefixes=(";", "#"),
interpolation=None)
parser.optionxform = str
with path.open(encoding="utf-8") as fh:
parser.read_file(fh)
result: dict[str, tuple[str, int]] = {}
if parser.has_section("jam"):
for key, value in parser["jam"].items():
m = _JAM_WERT.match(value.strip())
if not m:
raise ValueError(f"{path.name} [jam] {key} = {value!r}: erwartet "
f"<NNNN>_<k>, z. B. 0054_3")
result[key.strip()] = (m.group(1), int(m.group(2)))
return result
def save_jam_overrides(path: Path, overrides: dict[str, tuple[str, int]]) -> None:
"""Korrekturdatei neu schreiben (sortiert nach TRO-ID); leer = Datei weg."""
if not overrides:
if path.is_file():
path.unlink()
return
zeilen = [
"; Staubereichs-Nummern, die von der Ableitung abweichen",
"; (scl_context.jam_names). Geschrieben von tro_annotate --read-jams aus",
"; dem Dialog JAM_EDIT; von Hand pflegbar.",
"; <TRO-ID>.<Eingang> = <Foerderer NNNN>_<laufende Nummer k>",
"",
"[jam]",
]
zeilen += [f"{key} = {nr}_{k}" for key, (nr, k) in sorted(overrides.items())]
path.write_text("\n".join(zeilen) + "\n", encoding="utf-8")
def jam_names(document: TroDocument, geordnet: list[TroBase], hw: HwSource,
overrides: dict[str, tuple[str, int]] | None = None) -> dict[str, str]:
"""Staubereichs-Namen je Kennung (J0048 -> '"DB_JamArea0042".stJam0042_1').
Die abgeleiteten Nummern (jam_numbers), ersetzt durch ``overrides``
(load_jam_overrides) fuer die dort genannten Staubereiche.
"""
nummern = jam_numbers(document, geordnet, hw)
for area in document.jam_areas_or_derived():
if overrides and jam_key(area) in overrides:
nummern[area.jam_id] = overrides[jam_key(area)]
return {jam_id: f'"DB_JamArea{nr}".stJam{nr}_{k}'
for jam_id, (nr, k) in nummern.items()}
def jam_numbers(document: TroDocument, geordnet: list[TroBase],
hw: HwSource) -> dict[str, tuple[str, int]]:
"""Abgeleitete Staubereichs-Nummern je Kennung (J0048 -> ("0042", 1)).
Regel der SPS-Programmierer (Dematic, 2026-10-22): <NNNN> ist der
Foerderer, auf dem der Staubereich liegt (Nummer des Kreisel- bzw.
Vario-Motors), <k> zaehlt auf diesem Foerderer in Fliessrichtung hoch.
@@ -706,11 +792,11 @@ def jam_names(document: TroDocument, geordnet: list[TroBase],
if nr:
je_foerderer.setdefault(nr, []).append(area)
nachfolger = {t.tro_id: list(t.successors) for t in geordnet}
namen: dict[str, str] = {}
nummern: dict[str, tuple[str, int]] = {}
for nr, liste in je_foerderer.items():
for k, area in enumerate(_entlang(liste, nachfolger), start=1):
namen[area.jam_id] = f'"DB_JamArea{nr}".stJam{nr}_{k}'
return namen
nummern[area.jam_id] = (nr, k)
return nummern
def _entlang(areas: list[JamArea], nachfolger: dict[str, list[str]]) -> list[JamArea]:
@@ -908,13 +994,15 @@ def _tro_context(tro: TroBase, alle: dict[str, TroBase],
def build_context(document: TroDocument, source: str, start_id: str = "",
created: str | None = None,
eplan: EplanDocument | None = None,
anlage: AnlageDocument | None = None) -> SclFile:
anlage: AnlageDocument | None = None,
jam_overrides: dict[str, tuple[str, int]] | None = None) -> SclFile:
"""TroDocument -> SclFile (TROs in Fliessrichtung).
anlage: Anlagen-JSON (merge_hw_layout) - ihr Layout ist ``document``,
ihre Zuordnungen liefern die Namen. eplan: alte Zuordnung Layout ->
E-Planung (e_planning.py), nur ohne ``anlage``. Ohne beide bleiben alle
Namen offen und die Ausgabe ist die bisherige.
Namen offen und die Ausgabe ist die bisherige. jam_overrides: Korrekturen
der Staubereichs-Nummern (load_jam_overrides).
"""
if anlage is not None and eplan is not None:
raise ValueError("anlage und eplan schliessen sich aus")
@@ -924,7 +1012,7 @@ def build_context(document: TroDocument, source: str, start_id: str = "",
for area in document.jam_areas_or_derived():
bereiche.setdefault(area.tro, []).append(area)
geordnet, startgrund = flow_order(document.tros, start_id)
namen = jam_names(document, geordnet, hw)
namen = jam_names(document, geordnet, hw, jam_overrides)
return SclFile(
source=source,
created=created or datetime.now().strftime("%Y-%m-%d %H:%M"),