SCL-Geruest: Stationen (SYSn) als Materialeingang; kurze TRO-Verbindungen nicht mehr verwerfen
scl_skeleton.py brach mit KeyError ab, sobald der TRO-Fluss Stationen (SYSn) enthielt. Stationen bekommen jetzt keine REGION/Instanz, der von einer Station gespeiste TRO ist der Anfang der Reihenfolge, in den Staubereich-Kommentaren sind sie als Station markiert. tro_annotate.py liess Verbindungen unter 2,2 Markergroessen still weg (HundM05 TRO03->TRO04, 500 mm) - sie fehlten dann in tro_extract.py und in der SCL. Kurze Verbindungen werden jetzt Mitte-zu-Mitte gezeichnet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7b98c8b8d4
commit
e2f1a558a2
@@ -432,6 +432,13 @@ no loading/unloading station: the flow is then a closed loop with no entry, so
|
|||||||
the order has to be broken somewhere arbitrary. The chosen start and the reason
|
the order has to be broken somewhere arbitrary. The chosen start and the reason
|
||||||
are recorded in the file header.
|
are recorded in the file header.
|
||||||
|
|
||||||
|
**Stations (`SYSn`).** Loading/unloading stations from `tro_flow.py` appear in
|
||||||
|
the JSON as `SYSn` neighbours, not as TROs. They get no REGION and no instance.
|
||||||
|
A TRO fed by a `SYSn` is the start of the order ("Materialeingang ueber SYSn"),
|
||||||
|
so `--start` is no longer needed once the export contains a station. In the
|
||||||
|
jam-area comments they are marked "(Station, kein TRO)", and an exit station
|
||||||
|
shows up as a destination in the per-switch lists.
|
||||||
|
|
||||||
## 6. `tro_catalog.py` — TRO type catalog (library, no CLI)
|
## 6. `tro_catalog.py` — TRO type catalog (library, no CLI)
|
||||||
|
|
||||||
The single source of truth for everything needed to generate or draw a TRO
|
The single source of truth for everything needed to generate or draw a TRO
|
||||||
|
|||||||
+25
-5
@@ -72,11 +72,25 @@ def flow_order(tros: list[dict], start_id: str = "") -> tuple[list[dict], str]:
|
|||||||
"""
|
"""
|
||||||
nach_id = {t["id"]: t for t in tros}
|
nach_id = {t["id"]: t for t in tros}
|
||||||
ohne_vorgaenger = sorted(t["id"] for t in tros if not t.get("predecessors"))
|
ohne_vorgaenger = sorted(t["id"] for t in tros if not t.get("predecessors"))
|
||||||
|
# Materialeingang ueber eine Station (SYSn von tro_flow.py): der TRO, den
|
||||||
|
# sie speist, ist der natuerliche Anfang - auch wenn er zusaetzlich einen
|
||||||
|
# TRO-Vorgaenger hat (Kreisel-Umlauf).
|
||||||
|
eingaenge = sorted({
|
||||||
|
t["id"] for t in tros
|
||||||
|
for vor in t.get("predecessors", []) if vor not in nach_id
|
||||||
|
})
|
||||||
|
|
||||||
if start_id and start_id in nach_id:
|
if start_id and start_id in nach_id:
|
||||||
start, grund = [start_id], "per --start gesetzt"
|
start, grund = [start_id], "per --start gesetzt"
|
||||||
elif ohne_vorgaenger:
|
elif ohne_vorgaenger:
|
||||||
start, grund = ohne_vorgaenger, "TRO ohne Vorgaenger (Materialeingang)"
|
start, grund = ohne_vorgaenger, "TRO ohne Vorgaenger (Materialeingang)"
|
||||||
|
elif eingaenge:
|
||||||
|
stationen = sorted({
|
||||||
|
vor for i in eingaenge for vor in nach_id[i]["predecessors"]
|
||||||
|
if vor not in nach_id
|
||||||
|
})
|
||||||
|
start = eingaenge
|
||||||
|
grund = f"Materialeingang ueber {', '.join(stationen)}"
|
||||||
else:
|
else:
|
||||||
start = [min(nach_id)]
|
start = [min(nach_id)]
|
||||||
grund = ("Kreislauf ohne Eingang - willkuerlich aufgetrennt; sobald "
|
grund = ("Kreislauf ohne Eingang - willkuerlich aufgetrennt; sobald "
|
||||||
@@ -90,7 +104,8 @@ def flow_order(tros: list[dict], start_id: str = "") -> tuple[list[dict], str]:
|
|||||||
continue
|
continue
|
||||||
gesehen.append(tro_id)
|
gesehen.append(tro_id)
|
||||||
for nxt in nach_id.get(tro_id, {}).get("successors", []):
|
for nxt in nach_id.get(tro_id, {}).get("successors", []):
|
||||||
if nxt not in gesehen:
|
# SYSn (Materialausgang) ist kein TRO - keine REGION, keine Instanz.
|
||||||
|
if nxt in nach_id and nxt not in gesehen:
|
||||||
queue.append(nxt)
|
queue.append(nxt)
|
||||||
for tro_id in sorted(nach_id):
|
for tro_id in sorted(nach_id):
|
||||||
if tro_id not in gesehen:
|
if tro_id not in gesehen:
|
||||||
@@ -116,6 +131,11 @@ def reachable_via(start: str, first: str, tros: dict[str, dict]) -> list[str]:
|
|||||||
return sorted(gesehen)
|
return sorted(gesehen)
|
||||||
|
|
||||||
|
|
||||||
|
def nachbar(node: str, alle: dict[str, dict]) -> str:
|
||||||
|
"""Nachbar fuer Kommentare; SYSn als Station kenntlich machen."""
|
||||||
|
return node if node in alle else f"{node} (Station, kein TRO)"
|
||||||
|
|
||||||
|
|
||||||
def region_title(tro: dict) -> str:
|
def region_title(tro: dict) -> str:
|
||||||
seps = ", ".join(tro.get("separators", []))
|
seps = ", ".join(tro.get("separators", []))
|
||||||
teile = f" Sep {seps}" if seps else ""
|
teile = f" Sep {seps}" if seps else ""
|
||||||
@@ -199,7 +219,7 @@ def emit_barcode(add, tro: dict, anzahl: int) -> None:
|
|||||||
add("")
|
add("")
|
||||||
|
|
||||||
|
|
||||||
def emit_call(add, tro: dict, items: dict[str, int]) -> None:
|
def emit_call(add, tro: dict, items: dict[str, int], alle: dict[str, dict]) -> None:
|
||||||
"""Aufrufstelle schreiben.
|
"""Aufrufstelle schreiben.
|
||||||
|
|
||||||
Die Zeilen werden als (Code, Kommentar) gefuehrt und das Komma erst beim
|
Die Zeilen werden als (Code, Kommentar) gefuehrt und das Komma erst beim
|
||||||
@@ -224,11 +244,11 @@ def emit_call(add, tro: dict, items: dict[str, int]) -> None:
|
|||||||
# nur, WIE VIELE es sein muessen und zu welchem Nachbarn sie gehoeren.
|
# nur, WIE VIELE es sein muessen und zu welchem Nachbarn sie gehoeren.
|
||||||
for n, vor in enumerate(tro.get("predecessors", []), start=1):
|
for n, vor in enumerate(tro.get("predecessors", []), start=1):
|
||||||
zeilen.append((f'arInOutJamEntr{n} := "DB_JamArea____".stJam____.arCarrier',
|
zeilen.append((f'arInOutJamEntr{n} := "DB_JamArea____".stJam____.arCarrier',
|
||||||
f"{TODO} - Staubereich von {vor}"))
|
f"{TODO} - Staubereich von {nachbar(vor, alle)}"))
|
||||||
zeilen.append((f'stInOutJamEntr{n} := "DB_JamArea____".stJam____.stData', ""))
|
zeilen.append((f'stInOutJamEntr{n} := "DB_JamArea____".stJam____.stData', ""))
|
||||||
for n, nach in enumerate(tro.get("successors", []), start=2):
|
for n, nach in enumerate(tro.get("successors", []), start=2):
|
||||||
zeilen.append((f'arInOutJamExit{n} := "DB_JamArea____".stJam____.arCarrier',
|
zeilen.append((f'arInOutJamExit{n} := "DB_JamArea____".stJam____.arCarrier',
|
||||||
f"{TODO} - Staubereich nach {nach}"))
|
f"{TODO} - Staubereich nach {nachbar(nach, alle)}"))
|
||||||
zeilen.append((f'stInOutJamExit{n} := "DB_JamArea____".stJam____.stData', ""))
|
zeilen.append((f'stInOutJamExit{n} := "DB_JamArea____".stJam____.stData', ""))
|
||||||
|
|
||||||
for i in range(1, items.get("Separator", 1) + 1):
|
for i in range(1, items.get("Separator", 1) + 1):
|
||||||
@@ -338,7 +358,7 @@ def render(daten: dict, quelle: str, start_id: str = "") -> str:
|
|||||||
|
|
||||||
add(" #%s.stInPriorityManager.xReleaseOk := TRUE;" % tro["id"])
|
add(" #%s.stInPriorityManager.xReleaseOk := TRUE;" % tro["id"])
|
||||||
add("")
|
add("")
|
||||||
emit_call(add, tro, items)
|
emit_call(add, tro, items, alle)
|
||||||
if n_swi:
|
if n_swi:
|
||||||
emit_direction(add, tro, n_swi)
|
emit_direction(add, tro, n_swi)
|
||||||
add("END_REGION")
|
add("END_REGION")
|
||||||
|
|||||||
+14
-4
@@ -680,23 +680,33 @@ def draw_connections(
|
|||||||
x1, y1 = placed[src]
|
x1, y1 = placed[src]
|
||||||
x2, y2 = placed[dst]
|
x2, y2 = placed[dst]
|
||||||
length = math.dist((x1, y1), (x2, y2))
|
length = math.dist((x1, y1), (x2, y2))
|
||||||
if length <= 2 * marker * 1.1:
|
if length < 1e-6:
|
||||||
continue
|
continue
|
||||||
ux, uy = (x2 - x1) / length, (y2 - y1) / length
|
ux, uy = (x2 - x1) / length, (y2 - y1) / length
|
||||||
# An beiden Enden um den Marker kuerzen, damit der Pfeil ihn nicht
|
# An beiden Enden um den Marker kuerzen, damit der Pfeil ihn nicht
|
||||||
# deckt; das Stationsende ist nur eine Beschriftung, dort weniger.
|
# deckt; das Stationsende ist nur eine Beschriftung, dort weniger.
|
||||||
kurz_a = marker * (0.3 if src in stationen else 1.1)
|
kurz_a = marker * (0.3 if src in stationen else 1.1)
|
||||||
kurz_b = marker * (0.3 if dst in stationen else 1.1)
|
kurz_b = marker * (0.3 if dst in stationen else 1.1)
|
||||||
|
# Zu kurz fuer einen gekuerzten Pfeil (Marker liegen dicht beieinander,
|
||||||
|
# z.B. HundM05 TRO03->TRO04 mit 500 mm): NICHT weglassen - sonst fehlt
|
||||||
|
# die Verbindung in tro_extract.py und damit in der SCL. Stattdessen
|
||||||
|
# Mitte-zu-Mitte ohne Kuerzung zeichnen.
|
||||||
|
kurz = length <= kurz_a + kurz_b + head
|
||||||
|
if kurz:
|
||||||
|
kurz_a = kurz_b = 0.0
|
||||||
sx, sy = x1 + ux * kurz_a, y1 + uy * kurz_a
|
sx, sy = x1 + ux * kurz_a, y1 + uy * kurz_a
|
||||||
ex, ey = x2 - ux * kurz_b, y2 - uy * kurz_b
|
ex, ey = x2 - ux * kurz_b, y2 - uy * kurz_b
|
||||||
shaft = msp.add_lwpolyline([(sx, sy), (ex, ey)],
|
shaft = msp.add_lwpolyline([(sx, sy), (ex, ey)],
|
||||||
dxfattribs={"layer": layer})
|
dxfattribs={"layer": layer})
|
||||||
tag(shaft, f"{src}->{dst}")
|
tag(shaft, f"{src}->{dst}")
|
||||||
# Pfeilspitze als gefuellte Flaeche
|
# Pfeilspitze als gefuellte Flaeche (bei kurzer Verbindung in der Mitte,
|
||||||
bx, by = ex - ux * head, ey - uy * head
|
# damit sie nicht im Zielmarker verschwindet)
|
||||||
|
hx, hy = ((x1 + x2) / 2 + ux * head / 2, (y1 + y2) / 2 + uy * head / 2) \
|
||||||
|
if kurz else (ex, ey)
|
||||||
|
bx, by = hx - ux * head, hy - uy * head
|
||||||
px, py = -uy * head * 0.38, ux * head * 0.38
|
px, py = -uy * head * 0.38, ux * head * 0.38
|
||||||
point = msp.add_solid(
|
point = msp.add_solid(
|
||||||
[(ex, ey), (bx + px, by + py), (bx - px, by - py)],
|
[(hx, hy), (bx + px, by + py), (bx - px, by - py)],
|
||||||
dxfattribs={"layer": layer},
|
dxfattribs={"layer": layer},
|
||||||
)
|
)
|
||||||
tag(point, f"{src}->{dst}")
|
tag(point, f"{src}->{dst}")
|
||||||
|
|||||||
Reference in New Issue
Block a user