Files
mission-control-v2/backend/services/system.py
T
Hitonabi 2e6655c398 fix(2.0): GPU-Metrik findet Strix-Halo-Card dynamisch (card1) + GTT
Real-Box-Befund: GPU ist card1 (nicht hardcoded card0), Connector-Dirs
uebersprungen. Zusaetzlich GTT (echter Unified-Memory-Pool) statt nur
VRAM-Carve-out. Frontend zeigt GTT bevorzugt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 08:33:29 +02:00

82 lines
2.7 KiB
Python

"""
System/OS-Metriken für die Box (Bosgame / Strix Halo).
CPU/RAM/Disk via psutil (plattformübergreifend). GPU-Auslastung/VRAM/Temperatur
via sysfs (amdgpu) — nur Linux; auf anderen Plattformen None (amd-smi fehlt auf
der Box, daher sysfs). Verschachtelte Struktur wie v1 (cpu.percent, ram.used Bytes).
"""
import glob
import os
import psutil
from config import MODELS_DIR
def _read_int(path: str) -> int | None:
try:
with open(path) as f:
return int(f.read().strip())
except Exception:
return None
def _gpu_sysfs() -> dict | None:
"""AMD-GPU-Auslastung + Speicher via sysfs (Linux). Findet die Basis-Card
dynamisch (Strix Halo ist oft card1, nicht card0) und überspringt die
Connector-Verzeichnisse (card1-DP-1 …). Strix Halo nutzt Unified Memory →
GTT ist der eigentliche große Pool; VRAM ist nur der kleine Carve-out."""
for dev in sorted(glob.glob("/sys/class/drm/card*/device")):
card = dev.split("/")[-2] # z.B. "card1" oder "card1-DP-1"
if "-" in card: # Connector-Dir → kein GPU-Device
continue
busy = _read_int(f"{dev}/gpu_busy_percent")
if busy is None:
continue
return {
"busy_percent": busy,
"vram_used": _read_int(f"{dev}/mem_info_vram_used"),
"vram_total": _read_int(f"{dev}/mem_info_vram_total"),
"gtt_used": _read_int(f"{dev}/mem_info_gtt_used"),
"gtt_total": _read_int(f"{dev}/mem_info_gtt_total"),
}
return None
def _temps() -> dict | None:
"""CPU/GPU-Temperatur via hwmon (Linux). None bei Fehlen."""
out: dict = {}
for hw in glob.glob("/sys/class/hwmon/hwmon*"):
name = ""
try:
with open(f"{hw}/name") as f:
name = f.read().strip()
except Exception:
continue
t = _read_int(f"{hw}/temp1_input")
if t is None:
continue
c = round(t / 1000.0, 1)
if name in ("k10temp", "zenpower", "coretemp"):
out["cpu"] = c
elif name in ("amdgpu", "edge"):
out["gpu"] = c
return out or None
def system_status() -> dict:
vm = psutil.virtual_memory()
try:
du = psutil.disk_usage(str(MODELS_DIR) if MODELS_DIR.exists() else os.getcwd())
disk = {"total": du.total, "used": du.used, "percent": du.percent}
except Exception:
disk = None
return {
"cpu": {"percent": psutil.cpu_percent(interval=0.1), "cores": psutil.cpu_count()},
"ram": {"total": vm.total, "used": vm.used, "percent": vm.percent},
"gpu": _gpu_sysfs(),
"temp": _temps(),
"disk": disk,
}