Merge pull request #10989 from Ultimaker/CURA-8671_dont_send_materials_to_um2c

[CURA-8671] Don't send materials to printers that can't receive them
This commit is contained in:
Jaime van Kessel 2021-12-06 09:48:18 +01:00 committed by GitHub
commit a64aa6ef2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 60 additions and 10 deletions

View File

@ -2,7 +2,7 @@
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import Qt, QTimer, pyqtProperty, pyqtSignal from PyQt5.QtCore import Qt, QTimer, pyqtProperty, pyqtSignal
from typing import Optional from typing import List, Optional
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
@ -11,6 +11,7 @@ from UM.Util import parseBool
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack
from cura.UltimakerCloud.UltimakerCloudConstants import META_CAPABILITIES # To filter on the printer's capabilities.
class GlobalStacksModel(ListModel): class GlobalStacksModel(ListModel):
@ -42,6 +43,7 @@ class GlobalStacksModel(ListModel):
self._filter_connection_type = None # type: Optional[ConnectionType] self._filter_connection_type = None # type: Optional[ConnectionType]
self._filter_online_only = False self._filter_online_only = False
self._filter_capabilities: List[str] = [] # Required capabilities that all listed printers must have.
# Listen to changes # Listen to changes
CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged) CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged)
@ -50,8 +52,13 @@ class GlobalStacksModel(ListModel):
self._updateDelayed() self._updateDelayed()
filterConnectionTypeChanged = pyqtSignal() filterConnectionTypeChanged = pyqtSignal()
filterCapabilitiesChanged = pyqtSignal()
filterOnlineOnlyChanged = pyqtSignal()
def setFilterConnectionType(self, new_filter: Optional[ConnectionType]) -> None: def setFilterConnectionType(self, new_filter: Optional[ConnectionType]) -> None:
self._filter_connection_type = new_filter if self._filter_connection_type != new_filter:
self._filter_connection_type = new_filter
self.filterConnectionTypeChanged.emit()
@pyqtProperty(int, fset = setFilterConnectionType, notify = filterConnectionTypeChanged) @pyqtProperty(int, fset = setFilterConnectionType, notify = filterConnectionTypeChanged)
def filterConnectionType(self) -> int: def filterConnectionType(self) -> int:
@ -65,9 +72,10 @@ class GlobalStacksModel(ListModel):
return -1 return -1
return self._filter_connection_type.value return self._filter_connection_type.value
filterOnlineOnlyChanged = pyqtSignal()
def setFilterOnlineOnly(self, new_filter: bool) -> None: def setFilterOnlineOnly(self, new_filter: bool) -> None:
self._filter_online_only = new_filter if self._filter_online_only != new_filter:
self._filter_online_only = new_filter
self.filterOnlineOnlyChanged.emit()
@pyqtProperty(bool, fset = setFilterOnlineOnly, notify = filterOnlineOnlyChanged) @pyqtProperty(bool, fset = setFilterOnlineOnly, notify = filterOnlineOnlyChanged)
def filterOnlineOnly(self) -> bool: def filterOnlineOnly(self) -> bool:
@ -76,6 +84,20 @@ class GlobalStacksModel(ListModel):
""" """
return self._filter_online_only return self._filter_online_only
def setFilterCapabilities(self, new_filter: List[str]) -> None:
if self._filter_capabilities != new_filter:
self._filter_capabilities = new_filter
self.filterCapabilitiesChanged.emit()
@pyqtProperty("QStringList", fset = setFilterCapabilities, notify = filterCapabilitiesChanged)
def filterCapabilities(self) -> List[str]:
"""
Capabilities to require on the list of printers.
Only printers that have all of these capabilities will be shown in this model.
"""
return self._filter_capabilities
def _onContainerChanged(self, container) -> None: def _onContainerChanged(self, container) -> None:
"""Handler for container added/removed events from registry""" """Handler for container added/removed events from registry"""
@ -108,6 +130,10 @@ class GlobalStacksModel(ListModel):
if self._filter_online_only and not is_online: if self._filter_online_only and not is_online:
continue continue
capabilities = set(container_stack.getMetaDataEntry(META_CAPABILITIES, "").split(","))
if set(self._filter_capabilities) - capabilities: # Not all required capabilities are met.
continue
device_name = container_stack.getMetaDataEntry("group_name", container_stack.getName()) device_name = container_stack.getMetaDataEntry("group_name", container_stack.getName())
section_name = "Connected printers" if has_remote_connection else "Preset printers" section_name = "Connected printers" if has_remote_connection else "Preset printers"
section_name = self._catalog.i18nc("@info:title", section_name) section_name = self._catalog.i18nc("@info:title", section_name)

