Fix adding by manual IP, code improvements

This commit is contained in:
ChrisTerBeke 2019-08-07 00:38:07 +02:00
parent ac177659e5
commit 8a2e394abc
No known key found for this signature in database
GPG key ID: A49F1AB9D7E0C263
4 changed files with 73 additions and 36 deletions

View file

@ -35,6 +35,9 @@ class CloudApiClient:
CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH) CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH)
CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH) CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH)
# In order to avoid garbage collection we keep the callbacks in this list.
_anti_gc_callbacks = [] # type: List[Callable[[], None]]
## Initializes a new cloud API client. ## Initializes a new cloud API client.
# \param account: The user's account object # \param account: The user's account object
# \param on_error: The callback to be called whenever we receive errors from the server. # \param on_error: The callback to be called whenever we receive errors from the server.
@ -44,8 +47,6 @@ class CloudApiClient:
self._account = account self._account = account
self._on_error = on_error self._on_error = on_error
self._upload = None # type: Optional[ToolPathUploader] self._upload = None # type: Optional[ToolPathUploader]
# In order to avoid garbage collection we keep the callbacks in this list.
self._anti_gc_callbacks = [] # type: List[Callable[[], None]]
## Gets the account used for the API. ## Gets the account used for the API.
@property @property

View file

@ -0,0 +1,20 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM import i18nCatalog
from UM.Message import Message
I18N_CATALOG = i18nCatalog("cura")
## Message shown when uploading a print job to a cluster is blocked because another upload is already in progress.
class LegacyDeviceNoLongerSupportedMessage(Message):
def __init__(self) -> None:
super().__init__(
text = I18N_CATALOG.i18nc("@info:status", "You are attempting to connect to a printer that is not "
"running Ultimaker Connect. Please update the printer to the "
"latest firmware."),
title = I18N_CATALOG.i18nc("@info:title", "Update your printer"),
lifetime = 10
)

View file

@ -25,6 +25,9 @@ class ClusterApiClient:
PRINTER_API_PREFIX = "/api/v1" PRINTER_API_PREFIX = "/api/v1"
CLUSTER_API_PREFIX = "/cluster-api/v1" CLUSTER_API_PREFIX = "/cluster-api/v1"
# In order to avoid garbage collection we keep the callbacks in this list.
_anti_gc_callbacks = [] # type: List[Callable[[], None]]
## Initializes a new cluster API client. ## Initializes a new cluster API client.
# \param address: The network address of the cluster to call. # \param address: The network address of the cluster to call.
# \param on_error: The callback to be called whenever we receive errors from the server. # \param on_error: The callback to be called whenever we receive errors from the server.
@ -33,8 +36,6 @@ class ClusterApiClient:
self._manager = QNetworkAccessManager() self._manager = QNetworkAccessManager()
self._address = address self._address = address
self._on_error = on_error self._on_error = on_error
# In order to avoid garbage collection we keep the callbacks in this list.
self._anti_gc_callbacks = [] # type: List[Callable[[], None]]
## Get printer system information. ## Get printer system information.
# \param on_finished: The callback in case the response is successful. # \param on_finished: The callback in case the response is successful.

View file

