STAR-322: Fixing printer matching by network key

This commit is contained in:
Daniel Schiavini 2018-12-05 16:02:38 +01:00
parent cd67100097
commit 7e76913736
5 changed files with 97 additions and 64 deletions

View file

@ -1,6 +1,6 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Dict, List, Optional
from typing import Dict, List
from PyQt5.QtCore import QTimer
@ -8,10 +8,12 @@ from UM import i18nCatalog
from UM.Logger import Logger
from UM.Message import Message
from cura.CuraApplication import CuraApplication
from cura.Settings.GlobalStack import GlobalStack
from .CloudApiClient import CloudApiClient
from .CloudOutputDevice import CloudOutputDevice
from .Models.CloudCluster import CloudCluster
from .Models.CloudErrorObject import CloudErrorObject
from .Utils import findChanges
## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters.
@ -19,7 +21,9 @@ from .Models.CloudErrorObject import CloudErrorObject
#
# API spec is available on https://api.ultimaker.com/docs/connect/spec/.
#
class CloudOutputDeviceManager:
META_CLUSTER_ID = "um_cloud_cluster_id"
# The interval with which the remote clusters are checked
CHECK_CLUSTER_INTERVAL = 5.0 # seconds
@ -42,59 +46,48 @@ class CloudOutputDeviceManager:
# When switching machines we check if we have to activate a remote cluster.
application.globalContainerStackChanged.connect(self._connectToActiveMachine)
self.update_timer = QTimer(CuraApplication.getInstance())
self.update_timer.setInterval(self.CHECK_CLUSTER_INTERVAL * 1000)
self.update_timer.setSingleShot(False)
self.update_timer.timeout.connect(self._getRemoteClusters)
# create a timer to update the remote cluster list
self._update_timer = QTimer(application)
self._update_timer.setInterval(self.CHECK_CLUSTER_INTERVAL * 1000)
self._update_timer.setSingleShot(False)
self._update_timer.timeout.connect(self._getRemoteClusters)
## Gets all remote clusters from the API.
def _getRemoteClusters(self) -> None:
Logger.log("i", "Retrieving remote clusters")
if self._account.isLoggedIn:
self._api.getClusters(self._onGetRemoteClustersFinished)
# Only start the polling timer after the user is authenticated
# The first call to _getRemoteClusters comes from self._account.loginStateChanged
if not self.update_timer.isActive():
self.update_timer.start()
# Only start the polling timer after the user is authenticated
# The first call to _getRemoteClusters comes from self._account.loginStateChanged
if not self._update_timer.isActive():
self._update_timer.start()
## Callback for when the request for getting the clusters. is finished.
def _onGetRemoteClustersFinished(self, clusters: List[CloudCluster]) -> None:
found_clusters = {c.cluster_id: c for c in clusters}
online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudCluster]
Logger.log("i", "Parsed remote clusters to %s", found_clusters)
removed_devices, added_clusters, updates = findChanges(self._remote_clusters, online_clusters)
known_cluster_ids = set(self._remote_clusters.keys())
found_cluster_ids = set(found_clusters.keys())
Logger.log("i", "Parsed remote clusters to %s", online_clusters)
# Remove output devices that are gone
for removed_cluster in removed_devices:
self._output_device_manager.removeOutputDevice(removed_cluster.key)
del self._remote_clusters[removed_cluster.key]
# Add an output device for each new remote cluster.
# We only add when is_online as we don't want the option in the drop down if the cluster is not online.
for cluster_id in found_cluster_ids.difference(known_cluster_ids):
if found_clusters[cluster_id].is_online:
self._addCloudOutputDevice(found_clusters[cluster_id])
for added_cluster in added_clusters:
device = CloudOutputDevice(self._api, added_cluster.cluster_id, added_cluster.host_name)
self._output_device_manager.addOutputDevice(device)
self._remote_clusters[added_cluster.cluster_id] = device
# Remove output devices that are gone
for cluster_id in known_cluster_ids.difference(found_cluster_ids):
self._removeCloudOutputDevice(found_clusters[cluster_id])
for device, cluster in updates:
device.host_name = cluster.host_name
# TODO: not pass clusters that are not online?
self._connectToActiveMachine()
## Adds a CloudOutputDevice for each entry in the remote cluster list from the API.
# \param cluster: The cluster that was added.
def _addCloudOutputDevice(self, cluster: CloudCluster):
device = CloudOutputDevice(self._api, cluster.cluster_id)
self._output_device_manager.addOutputDevice(device)
self._remote_clusters[cluster.cluster_id] = device
## Remove a CloudOutputDevice
# \param cluster: The cluster that was removed
def _removeCloudOutputDevice(self, cluster: CloudCluster):
self._output_device_manager.removeOutputDevice(cluster.cluster_id)
if cluster.cluster_id in self._remote_clusters:
del self._remote_clusters[cluster.cluster_id]
## Callback for when the active machine was changed by the user or a new remote cluster was found.
def _connectToActiveMachine(self) -> None:
active_machine = CuraApplication.getInstance().getGlobalContainerStack()
@ -102,23 +95,27 @@ class CloudOutputDeviceManager:
return
# Check if the stored cluster_id for the active machine is in our list of remote clusters.
stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id")
if stored_cluster_id in self._remote_clusters.keys():
return self._remote_clusters.get(stored_cluster_id).connect()
stored_cluster_id = active_machine.getMetaDataEntry(self.META_CLUSTER_ID)
if stored_cluster_id in self._remote_clusters:
device = self._remote_clusters[stored_cluster_id]
if not device.isConnected():
device.connect()
else:
self._connectByNetworkKey(active_machine)
## Tries to match the
def _connectByNetworkKey(self, active_machine: GlobalStack) -> None:
# Check if the active printer has a local network connection and match this key to the remote cluster.
# The local network key is formatted as ultimakersystem-xxxxxxxxxxxx._ultimaker._tcp.local.
# The optional remote host_name is formatted as ultimakersystem-xxxxxxxxxxxx.
# This means we can match the two by checking if the host_name is in the network key string.
local_network_key = active_machine.getMetaDataEntry("um_network_key")
if not local_network_key:
return
# TODO: get host_name in the output device so we can iterate here
# cluster_id = next(local_network_key in cluster.host_name for cluster in self._remote_clusters.items())
# if cluster_id in self._remote_clusters.keys():
# return self._remote_clusters.get(cluster_id).connect()
device = next((c for c in self._remote_clusters.values() if c.matchesNetworkKey(local_network_key)), None)
if not device:
return
active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key)
return device.connect()
## Handles an API error received from the cloud.
# \param errors: The errors received