hw_import --dxf: gemeinsame Zeichnung (Layout + E-Planung) als Hardwarequelle

hw_import liest mit --dxf statt der Positionsdatei eine DXF, in der Layout
und E-Planung zusammen liegen (Dematic): jeder Block mit Attribut IO ist ein
E-Bauteil (IO/VERW/BEZEICHNUNG/KENNZEICHNUNG, Einfuegepunkt), schon in
Layout-Koordinaten (HardwareDocument.layoutCoordinates). --conveyor-from-motor
nimmt den Foerderer aus der Motornummer, wenn kein K0102/#106 dasteht
("Motor KR-MA0055" -> K0055); "ILS-VARIO" zaehlt als Strecke.

merge_hw_layout: bei layoutCoordinates kein Lagebezug (Identitaet, nichts
abgelegt); --rules findet einen blossen Namen in SKEL_CFG. Neue Regeldatei
cfg/e_planning_dematic.ini (Sensor/Stopper gleiche Nummer, Weichenventil =
Nummer des Stausensors, Abstaende an dematic_merge..dxf gemessen).

Fehler behoben: zwei Antriebs-TROs auf einem Layout-Foerderer (Dematic VF_6,
TRO04/TRO06) bekamen beide denselben Vario-Motor. Jetzt Widerspruch mit
Vorschlag; scl_context faellt dann nicht auf den Motor des Foerderers zurueck.

