diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index f233e59fe5..8a87438b0e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -26,6 +26,9 @@ class CloudOutputDeviceManager: API spec is available on https://api.ultimaker.com/docs/connect/spec/. """ + SYNC_TIMEOUT = 50.0 + """seconds. Adding many different kinds of printers can take a long time""" + META_CLUSTER_ID = "um_cloud_cluster_id" META_NETWORK_KEY = "um_network_key" SYNC_SERVICE_NAME = "CloudOutputDeviceManager" @@ -46,7 +49,13 @@ class CloudOutputDeviceManager: # Ensure we don't start twice. self._running = False - self._syncing = False + # Unfortunately, not all cases of a failed request result in an error callback, such as VPN connection + # being broken or possibly switching wifi networks. Better solution: Refactor CloudApiClient to use + # HttpRequestManager, which supports timeout. + self._sync_timeout_timer = QTimer() + self._sync_timeout_timer.setInterval(int(self.SYNC_TIMEOUT * 1000)) + self._sync_timeout_timer.setSingleShot(True) + self._sync_timeout_timer.timeout.connect(self._onSyncTimeout) def start(self): """Starts running the cloud output device manager, thus periodically requesting cloud data.""" @@ -84,12 +93,13 @@ class CloudOutputDeviceManager: def _getRemoteClusters(self) -> None: """Gets all remote clusters from the API.""" - if self._syncing: + if self._sync_timeout_timer.isActive(): + # A sync is running return Logger.info("Syncing cloud printer clusters") - self._syncing = True + self._sync_timeout_timer.start() self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SYNCING) self._api.getClusters(self._onGetRemoteClustersFinished, self._onGetRemoteClusterFailed) @@ -115,12 +125,10 @@ class CloudOutputDeviceManager: # If the removed device was active we should connect to the new active device self._connectToActiveMachine() - self._syncing = False - self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS) + self._onSyncFinished(True) def _onGetRemoteClusterFailed(self): - self._syncing = False - self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR) + self._onSyncFinished(False) def _onDevicesDiscovered(self, clusters: List[CloudClusterResponse]) -> None: """**Synchronously** create machines for discovered devices @@ -223,6 +231,19 @@ class CloudOutputDeviceManager: if device.key in output_device_manager.getOutputDeviceIds(): output_device_manager.removeOutputDevice(device.key) + def _onSyncTimeout(self): + Logger.warning("Cloud printer sync timed out after {} seconds".format(self.SYNC_TIMEOUT)) + self._onSyncFinished(False) + + def _onSyncFinished(self, success: bool): + if self._sync_timeout_timer.isActive(): + self._sync_timeout_timer.stop() + + if success: + self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS) + else: + self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR) + def _createMachineFromDiscoveredDevice(self, key: str, activate: bool = True) -> None: device = self._remote_clusters[key] if not device: @@ -277,4 +298,4 @@ class CloudOutputDeviceManager: output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() if device.key not in output_device_manager.getOutputDeviceIds(): - output_device_manager.addOutputDevice(device) \ No newline at end of file + output_device_manager.addOutputDevice(device)