Take the conveying direction from the element's own height change
The new export fills Hoehe_Von_mm / Hoehe_Bis_mm, so the direction of a transport
element no longer has to be guessed from its neighbours. drive_uphill() now
decides in this order:
1. Hoehe_Von_mm -> Hoehe_Bis_mm: the rise of the element itself. Only the sign
is read, since the values are absolute on a Gefaellestrecke (1941 -> 1466)
but relative on a Strecke (0 -> 474).
2. Antriebfahrtrichtung "Auf" (up) or "Ab" (down).
3. A Gefaellestrecke without either runs downhill by definition.
Previously a Gefaellestrecke was assumed downhill outright and everything else
depended on Antriebfahrtrichtung alone; now both are backed by the height data
and the fallback order is explicit.
The node label states what was read and what follows from it, e.g.
"0 -> 474 mm (+474) = aufwaerts", so the arrow can be checked against the export
without opening the CSV.
Adds a plausibility check for the case where Antriebfahrtrichtung and the height
change disagree. The height wins and the conflict is reported, rather than one of
the two silently deciding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+89
-13
@@ -168,6 +168,30 @@ class Element:
|
|||||||
"""Drehrichtung eines Kreisels: UZS (im Uhrzeigersinn) oder GUZ."""
|
"""Drehrichtung eines Kreisels: UZS (im Uhrzeigersinn) oder GUZ."""
|
||||||
return str(_merkmal(self.merkmale, "Drehrichtung") or "").strip().upper()
|
return str(_merkmal(self.merkmale, "Drehrichtung") or "").strip().upper()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height_from(self) -> float | None:
|
||||||
|
"""Hoehe am Anfang des Objekts in mm (Merkmal Hoehe_Von_mm)."""
|
||||||
|
return _as_float(_merkmal(self.merkmale, "Hoehe_Von_mm", "Höhe_Von_mm"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height_to(self) -> float | None:
|
||||||
|
"""Hoehe am Ende des Objekts in mm (Merkmal Hoehe_Bis_mm)."""
|
||||||
|
return _as_float(_merkmal(self.merkmale, "Hoehe_Bis_mm", "Höhe_Bis_mm"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rise(self) -> float | None:
|
||||||
|
"""
|
||||||
|
Hoehenaenderung in Foerderrichtung in mm.
|
||||||
|
|
||||||
|
Positiv = das Objekt foerdert nach oben, negativ = nach unten. Die Werte
|
||||||
|
koennen absolut sein (Gefaellestrecke: 1941 -> 1466) oder relativ zum
|
||||||
|
Objekt (Strecke: 0 -> 474); nur das Vorzeichen wird ausgewertet.
|
||||||
|
"""
|
||||||
|
start, end = self.height_from, self.height_to
|
||||||
|
if start is None or end is None:
|
||||||
|
return None
|
||||||
|
return end - start
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assignment(self) -> str:
|
def assignment(self) -> str:
|
||||||
return str(_merkmal(self.merkmale, "Zuordnung") or "").strip()
|
return str(_merkmal(self.merkmale, "Zuordnung") or "").strip()
|
||||||
@@ -461,24 +485,52 @@ def _endpoints(
|
|||||||
return [f"{base}-{LANE_LEFT}", f"{base}-{LANE_RIGHT}"]
|
return [f"{base}-{LANE_LEFT}", f"{base}-{LANE_RIGHT}"]
|
||||||
|
|
||||||
|
|
||||||
def _flow_uphill(element: Element, warnings: list[str]) -> bool | None:
|
def drive_uphill(element: Element) -> tuple[bool | None, str]:
|
||||||
"""
|
"""
|
||||||
|
Foerdert das Objekt nach oben?
|
||||||
|
|
||||||
True = Fluss laeuft zum hoechsten Nachbarn hin (bergauf)
|
True = Fluss laeuft zum hoechsten Nachbarn hin (bergauf)
|
||||||
False = Fluss laeuft vom hoechsten Nachbarn weg (bergab)
|
False = Fluss laeuft vom hoechsten Nachbarn weg (bergab)
|
||||||
None = nicht bestimmbar
|
None = nicht bestimmbar
|
||||||
|
|
||||||
|
Bewertet wird in dieser Reihenfolge:
|
||||||
|
1. Hoehe_Von_mm / Hoehe_Bis_mm - die Hoehenaenderung des Objekts selbst
|
||||||
|
2. Antriebfahrtrichtung "Auf" (nach oben) bzw. "Ab" (nach unten)
|
||||||
|
3. Gefaellestrecke ohne Angaben laeuft per Definition bergab
|
||||||
|
|
||||||
|
Der zweite Rueckgabewert nennt die verwendete Quelle fuer Bericht und
|
||||||
|
Zeichnung.
|
||||||
"""
|
"""
|
||||||
|
rise = element.rise
|
||||||
|
if rise is not None and rise != 0:
|
||||||
|
direction = "Auf" if rise > 0 else "Ab"
|
||||||
|
return rise > 0, (
|
||||||
|
f"Hoehe_Von {element.height_from:.0f} mm -> Hoehe_Bis "
|
||||||
|
f"{element.height_to:.0f} mm ({direction}, {rise:+.0f} mm)"
|
||||||
|
)
|
||||||
|
|
||||||
|
drive = element.drive_dir.lower()
|
||||||
|
if drive.startswith("auf"):
|
||||||
|
return True, f"Antriebfahrtrichtung '{element.drive_dir}' (nach oben)"
|
||||||
|
if drive.startswith("ab"):
|
||||||
|
return False, f"Antriebfahrtrichtung '{element.drive_dir}' (nach unten)"
|
||||||
|
|
||||||
if element.kind == "Gefaellestrecke":
|
if element.kind == "Gefaellestrecke":
|
||||||
return False
|
return False, "Gefaellestrecke ohne Hoehenangabe - laeuft bergab"
|
||||||
direction = element.drive_dir.lower()
|
|
||||||
if direction.startswith("auf"):
|
return None, (
|
||||||
return True
|
f"weder Hoehe_Von/Bis noch Antriebfahrtrichtung verwertbar "
|
||||||
if direction.startswith("ab"):
|
f"(Antrieb '{element.drive_dir}', Hoehen "
|
||||||
return False
|
f"{element.height_from}/{element.height_to})"
|
||||||
warnings.append(
|
|
||||||
f"{element.describe()}: Antriebfahrtrichtung fehlt oder unbekannt "
|
|
||||||
f"('{element.drive_dir}') - Richtung nicht bestimmbar"
|
|
||||||
)
|
)
|
||||||
return None
|
|
||||||
|
|
||||||
|
def _flow_uphill(element: Element, warnings: list[str]) -> bool | None:
|
||||||
|
"""Wie drive_uphill, meldet aber unbestimmbare Faelle als Warnung."""
|
||||||
|
uphill, reason = drive_uphill(element)
|
||||||
|
if uphill is None:
|
||||||
|
warnings.append(f"{element.describe()}: {reason} - Richtung nicht bestimmbar")
|
||||||
|
return uphill
|
||||||
|
|
||||||
|
|
||||||
def _orient(
|
def _orient(
|
||||||
@@ -645,8 +697,16 @@ def _node_label(node: Node) -> str:
|
|||||||
if height is not None:
|
if height is not None:
|
||||||
lines.append(f"h = {height:.3f} m")
|
lines.append(f"h = {height:.3f} m")
|
||||||
|
|
||||||
if element.kind == "Strecke" and element.drive_dir:
|
if element.is_transport:
|
||||||
lines.append(f"Antrieb: {element.drive_dir}")
|
uphill = drive_uphill(element)[0]
|
||||||
|
arrow = {True: "aufwaerts", False: "abwaerts", None: "Richtung offen"}[uphill]
|
||||||
|
if element.rise is not None:
|
||||||
|
lines.append(f"{element.height_from:.0f} -> {element.height_to:.0f} mm "
|
||||||
|
f"({element.rise:+.0f}) = {arrow}")
|
||||||
|
elif element.drive_dir:
|
||||||
|
lines.append(f"Antrieb: {element.drive_dir} = {arrow}")
|
||||||
|
else:
|
||||||
|
lines.append(arrow)
|
||||||
|
|
||||||
for kind in DEVICE_KINDS:
|
for kind in DEVICE_KINDS:
|
||||||
ids = node.devices.get(kind)
|
ids = node.devices.get(kind)
|
||||||
@@ -818,6 +878,22 @@ def check_elements(graph: Graph, elements: list[Element]) -> list[str]:
|
|||||||
for kind, ids in node.devices.items():
|
for kind, ids in node.devices.items():
|
||||||
counted.setdefault(node_id, {})[kind] = len(ids)
|
counted.setdefault(node_id, {})[kind] = len(ids)
|
||||||
|
|
||||||
|
# Widerspruch zwischen Hoehenangabe und Antriebfahrtrichtung
|
||||||
|
for element in elements:
|
||||||
|
if not element.is_transport:
|
||||||
|
continue
|
||||||
|
rise, drive = element.rise, element.drive_dir.lower()
|
||||||
|
if rise is None or rise == 0 or not drive:
|
||||||
|
continue
|
||||||
|
stated_up = drive.startswith("auf")
|
||||||
|
if stated_up != (rise > 0):
|
||||||
|
findings.append(
|
||||||
|
f"{element.kind} {element.teile_id} '{element.name}': "
|
||||||
|
f"Antriebfahrtrichtung '{element.drive_dir}' passt nicht zu "
|
||||||
|
f"Hoehe_Von {element.height_from:.0f} -> Hoehe_Bis "
|
||||||
|
f"{element.height_to:.0f} mm ({rise:+.0f}) - die Hoehenangabe gilt"
|
||||||
|
)
|
||||||
|
|
||||||
for element in elements:
|
for element in elements:
|
||||||
if not (element.is_transport or element.is_circle):
|
if not (element.is_transport or element.is_circle):
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user