Files
dxfmakros/tests/test_lager_yaml.py
T

209 lines
8.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
test_lager_yaml.py - lib/lager_yaml_export.py (Lagerplanung -> RFQ-Overlay-YAML).
Laeuft ohne BricsCAD: die Rohdaten sind von Hand nachgestellt, im Format von
LAGER_EXPORT (Lisp/LagerInsert.lsp, lg-export-json). Geprueft wird:
1. levels: Belagshoehen, lichte Hoehe aus dem Geschoss darueber,
Haengeebenen (Summe aller Reihen je Schienenhoehe/Warenlaenge, inkl.
goh_rack-Bloecke), Belag/Gitterrost nur auf Buehnen.
2. layout: Bereichsparameter, Ganganzahl/-laenge, Huelle.
3. Provenienz: jeder Overlay-Wert hat einen Herkunfts-Eintrag.
4. Abgleich gegen cfg/example_goh_storage.yaml: Abweichungen -> Konflikte,
null/fehlend -> gefuellt.
5. CLI schreibt YAML (wieder einlesbar) und Nachweis-HTML.
"""
import json
import os
import sys
import pytest
import yaml
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "lib"))
import lager_yaml_export as lye # noqa: E402
BEISPIEL_YAML = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "cfg", "example_goh_storage.yaml")
EBENEN_EG = [{"schienenhoehe_mm": 2300, "warenlaenge_mm": 1200},
{"schienenhoehe_mm": 1100, "warenlaenge_mm": 1100}]
EBENEN_OG = [{"schienenhoehe_mm": 2800, "warenlaenge_mm": 1200},
{"schienenhoehe_mm": 1400, "warenlaenge_mm": 1200}]
def _reihe(gs, laenge, ebenen, tiefe=700):
return {"geschoss": gs, "laenge_mm": laenge, "tiefe_mm": tiefe, "haengeebenen": ebenen,
"schienen": 1, "quelle": "auto", "bereich": 1}
def _gang(gs, laenge, breite=1000, art="gang", belag="spanplatte"):
return {"geschoss": gs, "laenge_mm": laenge, "breite_mm": breite, "art": art,
"belag": belag, "bereich": 1}
@pytest.fixture
def raw():
"""Zwei Geschosse: Hallenboden (EG) + Buehne auf 3100 mm, je ein 2-Reihen-Modul."""
return {
"version": 1, "zeichnung": "test.dwg", "erzeugt": "20261021120000", "einheit": "mm",
"land": "DE",
"belag_yaml": {"spanplatte": "chipboard", "gitterrost": "grating", "hallenboden": ""},
"reihen_als_gitterrost": True,
"geschosse": [
# bewusst nicht nach Hoehe sortiert - der Export sortiert selbst
{"nr": 1, "bezeichnung": "Level 1", "hoehe_belag_mm": 3100, "konstr_hoehe_mm": 150,
"lichte_hoehe_mm": None, "belag_art": "spanplatte", "personen": 20,
"kontur_flaeche_mm2": 60e6, "kontur_bbox": [0, 0, 10000, 6000]},
{"nr": 0, "bezeichnung": "ground floor", "hoehe_belag_mm": 0, "konstr_hoehe_mm": 0,
"lichte_hoehe_mm": None, "belag_art": "hallenboden", "personen": 20,
"kontur_flaeche_mm2": 60e6, "kontur_bbox": [0, 0, 10000, 6000]},
],
"bereiche": [
{"nr": 1, "geschoss": 0, "gangbreite_mm": 1000, "regaltiefe_mm": 700,
"reihen_je_gang": 2, "schienen_je_gang": 1},
{"nr": 2, "geschoss": 1, "gangbreite_mm": 1000, "regaltiefe_mm": 700,
"reihen_je_gang": 2, "schienen_je_gang": 1},
],
"reihen": [_reihe(0, 10000, EBENEN_EG), _reihe(0, 10000, EBENEN_EG),
_reihe(1, 10000, EBENEN_OG), _reihe(1, 10000, EBENEN_OG)],
"gaenge": [_gang(0, 10000), _gang(1, 10000),
_gang(1, 700, breite=1200, art="quergang"),
_gang(1, 500, breite=1200, art="quergang", belag="gitterrost")],
"goh_racks": [{"geschoss": 1, "name": "goh_rack", "laenge_mm": 5000, "ebenen": 2,
"tiefe_mm": 700, "haengeebenen": EBENEN_OG, "haengeebenen_quelle": "vorgabe"}],
"warnungen": ["Testwarnung"],
}
def test_levels_sortiert_und_hoehen(raw):
overlay, _ = lye.aggregiere(raw)
lv = overlay["levels"]
assert [l["floor_elevation_m"] for l in lv] == [0.0, 3.1]
assert lv[0]["remark"] == "ground floor"
# lichte Hoehe EG = 3100 - 150 (Konstruktion darueber) - 0
assert lv[0]["clear_height_m"] == pytest.approx(2.95)
# oberstes Geschoss ohne LICHTE_HOEHE_mm -> nicht berechenbar
assert "clear_height_m" not in lv[1]
def test_haengeebenen_summe_inkl_goh_rack(raw):
overlay, _ = lye.aggregiere(raw)
eg, og = overlay["levels"]
assert eg["hanging_levels"] == [
{"rail_height_m": 2.3, "rack_length_m": 20.0, "goods_length_m": 1.2},
{"rail_height_m": 1.1, "rack_length_m": 20.0, "goods_length_m": 1.1},
]
# 2 Reihen a 10 m + goh_rack 5 m, oberste Schiene zuerst
assert [h["rack_length_m"] for h in og["hanging_levels"]] == [25.0, 25.0]
assert og["hanging_levels"][0]["rail_height_m"] == 2.8
def test_belag_nur_auf_buehne(raw):
overlay, _ = lye.aggregiere(raw)
eg, og = overlay["levels"]
assert "walkway" not in eg and "grating" not in eg
# Belag: Hauptgang 10 m2 + Quergang Spanplatte 0,84 m2
assert og["walkway"]["area_m2"] == pytest.approx(10.8)
assert og["walkway"]["spec"] == "chipboard"
# Gitterrost: Quergang 0,6 m2 + Reihen 2 x 7 m2 + goh_rack 3,5 m2
assert og["grating"] == pytest.approx(18.1)
def test_belag_ohne_gaenge_aus_kontur(raw):
raw["gaenge"] = []
overlay, s = lye.aggregiere(raw)
og = overlay["levels"][1]
# 60 m2 Kontur - (2 x 7 + 3,5) m2 Reihen
assert og["walkway"]["area_m2"] == pytest.approx(42.5)
assert "Konturflaeche" in s.provenance["levels[1].walkway.area_m2"]["formel"]
def test_layout(raw):
overlay, _ = lye.aggregiere(raw)
lay = overlay["layout"]
assert lay["aisle_width_m"] == 1.0
assert lay["rack_rows_per_aisle"] == 2
assert lay["rack_depth_m"] == 0.7
assert lay["trolley_rails_per_aisle"] == 1
assert lay["aisles"] == 1 # Quergaenge zaehlen nicht
assert lay["aisle_length_m"] == 10.0
assert lay["block_length_m"] == 10.0
assert lay["block_width_m"] == 6.0
def test_randgang_im_belag_nicht_in_aisles(raw):
# Randgang 10 m x 1 m (Spanplatte) im OG: Belag waechst, aisles bleibt
raw["gaenge"].append(_gang(1, 10000, art="randgang"))
overlay, s = lye.aggregiere(raw)
og = overlay["levels"][1]
assert og["walkway"]["area_m2"] == pytest.approx(20.8)
assert overlay["layout"]["aisles"] == 1
assert overlay["layout"]["aisle_length_m"] == 10.0
assert "Randgaenge G1: 10.0 m" in s.provenance["layout.aisles"]["formel"]
def test_unterschiedliche_bereiche_warnen(raw):
raw["bereiche"][1]["gangbreite_mm"] = 1200
raw["bereiche"].append(dict(raw["bereiche"][1]))
overlay, s = lye.aggregiere(raw)
assert overlay["layout"]["aisle_width_m"] == 1.2
assert any("aisle_width_m" in w for w in s.warnungen)
def test_provenienz_vollstaendig(raw):
ausgabe, _ = lye.baue_ausgabe(raw)
# hanging_levels: Provenienz je rack_length_m; Schienenhoehe/Warenlaenge
# sind der Gruppenschluessel derselben Zeile und stehen in deren Formel.
fehlend = [p for p, _ in lye._blaetter(ausgabe)
if p not in ausgabe["_provenance"]
and not (".hanging_levels[" in p and not p.endswith(".rack_length_m"))]
assert fehlend == []
assert ausgabe["_warnings"][0] == "Testwarnung"
def test_abgleich_mit_beispiel_yaml(raw):
with open(BEISPIEL_YAML, encoding="utf-8") as f:
hand = yaml.safe_load(f)
konflikte, fuellt = lye.abgleich(lye.aggregiere(raw)[0], hand)
pfade = {k["pfad"]: k for k in konflikte}
# Hand 4 Ebenen, Zeichnung 2
assert "levels" in pfade
# Hand 1,0 m Gangbreite = Zeichnung -> kein Konflikt
assert "layout.aisle_width_m" not in pfade
# Hand 4286 m2, Zeichnung 10,8 m2
assert pfade["levels[1].walkway.area_m2"]["hand"] == 4286
assert pfade["levels[1].walkway.area_m2"]["abweichung_prozent"] < -99
# Hand null -> Zeichnung fuellt
gef = {f["pfad"] for f in fuellt}
assert "layout.block_length_m" in gef
assert "levels[1].grating" in gef
def test_hole_pfad():
obj = {"levels": [{"a": 1}, {"walkway": {"area_m2": 5}}]}
assert lye._hole(obj, "levels[1].walkway.area_m2") == (True, 5)
assert lye._hole(obj, "levels[2].walkway") == (False, None)
assert lye._hole(obj, "layout.x") == (False, None)
def test_cli(raw, tmp_path):
rp = tmp_path / "x_lager_raw.json"
rp.write_text(json.dumps(raw), encoding="cp1252")
yp = tmp_path / "x_drawing.yaml"
hp = tmp_path / "x_nachweis.html"
rc = lye.main([str(rp), str(yp), "--hand", BEISPIEL_YAML, "--nachweis", str(hp)])
assert rc == 0
daten = yaml.safe_load(yp.read_text(encoding="utf-8"))
assert daten["levels"][1]["floor_elevation_m"] == 3.1
assert daten["_conflicts"]
assert "levels[0].floor_elevation_m" in daten["_provenance"]
assert "Lager-Nachweis" in hp.read_text(encoding="utf-8")
def test_cli_fehlende_datei(tmp_path):
assert lye.main([str(tmp_path / "fehlt.json"), str(tmp_path / "o.yaml")]) == 2