Merge e-planung-bruecke: stabile TRO-IDs, TRO-JSON v4, E-Planung-Bruecke
Uebernimmt Michael Stangls Zweig (tro_ids, Slots/jamAreas v4, e_planning --eplan, Konzept- und Datenquellen-Doku) in master, auf dem seitdem die SCL-Templates fuer alle 7 Typen mit Referenz-Aufrufstelle liegen. Konfliktloesung: - lib/scl_context.py: beide Seiten zusammengefuehrt - Michaels _separator (E-Planung-Namen) und Staubereiche (_entries/_exit_area, ein JamEntr je eigenem Staubereich) plus die Template-Felder (_scanner, _store, carriers/data, part_sensor). _jam() fuehrt jetzt auch jam_id. Die beim automatischen Zusammenfuehren verlorene Property SclJam.title wiederhergestellt. - Templates 1Sep, 1Sep2Swi, 1Sep_SSCC, Vario, Vario_workStation, PinStore_Auto auf v4 nachgezogen: Kommentar "Staubereich J.... von/vor ..." wie im Pilot; PinStore-TODO zaehlt Staubereiche statt Nachbarn (mubea: 2 Zulaeufe laufen in einen Eingang). - Golden-Files *_templates neu erzeugt; geaendert sind ausschliesslich Staubereichs-Zeilen (Kennung im Kommentar, ein Eingang statt einem je Vorgaenger). Generische Golden-Files = Michaels Stand. - Neuer Test: E-Planung-Namen kommen auch in der Template-Ausgabe an. - Doku: README Schema v4, SCL_Templates.md Staubereiche und TODO-Zahlen. Geprueft: 71 Tests gruen; tro_flow --json + scl_skeleton --templates auf allen 8 CSVs ohne Fehler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+105
-27
@@ -16,8 +16,11 @@ Schichten:
|
||||
|
||||
Namen (Separator-, Weichen-, MainTro-Nummer, Adressen ...) sind ein
|
||||
:class:`SclName`: Wert plus Merker, ob er noch aus der E-Planung kommen muss.
|
||||
Heute sind alle Platzhalter mit ``todo=True``; die Namensregeln aus Schritt 4
|
||||
(cfg/scl_naming.ini) ersetzen dann nur den Wert und loeschen den Merker.
|
||||
Ohne E-Planung sind alle Platzhalter ``todo=True``. Mit einer Zuordnung aus
|
||||
lib/e_planning.py (``eplan``) werden Separator-Sensor und Stopperventil der
|
||||
zugeordneten Separatoren eingesetzt und ``todo`` geloescht; alles andere
|
||||
bleibt offen. Die Namensregeln aus Schritt 4 (cfg/scl_naming.ini) arbeiten
|
||||
spaeter genauso: Wert ersetzen, Merker loeschen.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -25,12 +28,16 @@ from __future__ import annotations
|
||||
import re
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from model import TroBase, TroDocument
|
||||
from model import JamArea, TroBase, TroDocument
|
||||
from tro_catalog import TroItem
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from e_planning import EplanDocument
|
||||
|
||||
__all__ = [
|
||||
"TODO",
|
||||
"SclName",
|
||||
@@ -69,6 +76,16 @@ def _offen(platzhalter: str) -> SclName:
|
||||
return SclName(value=platzhalter, todo=True)
|
||||
|
||||
|
||||
def _bekannt(wert: str) -> SclName:
|
||||
return SclName(value=wert, todo=False)
|
||||
|
||||
|
||||
# Rolle in e_planning.ini -> wie der Name in der SCL geschrieben wird
|
||||
# (Sensor als Konstante der DB_Inputs-Sensorliste, Ventil als Variable)
|
||||
_SENSOR = "cIn{bmk}"
|
||||
_VENTIL = "{bmk}"
|
||||
|
||||
|
||||
class SclSeparator(BaseModel):
|
||||
"""Ein stInSeparator<n>-Block."""
|
||||
|
||||
@@ -130,12 +147,19 @@ class SclScanner(BaseModel):
|
||||
|
||||
|
||||
class SclJam(BaseModel):
|
||||
"""Ein Staubereich am Ein- oder Ausgang (JamEntr<n> / JamExit<n>)."""
|
||||
"""Ein Staubereich am Ein- oder Ausgang (JamEntr<n> / JamExit<n>).
|
||||
|
||||
Ein Eingang ist ein Staubereich des TRO selbst (TroDocument.jamAreas), ein
|
||||
Ausgang der Eingangs-Staubereich des Nachfolgers.
|
||||
"""
|
||||
|
||||
model_config = _CONFIG
|
||||
|
||||
n: int
|
||||
# Nachbar als Text fuer den Kommentar, SYSn als Station markiert
|
||||
# Kennung aus der TRO-JSON (J<Separator>); "" am Ausgang zu einer Station
|
||||
jam_id: str = ""
|
||||
# Eingang: die Vorgaenger, Ausgang: der Nachfolger - als Text fuer den
|
||||
# Kommentar, SYSn als Station markiert
|
||||
neighbour: str
|
||||
db: SclName
|
||||
# Stausensor des Ausgangs (stInSenJam<n>); None an Eingaengen
|
||||
@@ -145,6 +169,11 @@ class SclJam(BaseModel):
|
||||
carriers: SclName
|
||||
data: SclName
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
"""Fuer den Kommentar: 'Staubereich J0003' (ohne Kennung nur 'Staubereich')."""
|
||||
return f"Staubereich {self.jam_id}" if self.jam_id else "Staubereich"
|
||||
|
||||
|
||||
class SclSscc(BaseModel):
|
||||
"""SSCC-Scanner eines 1Sep_SSCC: zweite Kamera, Endmessung, HMI-Anzeige."""
|
||||
@@ -209,6 +238,11 @@ class SclStore(BaseModel):
|
||||
# Eine Linie je Speicherbahn im Layout (nicht die 19 aus dem Katalog)
|
||||
lines: list[SclStoreLine]
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
"""Fuer den Kommentar: 'Staubereich J0003' (ohne Kennung nur 'Staubereich')."""
|
||||
return f"Staubereich {self.jam_id}" if self.jam_id else "Staubereich"
|
||||
|
||||
|
||||
class SclTro(BaseModel):
|
||||
"""Alles, was eine REGION fuer einen TRO braucht."""
|
||||
@@ -282,6 +316,8 @@ class SclFile(BaseModel):
|
||||
created: str
|
||||
start_reason: str
|
||||
tros: list[SclTro] = Field(default_factory=list)
|
||||
# Zuordnung zur E-Planung, aus der Namen eingesetzt wurden ("" = keine)
|
||||
eplan_source: str = ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -388,9 +424,10 @@ def _scanner(n: int, suffix: str, interface: str = "cScanner____") -> SclScanner
|
||||
)
|
||||
|
||||
|
||||
def _jam(n: int, neighbour: str, jam_db: str, sensor: SclName | None = None) -> SclJam:
|
||||
def _jam(n: int, neighbour: str, jam_db: str, sensor: SclName | None = None,
|
||||
jam_id: str = "") -> SclJam:
|
||||
return SclJam(
|
||||
n=n, neighbour=neighbour, db=_offen(jam_db), sensor=sensor,
|
||||
n=n, jam_id=jam_id, neighbour=neighbour, db=_offen(jam_db), sensor=sensor,
|
||||
carriers=_offen(f"{jam_db}.arCarrier"), data=_offen(f"{jam_db}.stData"),
|
||||
)
|
||||
|
||||
@@ -434,23 +471,55 @@ def _store(tro: TroBase) -> SclStore:
|
||||
)
|
||||
|
||||
|
||||
def _tro_context(tro: TroBase, alle: dict[str, TroBase]) -> SclTro:
|
||||
def _separator(i: int, layout: str, eplan: EplanDocument | None) -> SclSeparator:
|
||||
rollen = eplan.for_layout(layout) if eplan is not None and layout != "?" else {}
|
||||
sensor = rollen.get("sensor")
|
||||
stopper = rollen.get("stopper")
|
||||
return SclSeparator(
|
||||
n=i,
|
||||
layout=layout,
|
||||
no=_offen("cSep_____"),
|
||||
sensor=_bekannt(_SENSOR.format(bmk=sensor.bmk)) if sensor else _offen("cInBG____"),
|
||||
stopper=_bekannt(_VENTIL.format(bmk=stopper.bmk)) if stopper else _offen("MB____"),
|
||||
# Teilesensor kennt die E-Planung-Bruecke noch nicht - offen
|
||||
part_sensor=_offen("cInBG____"),
|
||||
)
|
||||
|
||||
|
||||
def _entries(tro: TroBase, eigene: list[JamArea],
|
||||
alle: dict[str, TroBase], jam_db: str) -> list[SclJam]:
|
||||
"""Ein JamEntr je eigenem Staubereich - nicht je Vorgaenger: mehrere
|
||||
Vorgaenger fahren in denselben Staubereich ein (Referenzen UH01-UH05)."""
|
||||
eingaenge = []
|
||||
for area in eigene:
|
||||
von = ", ".join(_nachbar(v, alle) for v in area.predecessors) or "-"
|
||||
if area.note:
|
||||
von = f"{von} ({area.note})"
|
||||
eingaenge.append(_jam(area.entry, von, jam_db, jam_id=area.jam_id))
|
||||
return eingaenge
|
||||
|
||||
|
||||
def _exit_area(tro_id: str, nach: str,
|
||||
bereiche: dict[str, list[JamArea]]) -> str:
|
||||
"""Kennung des Staubereichs, in den tro_id beim Nachfolger einfaehrt."""
|
||||
ziel = bereiche.get(nach, [])
|
||||
passend = [a for a in ziel if tro_id in a.predecessors] or ziel
|
||||
return passend[0].jam_id if passend else ""
|
||||
|
||||
|
||||
def _tro_context(tro: TroBase, alle: dict[str, TroBase],
|
||||
eplan: EplanDocument | None = None,
|
||||
bereiche: dict[str, list[JamArea]] | None = None) -> SclTro:
|
||||
items = tro.items
|
||||
n_sep = items.get(TroItem.SEPARATOR, 1)
|
||||
n_swi = items.get(TroItem.SWITCH, 0)
|
||||
n_scan = items.get(TroItem.SCANNER, 0)
|
||||
tro_id = tro.tro_id
|
||||
jam_db = '"DB_JamArea____".stJam____'
|
||||
bereiche = bereiche or {}
|
||||
|
||||
separators = [
|
||||
SclSeparator(
|
||||
n=i,
|
||||
layout=tro.separators[i - 1] if i <= len(tro.separators) else "?",
|
||||
no=_offen("cSep_____"),
|
||||
sensor=_offen("cInBG____"),
|
||||
stopper=_offen("MB____"),
|
||||
part_sensor=_offen("cInBG____"),
|
||||
)
|
||||
_separator(i, tro.separators[i - 1] if i <= len(tro.separators) else "?", eplan)
|
||||
for i in range(1, n_sep + 1)
|
||||
]
|
||||
switches = [
|
||||
@@ -475,15 +544,14 @@ def _tro_context(tro: TroBase, alle: dict[str, TroBase]) -> SclTro:
|
||||
SclSwitchExit(via=abgang, targets=reachable_via(tro_id, abgang, alle))
|
||||
for abgang in tro.successors
|
||||
] if n_swi else []
|
||||
# Staubereiche: je ankommender/abgehender Verbindung einer. Welche
|
||||
# DB-Struktur dahintersteht, ist eine Planungsentscheidung - hier steht
|
||||
# nur, WIE VIELE es sein muessen und zu welchem Nachbarn sie gehoeren.
|
||||
jam_entries = [
|
||||
_jam(n, _nachbar(vor, alle), jam_db)
|
||||
for n, vor in enumerate(tro.predecessors, start=1)
|
||||
]
|
||||
# Staubereiche aus der TRO-JSON (jamAreas): Eingaenge sind die eigenen
|
||||
# (ein JamEntr je eigenem Staubereich, nicht je Vorgaenger), Ausgaenge die
|
||||
# Eingaenge der Nachfolger. Der DB-Name braucht die Foerderernummer aus der
|
||||
# Hardwareplanung und bleibt offen.
|
||||
jam_entries = _entries(tro, bereiche.get(tro_id, []), alle, jam_db)
|
||||
jam_exits = [
|
||||
_jam(n, _nachbar(nach, alle), jam_db, sensor=_offen("cInBG____"))
|
||||
_jam(n, _nachbar(nach, alle), jam_db, sensor=_offen("cInBG____"),
|
||||
jam_id=_exit_area(tro_id, nach, bereiche))
|
||||
for n, nach in enumerate(tro.successors, start=2)
|
||||
]
|
||||
|
||||
@@ -541,13 +609,23 @@ def _tro_context(tro: TroBase, alle: dict[str, TroBase]) -> SclTro:
|
||||
|
||||
|
||||
def build_context(document: TroDocument, source: str, start_id: str = "",
|
||||
created: str | None = None) -> SclFile:
|
||||
"""TroDocument -> SclFile (TROs in Fliessrichtung)."""
|
||||
created: str | None = None,
|
||||
eplan: EplanDocument | None = None) -> SclFile:
|
||||
"""TroDocument -> SclFile (TROs in Fliessrichtung).
|
||||
|
||||
eplan: Zuordnung Layout -> E-Planung (e_planning.py); ohne sie bleiben
|
||||
alle Namen offen und die Ausgabe ist die bisherige.
|
||||
"""
|
||||
alle = {t.tro_id: t for t in document.tros}
|
||||
bereiche: dict[str, list[JamArea]] = {}
|
||||
for area in document.jam_areas_or_derived():
|
||||
bereiche.setdefault(area.tro, []).append(area)
|
||||
geordnet, startgrund = flow_order(document.tros, start_id)
|
||||
return SclFile(
|
||||
source=source,
|
||||
created=created or datetime.now().strftime("%Y-%m-%d %H:%M"),
|
||||
start_reason=startgrund,
|
||||
tros=[_tro_context(t, alle) for t in geordnet],
|
||||
tros=[_tro_context(t, alle, eplan, bereiche) for t in geordnet],
|
||||
eplan_source=(f"{eplan.eplan_file} (Zuordnung vom {eplan.created})"
|
||||
if eplan is not None else ""),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user