- lib/tro_ids.py: cfg/<csv>_tro_ids.ini lesen/schreiben, TROs ueber die Lage ihrer Separatoren wiederfinden, neue IDs vergeben, Weggefallene melden - tro_flow: Nummerierung entlang des Materialflusses, Schalter --ids, --renumber, --id-tolerance; Bericht der IDs - tro_annotate: liest dieselbe ID-Datei (nur lesend) - Tests tests/test_tro_ids.py, Doku Python_Scripts.md, Konzept (Stufe 1 erledigt) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
127 lines
4.2 KiB
Python
127 lines
4.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Tests fuer lib/tro_ids.py: ID-Datei lesen/schreiben, TROs ueber die Lage
|
|
wiederfinden (doc/Konzept_Layout_Hardware_SCL.md, Abschnitt TRO-IDs)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
import pytest
|
|
|
|
from tro_ids import (
|
|
IdEntry,
|
|
TroIds,
|
|
assign_ids,
|
|
insert_entries,
|
|
load_ids,
|
|
other_sections,
|
|
render_ids,
|
|
render_new_entries,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class FakeTro:
|
|
separators: list[str]
|
|
type_name: str = "1Sep"
|
|
label: str = ""
|
|
tro_id: str = ""
|
|
hosts: list[str] = field(default_factory=list)
|
|
|
|
|
|
ANCHORS = {
|
|
"0001": (0.0, 0.0),
|
|
"0002": (1000.0, 0.0),
|
|
"0003": (2000.0, 0.0),
|
|
"0004": (2000.0, 500.0),
|
|
}
|
|
|
|
|
|
def _tros(*seps: list[str]) -> list[FakeTro]:
|
|
return [FakeTro(separators=list(s)) for s in seps]
|
|
|
|
|
|
def test_ohne_eintraege_wird_durchnummeriert():
|
|
tros = _tros(["0001"], ["0002"], ["0003"])
|
|
result = assign_ids(tros, ANCHORS, [])
|
|
assert result.renumbered
|
|
assert [t.tro_id for t in tros] == ["TRO01", "TRO02", "TRO03"]
|
|
|
|
|
|
def test_wiederfinden_ueber_lage_auch_umbenannt():
|
|
# Neuer Export: andere TeileIds, gleiche Lage, andere Reihenfolge
|
|
anchors = {"0101": (2000.0, 0.0), "0102": (0.0, 0.0), "0103": (1000.0, 30.0)}
|
|
tros = _tros(["0101"], ["0102"], ["0103"])
|
|
entries = [IdEntry("TRO01", 0.0, 0.0), IdEntry("K0102_1", 1000.0, 0.0),
|
|
IdEntry("TRO03", 2000.0, 0.0)]
|
|
result = assign_ids(tros, anchors, entries)
|
|
assert [t.tro_id for t in tros] == ["TRO03", "TRO01", "K0102_1"]
|
|
assert not result.new and not result.missing
|
|
|
|
|
|
def test_neue_tros_naechste_freie_nummer_weggefallene_nicht_wiederverwendet():
|
|
tros = _tros(["0001"], ["0003"])
|
|
entries = [IdEntry("TRO01", 0.0, 0.0), IdEntry("TRO02", 1000.0, 0.0)]
|
|
result = assign_ids(tros, ANCHORS, entries)
|
|
assert [t.tro_id for t in tros] == ["TRO01", "TRO03"]
|
|
assert result.new == ["TRO03"]
|
|
assert [e.tro_id for e in result.missing] == ["TRO02"]
|
|
|
|
|
|
def test_ausserhalb_toleranz_gilt_als_neu():
|
|
tros = _tros(["0001"])
|
|
entries = [IdEntry("TRO01", 150.0, 0.0)]
|
|
result = assign_ids(tros, ANCHORS, entries, tolerance=100.0)
|
|
assert tros[0].tro_id == "TRO02"
|
|
assert result.missing[0].tro_id == "TRO01"
|
|
assert assign_ids(tros, ANCHORS, entries, tolerance=200.0).kept[0][0] == "TRO01"
|
|
|
|
|
|
def test_zusammengelegte_tros_naechster_eintrag_gewinnt():
|
|
# zwei Separatoren, frueher zwei TROs, jetzt ein 2Sep-TRO
|
|
tros = _tros(["0003", "0004"])
|
|
entries = [IdEntry("TRO05", 2000.0, 0.0), IdEntry("TRO06", 2000.0, 510.0)]
|
|
result = assign_ids(tros, ANCHORS, entries)
|
|
assert tros[0].tro_id == "TRO05"
|
|
assert result.merged_into == {"TRO06": "TRO05"}
|
|
|
|
|
|
def test_renumber_ignoriert_eintraege():
|
|
tros = _tros(["0001"], ["0002"])
|
|
result = assign_ids(tros, ANCHORS, [IdEntry("X", 1000.0, 0.0)], renumber=True)
|
|
assert result.renumbered
|
|
assert [t.tro_id for t in tros] == ["TRO01", "TRO02"]
|
|
|
|
|
|
def test_datei_rundlauf_und_anhaengen(tmp_path):
|
|
path = tmp_path / "anlage_tro_ids.ini"
|
|
tros = _tros(["0001"], ["0002"])
|
|
assign_ids(tros, ANCHORS, [])
|
|
path.write_text(render_ids(tros, ANCHORS, "anlage.csv")
|
|
+ "\n[separator]\n; spaeter\n", encoding="utf-8")
|
|
entries = load_ids(path)
|
|
assert [(e.tro_id, e.x, e.y) for e in entries] == [
|
|
("TRO01", 0.0, 0.0), ("TRO02", 1000.0, 0.0)]
|
|
|
|
tros = _tros(["0001"], ["0002"], ["0003"])
|
|
result = assign_ids(tros, ANCHORS, entries)
|
|
insert_entries(path, render_new_entries(tros, ANCHORS, result.new, "anlage.csv"))
|
|
assert [e.tro_id for e in load_ids(path)] == ["TRO01", "TRO02", "TRO03"]
|
|
# neuer Eintrag steht in [tro], nicht im folgenden Abschnitt
|
|
assert other_sections(path) == "[separator]\n; spaeter"
|
|
assert TroIds.load(path).exists
|
|
|
|
|
|
@pytest.mark.parametrize("inhalt, meldung", [
|
|
("[tro]\nTRO01 = 1, 2\nTRO01 = 3, 4\n", "already exists"),
|
|
("[tro]\nSYS1 = 1, 2\n", "reserviert"),
|
|
("[tro]\nTRO-1 = 1, 2\n", "SCL-Bezeichner"),
|
|
("[tro]\nTRO01 = 1\n", "erwartet"),
|
|
("[andere]\n", "fehlt"),
|
|
])
|
|
def test_fehlerhafte_datei(tmp_path, inhalt, meldung):
|
|
path = tmp_path / "x.ini"
|
|
path.write_text(inhalt, encoding="utf-8")
|
|
with pytest.raises(ValueError, match=meldung):
|
|
load_ids(path)
|