neuer Schalter dazu
This commit is contained in:
@@ -161,6 +161,7 @@ attached.
|
||||
| `--doc` | — | off | Also write a Markdown report (`<name>_material_flow.md`) with object list, connections and plausibility findings. |
|
||||
| `--connect` | — | off | Also write the topology as `<name>_connect.ini` (item/flow level): `[…nodes]` (Kreisel lanes, Strecke, Gefällestrecke), `[…connections]` with a `kind` per edge (`normal`/`umlauf`/`weiche`/`einschleusung`/`ausschleusung`), and `[…externals]` — the BTMT entry/exit if present, otherwise the open lane-ends listed as entry candidates. A hand-editable bridge into the TRO step, styled after `doc/TRO_Katalog/connect.ini`. |
|
||||
| `--show-bbs` | — | off | Also write `<name>_bbs.svg`: a plain 2-D top-down view of every CSV row (not just flow nodes — Separator/Scanner/Ein-/Ausschleuselement too), one axis-aligned rectangle per row from the `Position` column (its center, X/Y) and the `Boundingbox` column (its width/depth, X/Y — the Z extent is ignored), with the row's `Bezeichnung` centered in it. No rotation, no Graphviz — hand-built SVG; the `viewBox` stays in plant millimetres (Y flipped so plant "up" renders as screen "up") but the printed page size is scaled to DIN A3 (landscape or portrait, whichever matches the layout's aspect ratio) instead of the plant's real-world extent. Larger objects are drawn first so small ones (Separator, Scanner, …) stay visible on top. |
|
||||
| `--show-kx` | — | off | Only valid together with `--show-bbs` (else exit 1). Additionally draw each object's connection coordinate systems K1–K4 into the `_bbs.svg`: the K1–K4 columns are 12-char base64 strings (three 24-bit fixed-point values `x,y,z`, factor 10, `csv:trans-encode` in `dxfmakros/Lisp/export.lsp`) decoded to a plant-mm X/Y. Each present K-point is drawn as a small red cross (two lines) with a small `K1`…`K4` label, and consecutive points are joined `K1→K2→K3→K4` by a blue line. Empty K columns are skipped, so an object with only K1/K2 gets two crosses and one link. |
|
||||
|
||||
**Output** (in `%SKEL_RESULTS%`): `<name>_material_flow.dot` (always, with `pos`
|
||||
attributes when `--use-cords` was used), `<name>_material_flow.svg` (with `--tosvg`),
|
||||
|
||||
+94
-2
@@ -70,6 +70,15 @@ CSV_DELIMITER = ";"
|
||||
CSV_ENCODING = "utf-8-sig"
|
||||
TEILEART_PREFIX = "ILS 2.0 "
|
||||
|
||||
# Dekodierung der CSV-Spalten K1-K4 (Anschluss-Koordinatensysteme eines Blocks):
|
||||
# 12-Zeichen-Base64-Strings mit je drei 24-Bit-Fixed-Point-Werten (x, y, z),
|
||||
# nur Position ohne Rotation. Kodierung siehe csv:trans-encode in
|
||||
# dxfmakros/Lisp/export.lsp (Faktor 10, gleiches Base64-Alphabet wie der
|
||||
# Insertpoint). Rueckgabe in mm im selben Koordinatensystem wie 'Position'.
|
||||
KX_B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
KX_B64_INDEX = {c: i for i, c in enumerate(KX_B64_CHARS)}
|
||||
KX_TRANS_FAKTOR = 10.0
|
||||
|
||||
# Objektarten, die je einen Materialfluss-Knoten bilden
|
||||
TRANSPORT_KINDS = ("Gefaellestrecke", "Strecke")
|
||||
# Objektarten, die als Kreis aus zwei Bahnen (links/rechts) modelliert werden
|
||||
@@ -165,6 +174,9 @@ class Element:
|
||||
row: int
|
||||
position: tuple[float, float, float] | None = None
|
||||
bbox: tuple[float, float, float] | None = None
|
||||
# Anschluss-Koordinatensysteme K1..K4 aus den gleichnamigen CSV-Spalten,
|
||||
# als (label, (x, y)) in mm; leere Spalten fehlen. Nur fuer --show-kx.
|
||||
kpoints: list[tuple[str, tuple[float, float]]] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def x(self) -> float | None:
|
||||
@@ -365,6 +377,32 @@ def resolve_input(name: str) -> Path:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def decode_kpoint(raw: str) -> tuple[float, float] | None:
|
||||
"""
|
||||
K1-K4-Base64-String -> (x, y) in mm, oder None bei leer/ungueltig.
|
||||
|
||||
Die Spalten K1..K4 enthalten je 12 Zeichen (drei 24-Bit-Werte x, y, z);
|
||||
fuer die Draufsicht interessiert nur die xy-Lage. Kodierung siehe
|
||||
csv:trans-encode in dxfmakros/Lisp/export.lsp (vgl. KX_* oben).
|
||||
"""
|
||||
text = (raw or "").strip()
|
||||
if len(text) < 8: # mindestens x und y (2 * 4 Zeichen)
|
||||
return None
|
||||
values: list[int] = []
|
||||
for start in range(0, len(text) - 3, 4):
|
||||
word = 0
|
||||
for char in text[start:start + 4]:
|
||||
if char not in KX_B64_INDEX:
|
||||
return None
|
||||
word = (word << 6) | KX_B64_INDEX[char]
|
||||
if word >= 8388608: # Zweierkomplement (24 Bit)
|
||||
word -= 16777216
|
||||
values.append(word)
|
||||
if len(values) < 2:
|
||||
return None
|
||||
return (values[0] / KX_TRANS_FAKTOR, values[1] / KX_TRANS_FAKTOR)
|
||||
|
||||
|
||||
def parse_position(raw: str) -> tuple[float, float, float] | None:
|
||||
"""'9510.00, -4882.95, 1942.50' -> (9510.0, -4882.95, 1942.5)"""
|
||||
parts = [p.strip() for p in (raw or "").split(",")]
|
||||
@@ -462,6 +500,11 @@ def read_elements(path: Path, warnings: list[str]) -> list[Element]:
|
||||
row=offset,
|
||||
position=parse_position(row.get("Position") or ""),
|
||||
bbox=parse_position(row.get("Boundingbox") or ""),
|
||||
kpoints=[
|
||||
(label, xy)
|
||||
for label in ("K1", "K2", "K3", "K4")
|
||||
if (xy := decode_kpoint(row.get(label) or "")) is not None
|
||||
],
|
||||
)
|
||||
|
||||
if teile_id in seen:
|
||||
@@ -1367,6 +1410,13 @@ BBS_DEFAULT_STYLE = dict(fill="#f2f2f2", stroke="#808080", text="#404040")
|
||||
BBS_MARGIN_MM = 500.0
|
||||
BBS_MIN_FONT_MM = 40.0
|
||||
|
||||
# Darstellung der Anschlusspunkte K1-K4 (--show-kx): Groesse des Kreuzes und
|
||||
# der Beschriftung in mm, Verbindungslinie K1->K2->K3->K4 in Blau.
|
||||
KX_CROSS_MM = 120.0
|
||||
KX_FONT_MM = 90.0
|
||||
KX_CROSS_COLOR = "#c00000"
|
||||
KX_LINK_COLOR = "#1f5fd0"
|
||||
|
||||
# Physisches Papierformat der SVG-Ausgabe: DIN A3, Quer- oder Hochformat je
|
||||
# nach Seitenverhaeltnis der Anlage. Die viewBox bleibt in Anlage-mm - nur
|
||||
# width/height (das gedruckte Format) werden auf A3 skaliert.
|
||||
@@ -1381,7 +1431,12 @@ def _xml_escape(text: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def render_bbs_svg(elements: list[Element], source: str, warnings: list[str]) -> str:
|
||||
def render_bbs_svg(
|
||||
elements: list[Element],
|
||||
source: str,
|
||||
warnings: list[str],
|
||||
show_kx: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
2D-Draufsicht der ganzen Szene: ein achsparalleles Rechteck je CSV-Zeile,
|
||||
aus 'Position' (Mittelpunkt) und 'Boundingbox' (Breite/Tiefe in X/Y - die
|
||||
@@ -1397,6 +1452,10 @@ def render_bbs_svg(elements: list[Element], source: str, warnings: list[str]) ->
|
||||
bleiben so zueinander proportional); das gedruckte Format (width/height)
|
||||
wird auf DIN A3 (Quer- oder Hochformat je nach Seitenverhaeltnis) skaliert
|
||||
- sonst waere die SVG-Datei bei einer 25 m breiten Anlage 25 m breit.
|
||||
|
||||
show_kx zeichnet zusaetzlich je Objekt die Anschlusspunkte K1-K4 (rotes
|
||||
Kreuz + kleine Beschriftung) und verbindet sie in der Reihenfolge
|
||||
K1->K2->K3->K4 mit einer blauen Linie.
|
||||
"""
|
||||
items: list[tuple[Element, float, float, float, float]] = []
|
||||
for element in elements:
|
||||
@@ -1451,6 +1510,25 @@ def render_bbs_svg(elements: list[Element], source: str, warnings: list[str]) ->
|
||||
f'font-family="Segoe UI, sans-serif" font-size="{font_size:.1f}" fill="{style["text"]}" '
|
||||
f'text-anchor="middle" dominant-baseline="middle">{_xml_escape(name)}</text>')
|
||||
|
||||
# Anschlusspunkte K1-K4 zuletzt, damit sie ueber den Rechtecken liegen.
|
||||
if show_kx:
|
||||
half = KX_CROSS_MM / 2
|
||||
for element, *_rest in items:
|
||||
# Anlage- in viewBox-Koordinaten (X verschoben, Y gespiegelt).
|
||||
pts = [(label, kx - min_x, max_y - ky) for label, (kx, ky) in element.kpoints]
|
||||
# Verbindungslinie K1->K2->K3->K4 (nur bei mindestens zwei Punkten).
|
||||
for (_, x1, y1), (_, x2, y2) in zip(pts, pts[1:]):
|
||||
add(f' <line x1="{x1:.2f}" y1="{y1:.2f}" x2="{x2:.2f}" y2="{y2:.2f}" '
|
||||
f'stroke="{KX_LINK_COLOR}" stroke-width="8"/>')
|
||||
for label, sx, sy in pts:
|
||||
add(f' <line x1="{sx - half:.2f}" y1="{sy:.2f}" x2="{sx + half:.2f}" y2="{sy:.2f}" '
|
||||
f'stroke="{KX_CROSS_COLOR}" stroke-width="8"/>')
|
||||
add(f' <line x1="{sx:.2f}" y1="{sy - half:.2f}" x2="{sx:.2f}" y2="{sy + half:.2f}" '
|
||||
f'stroke="{KX_CROSS_COLOR}" stroke-width="8"/>')
|
||||
add(f' <text x="{sx + half + 20:.2f}" y="{sy - half:.2f}" '
|
||||
f'font-family="Segoe UI, sans-serif" font-size="{KX_FONT_MM:.1f}" '
|
||||
f'fill="{KX_CROSS_COLOR}" dominant-baseline="middle">{label}</text>')
|
||||
|
||||
add("</svg>")
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
@@ -1857,6 +1935,14 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
"(Mittelpunkt) und 'Boundingbox' (Breite/Tiefe), mit der Bezeichnung "
|
||||
"im Zentrum, skaliert auf DIN A3. Braucht kein Graphviz.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--show-kx",
|
||||
dest="show_kx",
|
||||
action="store_true",
|
||||
help="Nur zusammen mit --show-bbs: je Objekt die Anschlusspunkte K1-K4 als "
|
||||
"kleines Kreuz mit Beschriftung einzeichnen und in der Reihenfolge "
|
||||
"K1->K2->K3->K4 mit einer blauen Linie verbinden.",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
@@ -1890,12 +1976,18 @@ def main(argv: list[str] | None = None) -> int:
|
||||
|
||||
stem = csv_file.stem
|
||||
|
||||
if args.show_kx and not args.show_bbs:
|
||||
print("FEHLER: --show-kx ist nur zusammen mit --show-bbs moeglich.",
|
||||
file=sys.stderr)
|
||||
return 1
|
||||
|
||||
bbs_file = None
|
||||
if args.show_bbs:
|
||||
bbs_target = results / f"{stem}_bbs.svg"
|
||||
try:
|
||||
bbs_target.write_text(
|
||||
render_bbs_svg(elements, csv_file.name, warnings), encoding="utf-8"
|
||||
render_bbs_svg(elements, csv_file.name, warnings, show_kx=args.show_kx),
|
||||
encoding="utf-8",
|
||||
)
|
||||
except ValueError as exc:
|
||||
print(f"FEHLER: --show-bbs: {exc}", file=sys.stderr)
|
||||
|
||||
Reference in New Issue
Block a user