Add CloudOutputDeviceManager, test implementation

This commit is contained in:
ChrisTerBeke 2018-11-19 21:59:57 +01:00
parent 115936c46b
commit 228325eb89
No known key found for this signature in database
GPG key ID: A49F1AB9D7E0C263
5 changed files with 78 additions and 17 deletions

View file

@ -17,6 +17,7 @@ from enum import IntEnum
import os # To get the username
import gzip
class AuthState(IntEnum):
NotAuthenticated = 1
AuthenticationRequested = 2

View file

@ -1,11 +1,15 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from .src import DiscoverUM3Action
from .src import UM3OutputDevicePlugin
def getMetaData():
return {}
def register(app):
return { "output_device": UM3OutputDevicePlugin.UM3OutputDevicePlugin(), "machine_action": DiscoverUM3Action.DiscoverUM3Action()}
return {
"output_device": UM3OutputDevicePlugin.UM3OutputDevicePlugin(app),
"machine_action": DiscoverUM3Action.DiscoverUM3Action()
}

View file

@ -3,13 +3,14 @@
import json
from typing import List, Optional, Dict
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QUrl
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
from UM import i18nCatalog
from UM.FileHandler.FileHandler import FileHandler
from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController
@ -37,19 +38,34 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
# Signal triggered when the print jobs in the queue were changed.
printJobsChanged = pyqtSignal()
def __init__(self, device_id: str, address: str, properties: Dict[bytes, bytes], parent: QObject = None):
super().__init__(device_id = device_id, address = address, properties = properties, parent = parent)
def __init__(self, device_id: str, parent: QObject = None):
super().__init__(device_id = device_id, address = "", properties = {}, parent = parent)
self._setInterfaceElements()
# The API prefix is automatically added when doing any HTTP call on the device.
self._api_prefix = self.API_ROOT_PATH_FORMAT.format(device_id) # TODO: verify we can use device_id here
self._authentication_state = AuthState.Authenticated # TODO: use cura.API.Account to set this?
self._device_id = device_id
self._account = CuraApplication.getInstance().getCuraAPI().account
# Properties to populate later on with received cloud data.
self._printers = []
self._print_jobs = []
self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines.
## We need to override _createEmptyRequest to work for the cloud.
def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest:
url = QUrl(self.API_ROOT_PATH_FORMAT.format(cluster_id = self._device_id) + path)
request = QNetworkRequest(url)
request.setHeader(QNetworkRequest.ContentTypeHeader, content_type)
request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent)
if not self._account.isLoggedIn:
# TODO: show message to user to sign in
self.setAuthenticationState(AuthState.NotAuthenticated)
else:
self.setAuthenticationState(AuthState.Authenticated)
request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode())
return request
## Set all the interface elements and texts for this output device.
def _setInterfaceElements(self):
self.setPriority(3)
@ -90,7 +106,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
if status_code != 200:
Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}"
.format(status_code, reply.getErrorString()))
.format(status_code, reply.readAll()))
return
data = self._parseStatusResponse(reply)

View file

@ -0,0 +1,31 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING
from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice
if TYPE_CHECKING:
from cura.CuraApplication import CuraApplication
## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters.
# Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code.
class CloudOutputDeviceManager:
def __init__(self, application: "CuraApplication"):
self._output_device_manager = application.getOutputDeviceManager()
self._account = application.getCuraAPI().account
self._getRemoteClusters()
# For testing:
application.globalContainerStackChanged.connect(self._addCloudOutputDevice)
def _getRemoteClusters(self):
# TODO: get list of remote clusters and create an output device for each.
pass
def _addCloudOutputDevice(self):
device = CloudOutputDevice("xxxx-xxxx-xxxx-xxxx")
self._output_device_manager.addOutputDevice(device)
device.connect()

View file

@ -1,11 +1,12 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
from UM.Logger import Logger
from UM.Application import Application
from UM.Signal import Signal, signalemitter
from UM.Version import Version
from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager
from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice
@ -19,6 +20,9 @@ from time import time
import json
if TYPE_CHECKING:
from cura.CuraApplication import CuraApplication
## 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.
@ -29,8 +33,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
removeDeviceSignal = Signal()
discoveredDevicesChanged = Signal()
def __init__(self):
def __init__(self, application: "CuraApplication"):
super().__init__()
self._application = application
self._zero_conf = None
self._zero_conf_browser = None
@ -38,7 +44,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self.addDeviceSignal.connect(self._onAddDevice)
self.removeDeviceSignal.connect(self._onRemoveDevice)
Application.getInstance().globalContainerStackChanged.connect(self.reCheckConnections)
application.globalContainerStackChanged.connect(self.reCheckConnections)
self._discovered_devices = {}
@ -53,7 +59,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/"
# Get list of manual instances from preferences
self._preferences = Application.getInstance().getPreferences()
self._preferences = self._application.getPreferences()
self._preferences.addPreference("um3networkprinting/manual_instances",
"") # A comma-separated list of ip adresses or hostnames
@ -71,6 +77,9 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._service_changed_request_thread = Thread(target=self._handleOnServiceChangedRequests, daemon=True)
self._service_changed_request_thread.start()
# Create a cloud output device manager that abstract all cloud connection logic away.
self._cloud_output_device_manager = CloudOutputDeviceManager(self._application)
def getDiscoveredDevices(self):
return self._discovered_devices
@ -104,7 +113,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self.resetLastManualDevice()
def reCheckConnections(self):
active_machine = Application.getInstance().getGlobalContainerStack()
active_machine = self._application.getGlobalContainerStack()
if not active_machine:
return
@ -129,7 +138,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
return
if self._discovered_devices[key].isConnected():
# Sometimes the status changes after changing the global container and maybe the device doesn't belong to this machine
um_network_key = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("um_network_key")
um_network_key = self._application.getGlobalContainerStack().getMetaDataEntry("um_network_key")
if key == um_network_key:
self.getOutputDeviceManager().addOutputDevice(self._discovered_devices[key])
else:
@ -281,7 +290,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._discovered_devices[device.getId()] = device
self.discoveredDevicesChanged.emit()
global_container_stack = Application.getInstance().getGlobalContainerStack()
global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"):
device.connect()
device.connectionStateChanged.connect(self._onDeviceConnectionStateChanged)
@ -299,7 +308,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
self._service_changed_request_event.wait(timeout = 5.0)
# Stop if the application is shutting down
if Application.getInstance().isShuttingDown():
if self._application.isShuttingDown():
return
self._service_changed_request_event.clear()