[FIX] Materialfluss: Kreisel als geschlossener Umlauf
Jeder Kreisel bekam nur EINE Bahnkante aus der Drehrichtung (UZS R->L, GUZ L->R). Die Gegenbahn hatte damit keinen Abgang bzw. Zugang, und der Sackgassen-Check meldete an fast jedem Kreisel "Sackgasse"/"nicht erreichbar", obwohl die Anlage durchlaufbar ist. - build_graph: immer L->R UND R->L; die Gegenkante traegt CIRCLE_RETURN_NOTE und wird in render_dot nicht gezeichnet - Sackgassen-Pruefung ersetzt durch Erreichbarkeit im Gesamtgraphen (von Beladung erreichbar / Entladung erreichbar); unbestimmte Kanten zaehlen in beide Richtungen - Weiche: Merkmal "Beruehrung" (Kreis/Kreis bzw. Tangente) hat Vorrang vor der Geometrie, die am Kappenuebergang per Rundung entschied - Test test_kreisel_umlauf_graph.py mit Fixture test_system_export.csv TRO-Graph (nach Hosts) fuer dematic/hundm_05/mubea/showroom unveraendert, HundM05 komplett unveraendert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
550bdeb464
commit
c4122b4497
@@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests fuer den Kreisel-Umlauf im Materialflussgraphen (lib/material_flow.py).
|
||||
|
||||
Ein Kreisel ist ein geschlossener Umlauf: das Ende von L laeuft in den Anfang
|
||||
von R und das Ende von R in den Anfang von L. Frueher bekam jeder Kreisel nur
|
||||
EINE Bahnkante (aus der Drehrichtung) - jede Bahn ohne Gegenkante galt dann
|
||||
als Sackgasse bzw. unerreichbar, obwohl die Anlage vollstaendig durchlaufbar
|
||||
ist. Fixture: Testanlage test_system (dxfmakros results/export.csv, 9 Kreisel,
|
||||
8 Kreisel-Kreisel-Weichen, 1 Beladung, 1 Entladung).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from material_flow import (
|
||||
CIRCLE_RETURN_NOTE,
|
||||
EDGE_CIRCLE,
|
||||
EDGE_FLOW,
|
||||
EDGE_UNRESOLVED,
|
||||
Edge,
|
||||
Element,
|
||||
Node,
|
||||
_reachability_warnings,
|
||||
build_graph,
|
||||
read_elements,
|
||||
)
|
||||
|
||||
FIXTURE = Path(__file__).parent / "fixtures" / "test_system_export.csv"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def elements() -> list[Element]:
|
||||
return read_elements(FIXTURE, [])
|
||||
|
||||
|
||||
def _graph(elements: list[Element]):
|
||||
warnings: list[str] = []
|
||||
return build_graph(elements, FIXTURE.name, warnings), warnings
|
||||
|
||||
|
||||
def test_jeder_kreisel_hat_beide_bahnkanten(elements):
|
||||
graph, _ = _graph(elements)
|
||||
for el in (e for e in elements if e.is_circle):
|
||||
left, right = f"{el.teile_id}-L", f"{el.teile_id}-R"
|
||||
circle = [e for e in graph.edges if e.kind == EDGE_CIRCLE
|
||||
and {e.src, e.dst} == {left, right}]
|
||||
assert {(e.src, e.dst) for e in circle} == {(left, right), (right, left)}
|
||||
# genau eine Hauptkante mit dem Drehrichtungs-Kuerzel
|
||||
assert sorted(e.note for e in circle) == sorted([el.rotation, CIRCLE_RETURN_NOTE])
|
||||
|
||||
|
||||
def test_keine_sackgassen_in_durchlaufbarer_anlage(elements):
|
||||
_, warnings = _graph(elements)
|
||||
bahn = [w for w in warnings if w.startswith("Bahn ")]
|
||||
assert bahn == []
|
||||
|
||||
|
||||
def test_kreis_kreis_weiche_ist_gerichtet(elements):
|
||||
"""Weiche 0006<->0005 ist "Kreis/Kreis" - liegt genau auf dem Uebergang
|
||||
Endkappe/Gerade, die Geometrie allein entschied dort per Rundung auf
|
||||
"Laengsseite, unbestimmt". Das Merkmal "Beruehrung" hat Vorrang."""
|
||||
graph, _ = _graph(elements)
|
||||
weiche = [e for e in graph.edges if e.note == "Weiche"
|
||||
and {e.src.split("-")[0], e.dst.split("-")[0]} == {"0005", "0006"}]
|
||||
assert weiche
|
||||
assert all(e.kind == EDGE_FLOW for e in weiche)
|
||||
|
||||
|
||||
def test_ohne_weichen_meldet_unerreichbare_bahnen(elements):
|
||||
"""Gegenprobe: ohne die Kreisel-Kreisel-Weichen (und ohne die rohen
|
||||
Kreisel-Nachbarn, die sonst als unbestimmte Kanten verbinden) ist nur der
|
||||
Kreisel der Beladung erreichbar - die Pruefung muss das melden."""
|
||||
ohne = [replace(e, neighbours=[]) if e.is_circle else e
|
||||
for e in elements if e.kind != "Weiche"]
|
||||
_, warnings = _graph(ohne)
|
||||
assert any("von keiner Beladung" in w for w in warnings)
|
||||
assert any("keine Entladung" in w for w in warnings)
|
||||
|
||||
|
||||
def _el(teile_id: str, kind: str) -> Element:
|
||||
return Element(
|
||||
number=teile_id, kind=kind, teile_art=kind, teile_id=teile_id,
|
||||
name=f"{kind} {teile_id}", plan_square="", neighbours=[], track_ids=[],
|
||||
merkmale={}, row=0, position=(0.0, 0.0, 0.0), bbox=None,
|
||||
)
|
||||
|
||||
|
||||
def test_unbestimmte_kante_in_beide_richtungen_begehbar():
|
||||
"""Eine Weiche mit unbestimmter Richtung (dir=both) darf nicht nur in
|
||||
src->dst-Richtung zaehlen."""
|
||||
k1, k2 = _el("0001", "Kreisel"), _el("0002", "Kreisel")
|
||||
nodes = {
|
||||
"0001-L": Node("0001-L", "Kreisel", k1, lane="L"),
|
||||
"0001-R": Node("0001-R", "Kreisel", k1, lane="R"),
|
||||
"0002-L": Node("0002-L", "Kreisel", k2, lane="L"),
|
||||
"0002-R": Node("0002-R", "Kreisel", k2, lane="R"),
|
||||
"B": Node("B", "Beladung", _el("B", "Beladung")),
|
||||
"E": Node("E", "Entladung", _el("E", "Entladung")),
|
||||
}
|
||||
edges = [
|
||||
Edge("0001-L", "0001-R", EDGE_CIRCLE, "GUZ"),
|
||||
Edge("0001-R", "0001-L", EDGE_CIRCLE, CIRCLE_RETURN_NOTE),
|
||||
Edge("0002-L", "0002-R", EDGE_CIRCLE, "GUZ"),
|
||||
Edge("0002-R", "0002-L", EDGE_CIRCLE, CIRCLE_RETURN_NOTE),
|
||||
# Beladung an Kreisel 2, Entladung an Kreisel 1, Weiche zeigt "falsch herum"
|
||||
Edge("B", "0002-L", EDGE_FLOW),
|
||||
Edge("0001-L", "E", EDGE_FLOW),
|
||||
Edge("0001-R", "0002-R", EDGE_UNRESOLVED, "Weiche"),
|
||||
]
|
||||
assert _reachability_warnings(nodes, edges) == []
|
||||
# gerichtet waere Kreisel 1 von der Beladung aus nicht erreichbar
|
||||
edges[-1] = Edge("0001-R", "0002-R", EDGE_FLOW, "Weiche")
|
||||
assert any("0001-L" in w and "von keiner Beladung" in w
|
||||
for w in _reachability_warnings(nodes, edges))
|
||||
Reference in New Issue
Block a user