View File

@ -83,6 +83,14 @@ class UploadMaterialsJob(Job):
host_guid = "*", # Required metadata field. Otherwise we get a KeyError. host_guid = "*", # Required metadata field. Otherwise we get a KeyError.
um_cloud_cluster_id = "*" # Required metadata field. Otherwise we get a KeyError. um_cloud_cluster_id = "*" # Required metadata field. Otherwise we get a KeyError.
) )
# Filter out any printer not capable of the 'import_material' capability. Needs FW 7.0.1-RC at the least!
self._printer_metadata = [ printer_data for printer_data in self._printer_metadata if (
UltimakerCloudConstants.META_CAPABILITIES in printer_data and
"import_material" in printer_data[UltimakerCloudConstants.META_CAPABILITIES]
)
]
for printer in self._printer_metadata: for printer in self._printer_metadata:
self._printer_sync_status[printer["host_guid"]] = self.PrinterStatus.UPLOADING.value self._printer_sync_status[printer["host_guid"]] = self.PrinterStatus.UPLOADING.value

View File

@ -13,6 +13,9 @@ DEFAULT_DIGITAL_FACTORY_URL = "https://digitalfactory.ultimaker.com" # type: st
META_UM_LINKED_TO_ACCOUNT = "um_linked_to_account" META_UM_LINKED_TO_ACCOUNT = "um_linked_to_account"
"""(bool) Whether a cloud printer is linked to an Ultimaker account""" """(bool) Whether a cloud printer is linked to an Ultimaker account"""
META_CAPABILITIES = "capabilities"
"""(list[str]) a list of capabilities this printer supports"""
try: try:
from cura.CuraVersion import CuraCloudAPIRoot # type: ignore from cura.CuraVersion import CuraCloudAPIRoot # type: ignore
if CuraCloudAPIRoot == "": if CuraCloudAPIRoot == "":

View File

@ -49,7 +49,9 @@ _ignored_machine_network_metadata = {
"removal_warning", "removal_warning",
"group_name", "group_name",
"group_size", "group_size",
"connection_type" "connection_type",
"capabilities",
"octoprint_api_key",
} # type: Set[str] } # type: Set[str]

View File

@ -154,7 +154,8 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
"group_name", "group_name",
"group_size", "group_size",
"connection_type", "connection_type",
"octoprint_api_key" "capabilities",
"octoprint_api_key",
} }
serialized_data = container.serialize(ignored_metadata_keys = ignore_keys) serialized_data = container.serialize(ignored_metadata_keys = ignore_keys)

View File

