diff --git a/cura/PrinterOutput/UploadMaterialsJob.py b/cura/PrinterOutput/UploadMaterialsJob.py index 5591e3b8e8..9d4011b91e 100644 --- a/cura/PrinterOutput/UploadMaterialsJob.py +++ b/cura/PrinterOutput/UploadMaterialsJob.py @@ -18,13 +18,14 @@ from UM.Signal import Signal from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To call the API. from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope -catalog = i18nCatalog("cura") - from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: from PyQt5.QtNetwork import QNetworkReply from cura.UltimakerCloud.CloudMaterialSync import CloudMaterialSync +catalog = i18nCatalog("cura") + + class UploadMaterialsJob(Job): """ Job that uploads a set of materials to the Digital Factory. @@ -43,6 +44,9 @@ class UploadMaterialsJob(Job): self._scope = JsonDecoratorScope(UltimakerCloudScope(cura.CuraApplication.CuraApplication.getInstance())) # type: JsonDecoratorScope self._archive_filename = None # type: Optional[str] self._archive_remote_id = None # type: Optional[str] # ID that the server gives to this archive. Used to communicate about the archive to the server. + self._num_synced_printers = 0 + self._completed_printers = set() # The printers that have successfully completed the upload. + self._failed_printers = set() uploadCompleted = Signal() uploadProgressChanged = Signal() @@ -111,13 +115,15 @@ class UploadMaterialsJob(Job): self.uploadCompleted.emit(self.getResult(), self.getError()) return - for container_stack in CuraContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine"): - if container_stack.get("connection_type", -1) != 3: # Not a cloud printer. - continue # Only upload to cloud printers. - if not container_stack.get("is_online", False): # Not online. - continue # Only upload to online printers. - if "host_guid" not in container_stack or "um_cloud_cluster_id" not in container_stack: - continue # Incomplete information about cloud printer. + online_cloud_printers = CuraContainerRegistry.getInstance().findContainerStacksMetadata( + type = "machine", + connection_type = 3, # Only cloud printers. + is_online = True, # Only online printers. Otherwise the server gives an error. + host_guid = "*", # Required metadata field. Otherwise we get a KeyError. + um_cloud_cluster_id = "*" # Required metadata field. Otherwise we get a KeyError. + ) + self._num_synced_printers = len(online_cloud_printers) + for container_stack in online_cloud_printers: cluster_id = container_stack["um_cloud_cluster_id"] printer_id = container_stack["host_guid"] @@ -125,20 +131,24 @@ class UploadMaterialsJob(Job): http.get( url = self.UPLOAD_CONFIRM_URL.format(cluster_id = cluster_id, cluster_printer_id = printer_id), callback = functools.partialmethod(self.onUploadConfirmed, printer_id), - error_callback = self.onError, + error_callback = functools.partialmethod(self.onUploadConfirmed, printer_id), # Let this same function handle the error too. scope = self._scope ) def onUploadConfirmed(self, printer_id: str, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"]) -> None: if error is not None: Logger.error(f"Failed to confirm uploading material archive to printer {printer_id}: {error}") - self.setError(UploadMaterialsError(catalog.i18nc("@text:error", "Failed to connect to Digital Factory."))) - self.setResult(self.Result.FAILED) - self.uploadCompleted.emit(self.getResult(), self.getError()) - return + self._failed_printers.add(printer_id) + else: + self._completed_printers.add(printer_id) - self.setResult(self.Result.SUCCCESS) - self.uploadCompleted.emit(self.getResult(), self.getError()) + if len(self._completed_printers) + len(self._failed_printers) >= self._num_synced_printers: # This is the last response to be processed. + if self._failed_printers: + self.setResult(self.Result.FAILED) + self.setError(UploadMaterialsError(catalog.i18nc("@text:error", "Failed to connect to Digital Factory to sync materials with some of the printers."))) + else: + self.setResult(self.Result.SUCCESS) + self.uploadCompleted.emit(self.getResult(), self.getError()) def onError(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"]): Logger.error(f"Failed to upload material archive: {error}") @@ -146,8 +156,9 @@ class UploadMaterialsJob(Job): self.setError(UploadMaterialsError(catalog.i18nc("@text:error", "Failed to connect to Digital Factory."))) self.uploadCompleted.emit(self.getResult(), self.getError()) + class UploadMaterialsError(Exception): """ Marker class to indicate something went wrong while uploading. """ - pass \ No newline at end of file + pass