Dematic: 115 Namen im FB_Main (alle im TIA-Import), offene Stellen 730 -> 615.
cfg/dematic_merge_tro_ids.ini: TRO-IDs der Dematic-Anlage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 18:21:20 +02:00
parent d414bd6cf3
commit 2fbd2f119d
10 changed files with 467 additions and 38 deletions
+48 -6
View File
@@ -371,6 +371,19 @@ def resolve_transform(points, devices, anchors, cfg_dir: Path, csv_name: str,
return transform
def _same_drawing(points: list[LayoutPoint], drawing: str) -> Transform:
"""Lagebezug einer gemeinsamen Zeichnung (hw_import --dxf): keiner.
Die E-Symbole stehen schon in Layout-Koordinaten. Ihr Versatz neben dem
Bauteil (Dematic ~600 mm) ist Zeichenabstand, keine Verschiebung - ein
eingepasster Bezug wuerde ihn nur verzerren. Er steckt in max_mm/soll_mm
der Regeln.
"""
n = sum(1 for p in points if p.kind == "Separator")
return Transform(dx=0.0, dy=0.0, source="zeichnung", anchor="gemeinsame Zeichnung",
matched=n, candidates=n, dxf_file=drawing)
# ---------------------------------------------------------------------------
# Slots
# ---------------------------------------------------------------------------
@@ -529,9 +542,20 @@ def _conveyor_kinds(hw: HardwareDocument) -> dict[str, str]:
def bind_drives(doc: TroDocument, conveyors: list[ConveyorBinding],
hw: HardwareDocument) -> list[Binding]:
"""Antriebs-Slots (Vario): Motor und Motorschutz des zugeordneten Foerderers."""
"""Antriebs-Slots (Vario): Motor und Motorschutz des zugeordneten Foerderers.
Tragen mehrere TROs einen Antriebs-Slot auf demselben Layout-Foerderer
(Dematic VF_6: zwei Vario-TROs, drei Vario-Motoren), hat jeder TRO seinen
eigenen Motor - welcher, sagt die Mehrheit je Foerderer nicht. Dann wird
der Antrieb ein Widerspruch (BMK als Vorschlag), Korrektur von Hand.
"""
devices = hw.by_bmk()
by_layout = {c.layout_id: c for c in conveyors if c.status == "zugeordnet"}
sharing: dict[str, list[str]] = {}
for tro in doc.tros:
for s in tro.slots:
if s.layout_id and any(r.rstrip("?") in _DRIVE_ROLES for r in s.roles):
sharing.setdefault(s.layout_id.split("-", 1)[0], []).append(tro.tro_id)
bindings = []
for tro in doc.tros:
for s in tro.slots:
@@ -541,17 +565,24 @@ def bind_drives(doc: TroDocument, conveyors: list[ConveyorBinding],
if conv is None:
continue
roles = [r.rstrip("?") for r in s.roles]
shared = sharing.get(s.layout_id.split("-", 1)[0], [])
for role, attr in _DRIVE_ROLES.items():
bmk = getattr(conv, attr)
if role not in roles or not bmk:
continue
dev = devices.get(bmk)
status = "zugeordnet" if dev and dev.in_tia else "widerspruch"
note = f"Antrieb von {conv.eplan_name}"
if len(shared) > 1:
status = "widerspruch"
note = (f"{conv.layout_name or conv.layout_id} traegt {len(shared)} "
f"Antriebs-TROs ({', '.join(shared)}) - Antrieb je TRO von Hand "
f"({OVERRIDES_FILE}); Vorschlag {conv.eplan_name}")
bindings.append(Binding(
tro=tro.tro_id, slot=s.slot, role=role, layout_id=s.layout_id,
bmk=bmk, constant=dev.constant if dev else "",
address=dev.address if dev else "", source="positionsdraw",
rule="mehrheit", status="zugeordnet" if dev and dev.in_tia else "widerspruch",
note=f"Antrieb von {conv.eplan_name}"))
rule="mehrheit", status=status, note=note))
return bindings
@@ -690,7 +721,9 @@ def build_anlage(doc: TroDocument, hw: HardwareDocument, rules: list[Rule],
assignments: list[Assignment] = []
conveyors: list[ConveyorBinding] = []
if hw.field_devices():
if transform is None:
if transform is None and hw.layout_coordinates:
transform = _same_drawing(points, hw.positions_file)
elif transform is None:
transform = resolve_transform(points, devices, anchors, cfg_dir, doc.source,
hw.positions_file, re_register, warnings,
hw.positions_sha256)
@@ -856,6 +889,14 @@ def _in_results(name: str, results: Path) -> Path:
return results / name
def _in_cfg(name: str, cfg_dir: Path) -> Path:
"""Regeldatei: ein blosser Name, der hier nicht liegt, steht in %SKEL_CFG%."""
path = Path(name)
if path.is_absolute() or path.parent != Path(".") or path.is_file():
return path
return cfg_dir / name
def main(argv: list[str] | None = None) -> int:
from material_flow import env_dir
@@ -864,7 +905,7 @@ def main(argv: list[str] | None = None) -> int:
cfg_dir = env_dir("SKEL_CFG", "cfg")
tro_path = _in_results(args.tro, results)
hw_path = _in_results(args.hw, results)
rules_path = Path(args.rules) if args.rules else cfg_dir / RULES_FILE
rules_path = _in_cfg(args.rules, cfg_dir) if args.rules else cfg_dir / RULES_FILE
overrides_path = Path(args.overrides) if args.overrides else cfg_dir / OVERRIDES_FILE
for label, path in (("TRO-JSON", tro_path), ("Hardware-JSON", hw_path),
("Regeln", rules_path)):
@@ -955,7 +996,8 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
help="Hardware-JSON <projekt>_hw.json in %%SKEL_RESULTS%% "
"(oder ein Pfad)")
parser.add_argument("--rules", metavar="DATEI",
help=f"Regeldatei. Standard: %%SKEL_CFG%%\\{RULES_FILE}")
help=f"Regeldatei (Name in %%SKEL_CFG%% oder ein Pfad), z. B. "
f"e_planning_dematic.ini. Standard: %%SKEL_CFG%%\\{RULES_FILE}")
parser.add_argument("--overrides", metavar="DATEI",
help=f"Handkorrekturen. Standard: %%SKEL_CFG%%\\{OVERRIDES_FILE} "
f"(fehlt sie, keine)")