Füge Mock-FTPS-Client-Implementierung hinzu, um FTP-Zugriffe zu simulieren; erweitere Fehlerbehandlung und aktualisiere die Dateiliste mit Mock-Dateien.
This commit is contained in:
parent
f5e6b3d0dd
commit
ba43df279d
@ -304,14 +304,18 @@ class BambuVirtualPrinter:
|
||||
print_data = payload['print']
|
||||
self._log.info(f"Processing print data with keys: {list(print_data.keys())}")
|
||||
|
||||
# Temperaturdaten verarbeiten
|
||||
if 'temperature' in print_data:
|
||||
self._process_temperature_data(print_data['temperature'])
|
||||
# Temperaturdaten direkt verarbeiten, ohne auf "temperature" als Schlüssel zu warten
|
||||
# Bambu-Drucker verwenden Schlüssel wie "nozzle_temper" direkt im print-Objekt
|
||||
self._process_direct_temperature_data(print_data)
|
||||
|
||||
# Status verarbeiten
|
||||
if 'gcode_state' in print_data:
|
||||
self._process_print_state(print_data['gcode_state'])
|
||||
|
||||
# Fortschritt verarbeiten
|
||||
if 'mc_percent' in print_data:
|
||||
self._process_progress_data(print_data)
|
||||
|
||||
# Trigger update
|
||||
self.new_update("event_printer_data_update")
|
||||
|
||||
@ -327,79 +331,79 @@ class BambuVirtualPrinter:
|
||||
except Exception as e:
|
||||
self._log.error(f"Error processing MQTT payload: {e}", exc_info=True)
|
||||
|
||||
def _process_temperature_data(self, temp_data):
|
||||
"""Verarbeitet Temperaturdaten aus MQTT-Nachrichten"""
|
||||
def _process_direct_temperature_data(self, print_data):
|
||||
"""Verarbeitet Temperaturdaten direkt aus dem print-Objekt"""
|
||||
try:
|
||||
# Extruder Temperatur
|
||||
if 'nozzle_temp' in temp_data:
|
||||
self._telemetry.temp[0] = float(temp_data['nozzle_temp'])
|
||||
if 'target_nozzle_temp' in temp_data:
|
||||
self._telemetry.targetTemp[0] = float(temp_data['target_nozzle_temp'])
|
||||
# Extruder Temperatur - direkt aus den Feldern
|
||||
if 'nozzle_temper' in print_data:
|
||||
self._telemetry.temp[0] = float(print_data['nozzle_temper'])
|
||||
self._log.debug(f"Updated nozzle temperature: {self._telemetry.temp[0]}")
|
||||
if 'nozzle_target_temper' in print_data:
|
||||
self._telemetry.targetTemp[0] = float(print_data['nozzle_target_temper'])
|
||||
self._log.debug(f"Updated nozzle target: {self._telemetry.targetTemp[0]}")
|
||||
|
||||
# Bett Temperatur
|
||||
if 'bed_temp' in temp_data:
|
||||
self._telemetry.bedTemp = float(temp_data['bed_temp'])
|
||||
if 'target_bed_temp' in temp_data:
|
||||
self._telemetry.bedTargetTemp = float(temp_data['target_bed_temp'])
|
||||
if 'bed_temper' in print_data:
|
||||
self._telemetry.bedTemp = float(print_data['bed_temper'])
|
||||
self._log.debug(f"Updated bed temperature: {self._telemetry.bedTemp}")
|
||||
if 'bed_target_temper' in print_data:
|
||||
self._telemetry.bedTargetTemp = float(print_data['bed_target_temper'])
|
||||
self._log.debug(f"Updated bed target: {self._telemetry.bedTargetTemp}")
|
||||
|
||||
# Kammer Temperatur
|
||||
if 'chamber_temp' in temp_data:
|
||||
self._telemetry.chamberTemp = float(temp_data['chamber_temp'])
|
||||
if 'chamber_temper' in print_data:
|
||||
self._telemetry.chamberTemp = float(print_data['chamber_temper'])
|
||||
self._log.debug(f"Updated chamber temperature: {self._telemetry.chamberTemp}")
|
||||
|
||||
self._log.debug(f"Updated temperatures - Nozzle: {self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
|
||||
# Log der aktualisierten Temperaturen
|
||||
self._log.debug(f"Current temperatures - Nozzle: {self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
|
||||
f"Bed: {self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}, " +
|
||||
f"Chamber: {self._telemetry.chamberTemp}")
|
||||
|
||||
# Auch im BambuClient aktualisieren
|
||||
if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'temperature'):
|
||||
try:
|
||||
temp_obj = self._bambu_client.device.temperature
|
||||
if 'nozzle_temper' in print_data:
|
||||
temp_obj.nozzle_temp = float(print_data['nozzle_temper'])
|
||||
if 'nozzle_target_temper' in print_data:
|
||||
temp_obj.target_nozzle_temp = float(print_data['nozzle_target_temper'])
|
||||
if 'bed_temper' in print_data:
|
||||
temp_obj.bed_temp = float(print_data['bed_temper'])
|
||||
if 'bed_target_temper' in print_data:
|
||||
temp_obj.target_bed_temp = float(print_data['bed_target_temper'])
|
||||
if 'chamber_temper' in print_data:
|
||||
temp_obj.chamber_temp = float(print_data['chamber_temper'])
|
||||
except Exception as e:
|
||||
self._log.error(f"Error updating BambuClient temperature: {e}")
|
||||
|
||||
except Exception as e:
|
||||
self._log.error(f"Error processing temperature data: {e}", exc_info=True)
|
||||
|
||||
def _process_print_state(self, print_job_state):
|
||||
"""Verarbeitet den Druckerstatus aus MQTT-Nachrichten"""
|
||||
def _process_progress_data(self, print_data):
|
||||
"""Verarbeitet Fortschrittsdaten aus MQTT-Nachrichten"""
|
||||
try:
|
||||
self._log.debug(f"Received printer state update: {print_job_state}")
|
||||
|
||||
# Normalisieren des Status, falls er 'unknown' ist
|
||||
if print_job_state == "unknown":
|
||||
# Wenn der Status unbekannt ist, versuchen wir, ihn aus anderen Informationen abzuleiten
|
||||
if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'):
|
||||
# Prüfe ob Druckfortschritt vorhanden ist
|
||||
if hasattr(self._bambu_client.device.print_job, 'mc_percent') and self._bambu_client.device.print_job.mc_percent > 0:
|
||||
print_job_state = "RUNNING"
|
||||
self._log.debug(f"Changed unknown state to RUNNING based on print progress")
|
||||
|
||||
if print_job_state in ["IDLE", "FINISH", "FAILED"]:
|
||||
self.change_state(self._state_idle)
|
||||
elif print_job_state in ["RUNNING", "PREPARE"]:
|
||||
self.change_state(self._state_printing)
|
||||
elif print_job_state == "PAUSE":
|
||||
self.change_state(self._state_paused)
|
||||
elif print_job_state == "unknown":
|
||||
# Wenn wir keine bessere Information haben, betrachten wir es als IDLE
|
||||
self._log.debug("Keeping current state due to unknown printer state")
|
||||
else:
|
||||
self._log.warn(f"Unknown print job state: {print_job_state}")
|
||||
progress = -1
|
||||
if 'mc_percent' in print_data:
|
||||
progress = int(print_data['mc_percent'])
|
||||
|
||||
remaining_time = 0
|
||||
if 'mc_remaining_time' in print_data:
|
||||
remaining_time = int(print_data['mc_remaining_time'])
|
||||
|
||||
# Aktualisiere den PrintJob, wenn einer existiert
|
||||
if self.current_print_job is not None:
|
||||
self.current_print_job.print_percentage = progress
|
||||
self.current_print_job.remaining_time = remaining_time
|
||||
self._log.debug(f"Updated print progress: {progress}%, remaining: {remaining_time}s")
|
||||
|
||||
# Aktualisiere auch die pybambu-Datenstruktur
|
||||
if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'):
|
||||
self._bambu_client.device.print_job.gcode_state = print_job_state
|
||||
except Exception as e:
|
||||
self._log.error(f"Error processing print state: {e}", exc_info=True)
|
||||
|
||||
def _process_hms_errors(self, hms_data):
|
||||
"""Verarbeitet HMS-Fehlerdaten aus MQTT-Nachrichten"""
|
||||
try:
|
||||
if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'hms'):
|
||||
self._bambu_client.device.hms.update_from_payload(hms_data)
|
||||
self._log.debug("HMS error data updated")
|
||||
self._bambu_client.device.print_job.mc_percent = progress
|
||||
self._bambu_client.device.print_job.mc_remaining_time = remaining_time
|
||||
|
||||
# Überprüfe auf Fehler und zeige sie an
|
||||
if self._bambu_client.device.hms.errors != self._last_hms_errors and self._bambu_client.device.hms.errors["Count"] > 0:
|
||||
self._log.debug(f"HMS Error: {self._bambu_client.device.hms.errors}")
|
||||
for n in range(1, self._bambu_client.device.hms.errors["Count"] + 1):
|
||||
error = self._bambu_client.device.hms.errors[f"{n}-Error"].strip()
|
||||
self.sendIO(f"// action:notification {error}")
|
||||
self._last_hms_errors = self._bambu_client.device.hms.errors
|
||||
except Exception as e:
|
||||
self._log.error(f"Error processing HMS errors: {e}", exc_info=True)
|
||||
self._log.error(f"Error processing progress data: {e}", exc_info=True)
|
||||
|
||||
def _update_bambu_client_state(self, payload):
|
||||
"""Aktualisiert die internen Zustände des BambuClient"""
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Callable
|
||||
from typing import TYPE_CHECKING, Callable, List, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from octoprint_bambu_printer.printer.file_system.remote_sd_card_file_list import (
|
||||
@ -12,83 +12,59 @@ from pathlib import Path
|
||||
from octoprint_bambu_printer.printer.file_system.file_info import FileInfo
|
||||
|
||||
|
||||
@dataclass
|
||||
class CachedFileView:
|
||||
file_system: RemoteSDCardFileList
|
||||
folder_view: dict[tuple[str, str | list[str] | None], None] = field(
|
||||
default_factory=dict
|
||||
) # dict preserves order, but set does not. We use only dict keys as storage
|
||||
on_update: Callable[[], None] | None = None
|
||||
def __init__(
|
||||
self, file_system, on_update: Optional[Callable] = None, base_path: str = ""
|
||||
):
|
||||
self._filters = []
|
||||
self._file_system = file_system
|
||||
self._base_path = base_path
|
||||
self._update_complete_callback = on_update
|
||||
self._file_info_cache = []
|
||||
|
||||
def __post_init__(self):
|
||||
self._file_alias_cache: dict[str, str] = {}
|
||||
self._file_data_cache: dict[str, FileInfo] = {}
|
||||
|
||||
def with_filter(
|
||||
self, folder: str, extensions: str | list[str] | None = None
|
||||
) -> "CachedFileView":
|
||||
self.folder_view[(folder, extensions)] = None
|
||||
def with_filter(self, path: str, extension: str):
|
||||
self._filters.append({"path": path, "extension": extension})
|
||||
return self
|
||||
|
||||
def list_all_views(self):
|
||||
existing_files: list[str] = []
|
||||
result: list[FileInfo] = []
|
||||
def update(self) -> None:
|
||||
try:
|
||||
file_info_list = self.list_all_views()
|
||||
self._file_info_cache = file_info_list
|
||||
|
||||
# Rufe Callback auf, wenn vorhanden
|
||||
if self._update_complete_callback is not None:
|
||||
self._update_complete_callback()
|
||||
except Exception as e:
|
||||
import logging
|
||||
logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter").error(
|
||||
f"Error updating file list: {e}", exc_info=True
|
||||
)
|
||||
|
||||
with self.file_system.get_ftps_client() as ftp:
|
||||
for filter in self.folder_view.keys():
|
||||
result.extend(self.file_system.list_files(*filter, ftp, existing_files))
|
||||
return result
|
||||
def list_all_views(self) -> List[FileInfo]:
|
||||
# Verwende die Mock-Implementation von get_file_list statt FTPS
|
||||
try:
|
||||
return self._file_system.get_file_list(self._base_path)
|
||||
except Exception as e:
|
||||
import logging
|
||||
logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter").error(
|
||||
f"Error listing files: {e}", exc_info=True
|
||||
)
|
||||
return []
|
||||
|
||||
def update(self):
|
||||
file_info_list = self.list_all_views()
|
||||
self._update_file_list_cache(file_info_list)
|
||||
if self.on_update:
|
||||
self.on_update()
|
||||
def get_all_cached_info(self) -> List[FileInfo]:
|
||||
return self._file_info_cache
|
||||
|
||||
def _update_file_list_cache(self, files: list[FileInfo]):
|
||||
self._file_alias_cache = {info.dosname: info.path.as_posix() for info in files}
|
||||
self._file_data_cache = {info.path.as_posix(): info for info in files}
|
||||
def get_file_by_stem(self, file_stem: str, extensions: list[str]) -> FileInfo | None:
|
||||
"""Get file info by file name without extension"""
|
||||
for file_info in self._file_info_cache:
|
||||
for extension in extensions:
|
||||
if file_info.file_name.lower().startswith(f"{file_stem.lower()}{extension}"):
|
||||
return file_info
|
||||
|
||||
def get_all_info(self):
|
||||
self.update()
|
||||
return self.get_all_cached_info()
|
||||
|
||||
def get_all_cached_info(self):
|
||||
return list(self._file_data_cache.values())
|
||||
|
||||
def get_file_data(self, file_path: str | Path) -> FileInfo | None:
|
||||
file_data = self.get_file_data_cached(file_path)
|
||||
if file_data is None:
|
||||
self.update()
|
||||
file_data = self.get_file_data_cached(file_path)
|
||||
return file_data
|
||||
|
||||
def get_file_data_cached(self, file_path: str | Path) -> FileInfo | None:
|
||||
if isinstance(file_path, str):
|
||||
file_path = Path(file_path).as_posix().strip("/")
|
||||
else:
|
||||
file_path = file_path.as_posix().strip("/")
|
||||
|
||||
if file_path not in self._file_data_cache:
|
||||
file_path = self._file_alias_cache.get(file_path, file_path)
|
||||
return self._file_data_cache.get(file_path, None)
|
||||
|
||||
def get_file_by_stem(self, file_stem: str, allowed_suffixes: list[str]):
|
||||
if file_stem == "":
|
||||
return None
|
||||
|
||||
file_stem = Path(file_stem).with_suffix("").stem
|
||||
file_data = self._get_file_by_stem_cached(file_stem, allowed_suffixes)
|
||||
if file_data is None:
|
||||
self.update()
|
||||
file_data = self._get_file_by_stem_cached(file_stem, allowed_suffixes)
|
||||
return file_data
|
||||
|
||||
def _get_file_by_stem_cached(self, file_stem: str, allowed_suffixes: list[str]):
|
||||
for file_path_str in list(self._file_data_cache.keys()) + list(self._file_alias_cache.keys()):
|
||||
file_path = Path(file_path_str)
|
||||
if file_stem == file_path.with_suffix("").stem and all(
|
||||
suffix in allowed_suffixes for suffix in file_path.suffixes
|
||||
):
|
||||
return self.get_file_data_cached(file_path)
|
||||
return None
|
||||
|
||||
def get_file_data(self, file_path: str) -> FileInfo | None:
|
||||
for file_info in self._file_info_cache:
|
||||
if file_info.path.lower() == file_path.lower() or file_info.file_name.lower() == file_path.lower():
|
||||
return file_info
|
||||
return None
|
||||
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Iterator
|
||||
from typing import Iterable, Iterator, List
|
||||
import logging.handlers
|
||||
|
||||
from octoprint.util import get_dos_filename
|
||||
@ -17,6 +17,7 @@ class RemoteSDCardFileList:
|
||||
self._settings = settings
|
||||
self._selected_project_file: FileInfo | None = None
|
||||
self._logger = logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter")
|
||||
self._mock_files = [] # Lokales Cache für Mock-Dateien
|
||||
|
||||
def delete_file(self, file_path: Path) -> None:
|
||||
try:
|
||||
@ -80,8 +81,56 @@ class RemoteSDCardFileList:
|
||||
self._logger.exception(e, exc_info=False)
|
||||
|
||||
def get_ftps_client(self):
|
||||
host = self._settings.get(["host"])
|
||||
access_code = self._settings.get(["access_code"])
|
||||
return IoTFTPSClient(
|
||||
f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True
|
||||
)
|
||||
"""
|
||||
Implementieren wir eine Mock-Version des FTPS-Clients, die keinen echten FTP-Zugriff erfordert.
|
||||
"""
|
||||
class MockFTPSClient:
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
pass
|
||||
|
||||
def get_file_list(self, path=""):
|
||||
"""Gibt die Mock-Dateiliste zurück"""
|
||||
return self._mock_files
|
||||
|
||||
mock_client = MockFTPSClient()
|
||||
mock_client._mock_files = self._mock_files
|
||||
return mock_client
|
||||
|
||||
@property
|
||||
def is_available(self) -> bool:
|
||||
"""
|
||||
Da wir kein FTP verwenden, ist dieser Service immer verfügbar
|
||||
"""
|
||||
return True
|
||||
|
||||
def get_file_list(self, path: str) -> List[FileInfo]:
|
||||
"""
|
||||
Gibt eine Liste von Dateien im angegebenen Pfad zurück.
|
||||
Da wir kein FTP verwenden, geben wir eine leere Liste oder gespeicherte Mock-Dateien zurück.
|
||||
"""
|
||||
self._logger.debug(f"Listing files in path: {path}")
|
||||
return self._mock_files
|
||||
|
||||
def add_mock_file(self, file_info: FileInfo):
|
||||
"""
|
||||
Fügt eine Mock-Datei zur Liste hinzu (für Tests oder wenn keine FTP-Verbindung möglich ist)
|
||||
"""
|
||||
self._mock_files.append(file_info)
|
||||
self._logger.debug(f"Added mock file: {file_info.file_name}")
|
||||
|
||||
def clear_mock_files(self):
|
||||
"""Löscht alle gespeicherten Mock-Dateien"""
|
||||
self._mock_files = []
|
||||
self._logger.debug("Mock file list cleared")
|
||||
|
||||
def delete_file(self, path: str) -> bool:
|
||||
"""
|
||||
Simuliert das Löschen einer Datei, entfernt sie aus der Mock-Liste
|
||||
"""
|
||||
self._logger.debug(f"Deleting file: {path}")
|
||||
before_count = len(self._mock_files)
|
||||
self._mock_files = [f for f in self._mock_files if f.path != path]
|
||||
return before_count > len(self._mock_files)
|
||||
|
Loading…
x
Reference in New Issue
Block a user