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:
@@ -191,6 +191,91 @@ def test_foerderer_aus_klartext(text, name, kind):
|
||||
assert conveyor_kind_of(text) == kind
|
||||
|
||||
|
||||
@pytest.mark.parametrize("text, name, kind", [
|
||||
# Dematic nennt nur den Motor
|
||||
("Motor KR-MA0055", "K0055", "Kreisel"),
|
||||
("Rad dreht ILS-KR (MA0046)", "K0046", "Kreisel"),
|
||||
("Motor ILS-VARIO-MA0043", "K0043", "Strecke"),
|
||||
("Abfrage Finger in Position ILS-VARIO (MA0050)", "K0050", "Strecke"),
|
||||
# K/# gehen vor: der Motor MA0202 treibt hier K0102
|
||||
("ANTRIEB KREISEL-K0102 MA0202", "K0102", "Kreisel"),
|
||||
("Separator Str. 1", "", ""),
|
||||
])
|
||||
def test_foerderer_aus_motornummer(text, name, kind):
|
||||
assert conveyor_of(text, motor=True) == name
|
||||
assert conveyor_kind_of(text) == kind
|
||||
# ohne motor=True (Mubea) zaehlt die Motornummer nicht
|
||||
assert conveyor_of(text) == ("K0102" if "K0102" in text else "")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Gemeinsame Zeichnung (Layout + E-Planung in einer DXF, Dematic)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def dxf_file(tmp_path: Path) -> Path:
|
||||
"""E-Symbole wie in dematic_merge..dxf: Bloecke mit IO/VERW/BEZEICHNUNG."""
|
||||
import ezdxf
|
||||
|
||||
doc = ezdxf.new()
|
||||
tags = ("SPS", "IO", "VERW", "BEZEICHNUNG", "KENNZEICHNUNG", "ID")
|
||||
sym = doc.blocks.new("ILS_720002003-Separator")
|
||||
for i, tag in enumerate(tags):
|
||||
sym.add_attdef(tag, (0, -i * 10))
|
||||
label = doc.blocks.new("ILS_Motorkennzeichnung-Kreisel")
|
||||
label.add_attdef("MOTORKENNZEICHNUNG", (0, 0))
|
||||
msp = doc.modelspace()
|
||||
|
||||
def place(x, y, **attrs):
|
||||
msp.add_blockref("ILS_720002003-Separator", (x, y)).add_auto_attribs(attrs)
|
||||
|
||||
place(100.0, 200.0, SPS="2", IO="BG0532", VERW="In Sep 1",
|
||||
BEZEICHNUNG="in Separator Str. 1", KENNZEICHNUNG="=A02+UC0103-KF1DI1")
|
||||
place(100.0, 200.0, SPS="2", IO="MB0533", VERW="Sep 1",
|
||||
BEZEICHNUNG="Separator Str. 1", KENNZEICHNUNG="=A02+UC0103-KF1DQ1")
|
||||
place(900.0, 50.0, SPS="2", IO="MA0202", VERW="ILS-KR-MA0202",
|
||||
BEZEICHNUNG="Motor KR-MA0202", KENNZEICHNUNG="=A02+UH01-KF1DQ2")
|
||||
# doppelt: der Eintrag mit VERW gewinnt
|
||||
place(5.0, 5.0, IO="BG0777")
|
||||
place(7.0, 7.0, IO="BG0777", VERW="Stau 1")
|
||||
# ohne BMK in IO: kein E-Bauteil
|
||||
place(0.0, 0.0, IO="")
|
||||
msp.add_blockref("ILS_Motorkennzeichnung-Kreisel", (0, 0)).add_auto_attribs(
|
||||
{"MOTORKENNZEICHNUNG": "MA0999"})
|
||||
path = tmp_path / "anlage_merge.dxf"
|
||||
doc.saveas(path)
|
||||
return path
|
||||
|
||||
|
||||
def test_zeichnung_liest_jeden_block_mit_bmk(tmp_path):
|
||||
warnings: list[str] = []
|
||||
devices = {d.bmk: d for d in hw_import.load_dxf(dxf_file(tmp_path), warnings)}
|
||||
assert sorted(devices) == ["BG0532", "BG0777", "MA0202", "MB0533"]
|
||||
s = devices["BG0532"]
|
||||
assert (s.verw, s.bezeichnung, s.pos, s.station) == (
|
||||
"In Sep 1", "in Separator Str. 1", (100.0, 200.0), "KF1DI1")
|
||||
assert (devices["BG0777"].verw, devices["BG0777"].pos) == ("Stau 1", (7.0, 7.0))
|
||||
assert warnings == ["Zeichnung: BG0777 steht 2x in der Zeichnung - verwendet wird ein Eintrag"]
|
||||
|
||||
|
||||
def test_zeichnung_mit_tia_lage_in_layout_koordinaten(tmp_path):
|
||||
doc = build_hardware(tia_file(tmp_path), None, "X", dxf_path=dxf_file(tmp_path),
|
||||
conveyor_from_motor=True)
|
||||
assert doc.layout_coordinates and doc.positions_file == "anlage_merge.dxf"
|
||||
s = doc.by_bmk()["BG0532"]
|
||||
assert s.sources == ["tia", "positions"] and s.constant == "cInBG0532"
|
||||
[drive] = doc.drives
|
||||
# K0102 aus dem TIA-Kommentar geht der Motornummer vor
|
||||
assert (drive.motor, drive.protection, drive.conveyor) == ("MA0202", "FC0202", "K0102")
|
||||
assert HardwareDocument.from_json(doc.to_json()).layout_coordinates
|
||||
|
||||
|
||||
def test_positionsdatei_und_zeichnung_schliessen_sich_aus(tmp_path):
|
||||
with pytest.raises(ValueError, match="nur eine Quelle"):
|
||||
build_hardware(tia_file(tmp_path), positions_file(tmp_path), "X",
|
||||
dxf_path=dxf_file(tmp_path))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Zusammenfuehren
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -37,7 +37,8 @@ def _tro(tro_id, type_name, seps, hosts=()):
|
||||
separators=list(seps), scanners=[], reason="Test")
|
||||
|
||||
|
||||
def layout() -> TroDocument:
|
||||
def layout(second_vario: bool = False) -> TroDocument:
|
||||
"""``second_vario``: TRO06 ist ein zweiter Vario-TRO auf VF_1 (Dematic VF_6)."""
|
||||
parts = [_part(i, "Separator", x, y) for i, (x, y) in SEPS.items()]
|
||||
parts += [
|
||||
_part("0030", "Ausschleus", 0.0, 2000.0),
|
||||
@@ -52,7 +53,8 @@ def layout() -> TroDocument:
|
||||
_tro("TRO03", "Vario", ["0003"], ["0020"]),
|
||||
_tro("TRO04", "1Sep", ["0004"]),
|
||||
_tro("TRO05", "1Sep", ["0005"]),
|
||||
_tro("TRO06", "1Sep", ["0006"]),
|
||||
_tro("TRO06", "Vario", ["0006"], ["0020"]) if second_vario
|
||||
else _tro("TRO06", "1Sep", ["0006"]),
|
||||
]
|
||||
return TroDocument.from_analysis(Analysis(tros=tros), source="x.csv", parts=parts)
|
||||
|
||||
@@ -183,6 +185,37 @@ def test_vario_antrieb_aus_dem_foerderer():
|
||||
assert anlage.binding("TRO03", "drive", "senJam").status == "offen"
|
||||
|
||||
|
||||
def test_zwei_antriebs_tros_auf_einem_foerderer_sind_widerspruch():
|
||||
"""Jeder Vario-TRO hat seinen eigenen Motor; die Mehrheit je Foerderer
|
||||
kennt nur einen - nicht beiden denselben Motor geben."""
|
||||
anlage = run(doc=layout(second_vario=True))
|
||||
for tro in ("TRO03", "TRO06"):
|
||||
motor = anlage.binding(tro, "drive", "motor")
|
||||
assert (motor.bmk, motor.status) == ("MA0210", "widerspruch")
|
||||
assert "TRO03, TRO06" in motor.note and "von Hand" in motor.note
|
||||
assert anlage.binding(tro, "drive", "motorProtection").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."""
|
||||
hw = hardware()
|
||||
hw.layout_coordinates = True
|
||||
anlage = run(hw=hw, transform=None, tmp_path=tmp_path)
|
||||
reg = anlage.registration
|
||||
assert (reg.dx, reg.dy, reg.rotation, reg.source) == (0.0, 0.0, 0.0, "zeichnung")
|
||||
assert not list(tmp_path.iterdir())
|
||||
assert anlage.binding("TRO01", "separator1", "sensor").bmk == "BG0101"
|
||||
|
||||
|
||||
def test_dematic_regeln_gleiche_nummer():
|
||||
rules, _ = load_rules(RULES.parent / "e_planning_dematic.ini")
|
||||
by_role = {r.role: r for r in rules}
|
||||
assert by_role["Separator.stopper"].number_from == "Separator.sensor"
|
||||
assert by_role["Separator.stopper"].number_offset == 0
|
||||
assert by_role["Ausschleus.valve"].number_offset == 0
|
||||
|
||||
|
||||
def test_foerderer_mit_zu_wenig_stimmen_bleibt_offen():
|
||||
hw = hardware()
|
||||
hw.devices = [d for d in hw.devices if d.bmk != "BG0802"]
|
||||
|
||||
@@ -120,6 +120,20 @@ def test_vario_antrieb_ueber_den_foerderer():
|
||||
assert tro.vario_sensors["stSenFinger"].todo
|
||||
|
||||
|
||||
def test_widerspruch_im_antrieb_erbt_nicht_den_foerderermotor():
|
||||
"""Zwei Vario-TROs auf einem Foerderer: merge_hw_layout fuehrt den Antrieb
|
||||
als Widerspruch - dann nicht doch den Motor des Foerderers einsetzen."""
|
||||
doc = anlage()
|
||||
doc.bindings += [
|
||||
_bind("TRO03", "drive", "motor", "MA0210", "cOutMA0210", status="widerspruch"),
|
||||
_bind("TRO03", "drive", "motorProtection", "FC0210", "cInFC0210",
|
||||
status="widerspruch"),
|
||||
]
|
||||
tro = _by_id(_kontext(doc), "TRO03")
|
||||
assert (tro.vario_motor.value, tro.vario_motor.todo) == ("MA____", True)
|
||||
assert tro.motor_protection.todo
|
||||
|
||||
|
||||
def test_ohne_hardwareplanung_alles_offen():
|
||||
doc = anlage()
|
||||
ctx = build_context(doc.layout, "anlage_tro.json")
|
||||
|
||||
Reference in New Issue
Block a user