@ -19,7 +19,7 @@ from cura.CuraApplication import CuraApplication
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To update printer metadata with information received about cloud printers. from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To update printer metadata with information received about cloud printers.
from cura.Settings.CuraStackBuilder import CuraStackBuilder from cura.Settings.CuraStackBuilder import CuraStackBuilder
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack
from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT from cura.UltimakerCloud.UltimakerCloudConstants import META_CAPABILITIES, META_UM_LINKED_TO_ACCOUNT
from .CloudApiClient import CloudApiClient from .CloudApiClient import CloudApiClient
from .CloudOutputDevice import CloudOutputDevice from .CloudOutputDevice import CloudOutputDevice
from ..Models.Http.CloudClusterResponse import CloudClusterResponse from ..Models.Http.CloudClusterResponse import CloudClusterResponse
@ -128,6 +128,8 @@ class CloudOutputDeviceManager:
# to the current account # to the current account
if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
if not self._um_cloud_printers[device_id].getMetaDataEntry(META_CAPABILITIES, None):
self._um_cloud_printers[device_id].setMetaDataEntry(META_CAPABILITIES, ",".join(cluster_data.capabilities))
self._onDevicesDiscovered(new_clusters) self._onDevicesDiscovered(new_clusters)
self._updateOnlinePrinters(all_clusters) self._updateOnlinePrinters(all_clusters)

View File

@ -37,7 +37,7 @@ class CloudClusterResponse(BaseModel):
self.friendly_name = friendly_name self.friendly_name = friendly_name
self.printer_type = printer_type self.printer_type = printer_type
self.printer_count = printer_count self.printer_count = printer_count
self.capabilities = capabilities self.capabilities = capabilities if capabilities is not None else []
super().__init__(**kwargs) super().__init__(**kwargs)
# Validates the model, raising an exception if the model is invalid. # Validates the model, raising an exception if the model is invalid.
@ -45,3 +45,10 @@ class CloudClusterResponse(BaseModel):
super().validate() super().validate()
if not self.cluster_id: if not self.cluster_id:
raise ValueError("cluster_id is required on CloudCluster") raise ValueError("cluster_id is required on CloudCluster")
def __repr__(self) -> str:
"""
Convenience function for printing when debugging.
:return: A human-readable representation of the data in this object.
"""
return str({k: v for k, v in self.__dict__.items() if k in {"cluster_id", "host_guid", "host_name", "status", "is_online", "host_version", "host_internal_ip", "friendly_name", "printer_type", "printer_count", "capabilities"}})

View File

@ -81,7 +81,7 @@
"material_bed_temperature_layer_0": { "maximum_value": 110 }, "material_bed_temperature_layer_0": { "maximum_value": 110 },
"material_print_temperature": { "maximum_value": 260 }, "material_print_temperature": { "maximum_value": 260 },
"meshfix_maximum_resolution": { "value": "(speed_wall_0 + speed_wall_x) / 60" }, "meshfix_maximum_resolution": { "value": "(speed_wall_0 + speed_wall_x) / 60" },
"meshfix_maximum_deviation": { "value": "layer_height / 4" }, "meshfix_maximum_deviation": { "value": "layer_height / 4" },
"meshfix_maximum_travel_resolution": { "value": 0.5 }, "meshfix_maximum_travel_resolution": { "value": 0.5 },
"prime_blob_enable": { "enabled": true, "default_value": true, "value": "resolveOrValue('print_sequence') != 'one_at_a_time'" } "prime_blob_enable": { "enabled": true, "default_value": true, "value": "resolveOrValue('print_sequence') != 'one_at_a_time'" }
} }

View File

@ -579,7 +579,7 @@ Window
} }
Label Label
{ {
text: catalog.i18nc("@text", "It seems like you don't have access to any printers connected to Digital Factory.") text: catalog.i18nc("@text", "It seems like you don't have any compatible printers connected to Digital Factory. Make sure your printer is connected and it's running the latest firmware.")
width: parent.width width: parent.width
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap wrapMode: Text.Wrap
@ -737,6 +737,7 @@ Window
id: cloudPrinterList id: cloudPrinterList
filterConnectionType: 3 //Only show cloud connections. filterConnectionType: 3 //Only show cloud connections.
filterOnlineOnly: true //Only show printers that are online. filterOnlineOnly: true //Only show printers that are online.
filterCapabilities: ["import_material"] //Only show printers that can receive the material profiles.
} }
Cura.GlobalStacksModel Cura.GlobalStacksModel
{ {