mirror of
https://github.com/RunLit/Bambu-Run.git
synced 2026-10-21 15:37:38 +01:00
feat(mcp): add get_metrics_history tool
This commit is contained in:
@@ -137,3 +137,51 @@ def _alerts_for_printer(printer, now, window_hours=ALERT_WINDOW_HOURS):
|
|||||||
for entry in hms_list:
|
for entry in hms_list:
|
||||||
counts[(entry.get("attr"), entry.get("code"))] += 1
|
counts[(entry.get("attr"), entry.get("code"))] += 1
|
||||||
return classify_alerts(counts, total)
|
return classify_alerts(counts, total)
|
||||||
|
|
||||||
|
|
||||||
|
def get_metrics_history(
|
||||||
|
printer: str | int | None = None,
|
||||||
|
hours: int = 6,
|
||||||
|
metrics: list[str] | None = None,
|
||||||
|
max_points: int = 300,
|
||||||
|
) -> dict:
|
||||||
|
"""Sampled time-series history for one printer: temps, humidity, progress, fans.
|
||||||
|
|
||||||
|
metrics selects which series to include: any of "temps", "humidity",
|
||||||
|
"progress", "fans" (omit for all four). Server-side sampled to at most
|
||||||
|
max_points (hard ceiling 500) evenly across the window -- do not expect
|
||||||
|
one point per collector poll on a long window. printer defaults to the
|
||||||
|
first active printer when omitted; pass an id or name explicitly if more
|
||||||
|
than one printer is registered.
|
||||||
|
"""
|
||||||
|
from bambu_run.views import sample_metrics # reuse: same thinning the dashboard already uses
|
||||||
|
|
||||||
|
max_points = min(max_points, 500)
|
||||||
|
target = resolve_printer(printer).first()
|
||||||
|
if target is None:
|
||||||
|
return {"printer": None, "timestamps": [], "series": {}}
|
||||||
|
|
||||||
|
since = timezone.now() - timedelta(hours=hours)
|
||||||
|
rows = list(
|
||||||
|
PrinterMetrics.objects.filter(device=target, timestamp__gte=since).order_by("timestamp")
|
||||||
|
)
|
||||||
|
rows = sample_metrics(rows, max_points=max_points)
|
||||||
|
|
||||||
|
wanted = set(metrics) if metrics else {"temps", "humidity", "progress", "fans"}
|
||||||
|
series = {}
|
||||||
|
if "temps" in wanted:
|
||||||
|
series["nozzle_c"] = [float(r.nozzle_temp) if r.nozzle_temp is not None else None for r in rows]
|
||||||
|
series["bed_c"] = [float(r.bed_temp) if r.bed_temp is not None else None for r in rows]
|
||||||
|
series["chamber_c"] = [float(r.chamber_temp) if r.chamber_temp is not None else None for r in rows]
|
||||||
|
if "humidity" in wanted:
|
||||||
|
series["ams_humidity_raw"] = [r.ams_humidity_raw for r in rows]
|
||||||
|
if "progress" in wanted:
|
||||||
|
series["print_percent"] = [r.print_percent for r in rows]
|
||||||
|
if "fans" in wanted:
|
||||||
|
series["cooling_fan_percent"] = [r.cooling_fan_speed for r in rows]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"printer": {"id": target.id, "name": target.name},
|
||||||
|
"timestamps": [r.timestamp.isoformat() for r in rows],
|
||||||
|
"series": series,
|
||||||
|
}
|
||||||
|
|||||||
@@ -129,3 +129,56 @@ def test_get_printer_status_alerts_use_frequency_heuristic_not_hms_emptiness():
|
|||||||
result = get_printer_status(printer=p.id)
|
result = get_printer_status(printer=p.id)
|
||||||
|
|
||||||
assert result["printers"][0]["alerts"] == []
|
assert result["printers"][0]["alerts"] == []
|
||||||
|
|
||||||
|
|
||||||
|
from bambu_run.mcp.status import get_metrics_history
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_get_metrics_history_returns_series_within_window():
|
||||||
|
p = Printer.objects.create(name="RNL-H2C", model="H2C", is_active=True)
|
||||||
|
now = timezone.now()
|
||||||
|
for i in range(3):
|
||||||
|
PrinterMetrics.objects.create(
|
||||||
|
device=p, timestamp=now - timedelta(hours=i),
|
||||||
|
nozzle_temp=200 + i, bed_temp=60, chamber_temp=35,
|
||||||
|
print_percent=50 + i, ams_humidity_raw=5, cooling_fan_speed=100,
|
||||||
|
)
|
||||||
|
PrinterMetrics.objects.create(device=p, timestamp=now - timedelta(hours=10), nozzle_temp=999)
|
||||||
|
|
||||||
|
result = get_metrics_history(printer=p.id, hours=6)
|
||||||
|
|
||||||
|
assert result["printer"]["id"] == p.id
|
||||||
|
assert len(result["timestamps"]) == 3
|
||||||
|
assert 999.0 not in result["series"]["nozzle_c"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_get_metrics_history_filters_to_requested_metrics():
|
||||||
|
p = Printer.objects.create(name="RNL-H2C", model="H2C", is_active=True)
|
||||||
|
PrinterMetrics.objects.create(device=p, timestamp=timezone.now(), nozzle_temp=200, print_percent=10)
|
||||||
|
|
||||||
|
result = get_metrics_history(printer=p.id, metrics=["progress"])
|
||||||
|
|
||||||
|
assert "print_percent" in result["series"]
|
||||||
|
assert "nozzle_c" not in result["series"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_get_metrics_history_hard_ceiling_500_points():
|
||||||
|
p = Printer.objects.create(name="RNL-H2C", model="H2C", is_active=True)
|
||||||
|
now = timezone.now()
|
||||||
|
PrinterMetrics.objects.bulk_create([
|
||||||
|
PrinterMetrics(device=p, timestamp=now - timedelta(minutes=i))
|
||||||
|
for i in range(600)
|
||||||
|
])
|
||||||
|
|
||||||
|
result = get_metrics_history(printer=p.id, hours=24, max_points=10000)
|
||||||
|
|
||||||
|
assert len(result["timestamps"]) <= 500
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_get_metrics_history_no_matching_printer_returns_empty_shape():
|
||||||
|
result = get_metrics_history(printer=None)
|
||||||
|
assert result == {"printer": None, "timestamps": [], "series": {}}
|
||||||
|
|||||||
Reference in New Issue
Block a user