Merge branch '0.1.8b' into feature/3d_plate_view_v2

This commit is contained in:
MartinNYHC
2026-02-03 20:50:58 +01:00
committed by GitHub
2 changed files with 49 additions and 5 deletions
+1
View File
@@ -10,6 +10,7 @@
<p align="center">
<a href="https://github.com/maziggy/bambuddy/releases"><img src="https://img.shields.io/github/v/release/maziggy/bambuddy?style=flat-square&color=blue" alt="Release"></a>
<img src="https://github.com/maziggy/bambuddy/actions/workflows/github-code-scanning/codeql/badge.svg">
<img src="https://github.com/maziggy/bambuddy/actions/workflows/ci.yml/badge.svg?branch=main">
<a href="https://github.com/maziggy/bambuddy/blob/main/LICENSE"><img src="https://img.shields.io/github/license/maziggy/bambuddy?style=flat-square" alt="License"></a>
<a href="https://github.com/maziggy/bambuddy/stargazers"><img src="https://img.shields.io/github/stars/maziggy/bambuddy?style=flat-square" alt="Stars"></a>
+48 -5
View File
@@ -5,7 +5,7 @@ import json
import logging
import xml.etree.ElementTree as ET
import zipfile
from datetime import datetime
from datetime import datetime, timedelta
from pathlib import Path
from sqlalchemy import func, select
@@ -76,6 +76,18 @@ class PrintScheduler:
if item.scheduled_time and item.scheduled_time > datetime.utcnow():
continue
# Safety: Skip stale items (older than 24 hours) to prevent phantom reprints
# This protects against items that got stuck in "pending" status due to
# crashes/restarts after the print already started
stale_threshold = timedelta(hours=24)
if item.created_at and datetime.utcnow() - item.created_at.replace(tzinfo=None) > stale_threshold:
logger.warning(f"Queue item {item.id} is stale (created {item.created_at}), marking as expired")
item.status = "expired"
item.error_message = "Queue item expired - older than 24 hours"
item.completed_at = datetime.utcnow()
await db.commit()
continue
# Skip items that require manual start
if item.manual_start:
continue
@@ -809,6 +821,28 @@ class PrintScheduler:
logger.error(f"Queue item {item.id}: Archive {item.archive_id} not found")
await self._power_off_if_needed(db, item)
return
# Safety: Check if this archive was printed recently (within 4 hours)
# This prevents phantom reprints if a queue item got stuck in "pending"
# after its print already started due to a crash/restart
if archive.status == "completed" and archive.completed_at:
completed_at = (
archive.completed_at.replace(tzinfo=None) if archive.completed_at.tzinfo else archive.completed_at
)
time_since_completed = datetime.utcnow() - completed_at
if time_since_completed < timedelta(hours=4):
logger.warning(
f"Queue item {item.id}: Archive {item.archive_id} was already printed "
f"{time_since_completed.total_seconds() / 3600:.1f} hours ago, skipping to prevent duplicate"
)
item.status = "skipped"
item.error_message = (
f"Archive was already printed {time_since_completed.total_seconds() / 3600:.1f} hours ago"
)
item.completed_at = datetime.utcnow()
await db.commit()
return
file_path = settings.base_dir / archive.file_path
filename = archive.filename
@@ -953,6 +987,17 @@ class PrintScheduler:
except json.JSONDecodeError:
logger.warning(f"Queue item {item.id}: Invalid AMS mapping JSON, ignoring")
# IMPORTANT: Set status to "printing" BEFORE sending the print command.
# This prevents phantom reprints if the backend crashes/restarts after the
# print command is sent but before the status update is committed.
# If we crash after this commit but before start_print(), the item will be
# in "printing" status without actually printing - but that's safer than
# accidentally reprinting the same file hours later.
item.status = "printing"
item.started_at = datetime.utcnow()
await db.commit()
logger.info(f"Queue item {item.id}: Status set to 'printing', sending print command...")
# Start the print with AMS mapping, plate_id and print options
started = printer_manager.start_print(
item.printer_id,
@@ -968,10 +1013,7 @@ class PrintScheduler:
)
if started:
item.status = "printing"
item.started_at = datetime.utcnow()
await db.commit()
logger.info(f"Queue item {item.id}: Print started - {filename}")
logger.info(f"Queue item {item.id}: Print started successfully - {filename}")
# Get estimated time for notification
estimated_time = None
@@ -1003,6 +1045,7 @@ class PrintScheduler:
except Exception:
pass # Don't fail if MQTT fails
else:
# Print command failed - revert status
item.status = "failed"
item.error_message = "Failed to send print command to printer"
item.completed_at = datetime.utcnow()