scl_context: Staubereichs-Namen je Foerderer in Fliessrichtung

Staubereiche heissen DB_JamArea<NNNN>.stJam<NNNN>_<k> (Regel der
SPS-Programmierung, Dematic): NNNN = Foerderer, auf dem der Staubereich
liegt (Kreisel- bzw. Vario-Motor), k zaehlt auf diesem Foerderer in
Fliessrichtung. Foerderer: eigener Vario-Antrieb, sonst Separator auf einem
Kreisel (staubereich_kreisel_max_mm in cfg/parameter.ini [scl_context]),
sonst Kreisel-Host, sonst letzter Antriebs-TRO davor auf derselben Strecke.
k folgt der Nachfolgerkette auf dem Foerderer, nicht der REGION-Reihenfolge
(Breitensuche, Dematic Kreisel3 sonst TRO20 vor TRO23). Programm-DBs zaehlen
in der --strict-Gegenpruefung nicht als Geraete.

merge_hw_layout: von Hand gesetzte Antriebe (hw_overrides.ini) bekommen
keinen zusaetzlichen automatischen Widerspruch-Eintrag mehr.

hw_overrides.ini Dematic: VF_6 = MA0043 (TRO04/05) + MA0044 (TRO06/07),
VF_4 = MA0045 (TRO01/02, K0045 = 0008); Separatoren nach Klartext statt
kleinstem Abstand - TRO04 hatte BG0111 von VF_4.

