diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 61d22052be..ff83c3fd5e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -23,14 +23,13 @@ from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOut # Note that this device represents a single remote cluster, not a list of multiple clusters. # # TODO: figure our how the QML interface for the cluster networking should operate with this limited functionality. -# TODO: figure out how to pair remote clusters, local networked clusters and local cura printer presets. class CloudOutputDevice(NetworkedPrinterOutputDevice): # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") - # The cloud URL to use for remote clusters. - API_ROOT_PATH_FORMAT = "https://api.ultimaker.com/connect/v1/clusters/{cluster_id}" + # The cloud URL to use for this remote cluster. + API_ROOT_PATH_FORMAT = "https://api-staging.ultimaker.com/connect/v1/clusters/{cluster_id}" # Signal triggered when the printers in the remote cluster were changed. printersChanged = pyqtSignal() @@ -79,8 +78,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: self.writeStarted.emit(self) - - # TODO: actually implement this self._addPrintJobToQueue() ## Get remote printers. @@ -102,6 +99,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): super()._update() self.get("/status", on_finished = self._onStatusCallFinished) + ## Method called when HTTP request to status endpoint is finished. + # Contains both printers and print jobs statuses in a single response. def _onStatusCallFinished(self, reply: QNetworkReply) -> None: status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if status_code != 200: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 1f75edc2cc..53f64241e5 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Dict from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice @@ -11,21 +11,47 @@ if TYPE_CHECKING: ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. # Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code. +# +# TODO: figure out how to pair remote clusters, local networked clusters and local cura printer presets. class CloudOutputDeviceManager: + # The cloud URL to use for remote clusters. + API_ROOT_PATH = "https://api-staging.ultimaker.com/connect/v1" + def __init__(self, application: "CuraApplication"): + self._application = application self._output_device_manager = application.getOutputDeviceManager() self._account = application.getCuraAPI().account - self._getRemoteClusters() - # For testing: - application.globalContainerStackChanged.connect(self._addCloudOutputDevice) + # Persistent dict containing the remote clusters for the authenticated user. + self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] + + # When switching machines we check if we have to activate a remote cluster. + self._application.globalContainerStackChanged.connect(self._activeMachineChanged) + + # Fetch all remote clusters for the authenticated user. + self._getRemoteClusters() def _getRemoteClusters(self): # TODO: get list of remote clusters and create an output device for each. - pass + # For testing we add a dummy device: + self._addCloudOutputDevice({"cluster_id": "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w"}) - def _addCloudOutputDevice(self): - device = CloudOutputDevice("xxxx-xxxx-xxxx-xxxx") + def _addCloudOutputDevice(self, cluster_data: Dict[str, any]): + # TODO: use model or named tuple for cluster_data + device = CloudOutputDevice(cluster_data["cluster_id"]) self._output_device_manager.addOutputDevice(device) - device.connect() + self._remote_clusters[cluster_data["cluster_id"]] = device + + def _activeMachineChanged(self): + active_machine = self._application.getGlobalContainerStack() + if not active_machine: + return + + stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") + if stored_cluster_id not in self._remote_clusters.keys(): + # Currently authenticated user does not have access to stored cluster or no user is signed in. + return + + # We found the active machine as remote cluster so let's connect to it. + self._remote_clusters.get(stored_cluster_id).connect() diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index d7a40626b9..b441df9eb5 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -40,6 +40,9 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._zero_conf = None self._zero_conf_browser = None + # Create a cloud output device manager that abstract all cloud connection logic away. + self._cloud_output_device_manager = CloudOutputDeviceManager(self._application) + # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addDeviceSignal.connect(self._onAddDevice) self.removeDeviceSignal.connect(self._onRemoveDevice) @@ -76,9 +79,6 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._service_changed_request_event = Event() self._service_changed_request_thread = Thread(target=self._handleOnServiceChangedRequests, daemon=True) self._service_changed_request_thread.start() - - # Create a cloud output device manager that abstract all cloud connection logic away. - self._cloud_output_device_manager = CloudOutputDeviceManager(self._application) def getDiscoveredDevices(self): return self._discovered_devices