|
@@ -1236,21 +1236,25 @@ async def on_printer_status_change(printer_id: int, state: PrinterState):
|
|
|
if current_milestone > last_milestone:
|
|
if current_milestone > last_milestone:
|
|
|
_last_progress_milestone[printer_id] = current_milestone
|
|
_last_progress_milestone[printer_id] = current_milestone
|
|
|
try:
|
|
try:
|
|
|
- async with async_session() as db:
|
|
|
|
|
- from backend.app.models.printer import Printer
|
|
|
|
|
|
|
+ from backend.app.models.printer import Printer
|
|
|
|
|
|
|
|
|
|
+ # Read the printer in a short session and release the connection
|
|
|
|
|
+ # BEFORE the ~15s camera snapshot below — holding it across the grab
|
|
|
|
|
+ # pinned a pooled connection per milestone, per printer (issue #2572).
|
|
|
|
|
+ async with async_session() as db:
|
|
|
result = await db.execute(select(Printer).where(Printer.id == printer_id))
|
|
result = await db.execute(select(Printer).where(Printer.id == printer_id))
|
|
|
printer = result.scalar_one_or_none()
|
|
printer = result.scalar_one_or_none()
|
|
|
- printer_name = printer.name if printer else f"Printer {printer_id}"
|
|
|
|
|
- filename = state.subtask_name or state.gcode_file or "Unknown"
|
|
|
|
|
- # remaining_time is in minutes, convert to seconds for notification
|
|
|
|
|
- remaining_time_seconds = state.remaining_time * 60 if state.remaining_time else None
|
|
|
|
|
|
|
|
|
|
- # Capture camera snapshot for notification image attachment
|
|
|
|
|
- image_data = await _capture_snapshot_for_notification(
|
|
|
|
|
- printer_id, printer, logging.getLogger(__name__)
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ printer_name = printer.name if printer else f"Printer {printer_id}"
|
|
|
|
|
+ filename = state.subtask_name or state.gcode_file or "Unknown"
|
|
|
|
|
+ # remaining_time is in minutes, convert to seconds for notification
|
|
|
|
|
+ remaining_time_seconds = state.remaining_time * 60 if state.remaining_time else None
|
|
|
|
|
|
|
|
|
|
+ # Capture camera snapshot for notification image attachment (no DB held).
|
|
|
|
|
+ image_data = await _capture_snapshot_for_notification(printer_id, printer, logging.getLogger(__name__))
|
|
|
|
|
+
|
|
|
|
|
+ # Notification send needs a session (provider/template lookups).
|
|
|
|
|
+ async with async_session() as db:
|
|
|
await notification_service.on_print_progress(
|
|
await notification_service.on_print_progress(
|
|
|
printer_id,
|
|
printer_id,
|
|
|
printer_name,
|
|
printer_name,
|
|
@@ -1293,30 +1297,35 @@ async def on_printer_status_change(printer_id: int, state: PrinterState):
|
|
|
new_errors = [e for e in current_hms_errors if f"{e.attr:08x}" in new_error_codes and e.severity >= 2]
|
|
new_errors = [e for e in current_hms_errors if f"{e.attr:08x}" in new_error_codes and e.severity >= 2]
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
- async with async_session() as db:
|
|
|
|
|
- from backend.app.models.printer import Printer
|
|
|
|
|
|
|
+ from backend.app.models.printer import Printer
|
|
|
|
|
|
|
|
|
|
+ # Read the printer in a short session and release the connection
|
|
|
|
|
+ # BEFORE the ~15s camera snapshot below (issue #2572).
|
|
|
|
|
+ async with async_session() as db:
|
|
|
result = await db.execute(select(Printer).where(Printer.id == printer_id))
|
|
result = await db.execute(select(Printer).where(Printer.id == printer_id))
|
|
|
printer = result.scalar_one_or_none()
|
|
printer = result.scalar_one_or_none()
|
|
|
- printer_name = printer.name if printer else f"Printer {printer_id}"
|
|
|
|
|
-
|
|
|
|
|
- # Format error details for notification
|
|
|
|
|
- # Module 0x07 = AMS/Filament, 0x05 = Nozzle, 0x0C = Motion Controller, etc.
|
|
|
|
|
- module_names = {
|
|
|
|
|
- 0x03: "Print/Task",
|
|
|
|
|
- 0x05: "Nozzle/Extruder",
|
|
|
|
|
- 0x07: "AMS/Filament",
|
|
|
|
|
- 0x0C: "Motion Controller",
|
|
|
|
|
- 0x12: "Chamber",
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- from backend.app.services.hms_errors import get_error_description
|
|
|
|
|
|
|
+ printer_name = printer.name if printer else f"Printer {printer_id}"
|
|
|
|
|
|
|
|
- # Capture camera snapshot once for all error notifications
|
|
|
|
|
- error_image_data = await _capture_snapshot_for_notification(
|
|
|
|
|
- printer_id, printer, logging.getLogger(__name__)
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ # Format error details for notification
|
|
|
|
|
+ # Module 0x07 = AMS/Filament, 0x05 = Nozzle, 0x0C = Motion Controller, etc.
|
|
|
|
|
+ module_names = {
|
|
|
|
|
+ 0x03: "Print/Task",
|
|
|
|
|
+ 0x05: "Nozzle/Extruder",
|
|
|
|
|
+ 0x07: "AMS/Filament",
|
|
|
|
|
+ 0x0C: "Motion Controller",
|
|
|
|
|
+ 0x12: "Chamber",
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ from backend.app.services.hms_errors import get_error_description
|
|
|
|
|
+
|
|
|
|
|
+ # Capture camera snapshot once for all error notifications (no DB held).
|
|
|
|
|
+ error_image_data = await _capture_snapshot_for_notification(
|
|
|
|
|
+ printer_id, printer, logging.getLogger(__name__)
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # Notification sends need a session (provider/template lookups).
|
|
|
|
|
+ async with async_session() as db:
|
|
|
sent_count = 0
|
|
sent_count = 0
|
|
|
for error in new_errors:
|
|
for error in new_errors:
|
|
|
module_name = module_names.get(error.module, f"Module 0x{error.module:02X}")
|
|
module_name = module_names.get(error.module, f"Module 0x{error.module:02X}")
|
|
@@ -1345,21 +1354,21 @@ async def on_printer_status_change(printer_id: int, state: PrinterState):
|
|
|
f"[HMS] Sent notification for {sent_count} error(s) on printer {printer_id}"
|
|
f"[HMS] Sent notification for {sent_count} error(s) on printer {printer_id}"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- # Also publish to MQTT relay
|
|
|
|
|
- printer_info = printer_manager.get_printer(printer_id)
|
|
|
|
|
- if printer_info:
|
|
|
|
|
- errors_data = [
|
|
|
|
|
- {
|
|
|
|
|
- "code": e.code,
|
|
|
|
|
- "attr": e.attr,
|
|
|
|
|
- "module": e.module,
|
|
|
|
|
- "severity": e.severity,
|
|
|
|
|
- }
|
|
|
|
|
- for e in new_errors
|
|
|
|
|
- ]
|
|
|
|
|
- await mqtt_relay.on_printer_error(
|
|
|
|
|
- printer_id, printer_info.name, printer_info.serial_number, errors_data
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ # Also publish to MQTT relay (no DB).
|
|
|
|
|
+ printer_info = printer_manager.get_printer(printer_id)
|
|
|
|
|
+ if printer_info:
|
|
|
|
|
+ errors_data = [
|
|
|
|
|
+ {
|
|
|
|
|
+ "code": e.code,
|
|
|
|
|
+ "attr": e.attr,
|
|
|
|
|
+ "module": e.module,
|
|
|
|
|
+ "severity": e.severity,
|
|
|
|
|
+ }
|
|
|
|
|
+ for e in new_errors
|
|
|
|
|
+ ]
|
|
|
|
|
+ await mqtt_relay.on_printer_error(
|
|
|
|
|
+ printer_id, printer_info.name, printer_info.serial_number, errors_data
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
logging.getLogger(__name__).warning(f"HMS error notification failed: {e}")
|
|
logging.getLogger(__name__).warning(f"HMS error notification failed: {e}")
|