mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 15:07:28 -06:00
Added stub for AbstractCloudOutputDevice
It doesn't actually allow you to send a print, but it does ask some info from the API CURA-8463
This commit is contained in:
parent
38e4ca1e0f
commit
6fed6b824c
4 changed files with 146 additions and 16 deletions
|
@ -0,0 +1,87 @@
|
||||||
|
from time import time
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from PyQt6.QtCore import QObject
|
||||||
|
from PyQt6.QtNetwork import QNetworkReply
|
||||||
|
|
||||||
|
from UM import i18nCatalog
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState
|
||||||
|
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
|
||||||
|
from .CloudApiClient import CloudApiClient
|
||||||
|
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
|
||||||
|
from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice
|
||||||
|
|
||||||
|
I18N_CATALOG = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractCloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||||
|
API_CHECK_INTERVAL = 10.0 # seconds
|
||||||
|
|
||||||
|
def __init__(self, api_client: CloudApiClient, printer_type: str, parent: QObject = None) -> None:
|
||||||
|
|
||||||
|
self._api = api_client
|
||||||
|
properties = {
|
||||||
|
#b"address": cluster.host_internal_ip.encode() if cluster.host_internal_ip else b"",
|
||||||
|
# b"name": cluster.friendly_name.encode() if cluster.friendly_name else b"",
|
||||||
|
##b"firmware_version": cluster.host_version.encode() if cluster.host_version else b"",
|
||||||
|
b"printer_type": printer_type.encode()
|
||||||
|
#b"cluster_size": str(cluster.printer_count).encode() if cluster.printer_count else b"1"
|
||||||
|
}
|
||||||
|
super().__init__(
|
||||||
|
device_id=f"ABSTRACT_{printer_type}",
|
||||||
|
address="",
|
||||||
|
connection_type=ConnectionType.CloudConnection,
|
||||||
|
properties=properties,
|
||||||
|
parent=parent
|
||||||
|
)
|
||||||
|
|
||||||
|
print("CREATING ABSTRACT CLOUD OUTPUT DEVIIICEEEEEE")
|
||||||
|
self._setInterfaceElements()
|
||||||
|
|
||||||
|
def connect(self) -> None:
|
||||||
|
"""Connects this device."""
|
||||||
|
|
||||||
|
if self.isConnected():
|
||||||
|
return
|
||||||
|
Logger.log("i", "Attempting to connect AbstractCloudOutputDevice %s", self.key)
|
||||||
|
super().connect()
|
||||||
|
|
||||||
|
#CuraApplication.getInstance().getBackend().backendStateChange.connect(self._onBackendStateChange)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def disconnect(self) -> None:
|
||||||
|
"""Disconnects the device"""
|
||||||
|
|
||||||
|
if not self.isConnected():
|
||||||
|
return
|
||||||
|
super().disconnect()
|
||||||
|
|
||||||
|
def _update(self) -> None:
|
||||||
|
"""Called when the network data should be updated."""
|
||||||
|
|
||||||
|
super()._update()
|
||||||
|
if time() - self._time_of_last_request < self.API_CHECK_INTERVAL:
|
||||||
|
return # avoid calling the cloud too often
|
||||||
|
self._time_of_last_request = time()
|
||||||
|
if self._api.account.isLoggedIn:
|
||||||
|
self.setAuthenticationState(AuthState.Authenticated)
|
||||||
|
self._last_request_time = time()
|
||||||
|
self._api.getClustersByMachineType(self.printerType, self._onCompleted, self._onError)
|
||||||
|
else:
|
||||||
|
self.setAuthenticationState(AuthState.NotAuthenticated)
|
||||||
|
|
||||||
|
def _setInterfaceElements(self) -> None:
|
||||||
|
"""Set all the interface elements and texts for this output device."""
|
||||||
|
|
||||||
|
self.setPriority(2) # Make sure we end up below the local networking and above 'save to file'.
|
||||||
|
self.setShortDescription(I18N_CATALOG.i18nc("@action:button", "Print via cloud"))
|
||||||
|
self.setDescription(I18N_CATALOG.i18nc("@properties:tooltip", "Print via cloud"))
|
||||||
|
self.setConnectionText(I18N_CATALOG.i18nc("@info:status", "Connected via cloud"))
|
||||||
|
|
||||||
|
def _onCompleted(self, clusters: List[CloudClusterResponse]) -> None:
|
||||||
|
self._responseReceived()
|
||||||
|
# Todo: actually handle the data that we get back!
|
||||||
|
|
||||||
|
def _onError(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None:
|
||||||
|
pass
|
|
@ -61,7 +61,6 @@ class CloudApiClient:
|
||||||
@property
|
@property
|
||||||
def account(self) -> Account:
|
def account(self) -> Account:
|
||||||
"""Gets the account used for the API."""
|
"""Gets the account used for the API."""
|
||||||
|
|
||||||
return self._account
|
return self._account
|
||||||
|
|
||||||
def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any], failed: Callable) -> None:
|
def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any], failed: Callable) -> None:
|
||||||
|
@ -77,6 +76,24 @@ class CloudApiClient:
|
||||||
error_callback = failed,
|
error_callback = failed,
|
||||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
def getClustersByMachineType(self, machine_type, on_finished: Callable[[List[CloudClusterResponse]], Any], failed: Callable) -> None:
|
||||||
|
# HACK: There is something weird going on with the API, as it reports printer types in formats like
|
||||||
|
# "ultimaker_s3", but wants "Ultimaker S3" when using the machine_variant filter query. So we need to do some
|
||||||
|
# conversion!
|
||||||
|
|
||||||
|
machine_type = machine_type.replace("_plus", "+")
|
||||||
|
machine_type = machine_type.replace("_", " ")
|
||||||
|
machine_type = machine_type.replace("ultimaker", "ultimaker ")
|
||||||
|
machine_type = machine_type.replace(" ", " ")
|
||||||
|
machine_type = machine_type.title()
|
||||||
|
|
||||||
|
url = f"{self.CLUSTER_API_ROOT}/clusters?machine_variant={machine_type}"
|
||||||
|
self._http.get(url,
|
||||||
|
scope=self._scope,
|
||||||
|
callback=self._parseCallback(on_finished, CloudClusterResponse, failed),
|
||||||
|
error_callback=failed,
|
||||||
|
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
||||||
|
|
||||||
def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None:
|
def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None:
|
||||||
"""Retrieves the status of the given cluster.
|
"""Retrieves the status of the given cluster.
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To upda
|
||||||
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_CAPABILITIES, META_UM_LINKED_TO_ACCOUNT
|
from cura.UltimakerCloud.UltimakerCloudConstants import META_CAPABILITIES, META_UM_LINKED_TO_ACCOUNT
|
||||||
|
from .AbstractCloudOutputDevice import AbstractCloudOutputDevice
|
||||||
from .CloudApiClient import CloudApiClient
|
from .CloudApiClient import CloudApiClient
|
||||||
from .CloudOutputDevice import CloudOutputDevice
|
from .CloudOutputDevice import CloudOutputDevice
|
||||||
from ..Messages.RemovedPrintersMessage import RemovedPrintersMessage
|
from ..Messages.RemovedPrintersMessage import RemovedPrintersMessage
|
||||||
|
@ -49,6 +50,8 @@ class CloudOutputDeviceManager:
|
||||||
# Persistent dict containing the remote clusters for the authenticated user.
|
# Persistent dict containing the remote clusters for the authenticated user.
|
||||||
self._remote_clusters: Dict[str, CloudOutputDevice] = {}
|
self._remote_clusters: Dict[str, CloudOutputDevice] = {}
|
||||||
|
|
||||||
|
self._abstract_clusters: Dict[str, AbstractCloudOutputDevice] = {}
|
||||||
|
|
||||||
# Dictionary containing all the cloud printers loaded in Cura
|
# Dictionary containing all the cloud printers loaded in Cura
|
||||||
self._um_cloud_printers: Dict[str, GlobalStack] = {}
|
self._um_cloud_printers: Dict[str, GlobalStack] = {}
|
||||||
|
|
||||||
|
@ -189,6 +192,10 @@ class CloudOutputDeviceManager:
|
||||||
|
|
||||||
for cluster_data in discovered_clusters:
|
for cluster_data in discovered_clusters:
|
||||||
output_device = CloudOutputDevice(self._api, cluster_data)
|
output_device = CloudOutputDevice(self._api, cluster_data)
|
||||||
|
|
||||||
|
if cluster_data.printer_type not in self._abstract_clusters:
|
||||||
|
self._abstract_clusters[cluster_data.printer_type] = AbstractCloudOutputDevice(self._api, cluster_data.printer_type)
|
||||||
|
|
||||||
# If the machine already existed before, it will be present in the host_guid_map
|
# If the machine already existed before, it will be present in the host_guid_map
|
||||||
if cluster_data.host_guid in host_guid_map:
|
if cluster_data.host_guid in host_guid_map:
|
||||||
machine = machine_manager.getMachine(output_device.printerType, {self.META_HOST_GUID: cluster_data.host_guid})
|
machine = machine_manager.getMachine(output_device.printerType, {self.META_HOST_GUID: cluster_data.host_guid})
|
||||||
|
@ -373,7 +380,10 @@ class CloudOutputDeviceManager:
|
||||||
if not active_machine:
|
if not active_machine:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check if we should directly connect with a "normal" CloudOutputDevice or that we should connect to an
|
||||||
|
# 'abstract' one
|
||||||
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||||
|
if active_machine.getMetaDataEntry("is_abstract_machine") != "True":
|
||||||
stored_cluster_id = active_machine.getMetaDataEntry(self.META_CLUSTER_ID)
|
stored_cluster_id = active_machine.getMetaDataEntry(self.META_CLUSTER_ID)
|
||||||
local_network_key = active_machine.getMetaDataEntry(self.META_NETWORK_KEY)
|
local_network_key = active_machine.getMetaDataEntry(self.META_NETWORK_KEY)
|
||||||
|
|
||||||
|
@ -389,6 +399,14 @@ class CloudOutputDeviceManager:
|
||||||
elif device.key in output_device_manager.getOutputDeviceIds():
|
elif device.key in output_device_manager.getOutputDeviceIds():
|
||||||
# Remove device if it is not meant for the active machine.
|
# Remove device if it is not meant for the active machine.
|
||||||
output_device_manager.removeOutputDevice(device.key)
|
output_device_manager.removeOutputDevice(device.key)
|
||||||
|
else: # Abstract it is!
|
||||||
|
remote_abstract_cluster_copy: List[CloudOutputDevice] = list(self._abstract_clusters.values())
|
||||||
|
for device in remote_abstract_cluster_copy:
|
||||||
|
if device.printerType == active_machine.definition.getId():
|
||||||
|
print("Found the device to activate", device)
|
||||||
|
self._connectToAbstractOutputDevice(device, active_machine)
|
||||||
|
else:
|
||||||
|
output_device_manager.removeOutputDevice(device.key)
|
||||||
|
|
||||||
def _setOutputDeviceMetadata(self, device: CloudOutputDevice, machine: GlobalStack):
|
def _setOutputDeviceMetadata(self, device: CloudOutputDevice, machine: GlobalStack):
|
||||||
machine.setName(device.name)
|
machine.setName(device.name)
|
||||||
|
@ -405,6 +423,15 @@ class CloudOutputDeviceManager:
|
||||||
machine.setMetaDataEntry("removal_warning", removal_warning_string)
|
machine.setMetaDataEntry("removal_warning", removal_warning_string)
|
||||||
machine.addConfiguredConnectionType(device.connectionType.value)
|
machine.addConfiguredConnectionType(device.connectionType.value)
|
||||||
|
|
||||||
|
def _connectToAbstractOutputDevice(self, device: AbstractCloudOutputDevice, machine: GlobalStack) -> None:
|
||||||
|
if not device.isConnected():
|
||||||
|
device.connect()
|
||||||
|
machine.addConfiguredConnectionType(device.connectionType.value)
|
||||||
|
|
||||||
|
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||||
|
if device.key not in output_device_manager.getOutputDeviceIds():
|
||||||
|
output_device_manager.addOutputDevice(device)
|
||||||
|
|
||||||
def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None:
|
def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None:
|
||||||
"""Connects to an output device and makes sure it is registered in the output device manager."""
|
"""Connects to an output device and makes sure it is registered in the output device manager."""
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ from ..BaseModel import BaseModel
|
||||||
class CloudClusterResponse(BaseModel):
|
class CloudClusterResponse(BaseModel):
|
||||||
"""Class representing a cloud connected cluster."""
|
"""Class representing a cloud connected cluster."""
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str,
|
def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str,
|
||||||
host_internal_ip: Optional[str] = None, host_version: Optional[str] = None,
|
host_internal_ip: Optional[str] = None, host_version: Optional[str] = None,
|
||||||
friendly_name: Optional[str] = None, printer_type: str = "ultimaker3", printer_count: int = 1,
|
friendly_name: Optional[str] = None, printer_type: str = "ultimaker3", printer_count: int = 1,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue