Merge branch 'WIP_onboarding' of github.com:Ultimaker/Cura

This commit is contained in:
Jaime van Kessel 2019-04-04 14:38:18 +02:00
commit 932a5c8fc9
146 changed files with 4594 additions and 3972 deletions

View file

@ -18,8 +18,8 @@ from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutputDevice import ConnectionType
from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from .CloudOutputController import CloudOutputController
from ..MeshFormatHandler import MeshFormatHandler

View file

@ -2,8 +2,8 @@
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Union, Dict, Optional, Any
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
from cura.PrinterOutput.Models.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.Models.ExtruderOutputModel import ExtruderOutputModel
from .CloudClusterPrinterConfigurationMaterial import CloudClusterPrinterConfigurationMaterial
from .BaseCloudModel import BaseCloudModel

View file

@ -2,7 +2,7 @@
# Cura is released under the terms of the LGPLv3 or higher.
from typing import List, Optional, Union, Dict, Any
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel
from ...UM3PrintJobOutputModel import UM3PrintJobOutputModel
from ...ConfigurationChangeModel import ConfigurationChangeModel
from ..CloudOutputController import CloudOutputController
@ -95,9 +95,9 @@ class CloudClusterPrintJobStatus(BaseCloudModel):
return model
## Creates a new configuration model
def _createConfigurationModel(self) -> ConfigurationModel:
def _createConfigurationModel(self) -> PrinterConfigurationModel:
extruders = [extruder.createConfigurationModel() for extruder in self.configuration or ()]
configuration = ConfigurationModel()
configuration = PrinterConfigurationModel()
configuration.setExtruderConfigurations(extruders)
return configuration

View file

@ -2,7 +2,7 @@ from typing import Optional
from UM.Logger import Logger
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutput.Models.MaterialOutputModel import MaterialOutputModel
from .BaseCloudModel import BaseCloudModel

View file

@ -3,7 +3,7 @@
from typing import List, Union, Dict, Optional, Any
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel
from .CloudClusterBuildPlate import CloudClusterBuildPlate
from .CloudClusterPrintCoreConfiguration import CloudClusterPrintCoreConfiguration
from .BaseCloudModel import BaseCloudModel

View file

@ -19,12 +19,12 @@ from UM.Scene.SceneNode import SceneNode # For typing.
from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel
from cura.PrinterOutput.Models.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutputDevice import ConnectionType
from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.Models.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from .Cloud.Utils import formatTimeCompleted, formatDateCompleted
from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController
@ -522,7 +522,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
print_job = UM3PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self),
key=data["uuid"], name= data["name"])
configuration = ConfigurationModel()
configuration = PrinterConfigurationModel()
extruders = [ExtruderConfigurationModel(position = idx) for idx in range(0, self._number_of_extruders)]
for index in range(0, self._number_of_extruders):
try:
@ -633,6 +633,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
printer.updateKey(data["uuid"])
printer.updateType(data["machine_variant"])
if data["status"] != "unreachable":
self._application.getDiscoveredPrintersModel().updateDiscoveredPrinter(data["ip_address"],
name = data["friendly_name"],
machine_type = data["machine_variant"])
# Do not store the build plate information that comes from connect if the current printer has not build plate information
if "build_plate" in data and machine_definition.getMetaDataEntry("has_variant_buildplates", False):
printer.updateBuildplate(data["build_plate"]["type"])

View file

@ -5,7 +5,7 @@ from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
MYPY = False
if MYPY:
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
class ClusterUM3PrinterOutputController(PrinterOutputController):
def __init__(self, output_device):

View file

@ -18,7 +18,7 @@ from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
from .UM3OutputDevicePlugin import UM3OutputDevicePlugin
if TYPE_CHECKING:
from cura.PrinterOutputDevice import PrinterOutputDevice
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice
catalog = i18nCatalog("cura")
@ -41,6 +41,11 @@ class DiscoverUM3Action(MachineAction):
# Time to wait after a zero-conf service change before allowing a zeroconf reset
self._zero_conf_change_grace_period = 0.25 #type: float
# Overrides the one in MachineAction.
# This requires not attention from the user (any more), so we don't need to show any 'upgrade screens'.
def needsUserInteraction(self) -> bool:
return False
@pyqtSlot()
def startDiscovery(self):
if not self._network_plugin:
@ -118,49 +123,14 @@ class DiscoverUM3Action(MachineAction):
if self._network_plugin:
# Ensure that the connection states are refreshed.
self._network_plugin.reCheckConnections()
self._network_plugin.refreshConnections()
# Associates the currently active machine with the given printer device. The network connection information will be
# stored into the metadata of the currently active machine.
@pyqtSlot(QObject)
def associateActiveMachineWithPrinterDevice(self, printer_device: Optional["PrinterOutputDevice"]) -> None:
if not printer_device:
return
Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key)
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if not global_container_stack:
return
meta_data = global_container_stack.getMetaData()
if "um_network_key" in meta_data: # Global stack already had a connection, but it's changed.
old_network_key = meta_data["um_network_key"]
# Since we might have a bunch of hidden stacks, we also need to change it there.
metadata_filter = {"um_network_key": old_network_key}
containers = CuraContainerRegistry.getInstance().findContainerStacks(type="machine", **metadata_filter)
for container in containers:
container.setMetaDataEntry("um_network_key", printer_device.key)
# Delete old authentication data.
Logger.log("d", "Removing old authentication id %s for device %s",
global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key)
container.removeMetaDataEntry("network_authentication_id")
container.removeMetaDataEntry("network_authentication_key")
# Ensure that these containers do know that they are configured for network connection
container.addConfiguredConnectionType(printer_device.connectionType.value)
else: # Global stack didn't have a connection yet, configure it.
global_container_stack.setMetaDataEntry("um_network_key", printer_device.key)
global_container_stack.addConfiguredConnectionType(printer_device.connectionType.value)
if self._network_plugin:
# Ensure that the connection states are refreshed.
self._network_plugin.reCheckConnections()
self._network_plugin.associateActiveMachineWithPrinterDevice(printer_device)
@pyqtSlot(result = str)
def getStoredKey(self) -> str:

View file

@ -2,10 +2,10 @@ from typing import List, Optional
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutputDevice import ConnectionType
from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.Models.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from cura.Settings.ContainerManager import ContainerManager
from cura.Settings.ExtruderManager import ExtruderManager

View file

@ -7,8 +7,8 @@ from UM.Version import Version
MYPY = False
if MYPY:
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel
class LegacyUM3PrinterOutputController(PrinterOutputController):

View file

@ -1,23 +1,25 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import json
import os
from queue import Queue
from threading import Event, Thread
from time import time
import os
from typing import Optional, TYPE_CHECKING, Dict
from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo
from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices
from cura.CuraApplication import CuraApplication
from cura.PrinterOutputDevice import ConnectionType
from cura.Settings.GlobalStack import GlobalStack # typing
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from UM.i18n import i18nCatalog
from UM.Logger import Logger
from UM.Message import Message
from UM.OutputDevice.OutputDeviceManager import ManualDeviceAdditionAttempt
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
from UM.PluginRegistry import PluginRegistry
from UM.Signal import Signal, signalemitter
@ -26,17 +28,23 @@ from UM.Version import Version
from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice
from .Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager
from typing import Optional
if TYPE_CHECKING:
from PyQt5.QtNetwork import QNetworkReply
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice
from cura.Settings.GlobalStack import GlobalStack
i18n_catalog = i18nCatalog("cura")
## This plugin handles the connection detection & creation of output device objects for the UM3 printer.
# Zero-Conf is used to detect printers, which are saved in a dict.
# If we discover a printer that has the same key as the active machine instance a connection is made.
@signalemitter
class UM3OutputDevicePlugin(OutputDevicePlugin):
addDeviceSignal = Signal()
removeDeviceSignal = Signal()
addDeviceSignal = Signal() # Called '...Signal' to avoid confusion with function-names.
removeDeviceSignal = Signal() # Ditto ^^^.
discoveredDevicesChanged = Signal()
cloudFlowIsPossible = Signal()
@ -55,7 +63,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self.addDeviceSignal.connect(self._onAddDevice)
self.removeDeviceSignal.connect(self._onRemoveDevice)
self._application.globalContainerStackChanged.connect(self.reCheckConnections)
self._application.globalContainerStackChanged.connect(self.refreshConnections)
self._discovered_devices = {}
@ -142,7 +150,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self.addManualDevice(address)
self.resetLastManualDevice()
def reCheckConnections(self):
def refreshConnections(self):
active_machine = CuraApplication.getInstance().getGlobalContainerStack()
if not active_machine:
return
@ -176,6 +184,8 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self.checkCloudFlowIsPossible()
else:
self.getOutputDeviceManager().removeOutputDevice(key)
if key.startswith("manual:"):
self.removeManualDeviceSignal.emit(self.getPluginId(), key, self._discovered_devices[key].address)
def stop(self):
if self._zero_conf is not None:
@ -183,6 +193,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._zero_conf.close()
self._cloud_output_device_manager.stop()
def canAddManualDevice(self, address: str = "") -> ManualDeviceAdditionAttempt:
# This plugin should always be the fallback option (at least try it):
return ManualDeviceAdditionAttempt.POSSIBLE
def removeManualDevice(self, key, address = None):
if key in self._discovered_devices:
if not address:
@ -194,6 +208,8 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._manual_instances.remove(address)
self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances))
self.removeManualDeviceSignal.emit(self.getPluginId(), key, address)
def addManualDevice(self, address):
if address not in self._manual_instances:
self._manual_instances.append(address)
@ -215,6 +231,61 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._checkManualDevice(address)
def _createMachineFromDiscoveredPrinter(self, key: str) -> None:
discovered_device = self._discovered_devices.get(key)
if discovered_device is None:
Logger.log("e", "Could not find discovered device with key [%s]", key)
return
group_name = discovered_device.getProperty("name")
machine_type_id = discovered_device.getProperty("printer_type")
Logger.log("i", "Creating machine from network device with key = [%s], group name = [%s], printer type = [%s]",
key, group_name, machine_type_id)
self._application.getMachineManager().addMachine(machine_type_id, group_name)
# connect the new machine to that network printer
self.associateActiveMachineWithPrinterDevice(discovered_device)
# ensure that the connection states are refreshed.
self.refreshConnections()
def associateActiveMachineWithPrinterDevice(self, printer_device: Optional["PrinterOutputDevice"]) -> None:
if not printer_device:
return
Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key)
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if not global_container_stack:
return
meta_data = global_container_stack.getMetaData()
if "um_network_key" in meta_data: # Global stack already had a connection, but it's changed.
old_network_key = meta_data["um_network_key"]
# Since we might have a bunch of hidden stacks, we also need to change it there.
metadata_filter = {"um_network_key": old_network_key}
containers = self._application.getContainerRegistry().findContainerStacks(type = "machine", **metadata_filter)
for container in containers:
container.setMetaDataEntry("um_network_key", printer_device.key)
# Delete old authentication data.
Logger.log("d", "Removing old authentication id %s for device %s",
global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key)
container.removeMetaDataEntry("network_authentication_id")
container.removeMetaDataEntry("network_authentication_key")
# Ensure that these containers do know that they are configured for network connection
container.addConfiguredConnectionType(printer_device.connectionType.value)
else: # Global stack didn't have a connection yet, configure it.
global_container_stack.setMetaDataEntry("um_network_key", printer_device.key)
global_container_stack.addConfiguredConnectionType(printer_device.connectionType.value)
self.refreshConnections()
def _checkManualDevice(self, address):
# Check if a UM3 family device exists at this address.
# If a printer responds, it will replace the preliminary printer created above
@ -223,21 +294,29 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
name_request = QNetworkRequest(url)
self._network_manager.get(name_request)
def _onNetworkRequestFinished(self, reply):
def _onNetworkRequestFinished(self, reply: "QNetworkReply") -> None:
reply_url = reply.url().toString()
if "system" in reply_url:
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
# Something went wrong with checking the firmware version!
return
address = reply.url().host()
device = None
properties = {} # type: Dict[bytes, bytes]
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
# Either:
# - Something went wrong with checking the firmware version!
# - Something went wrong with checking the amount of printers the cluster has!
# - Couldn't find printer at the address when trying to add it manually.
if address in self._manual_instances:
self.removeManualDeviceSignal.emit(self.getPluginId(), "", address)
return
if "system" in reply_url:
try:
system_info = json.loads(bytes(reply.readAll()).decode("utf-8"))
except:
Logger.log("e", "Something went wrong converting the JSON.")
return
address = reply.url().host()
has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version
instance_name = "manual:%s" % address
properties = {
@ -265,16 +344,12 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._network_manager.get(cluster_request)
elif "printers" in reply_url:
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
# Something went wrong with checking the amount of printers the cluster has!
return
# So we confirmed that the device is in fact a cluster printer, and we should now know how big it is.
try:
cluster_printers_list = json.loads(bytes(reply.readAll()).decode("utf-8"))
except:
Logger.log("e", "Something went wrong converting the JSON.")
return
address = reply.url().host()
instance_name = "manual:%s" % address
if instance_name in self._discovered_devices:
device = self._discovered_devices[instance_name]
@ -285,7 +360,11 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._onRemoveDevice(instance_name)
self._onAddDevice(instance_name, address, properties)
def _onRemoveDevice(self, device_id):
if device and address in self._manual_instances:
self.getOutputDeviceManager().addOutputDevice(device)
self.addManualDeviceSignal.emit(self.getPluginId(), device.getId(), address, properties)
def _onRemoveDevice(self, device_id: str) -> None:
device = self._discovered_devices.pop(device_id, None)
if device:
if device.isConnected():
@ -295,7 +374,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
except TypeError:
# Disconnect already happened.
pass
self._application.getDiscoveredPrintersModel().removeDiscoveredPrinter(device.address)
self.discoveredDevicesChanged.emit()
def _onAddDevice(self, name, address, properties):
@ -320,7 +399,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties)
else:
device = LegacyUM3OutputDevice.LegacyUM3OutputDevice(name, address, properties)
self._application.getDiscoveredPrintersModel().addDiscoveredPrinter(address, device.getId(), name, self._createMachineFromDiscoveredPrinter, properties[b"printer_type"].decode("utf-8"), device)
self._discovered_devices[device.getId()] = device
self.discoveredDevicesChanged.emit()
@ -412,9 +491,8 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
Logger.log("d", "Checking if cloud connection is possible...")
# Pre-Check: Skip if active machine already has been cloud connected or you said don't ask again
active_machine = self._application.getMachineManager().activeMachine # type: Optional["GlobalStack"]
active_machine = self._application.getMachineManager().activeMachine # type: Optional[GlobalStack]
if active_machine:
# Check 1A: Printer isn't already configured for cloud
if ConnectionType.CloudConnection.value in active_machine.configuredConnectionTypes:
Logger.log("d", "Active machine was already configured for cloud.")
@ -486,7 +564,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
return
def _onDontAskMeAgain(self, checked: bool) -> None:
active_machine = self._application.getMachineManager().activeMachine # type: Optional["GlobalStack"]
active_machine = self._application.getMachineManager().activeMachine # type: Optional[GlobalStack]
if active_machine:
active_machine.setMetaDataEntry("do_not_show_cloud_message", checked)
if checked:

View file

@ -5,7 +5,7 @@ from typing import List
from PyQt5.QtCore import pyqtProperty, pyqtSignal
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
from .ConfigurationChangeModel import ConfigurationChangeModel