Dematic: alle 25 Staubereiche benannt, 123 Slot-Rollen, 0 Widersprueche,
offene Stellen 615 -> 494.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 16:26:10 +02:00
parent 2fbd2f119d
commit 404d500271
9 changed files with 312 additions and 38 deletions
+11
View File
@@ -196,6 +196,17 @@ def test_zwei_antriebs_tros_auf_einem_foerderer_sind_widerspruch():
assert anlage.binding(tro, "drive", "motorProtection").status == "widerspruch"
def test_antrieb_von_hand_wird_nicht_ueberschrieben():
"""hw_overrides.ini legt den Motor je Vario fest - die automatische
Antriebszuordnung haengt keinen zweiten (Widerspruch-)Eintrag daneben."""
overrides = merge_hw_layout.Overrides(bindings={("TRO03", "drive", "motor"): "MA0210"})
anlage = run(doc=layout(second_vario=True), overrides=overrides)
motors = [b for b in anlage.bindings
if (b.tro, b.slot, b.role) == ("TRO03", "drive", "motor")]
assert [(b.bmk, b.rule, b.status) for b in motors] == [("MA0210", "hand", "zugeordnet")]
assert anlage.binding("TRO06", "drive", "motor").status == "widerspruch"
def test_gemeinsame_zeichnung_ohne_lagebezug(tmp_path):
"""hw_import --dxf: E-Symbole stehen schon in Layout-Koordinaten - kein
Lagebezug wird ermittelt oder abgelegt."""
+88 -4
View File
@@ -103,15 +103,99 @@ def test_kreisel_freigabe_und_staubereich_aus_der_foerderernummer():
tro01 = _by_id(ctx, "TRO01")
assert tro01.carousel_run.value == "#fbCarousel0102.xOutConveyorFullSpeed"
assert not tro01.carousel_run.todo
# Staubereich: Foerderer bekannt, laufende Nummer <k> nicht -> weiter offen
assert tro01.jam_entries[0].db.value == '"DB_JamArea0102".stJam0102_____'
assert tro01.jam_entries[0].db.todo
# Ausgang zu TRO03 liegt auf VF_1 (K0106)
# Staubereich am Kreisel: Foerderer K0102, in Fliessrichtung gezaehlt
assert tro01.jam_entries[0].db.value == '"DB_JamArea0102".stJam0102_1'
assert not tro01.jam_entries[0].db.todo
assert tro01.jam_exits[0].db.value == '"DB_JamArea0102".stJam0102_2'
# Ausgang zu TRO03 liegt auf VF_1 (K0106); ohne Antrieb des Vario steht
# die laufende Nummer nicht fest -> offen
assert tro01.jam_exits[1].db.value == '"DB_JamArea0106".stJam0106_____'
assert tro01.jam_exits[1].db.todo
# TRO03 sitzt auf einer Strecke, nicht am Kreisel -> Freigabe bleibt offen
assert _by_id(ctx, "TRO03").carousel_run.todo
def test_staubereiche_je_foerderer_in_fliessrichtung_gezaehlt():
"""Regel der SPS (Dematic): "DB_JamArea<NNNN>".stJam<NNNN>_<k>, NNNN =
Kreisel- bzw. Vario-Motor, k zaehlt in Fliessrichtung. Nachbau von VF_6:
zwei Varios auf einer Strecke, je ein 1Sep dahinter; davor ein TRO, dessen
Separator auf dem Kreisel liegt, obwohl sein Host die Strecke ist."""
def tro(tro_id, typ, sep, host, vor, nach):
return _tro(tro_id, typ, [sep], [host], predecessors=[vor], successors=[nach])
tros = [
tro("TRO01", "1Sep", "0048", "0008", "SYS1", "TRO02"),
tro("TRO02", "Vario", "0082", "0010", "TRO01", "TRO03"),
tro("TRO03", "1Sep", "0083", "0010", "TRO02", "TRO04"),
tro("TRO04", "Vario", "0084", "0010", "TRO03", "TRO05"),
tro("TRO05", "1Sep", "0085", "0010", "TRO04", "TRO06"),
tro("TRO06", "1Sep", "0049", "0004-L", "TRO05", "SYS2"),
]
parts = [
LayoutPart(layout_id="0004", kind="Kreisel", name="Kreisel4",
position=(0.0, 0.0, 0.0), bbox=(800.0, 10000.0, 1.0)),
LayoutPart(layout_id="0010", kind="Strecke", name="VF_6",
position=(8000.0, 0.0, 0.0), bbox=(12000.0, 800.0, 1.0)),
LayoutPart(layout_id="0048", kind="Separator", name="S48",
position=(350.0, 100.0, 0.0)),
]
layout = TroDocument.from_analysis(Analysis(tros=tros), source="vf6.csv", parts=parts)
doc = AnlageDocument(
layout=layout,
bindings=[_bind("TRO02", "drive", "motor", "MA0043", "cOutMA0043"),
_bind("TRO04", "drive", "motor", "MA0044", "cOutMA0044")],
conveyors=[
ConveyorBinding(eplan_name="K0042", layout_id="0004", motor="MA0042"),
ConveyorBinding(eplan_name="K0043", layout_id="0010", motor="MA0043"),
ConveyorBinding(eplan_name="K0044", layout_id="0010", motor="MA0044",
status="widerspruch"),
],
)
ctx = _kontext(doc)
namen = {t.id: t.jam_entries[0].db for t in ctx.tros}
assert {k: v.value for k, v in namen.items()} == {
"TRO01": '"DB_JamArea0042".stJam0042_1', # Separator auf dem Kreisel
"TRO02": '"DB_JamArea0043".stJam0043_1',
"TRO03": '"DB_JamArea0043".stJam0043_2', # 1Sep hinter Vario MA0043
"TRO04": '"DB_JamArea0044".stJam0044_1',
"TRO05": '"DB_JamArea0044".stJam0044_2',
"TRO06": '"DB_JamArea0042".stJam0042_2', # Host Kreisel, weiter gezaehlt
}
assert not any(v.todo for v in namen.values())
# Ausgang = Eingang des Nachfolgers, derselbe Name
assert _by_id(ctx, "TRO02").jam_exits[0].carriers.value == \
'"DB_JamArea0043".stJam0043_2.arCarrier'
def test_staubereiche_am_kreisel_entlang_des_umlaufs():
"""Die Breitensuche erreicht T4 ueber die Strecke (T5) vor T6; auf dem
Kreisel kommt aber erst T6, dann T4 - gezaehlt wird entlang des Umlaufs."""
def tro(tro_id, host, vor, nach):
return _tro(tro_id, "1Sep", [f"9{tro_id[-1]}00"], [host],
predecessors=vor, successors=nach)
tros = [
tro("TRO01", "0004-L", ["SYS1"], ["TRO02", "TRO05"]),
tro("TRO02", "0004-L", ["TRO01"], ["TRO03"]),
tro("TRO03", "0004-L", ["TRO02"], ["TRO06"]),
tro("TRO05", "0010", ["TRO01"], ["TRO04"]),
tro("TRO06", "0004-R", ["TRO03"], ["TRO04"]),
tro("TRO04", "0004-R", ["TRO06", "TRO05"], ["SYS2"]),
]
layout = TroDocument.from_analysis(
Analysis(tros=tros), source="loop.csv",
parts=[_part("0004", "Kreisel", "Kreisel4"), _part("0010", "Strecke", "VF")])
doc = AnlageDocument(layout=layout, conveyors=[
ConveyorBinding(eplan_name="K0042", layout_id="0004", motor="MA0042")])
ctx = _kontext(doc)
assert [t.id for t in ctx.tros][:5] == ["TRO01", "TRO02", "TRO05", "TRO03", "TRO04"]
k = {t.id: t.jam_entries[0].db.value.rsplit("_", 1)[-1] for t in ctx.tros
if not t.jam_entries[0].db.todo}
assert k == {"TRO01": "1", "TRO02": "2", "TRO03": "3", "TRO06": "4", "TRO04": "5"}
# Strecke ohne Foerderer der Hardwareplanung: bleibt offen
assert _by_id(ctx, "TRO05").jam_entries[0].db.todo
def test_vario_antrieb_ueber_den_foerderer():
tro = _by_id(_kontext(anlage()), "TRO03")
assert (tro.vario_motor.value, tro.vario_motor.todo) == ("MA0210", False)