@ -1,19 +1,21 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Dict, Optional, Callable from typing import Dict, Optional, Callable, List
from UM import i18nCatalog from UM import i18nCatalog
from UM.Logger import Logger
from UM.Signal import Signal from UM.Signal import Signal
from UM.Version import Version from UM.Version import Version
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack
from .ZeroConfClient import ZeroConfClient from .ZeroConfClient import ZeroConfClient
from .ClusterApiClient import ClusterApiClient from .ClusterApiClient import ClusterApiClient
from .LocalClusterOutputDevice import LocalClusterOutputDevice from .LocalClusterOutputDevice import LocalClusterOutputDevice
from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice
from ..CloudFlowMessage import CloudFlowMessage from ..CloudFlowMessage import CloudFlowMessage
from ..Messages.LegacyDeviceNoLongerSupportedMessage import LegacyDeviceNoLongerSupportedMessage
from ..Models.Http.PrinterSystemStatus import PrinterSystemStatus from ..Models.Http.PrinterSystemStatus import PrinterSystemStatus
@ -45,15 +47,10 @@ class LocalClusterOutputDeviceManager:
self._zero_conf_client.addedNetworkCluster.connect(self._onDeviceDiscovered) self._zero_conf_client.addedNetworkCluster.connect(self._onDeviceDiscovered)
self._zero_conf_client.removedNetworkCluster.connect(self._onDiscoveredDeviceRemoved) self._zero_conf_client.removedNetworkCluster.connect(self._onDiscoveredDeviceRemoved)
# Persistent dict containing manually connected clusters.
self._manual_instances = {} # type: Dict[str, Optional[Callable]]
## Start the network discovery. ## Start the network discovery.
def start(self) -> None: def start(self) -> None:
self._zero_conf_client.start() self._zero_conf_client.start()
# Load all manual devices. for address in self._getStoredManualAddresses():
self._manual_instances = self._getStoredManualInstances()
for address in self._manual_instances:
self.addManualDevice(address) self.addManualDevice(address)
## Stop network discovery and clean up discovered devices. ## Stop network discovery and clean up discovered devices.
@ -65,11 +62,8 @@ class LocalClusterOutputDeviceManager:
## Add a networked printer manually by address. ## Add a networked printer manually by address.
def addManualDevice(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None: def addManualDevice(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None:
self._manual_instances[address] = callback
new_manual_devices = ",".join(self._manual_instances.keys())
CuraApplication.getInstance().getPreferences().setValue(self.MANUAL_DEVICES_PREFERENCE_KEY, new_manual_devices)
api_client = ClusterApiClient(address, lambda error: print(error)) api_client = ClusterApiClient(address, lambda error: print(error))
api_client.getSystem(lambda status: self._onCheckManualDeviceResponse(address, status)) api_client.getSystem(lambda status: self._onCheckManualDeviceResponse(address, status, callback))
## Remove a manually added networked printer. ## Remove a manually added networked printer.
def removeManualDevice(self, device_id: str, address: Optional[str] = None) -> None: def removeManualDevice(self, device_id: str, address: Optional[str] = None) -> None:
@ -80,19 +74,15 @@ class LocalClusterOutputDeviceManager:
address = address or self._discovered_devices[device_id].ipAddress address = address or self._discovered_devices[device_id].ipAddress
self._onDiscoveredDeviceRemoved(device_id) self._onDiscoveredDeviceRemoved(device_id)
if address in self._manual_instances: if address in self._getStoredManualAddresses():
manual_instance_callback = self._manual_instances.pop(address) self._removeStoredManualAddress(address)
new_devices = ",".join(self._manual_instances.keys())
CuraApplication.getInstance().getPreferences().setValue(self.MANUAL_DEVICES_PREFERENCE_KEY, new_devices)
if manual_instance_callback:
CuraApplication.getInstance().callLater(manual_instance_callback, False, address)
## Force reset all network device connections. ## Force reset all network device connections.
def refreshConnections(self): def refreshConnections(self) -> None:
self._connectToActiveMachine() self._connectToActiveMachine()
## Callback for when the active machine was changed by the user or a new remote cluster was found. ## Callback for when the active machine was changed by the user or a new remote cluster was found.
def _connectToActiveMachine(self): def _connectToActiveMachine(self) -> None:
active_machine = CuraApplication.getInstance().getGlobalContainerStack() active_machine = CuraApplication.getInstance().getGlobalContainerStack()
if not active_machine: if not active_machine:
return return
@ -108,10 +98,8 @@ class LocalClusterOutputDeviceManager:
CuraApplication.getInstance().getOutputDeviceManager().removeOutputDevice(device.key) CuraApplication.getInstance().getOutputDeviceManager().removeOutputDevice(device.key)
## Callback for when a manual device check request was responded to. ## Callback for when a manual device check request was responded to.
def _onCheckManualDeviceResponse(self, address: str, status: PrinterSystemStatus) -> None: def _onCheckManualDeviceResponse(self, address: str, status: PrinterSystemStatus,
callback = self._manual_instances.get(address, None) callback: Optional[Callable[[bool, str], None]] = None) -> None:
if callback is None:
return
self._onDeviceDiscovered("manual:{}".format(address), address, { self._onDeviceDiscovered("manual:{}".format(address), address, {
b"name": status.name.encode("utf-8"), b"name": status.name.encode("utf-8"),
b"address": address.encode("utf-8"), b"address": address.encode("utf-8"),
@ -120,6 +108,8 @@ class LocalClusterOutputDeviceManager:
b"firmware_version": status.firmware.encode("utf-8"), b"firmware_version": status.firmware.encode("utf-8"),
b"cluster_size": b"1" b"cluster_size": b"1"
}) })
self._storeManualAddress(address)
if callback is not None:
CuraApplication.getInstance().callLater(callback, True, address) CuraApplication.getInstance().callLater(callback, True, address)
## Returns a dict of printer BOM numbers to machine types. ## Returns a dict of printer BOM numbers to machine types.
@ -139,6 +129,7 @@ class LocalClusterOutputDeviceManager:
## Add a new device. ## Add a new device.
def _onDeviceDiscovered(self, key: str, address: str, properties: Dict[bytes, bytes]) -> None: def _onDeviceDiscovered(self, key: str, address: str, properties: Dict[bytes, bytes]) -> None:
cluster_size = int(properties.get(b"cluster_size", -1)) cluster_size = int(properties.get(b"cluster_size", -1))
firmware_version = Version(properties.get(b"firmware", "1.0.0"))
machine_identifier = properties.get(b"machine", b"").decode("utf-8") machine_identifier = properties.get(b"machine", b"").decode("utf-8")
printer_type_identifiers = self._getPrinterTypeIdentifiers() printer_type_identifiers = self._getPrinterTypeIdentifiers()
@ -149,8 +140,8 @@ class LocalClusterOutputDeviceManager:
properties[b"printer_type"] = bytes(p_type, encoding="utf8") properties[b"printer_type"] = bytes(p_type, encoding="utf8")
break break
# We no longer support legacy devices, so check that here. # We no longer support legacy devices, prevent them from showing up in the discovered devices list.
if cluster_size == -1: if cluster_size == -1 or firmware_version < self.MIN_SUPPORTED_CLUSTER_VERSION:
return return
device = LocalClusterOutputDevice(key, address, properties) device = LocalClusterOutputDevice(key, address, properties)
@ -191,16 +182,40 @@ class LocalClusterOutputDeviceManager:
self._connectToOutputDevice(device, active_machine) self._connectToOutputDevice(device, active_machine)
CloudFlowMessage(device.ipAddress).show() # Nudge the user to start using Ultimaker Cloud. CloudFlowMessage(device.ipAddress).show() # Nudge the user to start using Ultimaker Cloud.
## Add an address to the stored preferences.
def _storeManualAddress(self, address: str) -> None:
stored_addresses = self._getStoredManualAddresses()
if address in stored_addresses:
return # Prevent duplicates.
stored_addresses.append(address)
new_value = ",".join(stored_addresses)
CuraApplication.getInstance().getPreferences().setValue(self.MANUAL_DEVICES_PREFERENCE_KEY, new_value)
## Remove an address from the stored preferences.
def _removeStoredManualAddress(self, address: str) -> None:
stored_addresses = self._getStoredManualAddresses()
try:
stored_addresses.remove(address) # Can throw a ValueError
new_value = ",".join(stored_addresses)
CuraApplication.getInstance().getPreferences().setValue(self.MANUAL_DEVICES_PREFERENCE_KEY, new_value)
except ValueError:
Logger.log("w", "Could not remove address from stored_addresses, it was not there")
## Load the user-configured manual devices from Cura preferences. ## Load the user-configured manual devices from Cura preferences.
def _getStoredManualInstances(self) -> Dict[str, Optional[Callable]]: def _getStoredManualAddresses(self) -> List[str]:
preferences = CuraApplication.getInstance().getPreferences() preferences = CuraApplication.getInstance().getPreferences()
preferences.addPreference(self.MANUAL_DEVICES_PREFERENCE_KEY, "") preferences.addPreference(self.MANUAL_DEVICES_PREFERENCE_KEY, "")
manual_instances = preferences.getValue(self.MANUAL_DEVICES_PREFERENCE_KEY).split(",") manual_instances = preferences.getValue(self.MANUAL_DEVICES_PREFERENCE_KEY).split(",")
return {address: None for address in manual_instances} return manual_instances
## Add a device to the current active machine. ## Add a device to the current active machine.
@staticmethod def _connectToOutputDevice(self, device: UltimakerNetworkedPrinterOutputDevice, machine: GlobalStack) -> None:
def _connectToOutputDevice(device: PrinterOutputDevice, active_machine: GlobalStack) -> None:
# Make sure users know that we no longer support legacy devices.
if device.clusterSize < 1 or Version(device.firmwareVersion) < self.MIN_SUPPORTED_CLUSTER_VERSION:
LegacyDeviceNoLongerSupportedMessage().show()
return
device.connect() device.connect()
active_machine.addConfiguredConnectionType(device.connectionType.value) machine.addConfiguredConnectionType(device.connectionType.value)
CuraApplication.getInstance().getOutputDeviceManager().addOutputDevice(device) CuraApplication.getInstance().getOutputDeviceManager().addOutputDevice(device)