Weichenknoten zwischen Kreiseln, Track-Mouseover, Parameter in cfg/parameter.ini
TRO-Typen an Kreisel-Beruehrpunkten (lib/kreisel_junctions.py, derive_tros Schritt 3c): jeder Kreisel als Stadion-Form mit Laufrichtung; jeder gleichlaufende Beruehrpunkt ist eine Weiche (exportierte ILS Weiche oder abgeleitet W<a>_<b>), Beruehrpunkte derselben Umlenkung bilden einen Knoten. Ein Separator steuert ihn, wenn er in Laufrichtung unmittelbar davor liegt (keine andere Weiche dazwischen). Typ = Separatoren x Weichen. export.csv: 1Sep1Swi (9/1, 9/8, 8/7), 2Sep1Swi (7/2), 2Sep2Swi (2/3/4), 3Sep2Swi (4/5/6). Neuer Katalogtyp 3Sep2Swi (nicht aus SCL belegt). Gegenlaeufige Beruehrpunkte (Mubea, Dematic) bleiben bei der alten Regel; Mubea/HundM05/Dematic unveraendert, Showroom 0018+0019 jetzt 2Sep1Swi. material_flow --track: Mouseover je Knoten mit Zugehoerigkeit zu Kreisel/VF/GF (Zuordnung, TrackIds-Kette, Lage laut Geometrie), Weiche mit beiden Kreiseln, Be-/Entladung mit ihrem Kreisel; Widersprueche mit "!". Zahlenwerte (Toleranzen, Schwellen, Massstaebe, Groessen) aus allen lib-Modulen nach cfg/parameter.ini (lib/parameter.py, Abschnitt je Modul, Ueberschreiben je Anlage ueber %SKEL_CFG%/parameter.ini). Ausgaben aller Anlagen bytegleich. tro_flow.py: Docstrings mit Parametern/Rueckgabe, fehlende Typangaben. Geprueft: 90 Tests gruen; tro_flow/material_flow/scl_skeleton auf 5 CSVs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0c69faf3db
commit
550bdeb464
+415
-59
@@ -78,6 +78,49 @@ Hinweis ausgegeben.
|
||||
Zusaetzlich: hat ein Host 2 bzw. 3 abgehende Flusskanten, wird 1Sep zu
|
||||
1Sep1Swi bzw. 1Sep2Swi aufgewertet (Weiche).
|
||||
|
||||
Wo im Code der Typ entsteht
|
||||
---------------------------
|
||||
Einstieg ist analyse_tros(); den TRO-Typ (DerivedTro.type_name) setzt aber
|
||||
ausschliesslich derive_tros(), Schritt fuer Schritt:
|
||||
|
||||
Schritt Bauteil-Muster im CSV -> TRO-Typ
|
||||
-----------------------------------------------------------------------
|
||||
1 >= 2 Gefaellestrecken mit Separator, PinStore_Auto
|
||||
gemeinsamer Zu-/Ablauf (_group_lanes,
|
||||
_speicher_bloecke)
|
||||
1 einzelne Gefaellestrecke mit Separator 1Sep
|
||||
2 Separator in der TrackIds-Kette einer Vario / 1Sep
|
||||
Strecke (alternierendes Muster, (abwechselnd)
|
||||
_incoming_drop)
|
||||
2 Separator mit Zuordnung auf eine Strecke, 1Sep
|
||||
aber nicht in deren TrackIds-Kette
|
||||
3 Separator auf einer Kreisel-Bahn 1Sep
|
||||
3b Separator vor einem Ausschleuselement 1Sep1Swi
|
||||
(_find_switch_separators, _weichen_*)
|
||||
3b' derselbe Separator vor einem zweiten 1Sep2Swi
|
||||
Ausschleuselement ohne Separator dazwischen
|
||||
3c Weichenknoten zwischen Kreiseln: n Separa- 1Sep1Swi, 2Sep1Swi,
|
||||
toren in Laufrichtung unmittelbar vor m 2Sep2Swi, 3Sep2Swi
|
||||
Beruehrpunkten derselben Umlenkung
|
||||
(_kreisel_knoten, lib/kreisel_junctions.py)
|
||||
3c' Separator an einer ILS Weiche, die kein 1Sep1Swi
|
||||
Weichenknoten aus 3c ist
|
||||
(_find_weiche_separators)
|
||||
3d 3b-TRO und 3c'-TRO direkt hintereinander 2Sep2Swi
|
||||
in der TrackIds-Kette eines Kreisels
|
||||
4 1Sep mit 2 bzw. 3 abgehenden Flusskanten 1Sep1Swi / 1Sep2Swi
|
||||
|
||||
Alle anderen Funktionen (link_tros, locate_tros, derive_boundaries, ...)
|
||||
aendern den Typ nicht mehr. cfg/tro_overrides.ini wird hier seit
|
||||
2026-10-05 nicht mehr angewendet (siehe tro_overrides.py) - ein falsch
|
||||
erkannter Typ wird in derive_tros() korrigiert, nicht per INI.
|
||||
|
||||
Nicht abgeleitet werden bisher: 1Sep_SSCC (nur Hinweis, Schritt 6),
|
||||
EmptyCarrBuffer und LoadingBoom. Schritt 3c braucht eine stimmige
|
||||
Drehrichtung beider Kreisel: laufen sie am Beruehrpunkt gegeneinander
|
||||
(Mubea, Dematic - vermutlich Exportfehler), bleibt es fuer diese ILS Weiche
|
||||
bei 3c' und 3d.
|
||||
|
||||
Typen, FB-Bausteine, Bauteile und Farben kommen ausschliesslich aus
|
||||
lib/tro_catalog.py.
|
||||
|
||||
@@ -108,6 +151,7 @@ import csv
|
||||
import json
|
||||
import math
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
@@ -123,6 +167,7 @@ from material_flow import (
|
||||
UNASSIGNED,
|
||||
Element,
|
||||
Graph,
|
||||
Node,
|
||||
_as_float,
|
||||
_dot_escape,
|
||||
_dot_label,
|
||||
@@ -133,7 +178,9 @@ from material_flow import (
|
||||
read_elements,
|
||||
resolve_input,
|
||||
)
|
||||
from kreisel_junctions import Junction, find_junctions
|
||||
from model import Analysis, DerivedTro, TroDocument
|
||||
from parameter import get_float, get_int
|
||||
from tro_ids import (
|
||||
DEFAULT_TOLERANCE,
|
||||
TroIds,
|
||||
@@ -159,42 +206,19 @@ from tro_catalog import (
|
||||
# Konstanten
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Ab dieser Anzahl zusammenhaengender Schwerkraftlinien (Gefaellestrecke mit je
|
||||
# einem Separator) wird ein Speicherblock (PinStore_Auto) angenommen.
|
||||
MIN_STORE_LANES = 2
|
||||
|
||||
# Mindest-Hoehenverlust (mm) auf dem der Reihe nach vorangehenden TrackIds-
|
||||
# Segment, damit ein Separator auf einer "Strecke" trotzdem als 1Sep statt
|
||||
# Vario gilt (siehe _incoming_drop). Muss nur "horizontal" gefuehrte, aber
|
||||
# tatsaechlich Gefaelle-Segmente erwischen (HundM05 Separator 0035: -104mm auf
|
||||
# 3,8m), nicht das Rauschen eines echten flachen VF-Laufs (HundM05 Separator
|
||||
# 0036: +26mm auf 0,5m). 30mm liegt bewusst weit unter dem echten Befund und
|
||||
# weit ueber dem Rauschen.
|
||||
GEFAELLE_DROP_MM = 30.0
|
||||
# Zahlenwerte aus cfg/parameter.ini, Abschnitt [tro_flow] - Bedeutung und
|
||||
# Einheit stehen dort (lib/parameter.py).
|
||||
_P = "tro_flow"
|
||||
MIN_STORE_LANES = get_int(_P, "min_store_lanes")
|
||||
GEFAELLE_DROP_MM = get_float(_P, "gefaelle_drop_mm")
|
||||
COORD_SCALE = get_float(_P, "coord_scale")
|
||||
COORD_PFEIL_LAENGE = get_int(_P, "coord_pfeil_laenge")
|
||||
COORD_MAX_LUECKE = get_int(_P, "coord_max_luecke")
|
||||
LEGEND_MARGIN = get_int(_P, "legend_margin")
|
||||
LEGEND_DROP = get_int(_P, "legend_drop")
|
||||
|
||||
GENERATED_MARKER = "<!-- erzeugt von lib/tro_flow.py -->"
|
||||
|
||||
# Zeichnungsmassstab fuer --use-cords: Graphviz rechnet in Punkt, die Anlage in
|
||||
# Millimeter. Seit die Symbole eine feste Groesse haben (COORD_SYMBOL_SIZE)
|
||||
# statt mit ihrem Label zu wachsen, reichen 0.1 pt/mm: ~11 x 20 m Anlage ergeben
|
||||
# etwa 1100 x 2000 pt - bildschirmfuellend lesbar. Vorher 0.25 (riesige
|
||||
# Zeichnung, in der Symbole und Schrift verschwanden; Review 2026-10-01).
|
||||
COORD_SCALE = 0.1
|
||||
|
||||
# Zu-/Ablaufpfeil einer Station in der lagerichtigen Zeichnung: feste Laenge
|
||||
# (mm) ab dem angehaengten TRO (siehe _station_positionen).
|
||||
COORD_PFEIL_LAENGE = 1300
|
||||
|
||||
# Groesste Luecke (mm) zwischen zwei benachbarten Knotenkoordinaten je Achse in
|
||||
# der lagerichtigen Zeichnung. Groessere Leerstrecken (lange Foerderer ohne
|
||||
# TRO) werden auf diesen Wert gekuerzt - Reihenfolge und Nachbarschaft der
|
||||
# Knoten bleiben erhalten, nur der Leerraum verschwindet (Review 2026-10-01:
|
||||
# "zu verstreut, zu viel Weissraum"). --true-scale schaltet das ab.
|
||||
COORD_MAX_LUECKE = 1500
|
||||
|
||||
# Abstand der Legende zum Anlagenrand in mm
|
||||
LEGEND_MARGIN = 3000
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TRO-Ermittlung
|
||||
@@ -207,6 +231,17 @@ def _group_lanes(lanes: list[str], graph: Graph) -> list[list[str]]:
|
||||
|
||||
Linien, die sich einen Zu- oder Ablaufknoten teilen, gehoeren zum selben
|
||||
Speicher. So entstehen bei zwei getrennten Speichern auch zwei TROs.
|
||||
|
||||
Parameter:
|
||||
lanes: Knoten-Ids der Gefaellestrecken, die einen Separator tragen
|
||||
(derive_tros Schritt 1).
|
||||
graph: Materialflussgraph aus material_flow.build_graph; nur
|
||||
graph.edges wird gelesen (Nachbarschaft der Linien).
|
||||
|
||||
Rueckgabe:
|
||||
Liste von Gruppen, jede eine sortierte Liste von Linien-Ids; die
|
||||
Gruppen sind nach ihrer ersten Linie sortiert. Eine Gruppe mit
|
||||
>= MIN_STORE_LANES Linien wird in derive_tros zu PinStore_Auto.
|
||||
"""
|
||||
adjacency: dict[str, set[str]] = {}
|
||||
for lane in lanes:
|
||||
@@ -246,6 +281,18 @@ def _kreisel_bahn(sep: Element, elements: list[Element], graph: Graph) -> str |
|
||||
der Achsrichtung L, rechts R - dieselbe Konvention wie die Zuordnungen,
|
||||
die der Export richtig setzt (0038 unterhalb von Kreisel2 mit Drehung 180
|
||||
-> L, 0047 oberhalb von Kreisel3 -> R).
|
||||
|
||||
Parameter:
|
||||
sep: der Separator (Element.kind "Separator").
|
||||
elements: alle Elemente des CSV-Exports; durchsucht werden die
|
||||
Kreisel und ihre track_ids.
|
||||
graph: Materialflussgraph; die gefundene Bahn muss dort ein Knoten
|
||||
sein (z. B. "0002-L").
|
||||
|
||||
Rueckgabe:
|
||||
Knoten-Id der Bahn ("<Kreisel-Id>-L" bzw. "-R") oder None, wenn der
|
||||
Separator in keiner Kreiselkette steht, Position bzw. "Drehung"
|
||||
fehlen oder die Bahn kein Graphknoten ist.
|
||||
"""
|
||||
for k in elements:
|
||||
if k.kind != "Kreisel" or sep.teile_id not in k.track_ids:
|
||||
@@ -278,6 +325,16 @@ def _weichen_nach_umlauf(
|
||||
Schluss mit dem Ausschleuselement 0057 von VF_1 "benachbart" (TRO03 als
|
||||
Weiche in VF_1), 0046 liegt hinter dem letzten Ausschleuselement von
|
||||
Speicherblock C. Beide sind gewoehnliche Kreisel-Separatoren (Schritt 3).
|
||||
|
||||
Parameter:
|
||||
switch_hits: Ergebnis von _find_switch_separators
|
||||
(Separator-Id -> (Ausschleuselement, Abstand mm)).
|
||||
elements: alle Elemente des CSV-Exports (Kreisel mit track_ids).
|
||||
findings: Fundliste; je verworfenem Paar wird ein Eintrag angehaengt.
|
||||
|
||||
Rueckgabe:
|
||||
Gefiltertes switch_hits. Paare ausserhalb einer Kreiselkette bleiben
|
||||
ungeprueft erhalten.
|
||||
"""
|
||||
by_id = {e.teile_id: e for e in elements}
|
||||
kreisel = [e for e in elements if e.kind == "Kreisel" and e.track_ids]
|
||||
@@ -324,6 +381,18 @@ def _weichen_aus_umlauf(
|
||||
Ausschleuselemente einer Gefaellestrecke bleiben aussen vor: das sind
|
||||
Speicherlinien, ihr Steuer-Separator entsteht in _speicher_bloecke nur
|
||||
aus der CSV-Nachbarschaft (Mubea 0044 bleibt eigener 1Sep vor dem Block).
|
||||
|
||||
Parameter:
|
||||
switch_hits: bisherige Paare (Separator-Id -> (Ausschleuselement,
|
||||
Abstand mm)), meist aus _weichen_nach_umlauf.
|
||||
kandidaten: Separatoren, die ueberhaupt Weiche sein duerfen (in
|
||||
derive_tros: ohne die eigenen VF-Kettenseparatoren).
|
||||
elements: alle Elemente des CSV-Exports.
|
||||
findings: Fundliste; je ergaenztem Paar wird ein Eintrag angehaengt.
|
||||
|
||||
Rueckgabe:
|
||||
Neues dict: switch_hits plus die ergaenzten Paare. Jedes
|
||||
Ausschleuselement gehoert hoechstens einem Separator.
|
||||
"""
|
||||
by_id = {e.teile_id: e for e in elements}
|
||||
erlaubt = {s.teile_id for s in kandidaten}
|
||||
@@ -381,8 +450,16 @@ def _speicher_bloecke(
|
||||
Mubea 0044 (TRO05) ist durch ein Einschleuselement vom Block getrennt,
|
||||
kein Nachbar eines Ausschleuselements und bleibt deshalb eigener 1Sep.
|
||||
|
||||
Rueckgabe: (Linie -> Block, Steuer-Separator -> Block). Linien ohne
|
||||
Ausschleuselement in einer Kreisel-Kette fehlen in der ersten Zuordnung.
|
||||
Parameter:
|
||||
lanes: Knoten-Ids der Gefaellestrecken mit Separator.
|
||||
elements: alle Elemente des CSV-Exports (Ausschleuselemente mit
|
||||
Merkmal "Strecke"/"Foerderer", Kreisel mit track_ids).
|
||||
switch_hits: Weichen-Separatoren (Separator-Id -> (Ausschleuselement,
|
||||
Abstand mm)), siehe _find_switch_separators.
|
||||
|
||||
Rueckgabe: (Linie -> Block, Steuer-Separator -> Block). Ein Block ist
|
||||
(Kreisel-Id, laufende Nummer ab 1). Linien ohne Ausschleuselement in
|
||||
einer Kreisel-Kette fehlen in der ersten Zuordnung.
|
||||
"""
|
||||
lane_set = set(lanes)
|
||||
by_id = {e.teile_id: e for e in elements}
|
||||
@@ -427,7 +504,17 @@ def _speicher_bloecke(
|
||||
|
||||
|
||||
def _switch_exits(hosts: list[str], graph: Graph) -> list[str]:
|
||||
"""Abgehende Flusskanten eines TRO-Hosts (ohne Kreisel-Umlauf)."""
|
||||
"""Abgehende Flusskanten eines TRO-Hosts (ohne Kreisel-Umlauf).
|
||||
|
||||
Parameter:
|
||||
hosts: Knoten-Ids, die zusammen den TRO tragen (DerivedTro.hosts).
|
||||
graph: Materialflussgraph; gezaehlt werden nur Kanten der Art
|
||||
EDGE_FLOW, die aus hosts hinaus fuehren.
|
||||
|
||||
Rueckgabe:
|
||||
Sortierte, eindeutige Ziel-Knoten-Ids. 2 bzw. 3 Ziele werten einen
|
||||
1Sep in derive_tros Schritt 4 zu 1Sep1Swi bzw. 1Sep2Swi auf.
|
||||
"""
|
||||
return sorted(
|
||||
{
|
||||
edge.dst
|
||||
@@ -438,7 +525,20 @@ def _switch_exits(hosts: list[str], graph: Graph) -> list[str]:
|
||||
|
||||
|
||||
def _default_resolve_host(sep: Element, findings: list[str], graph: Graph) -> str | None:
|
||||
"""Host eines Separators aus der CSV-Spalte 'Zuordnung' (Default-Resolver)."""
|
||||
"""Host eines Separators aus der CSV-Spalte 'Zuordnung' (Default-Resolver).
|
||||
|
||||
Parameter:
|
||||
sep: der Separator; gelesen wird sep.assignment (Merkmal
|
||||
"Zuordnung", z. B. "0002-L" oder die Id einer Strecke).
|
||||
findings: Fundliste; ist die Zuordnung leer oder kein Graphknoten,
|
||||
wird ein Eintrag angehaengt.
|
||||
graph: Materialflussgraph, gegen dessen Knoten geprueft wird.
|
||||
|
||||
Rueckgabe:
|
||||
Knoten-Id des Hosts oder None (Separator bekommt dann keinen TRO).
|
||||
Die Art dieses Knotens (Kreisel/Strecke/Gefaellestrecke) entscheidet
|
||||
in derive_tros, in welchem Schritt der Separator landet.
|
||||
"""
|
||||
host = sep.assignment
|
||||
if host and host != UNASSIGNED and host in graph.nodes:
|
||||
return host
|
||||
@@ -503,6 +603,18 @@ def _find_switch_separators(
|
||||
|
||||
Die Distanz im Rueckgabewert ist nur noch Beiwerk fuer die Doku ("XXXmm
|
||||
von Ausschleuselement YYYY entfernt"), kein Kriterium mehr.
|
||||
|
||||
Parameter:
|
||||
separators: zu pruefende Separatoren (in derive_tros ohne die
|
||||
eigenen VF-Kettenseparatoren).
|
||||
elements: alle Elemente des CSV-Exports (Ausschleuselemente und
|
||||
Kreisel mit track_ids).
|
||||
|
||||
Rueckgabe:
|
||||
Separator-Id -> (Ausschleuselement, Abstand mm); je Separator das
|
||||
erste passende Element aus seinen Nachbarn. Wird in derive_tros
|
||||
Schritt 3b zu 1Sep1Swi (oder zum Steuer-Separator eines
|
||||
PinStore_Auto, siehe _speicher_bloecke).
|
||||
"""
|
||||
transfers = {e.teile_id: e for e in elements if e.kind == "Ausschleus"}
|
||||
kreisel_by_id = {e.teile_id: e for e in elements if e.kind == "Kreisel"}
|
||||
@@ -555,6 +667,22 @@ def _find_weiche_separators(
|
||||
bestaetigt (Review 2026-08-12 und 2026-10-01). Ist nur ein Separator
|
||||
benachbart, gilt dieser. Bleibt es mehrdeutig, wird nichts geraten,
|
||||
sondern ein Finding geschrieben.
|
||||
|
||||
Grenze: einer Weiche wird hoechstens EIN Separator zugeordnet. Weichen
|
||||
mit mehreren Separatoren (2Sep1Swi, 3Sep2Swi, ...) ermittelt vorher
|
||||
_kreisel_knoten aus Geometrie und Laufrichtung; hier kommen nur die
|
||||
ILS Weichen an, die dort keinen Knoten bilden.
|
||||
|
||||
Parameter:
|
||||
separators: zu pruefende Separatoren (in derive_tros ohne die
|
||||
Ausschleus-Weichen-Separatoren aus switch_hits).
|
||||
elements: alle Elemente des CSV-Exports (ILS Weichen und Scanner).
|
||||
findings: Fundliste; je mehrdeutiger Weiche wird ein Eintrag
|
||||
angehaengt.
|
||||
|
||||
Rueckgabe:
|
||||
Separator-Id -> ILS Weiche (Element.kind "Weiche"). Wird in
|
||||
derive_tros Schritt 3c' zu 1Sep1Swi.
|
||||
"""
|
||||
by_id = {s.teile_id: s for s in separators}
|
||||
mit_scanner = {
|
||||
@@ -578,6 +706,48 @@ def _find_weiche_separators(
|
||||
return result
|
||||
|
||||
|
||||
def _kreisel_knoten(
|
||||
elements: list[Element], findings: list[str], frei: set[str]
|
||||
) -> list[Junction]:
|
||||
"""Weichenknoten zwischen Kreiseln, die einen eigenen TRO bilden.
|
||||
|
||||
Geometrie und Steuerregel siehe lib/kreisel_junctions.py: ein Separator
|
||||
steuert einen Knoten, wenn er auf einem beteiligten Kreisel in
|
||||
Laufrichtung unmittelbar davor liegt (keine andere Weiche dazwischen).
|
||||
|
||||
Parameter:
|
||||
elements: alle Elemente des CSV-Exports.
|
||||
findings: Fundliste; je verworfenem Knoten ein Eintrag.
|
||||
frei: Separator-Ids, die einen Knoten steuern duerfen. Steuert ein
|
||||
anderer Separator (Ausschleus-Weiche aus 3b, eigener VF-
|
||||
Kettenseparator) mit, bleibt der Knoten den bisherigen Regeln
|
||||
(3b, 3c', 3d) ueberlassen.
|
||||
|
||||
Rueckgabe:
|
||||
Die Knoten, deren Typ (j.type_name) im Katalog steht und die nur von
|
||||
freien Separatoren gesteuert werden.
|
||||
"""
|
||||
result: list[Junction] = []
|
||||
for j in find_junctions(elements, findings):
|
||||
weichen = ", ".join(c.weiche_id for c in j.contacts)
|
||||
if not j.separators:
|
||||
if any(not c.exported for c in j.contacts):
|
||||
findings.append(
|
||||
f"Weichenknoten Kreisel {'/'.join(j.kreisel)} ({weichen}): kein "
|
||||
f"Separator in Laufrichtung davor - kein TRO")
|
||||
continue
|
||||
if not set(j.separators) <= frei:
|
||||
continue
|
||||
if get_tro(j.type_name) is None:
|
||||
findings.append(
|
||||
f"Weichenknoten Kreisel {'/'.join(j.kreisel)} ({weichen}): "
|
||||
f"{len(j.separators)} Separatoren und {len(j.contacts)} Weichen - "
|
||||
f"Typ {j.type_name} fehlt im Katalog, kein TRO")
|
||||
continue
|
||||
result.append(j)
|
||||
return result
|
||||
|
||||
|
||||
def _incoming_drop(
|
||||
host: Element, sep_id: str, elements_by_id: dict[str, Element]
|
||||
) -> tuple[str | None, float | None]:
|
||||
@@ -591,6 +761,18 @@ def _incoming_drop(
|
||||
oder fehlt einem der beiden Enden das Merkmal "Hoehe" (z. B. Aus-/
|
||||
Einschleuselemente tragen keins), liefert die Funktion (None, None) - der
|
||||
Aufrufer behandelt das als "kein Gefaelle-Nachweis", bleibt also bei Vario.
|
||||
|
||||
Parameter:
|
||||
host: die Streckengruppe (Element.kind "Strecke") mit track_ids und
|
||||
drive_dir.
|
||||
sep_id: TeileId des Separators in host.track_ids.
|
||||
elements_by_id: TeileId -> Element, fuer die Hoehen der beiden
|
||||
Segmentenden.
|
||||
|
||||
Rueckgabe:
|
||||
(Antriebfahrtrichtung des Segments, Hoehendifferenz in mm; negativ =
|
||||
Gefaelle). "horizontal" mit <= -GEFAELLE_DROP_MM stuft in
|
||||
derive_tros Schritt 2 einen Vario auf 1Sep herab.
|
||||
"""
|
||||
chain = host.track_ids
|
||||
if sep_id not in chain:
|
||||
@@ -677,23 +859,67 @@ def derive_tros(
|
||||
) -> list[DerivedTro]:
|
||||
"""TRO-Liste aus den Separatoren und ihren angeschlossenen Objekten bilden.
|
||||
|
||||
Host eines Separators aus der CSV-Spalte "Zuordnung" (_default_resolve_host),
|
||||
abgehende Wege eines TRO-Hosts aus graph.edges (_switch_exits).
|
||||
**Hier - und nur hier - wird der TRO-Typ bestimmt** (DerivedTro.type_name,
|
||||
Uebersicht im Modul-Docstring "Wo im Code der Typ entsteht").
|
||||
|
||||
sepliste_counts (optional, siehe load_sepliste_counts): Container-Id ->
|
||||
Anzahl 'SEP'-Eintraege aus dem Roh-JSON. Reine Gegenprobe - stimmt die aus
|
||||
der CSV abgeleitete Separatorenzahl eines Strecke/Gefaellestrecke-Hosts
|
||||
nicht mit dieser Zahl ueberein, wird ein Finding angehaengt; die TRO-Daten
|
||||
selbst bleiben unangetastet (CSV bleibt die massgebliche Quelle).
|
||||
Vorbereitung (vor den Schritten):
|
||||
a) Weichen-Separatoren: _find_switch_separators ->
|
||||
_weichen_nach_umlauf -> _weichen_aus_umlauf (Ausschleuselemente),
|
||||
_kreisel_knoten (Weichenknoten zwischen Kreiseln) und
|
||||
_find_weiche_separators (uebrige ILS Weichen). Diese Separatoren
|
||||
bekommen keinen Host aus der Zuordnung.
|
||||
b) Uebrige Separatoren -> Host-Knoten (_default_resolve_host, bei
|
||||
Gefaellestrecken ggf. korrigiert durch _kreisel_bahn).
|
||||
c) Scanner -> Separator ueber das Merkmal "Naechster Separator".
|
||||
|
||||
tro_ids (optional): ID-Datei der Anlage. Ohne sie werden die TROs entlang
|
||||
des Materialflusses neu durchnummeriert; mit ihr behalten wiedergefundene
|
||||
TROs ihre ID (Ergebnis in tro_ids.result).
|
||||
Schritte (setzen type_name):
|
||||
1 Gefaellestrecken: PinStore_Auto (>= MIN_STORE_LANES Linien) bzw.
|
||||
1Sep (einzelne Linie).
|
||||
2 Strecken: je Separator Vario oder 1Sep (alternierendes Muster,
|
||||
Gefaelle-Pruefung ueber _incoming_drop).
|
||||
3 Kreisel-Bahnen: je Separator 1Sep.
|
||||
3b Separator vor Ausschleuselement: 1Sep1Swi.
|
||||
3b' zweites Ausschleuselement desselben Separators: 1Sep2Swi.
|
||||
3c Weichenknoten zwischen Kreiseln (_kreisel_knoten): Typ aus der
|
||||
Zahl der Separatoren unmittelbar davor und der Weichen, z. B.
|
||||
2Sep1Swi, 3Sep2Swi.
|
||||
3c' Separator an einer uebrigen ILS Weiche: 1Sep1Swi.
|
||||
3d 3b- und 3c'-TRO direkt hintereinander am Kreisel: 2Sep2Swi.
|
||||
4 1Sep mit 2/3 abgehenden Flusskanten: 1Sep1Swi/1Sep2Swi.
|
||||
5 Sortieren entlang des Materialflusses, IDs vergeben.
|
||||
6 Hinweis bei 1Sep mit Scanner (evtl. 1Sep_SSCC, Typ bleibt).
|
||||
|
||||
Parameter:
|
||||
graph: Materialflussgraph aus material_flow.build_graph (Knoten =
|
||||
Kreisel-Bahnen "<Id>-L/-R", Strecken, Gefaellestrecken,
|
||||
Stationen; Kanten = Materialfluss).
|
||||
elements: alle Elemente des CSV-Exports (material_flow.read_elements);
|
||||
Separatoren, Scanner, Aus-/Einschleuselemente, ILS Weichen und
|
||||
Kreisel werden daraus gelesen.
|
||||
findings: Fundliste, an die jeder Schritt Hinweise anhaengt (wird
|
||||
veraendert, nicht ersetzt).
|
||||
sepliste_counts: optional, siehe load_sepliste_counts: Container-Id
|
||||
-> Anzahl 'SEP'-Eintraege aus dem Roh-JSON. Reine Gegenprobe -
|
||||
stimmt die aus der CSV abgeleitete Separatorenzahl eines
|
||||
Strecke/Gefaellestrecke-Hosts nicht mit dieser Zahl ueberein,
|
||||
wird ein Finding angehaengt; die TRO-Daten selbst bleiben
|
||||
unangetastet (CSV bleibt die massgebliche Quelle).
|
||||
tro_ids: optional, ID-Datei der Anlage. Ohne sie werden die TROs
|
||||
entlang des Materialflusses neu durchnummeriert; mit ihr behalten
|
||||
wiedergefundene TROs ihre ID (Ergebnis in tro_ids.result).
|
||||
|
||||
Rueckgabe:
|
||||
Die TROs in Materialfluss-Reihenfolge, mit tro_id, type_name, label,
|
||||
hosts, separators, scanners, reason und confidence. Position,
|
||||
Vorgaenger/Nachfolger fuellt erst analyse_tros (locate_tros,
|
||||
link_tros).
|
||||
"""
|
||||
def resolve_host(sep: Element, findings: list[str]) -> str | None:
|
||||
"""Host-Knoten eines Separators (siehe _default_resolve_host)."""
|
||||
return _default_resolve_host(sep, findings, graph)
|
||||
|
||||
def count_exits(hosts: list[str]) -> list[str]:
|
||||
"""Abgehende Flussziele der Hosts (siehe _switch_exits)."""
|
||||
return _switch_exits(hosts, graph)
|
||||
|
||||
separators = sorted(
|
||||
@@ -732,17 +958,26 @@ def derive_tros(
|
||||
_weichen_nach_umlauf(
|
||||
_find_switch_separators(switch_candidates, elements), elements, findings),
|
||||
switch_candidates, elements, findings)
|
||||
# Weichenknoten zwischen Kreiseln aus Geometrie und Laufrichtung (siehe
|
||||
# _kreisel_knoten, Schritt 3c) - ihre Separatoren und Weichen sind damit
|
||||
# vergeben und laufen nicht mehr durch _find_weiche_separators.
|
||||
knoten = _kreisel_knoten(
|
||||
elements, findings, frei={s.teile_id for s in switch_candidates} - set(switch_hits))
|
||||
knoten_seps = {s for j in knoten for s in j.separators}
|
||||
knoten_weichen = {c.weiche_id for j in knoten for c in j.contacts}
|
||||
# Separatoren an einer echten ILS Weiche (siehe _find_weiche_separators) -
|
||||
# wie switch_hits vor der Host-Aufloesung herausgenommen, Schritt 3c.
|
||||
# wie switch_hits vor der Host-Aufloesung herausgenommen, Schritt 3c'.
|
||||
weiche_hits = _find_weiche_separators(
|
||||
[s for s in switch_candidates if s.teile_id not in switch_hits],
|
||||
elements, findings,
|
||||
[s for s in switch_candidates
|
||||
if s.teile_id not in switch_hits and s.teile_id not in knoten_seps],
|
||||
[e for e in elements if e.teile_id not in knoten_weichen], findings,
|
||||
)
|
||||
|
||||
# Separator -> Host-Knoten
|
||||
sep_host: dict[str, str] = {}
|
||||
for sep in separators:
|
||||
if sep.teile_id in switch_hits or sep.teile_id in weiche_hits:
|
||||
if (sep.teile_id in switch_hits or sep.teile_id in weiche_hits
|
||||
or sep.teile_id in knoten_seps):
|
||||
continue
|
||||
host = resolve_host(sep, findings)
|
||||
# Steht er in der Umlaufkette eines Kreisels, zeigt die Zuordnung aber
|
||||
@@ -764,7 +999,8 @@ def derive_tros(
|
||||
sep_scanners: dict[str, list[str]] = {}
|
||||
for scanner in scanners:
|
||||
target = str(_merkmal(scanner.merkmale, "Naechster Separator") or "").strip()
|
||||
if target in sep_host or target in switch_hits or target in weiche_hits:
|
||||
if (target in sep_host or target in switch_hits or target in weiche_hits
|
||||
or target in knoten_seps):
|
||||
sep_scanners.setdefault(target, []).append(scanner.teile_id)
|
||||
mounted = scanner.assignment
|
||||
target_host = sep_host.get(target)
|
||||
@@ -784,9 +1020,11 @@ def derive_tros(
|
||||
)
|
||||
|
||||
def seps_on(hosts: list[str]) -> list[str]:
|
||||
"""Sortierte Separator-Ids, deren Host in hosts liegt."""
|
||||
return sorted(s for s, h in sep_host.items() if h in hosts)
|
||||
|
||||
def scanners_for(seps: list[str]) -> list[str]:
|
||||
"""Sortierte Scanner-Ids, die an einem der Separatoren lesen."""
|
||||
found: list[str] = []
|
||||
for sep in seps:
|
||||
found.extend(sep_scanners.get(sep, []))
|
||||
@@ -1106,7 +1344,35 @@ def derive_tros(
|
||||
f"Barcode-Scanner am Eingang, im Layout fehlt einer vor Separator "
|
||||
f"{tro.separators[0]}")
|
||||
|
||||
# 3c) Separatoren an einer echten ILS Weiche -> 1Sep1Swi (siehe
|
||||
# 3c) Weichenknoten zwischen Kreiseln -> nSepmSwi (siehe _kreisel_knoten):
|
||||
# n = Separatoren, die in Laufrichtung unmittelbar vor dem Knoten liegen,
|
||||
# m = Weichen (Beruehrpunkte) des Knotens. Host je Separator ist die Bahn,
|
||||
# auf der er physisch liegt - nicht die Zuordnung (export.csv 0015:
|
||||
# Zuordnung 0003-L, liegt aber auf Kreisel 0002 Bahn R).
|
||||
for j in knoten:
|
||||
weichen = [c.weiche_id for c in j.contacts]
|
||||
vor = ", ".join(f"{s} ({j.sep_lane[s]})" for s in j.separators)
|
||||
hosts = list(dict.fromkeys(j.sep_lane[s] for s in j.separators
|
||||
if j.sep_lane[s] in graph.nodes))
|
||||
tros.append(DerivedTro(
|
||||
tro_id="",
|
||||
type_name=j.type_name,
|
||||
label=(f"Weichenknoten Kreisel {'/'.join(j.kreisel)}: "
|
||||
f"Weiche{'n' if len(weichen) > 1 else ''} {', '.join(weichen)}"),
|
||||
hosts=hosts or [j.sep_kreisel[s] for s in j.separators],
|
||||
separators=list(j.separators),
|
||||
scanners=scanners_for(j.separators),
|
||||
reason=(f"Separator{'en' if len(j.separators) > 1 else ''} {vor} "
|
||||
f"in Laufrichtung unmittelbar vor "
|
||||
f"{'den Weichen' if len(weichen) > 1 else 'der Weiche'} "
|
||||
f"{', '.join(weichen)} (Beruehrpunkt"
|
||||
f"{'e' if len(weichen) > 1 else ''} der Kreisel "
|
||||
f"{'/'.join(j.kreisel)})"),
|
||||
confidence="hoch" if all(c.exported for c in j.contacts) else "mittel",
|
||||
))
|
||||
|
||||
# 3c') Separatoren an einer ILS Weiche, die kein Weichenknoten aus 3c ist
|
||||
# (z. B. gegenlaeufige Drehrichtung im Export) -> 1Sep1Swi (siehe
|
||||
# _find_weiche_separators). Host wie bei 3b: der aufgeloeste Zuordnung-Host
|
||||
# des Separators, sonst die Weiche selbst.
|
||||
weiche_tro: dict[str, DerivedTro] = {} # Weichen-Id -> 1Sep1Swi
|
||||
@@ -1232,15 +1498,26 @@ def _flow_rank(graph: Graph) -> dict[str, int]:
|
||||
return rank
|
||||
|
||||
|
||||
def _flow_key(graph: Graph, elements_by_id: dict[str, Element]):
|
||||
def _flow_key(
|
||||
graph: Graph, elements_by_id: dict[str, Element]
|
||||
) -> Callable[[DerivedTro], tuple[int, int, str]]:
|
||||
"""Sortierschluessel fuer TROs entlang des Materialflusses.
|
||||
|
||||
Erst der Rang des Host-Knotens, dann die Lage des Separators in der
|
||||
TrackIds-Kette des Hosts (Kreisel: Umlaufrichtung, Strecke: Baureihenfolge).
|
||||
|
||||
Parameter:
|
||||
graph: Materialflussgraph (fuer _flow_rank).
|
||||
elements_by_id: TeileId -> Element, fuer die track_ids des Hosts.
|
||||
|
||||
Rueckgabe:
|
||||
Funktion fuer sorted()/list.sort(key=...), die je TRO
|
||||
(Host-Rang, Index in der TrackIds-Kette, erster Separator) liefert.
|
||||
"""
|
||||
rank = _flow_rank(graph)
|
||||
|
||||
def key(tro: DerivedTro) -> tuple:
|
||||
def key(tro: DerivedTro) -> tuple[int, int, str]:
|
||||
"""(Host-Rang, Index in der TrackIds-Kette, erster Separator)."""
|
||||
host_rank = min((rank.get(h, len(rank)) for h in tro.hosts),
|
||||
default=len(rank))
|
||||
sep = tro.separators[0] if tro.separators else ""
|
||||
@@ -1328,7 +1605,7 @@ def _tro_edge_kind(edge: Edge) -> str:
|
||||
return "normal"
|
||||
|
||||
|
||||
def _bahn_richtung(node) -> tuple[float, float] | None:
|
||||
def _bahn_richtung(node: Node) -> tuple[float, float] | None:
|
||||
"""Foerderrichtung einer Kreisel-Bahn als Einheitsvektor.
|
||||
|
||||
Der Kreisel ist eine geschlossene Schleife: auf der einen Bahn laeuft das
|
||||
@@ -1414,7 +1691,8 @@ def _chain_mit_ungelisteten_separatoren(
|
||||
if dx == 0 and dy == 0:
|
||||
return chain
|
||||
|
||||
def _t(pos) -> float:
|
||||
def _t(pos: tuple[float, float, float]) -> float:
|
||||
"""Projektion von pos auf die Richtung anfang -> ende (unskaliert)."""
|
||||
return (pos[0] - anfang.position[0]) * dx + (pos[1] - anfang.position[1]) * dy
|
||||
|
||||
# Kettenelemente ohne Position behalten ihren Platz: sie erben den Wert des
|
||||
@@ -1507,6 +1785,7 @@ def link_tros(
|
||||
elem_owner.setdefault(sep_id, tro.tro_id)
|
||||
|
||||
def _add_chain_pair(a: str, b: str) -> None:
|
||||
"""Gerichtete Kante TRO a -> TRO b aus der TrackIds-Kette eintragen."""
|
||||
if a == b:
|
||||
return
|
||||
# Keine Sperre fuer Systemgrenzen noetig: sie sind kein Kettenglied
|
||||
@@ -1863,7 +2142,23 @@ def analyse_tros(
|
||||
) -> Analysis:
|
||||
"""TROs ermitteln, verknuepfen, verorten und gegen die Typdefinition pruefen.
|
||||
|
||||
tro_ids: ID-Datei der Anlage (siehe derive_tros); None = neu nummerieren.
|
||||
Gemeinsamer Einstieg fuer tro_flow.py und tro_annotate.py. Der TRO-Typ
|
||||
wird NICHT hier bestimmt, sondern in derive_tros (erster Aufruf unten);
|
||||
danach kommen nur noch Systemgrenzen (derive_boundaries), Koordinaten
|
||||
(locate_tros) und Verbindungen (link_tros) dazu.
|
||||
|
||||
Parameter:
|
||||
graph: Materialflussgraph aus material_flow.build_graph.
|
||||
elements: alle Elemente des CSV-Exports (material_flow.read_elements).
|
||||
sepliste_counts: optional, Separatorenzahl je Container aus dem
|
||||
Roh-JSON (load_sepliste_counts) - nur Gegenprobe.
|
||||
tro_ids: ID-Datei der Anlage (siehe derive_tros); None = neu
|
||||
nummerieren.
|
||||
|
||||
Rueckgabe:
|
||||
Analysis mit tros, boundaries, edges/edge_kinds, unknown_types
|
||||
(Typnamen ohne Katalogeintrag), isolated (TROs ohne Verbindung) und
|
||||
den sortierten findings.
|
||||
"""
|
||||
analysis = Analysis()
|
||||
analysis.tros = derive_tros(graph, elements, analysis.findings,
|
||||
@@ -1892,6 +2187,12 @@ def analyse_tros(
|
||||
|
||||
|
||||
def _tro_label(tro: DerivedTro) -> str:
|
||||
"""Mehrzeiliges DOT-Label eines TRO-Knotens.
|
||||
|
||||
Zeilen: ID und Typ, Bezeichnung, FB-Baustein, Bauteile laut Katalog,
|
||||
Separatoren (ab 7 gekuerzt), Scanner, Koordinate - jeweils nur, wenn
|
||||
vorhanden.
|
||||
"""
|
||||
lines = [f"{tro.tro_id} ({tro.type_name})", tro.label]
|
||||
if tro.fb_block:
|
||||
lines.append(tro.fb_block)
|
||||
@@ -1981,6 +2282,7 @@ def _station_positionen(
|
||||
je_tro.setdefault(anker, []).append(station)
|
||||
|
||||
def _abstand(a: float, b: float) -> float:
|
||||
"""Kleinster Winkelabstand (rad) zweier Richtungen."""
|
||||
d = abs(a - b) % (2 * math.pi)
|
||||
return min(d, 2 * math.pi - d)
|
||||
|
||||
@@ -2060,7 +2362,7 @@ def _legend_pos_rechts(zeichen: dict[str, tuple[float, float]]) -> tuple[float,
|
||||
return (0.0, 0.0)
|
||||
xs = [p[0] for p in zeichen.values()]
|
||||
ys = [p[1] for p in zeichen.values()]
|
||||
return (max(xs) + LEGEND_MARGIN, max(ys) - 1000)
|
||||
return (max(xs) + LEGEND_MARGIN, max(ys) - LEGEND_DROP)
|
||||
|
||||
|
||||
def _zeichen_positionen(
|
||||
@@ -2099,6 +2401,7 @@ STATION_FARBE = {
|
||||
|
||||
|
||||
def _tro_edge_attrs(kind: str | None) -> str:
|
||||
"""DOT-Kantenattribute zur Kantenart (TRO_EDGE_STYLE); None = "normal"."""
|
||||
return TRO_EDGE_STYLE.get(kind or "normal", "")
|
||||
|
||||
|
||||
@@ -2118,6 +2421,7 @@ def render_dot(analysis: Analysis, source: str, use_coords: bool = False,
|
||||
zeichen = _zeichen_positionen(analysis, compact) if use_coords else {}
|
||||
|
||||
def _pos(node: DerivedTro) -> str:
|
||||
"""DOT-Attribut ', pos="x,y"' des Knotens oder ''."""
|
||||
if use_coords:
|
||||
p = zeichen.get(node.tro_id)
|
||||
return (f', pos="{p[0] * COORD_SCALE:.2f},{p[1] * COORD_SCALE:.2f}"'
|
||||
@@ -2336,6 +2640,16 @@ def render_connect_ini(analysis: Analysis, section: str, source: str) -> str:
|
||||
|
||||
|
||||
def render_errors(analysis: Analysis, csv_file: Path) -> str:
|
||||
"""Fehlerbericht <name>_tro_errors.md als Markdown-Text.
|
||||
|
||||
Parameter:
|
||||
analysis: Ergebnis von analyse_tros (isolated, unknown_types).
|
||||
csv_file: Eingabe-CSV, nur fuer Titel und Quellenangabe.
|
||||
|
||||
Rueckgabe:
|
||||
Der Markdown-Text. Ob er geschrieben wird (nur bei isolierten TROs
|
||||
oder unbekannten Typen), entscheidet write_error_file.
|
||||
"""
|
||||
out = [GENERATED_MARKER, f"# TRO-Fehler - {csv_file.name}", ""]
|
||||
out.append(f"**Erzeugt:** {datetime.now().strftime('%Y-%m-%d %H:%M')} ")
|
||||
out.append(f"**Quelle:** `{csv_file}` ")
|
||||
@@ -2406,10 +2720,16 @@ def write_error_file(analysis: Analysis, csv_file: Path, path: Path) -> bool:
|
||||
|
||||
|
||||
def _mermaid_text(text: str) -> str:
|
||||
"""Text fuer ein Mermaid-Knotenlabel entschaerfen (\" und [] ersetzen)."""
|
||||
return text.replace('"', "'").replace("[", "(").replace("]", ")")
|
||||
|
||||
|
||||
def _tro_mermaid(analysis: Analysis) -> list[str]:
|
||||
"""TRO-Fluss als Mermaid-Flowchart (Zeilen inklusive ```-Zaun).
|
||||
|
||||
Knoten je TRO (Farbe nach Katalogtyp) und Systemgrenze, Kanten aus
|
||||
analysis.edges, gestrichelt TROs auf derselben Bahn (colocated).
|
||||
"""
|
||||
out = ["```mermaid", "flowchart LR"]
|
||||
out.extend(f" {line}" for line in mermaid_classdefs())
|
||||
|
||||
@@ -2447,6 +2767,20 @@ def render_doc(
|
||||
svg_file: Path | None,
|
||||
error_file: Path | None,
|
||||
) -> str:
|
||||
"""TRO-Dokumentation <name>_tro_doc.md (--doc) als Markdown-Text.
|
||||
|
||||
Parameter:
|
||||
analysis: Ergebnis von analyse_tros.
|
||||
graph: Materialflussgraph (Host-Namen und -Arten fuer die Tabellen).
|
||||
csv_file: Eingabe-CSV (Titel, Quellenangabe).
|
||||
dot_file: erzeugte DOT-Datei, wird verlinkt.
|
||||
svg_file: erzeugtes SVG oder None (ohne --tosvg).
|
||||
error_file: Fehlerdatei oder None (fehlerfreier Lauf).
|
||||
|
||||
Rueckgabe:
|
||||
Der Markdown-Text: TRO-Liste mit Typ und Begruendung, Mermaid-Fluss,
|
||||
Verbindungen, Typdefinition aus dem Katalog, Findings.
|
||||
"""
|
||||
out: list[str] = [GENERATED_MARKER]
|
||||
add = out.append
|
||||
|
||||
@@ -2650,6 +2984,17 @@ def report(
|
||||
warnings: list[str],
|
||||
engine: str = "dot",
|
||||
) -> None:
|
||||
"""Konsolenbericht eines Laufs: TRO-Zahl je Typ, Findings, Warnungen,
|
||||
erzeugte Dateien.
|
||||
|
||||
Parameter:
|
||||
analysis: Ergebnis von analyse_tros.
|
||||
csv_file: Eingabe-CSV.
|
||||
dot_file: erzeugte DOT-Datei.
|
||||
svg_file / error_file / doc_file: erzeugte Dateien oder None.
|
||||
warnings: Warnungen aus material_flow (Einlesen, Graphaufbau).
|
||||
engine: Graphviz-Programm des SVG ("dot" oder "neato", mit -n).
|
||||
"""
|
||||
types_used: dict[str, int] = {}
|
||||
for tro in analysis.tros:
|
||||
types_used[tro.type_name] = types_used.get(tro.type_name, 0) + 1
|
||||
@@ -2709,6 +3054,7 @@ def report(
|
||||
|
||||
|
||||
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
"""Kommandozeile auswerten; argv=None liest sys.argv[1:]."""
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="tro_flow",
|
||||
description="Ermittelt aus einem CSV-Export der Anlagenobjekte die "
|
||||
@@ -2866,6 +3212,16 @@ def report_ids(tro_ids: TroIds, status: str) -> None:
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
"""CLI-Einstieg: CSV lesen, Graph bauen, analyse_tros, Ausgaben schreiben.
|
||||
|
||||
Parameter:
|
||||
argv: Argumente ohne Programmnamen; None = sys.argv[1:].
|
||||
|
||||
Rueckgabe:
|
||||
Exitcode: 0 = Erfolg, 1 = Eingabe- oder Ausgabefehler, 2 = SVG nicht
|
||||
erzeugt, 3 = TRO-Pruefung fehlgeschlagen (isolierte TROs oder
|
||||
unbekannte Typen, siehe Fehlerdatei).
|
||||
"""
|
||||
args = parse_args(argv)
|
||||
|
||||
csv_file = resolve_input(args.file)
|
||||
|
||||
Reference in New Issue
Block a user