209 lines
8.6 KiB
Python
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
|