mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-19 15:54:27 +02:00
Fix maintenance tasks showing wrong rod type for printer models (#351)
H2/A1 series have linear rails, not carbon rods. X1/P1/P2S have carbon rods, not linear rails. Split rod-specific maintenance into model-aware pairs: "Lubricate/Clean Carbon Rods" for X1/P1/P2S and "Lubricate/Clean Linear Rails" for A1/H2. Added rod type classification to printer_models and startup cleanup for stale/duplicate system maintenance types.
This commit is contained in:
@@ -26,6 +26,7 @@ from backend.app.schemas.maintenance import (
|
||||
PrinterMaintenanceUpdate,
|
||||
)
|
||||
from backend.app.services.notification_service import notification_service
|
||||
from backend.app.utils.printer_models import get_rod_type
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -33,12 +34,33 @@ router = APIRouter(prefix="/maintenance", tags=["maintenance"])
|
||||
|
||||
# Default maintenance types
|
||||
DEFAULT_MAINTENANCE_TYPES = [
|
||||
# Carbon rod models only (X1/P1/P2S)
|
||||
{
|
||||
"name": "Lubricate Linear Rails",
|
||||
"description": "Apply lubricant to linear rails and rods for smooth motion",
|
||||
"name": "Lubricate Carbon Rods",
|
||||
"description": "Apply lubricant to carbon rods for smooth motion",
|
||||
"default_interval_hours": 50.0,
|
||||
"icon": "Droplet",
|
||||
},
|
||||
{
|
||||
"name": "Clean Carbon Rods",
|
||||
"description": "Wipe carbon rods with a dry cloth",
|
||||
"default_interval_hours": 100.0,
|
||||
"icon": "Sparkles",
|
||||
},
|
||||
# Linear rail models only (A1/H2)
|
||||
{
|
||||
"name": "Lubricate Linear Rails",
|
||||
"description": "Apply lubricant to linear rails for smooth motion",
|
||||
"default_interval_hours": 50.0,
|
||||
"icon": "Droplet",
|
||||
},
|
||||
{
|
||||
"name": "Clean Linear Rails",
|
||||
"description": "Wipe linear rails with a dry cloth to remove dust and debris",
|
||||
"default_interval_hours": 100.0,
|
||||
"icon": "Sparkles",
|
||||
},
|
||||
# Universal (all models)
|
||||
{
|
||||
"name": "Clean Nozzle/Hotend",
|
||||
"description": "Clean nozzle exterior and perform cold pull if needed",
|
||||
@@ -51,12 +73,6 @@ DEFAULT_MAINTENANCE_TYPES = [
|
||||
"default_interval_hours": 200.0,
|
||||
"icon": "Ruler",
|
||||
},
|
||||
{
|
||||
"name": "Clean Carbon Rods",
|
||||
"description": "Wipe carbon rods with a dry cloth",
|
||||
"default_interval_hours": 100.0,
|
||||
"icon": "Sparkles",
|
||||
},
|
||||
{
|
||||
"name": "Clean Build Plate",
|
||||
"description": "Deep clean build plate with IPA or soap",
|
||||
@@ -71,6 +87,30 @@ DEFAULT_MAINTENANCE_TYPES = [
|
||||
},
|
||||
]
|
||||
|
||||
# System types that only apply to printers with a specific rod/rail type.
|
||||
# "carbon" = X1/P1/P2S series (carbon rods), "linear_rail" = A1/H2 series.
|
||||
# Types not listed here apply to all printers.
|
||||
_ROD_TYPE_REQUIREMENTS: dict[str, str] = {
|
||||
"Lubricate Carbon Rods": "carbon",
|
||||
"Clean Carbon Rods": "carbon",
|
||||
"Lubricate Linear Rails": "linear_rail",
|
||||
"Clean Linear Rails": "linear_rail",
|
||||
}
|
||||
|
||||
|
||||
def _should_apply_to_printer(type_name: str, printer_model: str | None) -> bool:
|
||||
"""Check if a system maintenance type should apply to a given printer model."""
|
||||
rod_requirement = _ROD_TYPE_REQUIREMENTS.get(type_name)
|
||||
if rod_requirement is None:
|
||||
return True # Not model-specific, applies to all
|
||||
|
||||
rod_type = get_rod_type(printer_model)
|
||||
if rod_type is None:
|
||||
# Unknown model — default to carbon rods (legacy behavior)
|
||||
return rod_requirement == "carbon"
|
||||
|
||||
return rod_type == rod_requirement
|
||||
|
||||
|
||||
async def get_printer_total_hours(db: AsyncSession, printer_id: int) -> float:
|
||||
"""Calculate total active hours for a printer from runtime counter plus offset.
|
||||
@@ -94,13 +134,27 @@ async def get_printer_total_hours(db: AsyncSession, printer_id: int) -> float:
|
||||
|
||||
|
||||
async def ensure_default_types(db: AsyncSession) -> None:
|
||||
"""Ensure default maintenance types exist."""
|
||||
result = await db.execute(select(MaintenanceType).where(MaintenanceType.is_system.is_(True)))
|
||||
"""Ensure default maintenance types exist, remove stale/duplicate ones."""
|
||||
result = await db.execute(
|
||||
select(MaintenanceType).where(MaintenanceType.is_system.is_(True)).order_by(MaintenanceType.id)
|
||||
)
|
||||
existing = result.scalars().all()
|
||||
existing_names = {t.name for t in existing}
|
||||
|
||||
default_names = {t["name"] for t in DEFAULT_MAINTENANCE_TYPES}
|
||||
|
||||
# Remove stale system types no longer in defaults (e.g. renamed types)
|
||||
# and deduplicate: if concurrent requests created the same type twice,
|
||||
# keep only the first (lowest id) and delete the rest.
|
||||
seen_names: set[str] = set()
|
||||
for t in existing:
|
||||
if t.name not in default_names or t.name in seen_names:
|
||||
await db.delete(t)
|
||||
else:
|
||||
seen_names.add(t.name)
|
||||
|
||||
# Create any missing default types
|
||||
for type_def in DEFAULT_MAINTENANCE_TYPES:
|
||||
if type_def["name"] not in existing_names:
|
||||
if type_def["name"] not in seen_names:
|
||||
new_type = MaintenanceType(
|
||||
name=type_def["name"],
|
||||
description=type_def["description"],
|
||||
@@ -228,6 +282,11 @@ async def _get_printer_maintenance_internal(
|
||||
now = datetime.utcnow()
|
||||
|
||||
for maint_type in all_types:
|
||||
# Skip system types that don't apply to this printer model
|
||||
# (e.g., "Clean Carbon Rods" for H2D which has steel rods)
|
||||
if maint_type.is_system and not _should_apply_to_printer(maint_type.name, printer.model):
|
||||
continue
|
||||
|
||||
item = existing_items.get(maint_type.id)
|
||||
default_interval_type = getattr(maint_type, "interval_type", "hours") or "hours"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user