Grotti 2.5.1: Druckbericht, Statistik, Spulenakte und Protokoll-Fixes.

This commit is contained in:
Dominic Gundel committed 2026-10-17 15:32:51 +02:00
1 parent b277ca48aa
commit efd0801cd1
30 files changed
+3393 -178

No files matched your search

+103
View File
@@ -0,0 +1,103 @@
"""ACE-Trockner und Platz-Schreiben: Grenzen und Mapping."""
import pytest
from grotti.moonraker import (
PrinterError,
ace_hub_index,
extract_dryers,
spool_rgb,
steuern,
)
def test_extract_dryers_skips_dummy_unit():
status = {
"mmu_machine": {
"num_units": 1,
"unit_0": {
"num_gates": 4,
"dryer_status": "drying",
"dryer_temp": 44,
"dryer_target_temp": 45,
"dryer_remaining": 120,
"dryer_humidity": 20,
},
"unit_1": {
"num_gates": 0,
"dryer_status": "stop",
},
}
}
dryers = extract_dryers(status)
assert len(dryers) == 1
assert dryers[0]["id"] == 0
assert dryers[0]["status"] == "drying"
assert dryers[0]["target"] == 45
assert dryers[0]["remain"] == 120
def test_extract_dryers_empty_without_machine():
assert extract_dryers({}) == []
assert extract_dryers({"mmu_machine": {}}) == []
def test_ace_hub_index():
assert ace_hub_index(1) == (0, 0)
assert ace_hub_index(4) == (0, 3)
assert ace_hub_index(5) == (1, 0)
assert ace_hub_index(8) == (1, 3)
with pytest.raises(PrinterError):
ace_hub_index(0)
with pytest.raises(PrinterError):
ace_hub_index(9)
def test_spool_rgb_from_colors():
assert spool_rgb({"colors": [{"name": "Blau", "hex": "#002B49"}]}) == {
"R": 0, "G": 43, "B": 73,
}
assert spool_rgb({"colors": [], "color": "Weiß"}) is None
assert spool_rgb({"color": "#ff00aa"}) == {"R": 255, "G": 0, "B": 170}
def test_dryer_start_bounds(monkeypatch):
calls = []
def fake_post(host, path, payload, timeout=20):
calls.append((host, path, payload))
return {"result": "ok"}
monkeypatch.setattr("grotti.moonraker.post_json", fake_post)
steuern("10.10.1.60", "dryer_start", {"id": 0, "temp": 45, "duration": 240})
assert calls[0][1] == "/server/filament_hub/start_drying"
assert calls[0][2]["fan_speed"] == 0
with pytest.raises(PrinterError):
steuern("10.10.1.60", "dryer_start", {"id": 2, "temp": 45, "duration": 60})
with pytest.raises(PrinterError):
steuern("10.10.1.60", "dryer_start", {"id": 0, "temp": 20, "duration": 60})
with pytest.raises(PrinterError):
steuern("10.10.1.60", "dryer_start", {"id": 0, "temp": 45, "duration": 0})
def test_slot_setzen_payload(monkeypatch):
calls = []
def fake_post(host, path, payload, timeout=20):
calls.append((host, path, payload))
return {"result": "ok"}
monkeypatch.setattr("grotti.moonraker.post_json", fake_post)
steuern(
"10.10.1.60",
"slot_setzen",
{"id": 0, "index": 2, "type": "PLA", "color": {"R": 1, "G": 2, "B": 3}},
)
assert calls[0][1] == "/printer/filament_hub/set_filament_info"
assert calls[0][2] == {
"id": 0,
"index": 2,
"type": "PLA",
"color": {"R": 1, "G": 2, "B": 3},
}
+75
View File
@@ -0,0 +1,75 @@
"""Ist-Gramm und Spulenanteile ohne Datenbank."""
import unittest
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from grotti import usage
class ResolveUsedTests(unittest.TestCase):
def test_measured_from_mm(self):
grams, source = usage.resolve_used_grams(100, 500, 1000, 0, "cancelled", 60)
self.assertAlmostEqual(grams, 50)
self.assertEqual(source, "measured")
def test_abort_before_extrude(self):
grams, source = usage.resolve_used_grams(100, 0, 1000, 0, "cancelled", 5)
self.assertEqual(grams, 0)
self.assertEqual(source, "none")
def test_abort_progress_estimate(self):
grams, source = usage.resolve_used_grams(100, 0, 0, 0.25, "cancelled", 120)
self.assertAlmostEqual(grams, 25)
self.assertEqual(source, "estimate")
def test_completed_keeps_planned_without_mm(self):
grams, source = usage.resolve_used_grams(80, 0, 0, 1, "completed", 3600)
self.assertAlmostEqual(grams, 80)
self.assertEqual(source, "estimate")
def test_overshoot_no_cap(self):
grams, source = usage.resolve_used_grams(100, 1200, 1000, 1, "completed", 3600)
self.assertAlmostEqual(grams, 120)
self.assertEqual(source, "measured")
class SplitTests(unittest.TestCase):
def test_single_is_measured(self):
parts, estimate = usage.split_used(50, [100], multi=False)
self.assertEqual(parts, [50])
self.assertFalse(estimate)
def test_multi_is_estimate(self):
parts, estimate = usage.split_used(90, [30, 60], multi=True)
self.assertAlmostEqual(parts[0], 30)
self.assertAlmostEqual(parts[1], 60)
self.assertTrue(estimate)
class SegmentTests(unittest.TestCase):
def test_deltas(self):
segs = [
{"spool_id": 1, "ace_slot": None, "mm_start": 0, "mm_end": 400},
{"spool_id": 2, "ace_slot": None, "mm_start": 400, "mm_end": None},
]
deltas = usage.segment_deltas(segs, 1000)
self.assertEqual(deltas[0], (1, None, 400))
self.assertEqual(deltas[1], (2, None, 600))
def test_grams_from_mm(self):
self.assertAlmostEqual(usage.grams_from_mm(250, 1000, 80), 20)
class WeightListTests(unittest.TestCase):
def test_list_and_sum(self):
meta = {"filament_weight_total": [10, 20, 5]}
self.assertEqual(usage.weight_list(meta), [10, 20, 5])
self.assertEqual(usage.weight_sum(meta), 35)
if __name__ == "__main__":
unittest.main()