STAR-332: Fixing warnings

This commit is contained in:
Daniel Schiavini 2018-12-03 11:02:49 +01:00
parent 8b42b84618
commit 8066074a2f
4 changed files with 146 additions and 131 deletions

View file

@ -41,7 +41,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._api_prefix = ""
self._address = address
self._properties = properties
self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(), CuraApplication.getInstance().getVersion())
self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(),
CuraApplication.getInstance().getVersion())
self._onFinishedCallbacks = {} # type: Dict[str, Callable[[QNetworkReply], None]]
self._authentication_state = AuthState.NotAuthenticated
@ -55,7 +56,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
self._gcode = [] # type: List[str]
self._connection_state_before_timeout = None # type: Optional[ConnectionState]
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False,
file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
raise NotImplementedError("requestWrite needs to be implemented")
def setAuthenticationState(self, authentication_state: AuthState) -> None:

View file

@ -3,20 +3,20 @@
import io
import json
import os
from typing import List, Optional, Dict, cast
from typing import List, Optional, Dict, cast, Union
from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
from UM import i18nCatalog
from UM.FileHandler import FileWriter
from UM.FileHandler.FileWriter import FileWriter
from UM.FileHandler.FileHandler import FileHandler
from UM.Logger import Logger
from UM.OutputDevice import OutputDeviceError
from UM.Scene.SceneNode import SceneNode
from UM.Version import Version
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput import PrinterOutputController, PrintJobOutputModel
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
@ -38,7 +38,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
# The cloud URL to use for this remote cluster.
# TODO: Make sure that this url goes to the live api before release
ROOT_PATH= "https://api-staging.ultimaker.com"
ROOT_PATH = "https://api-staging.ultimaker.com"
CLUSTER_API_ROOT = "{}/connect/v1/".format(ROOT_PATH)
CURA_API_ROOT = "{}/cura/v1/".format(ROOT_PATH)
CURA_DRIVE_API_ROOT = "{}/cura-drive/v1/".format(ROOT_PATH)
@ -67,9 +67,25 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
self._print_jobs = {} # type: Dict[str, PrintJobOutputModel]
self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines.
@staticmethod
def _parseReply(reply: QNetworkReply) -> Tuple[int, Union[None, str, bytes]]:
"""
Parses a reply from the stardust server.
:param reply: The reply received from the server.
:return: The status code and the response dict.
"""
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
response = None
try:
response = bytes(reply.readAll()).decode("utf-8")
response = json.loads(response)
except JSONDecodeError:
Logger.logException("w", "Unable to decode JSON from reply.")
return status_code, response
## 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.CLUSTER_API_ROOT_PATH_FORMAT.format(cluster_id = self._device_id) + path)
# noinspection PyArgumentList
url = QUrl(path)
request = QNetworkRequest(url)
request.setHeader(QNetworkRequest.ContentTypeHeader, content_type)
@ -98,8 +114,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False,
file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
self.writeStarted.emit(self)
file_format = self._determineFileFormat(file_handler)
writer = self._determineWriter(file_format)
writer = self._determineWriter(file_handler, file_format)
# This function pauses with the yield, waiting on instructions on which printer it needs to print with.
if not writer:
@ -123,7 +140,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
self._addPrintJobToQueue(stream, request)
# TODO: This is yanked right out of ClusterUM3OoutputDevice, great candidate for a utility or base class
def _determineFileFormat(self, file_handler) -> None:
def _determineFileFormat(self, file_handler) -> Optional[Dict[str, Union[str, int]]]:
# Formats supported by this application (file types that we can actually write).
if file_handler:
file_formats = file_handler.getSupportedFileTypesWrite()
@ -143,21 +160,28 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
machine_file_formats = ["application/x-ufp"] + machine_file_formats
# Take the intersection between file_formats and machine_file_formats.
format_by_mimetype = {format["mime_type"]: format for format in file_formats}
file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] #Keep them ordered according to the preference in machine_file_formats.
format_by_mimetype = {f["mime_type"]: f for f in file_formats}
# Keep them ordered according to the preference in machine_file_formats.
file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats]
if len(file_formats) == 0:
Logger.log("e", "There are no file formats available to write with!")
raise OutputDeviceError.WriteRequestFailedError(self.I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!"))
raise OutputDeviceError.WriteRequestFailedError(
self.I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!")
)
return file_formats[0]
# TODO: This is yanked right out of ClusterUM3OoutputDevice, great candidate for a utility or base class
def _determineWriter(self, file_handler, file_format):
@staticmethod
def _determineWriter(file_handler, file_format) -> Optional[FileWriter]:
# Just take the first file format available.
if file_handler is not None:
writer = file_handler.getWriterByMimeType(cast(str, file_format["mime_type"]))
else:
writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType(cast(str, file_format["mime_type"]))
writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType(
cast(str, file_format["mime_type"])
)
if not writer:
Logger.log("e", "Unexpected error when trying to get the FileWriter")
@ -173,7 +197,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
## Get remote print jobs.
@pyqtProperty("QVariantList", notify = printJobsChanged)
def queuedPrintJobs(self) -> List[UM3PrintJobOutputModel]:
return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"]
return [print_job for print_job in self._print_jobs.values()
if print_job.state == "queued" or print_job.state == "error"]
## Called when the connection to the cluster changes.
def connect(self) -> None:
@ -182,20 +207,19 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
## Called when the network data should be updated.
def _update(self) -> None:
super()._update()
self.get("{root}/cluster/{cluster_id}/status".format(self.CLUSTER_API_ROOT, self._device_id),
self.get("{root}/cluster/{cluster_id}/status".format(root=self.CLUSTER_API_ROOT, cluster_id=self._device_id),
on_finished = self._onStatusCallFinished)
## Method called when HTTP request to status endpoint is finished.
# Contains both printers and print jobs statuses in a single response.
def _onStatusCallFinished(self, reply: QNetworkReply) -> None:
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
if status_code != 200:
status_code, response = self._parseReply(reply)
if status_code > 204:
Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}"
.format(status_code, reply.readAll()))
.format(status_code, status, response))
return
printers, print_jobs = self._parseStatusResponse(reply)
printers, print_jobs = self._parseStatusResponse(response)
if not printers and not print_jobs:
return
@ -204,26 +228,23 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
self._updatePrintJobs(print_jobs)
@staticmethod
def _parseStatusResponse(reply: QNetworkReply): # Optional[(CloudClusterPrinter, CloudClusterPrintJob)] doesn't work
def _parseStatusResponse(response: dict) -> Optional[Tuple[CloudClusterPrinter, CloudClusterPrintJob]]:
printers = []
print_jobs = []
s = ''
try:
s = json.loads(bytes(reply.readAll()).decode("utf-8"))
for p in s["printers"]:
data = response["data"]
for p in data["printers"]:
printer = CloudClusterPrinter(**p)
configuration = printer.configuration
printer.configuration = []
for c in configuration:
extruder = CloudClusterPrinterConfiguration(**c)
extruder.material = CloudClusterPrinterConfigurationMaterial(extruder.material)
extruder.material = CloudClusterPrinterConfigurationMaterial(material=extruder.material)
printer.configuration.append(extruder)
printers.append(printer)
for j in s["print_jobs"]:
for j in data["print_jobs"]:
job = CloudClusterPrintJob(**j)
constraints = job.constraints
job.constraints = []
@ -234,49 +255,42 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
job.configuration = []
for c in configuration:
configuration = CloudClusterPrinterConfiguration(**c)
configuration.material = CloudClusterPrinterConfigurationMaterial(configuration.material)
configuration.material = CloudClusterPrinterConfigurationMaterial(material=configuration.material)
job.configuration.append(configuration)
print_jobs.append(job)
except json.decoder.JSONDecodeError:
Logger.logException("w", "Unable to decode JSON from reply.")
return None
return printers, print_jobs
def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None:
remote_printers = {p.uuid: p for p in printers}
remote_printers = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter]
removed_printers = set(self._printers.keys()).difference(set(remote_printers.keys()))
new_printers = set(remote_printers.keys()).difference(set(self._printers.keys()))
updated_printers = set(self._printers.keys()).intersection(set(remote_printers.keys()))
removed_printer_ids = set(self._printers).difference(remote_printers)
new_printer_ids = set(remote_printers).difference(self._printers)
updated_printer_ids = set(self._printers).intersection(remote_printers)
for p in removed_printers:
self._removePrinter(p)
for printer_guid in removed_printer_ids:
self._removePrinter(printer_guid)
for p in new_printers:
self._addPrinter(printers[p])
self._updatePrinter(printers[p])
for printer_guid in new_printer_ids:
self._addPrinter(remote_printers[printer_guid])
self._updatePrinter(remote_printers[printer_guid])
for p in updated_printers:
self._updatePrinter(printers[p])
for printer_guid in updated_printer_ids:
self._updatePrinter(remote_printers[printer_guid])
# TODO: properly handle removed and updated printers
self.printersChanged.emit()
def _addPrinter(self, printer):
self._printers[printer.uuid] = self._createPrinterOutputModel(self, printer)
def _addPrinter(self, printer: CloudClusterPrinter) -> None:
self._printers[printer.uuid] = self._createPrinterOutputModel(printer)
def _createPrinterOutputModel(self, printer: CloudClusterPrinter) -> PrinterOutputModel:
return PrinterOutputModel(PrinterOutputController(self), len(printer.configuration),
firmware_version=printer.firmware_version)
def _updatePrinter(self, guid : str, printer : CloudClusterPrinter):
self._printers[guid] = self._updatePrinterOutputModel(self, printer)
def _updatePrinterOutputModel(self, printer: CloudClusterPrinter, model : PrinterOutputModel) -> PrinterOutputModel:
def _updatePrinter(self, printer: CloudClusterPrinter) -> None:
model = self._printers[printer.uuid]
model.updateKey(printer.uuid)
model.updateName(printer.friendly_name)
model.updateType(printer.machine_variant)
@ -291,12 +305,12 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
extruder.updateHotendID(extruder_data.print_core_id)
material_data = extruder_data.material
if extruder.activeMaterial is None or extruder.activeMaterial.guid != material.guid:
material = self._createMaterialOutputModel(material_data)
if extruder.activeMaterial is None or extruder.activeMaterial.guid != extruder_data.material.guid:
material = self._createMaterialOutputModel(extruder_data.material)
extruder.updateActiveMaterial(material)
def _createMaterialOutputModel(self, material: CloudClusterPrinterConfigurationMaterial) -> MaterialOutputModel:
@staticmethod
def _createMaterialOutputModel(material: CloudClusterPrinterConfigurationMaterial) -> MaterialOutputModel:
material_manager = CuraApplication.getInstance().getMaterialManager()
material_group_list = material_manager.getMaterialGroupListByGUID(material.guid) or []
@ -328,7 +342,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name)
def _removePrinter(self, guid):
del self._printers[guid]
@ -346,53 +359,51 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
self._addPrintJob(jobs[j])
for j in updated_jobs:
self._updatePrintJob(jobs[j])
self._updatePrintJob(remote_jobs[j])
# TODO: properly handle removed and updated printers
self.printJobsChanged()
def _addPrintJob(self, job: CloudClusterPrintJob):
def _addPrintJob(self, job: CloudClusterPrintJob) -> None:
self._print_jobs[job.uuid] = self._createPrintJobOutputModel(job)
def _createPrintJobOutputModel(self, job:CloudClusterPrintJob) -> PrintJobOutputModel:
def _createPrintJobOutputModel(self, job: CloudClusterPrintJob) -> PrintJobOutputModel:
controller = self._printers[job.printer_uuid]._controller # TODO: Can we access this property?
model = PrintJobOutputModel(controller, job.uuid, job.name)
assigned_printer = self._printes[job.printer_uuid] # TODO: Or do we have to use the assigned_to field?
model.updateAssignedPrinter(assigned_printer)
return model
def _updatePrintJobOutputModel(self, guid: str, job:CloudClusterPrintJob):
model =self._print_jobs[guid]
def _updatePrintJobOutputModel(self, guid: str, job: CloudClusterPrintJob) -> None:
model = self._print_jobs[guid]
model.updateTimeTotal(job.time_total)
model.updateTimeElapsed(job.time_elapsed)
model.updateOwner(job.owner)
model.updateState(job.status)
def _removePrintJob(self, guid:str):
def _removePrintJob(self, guid: str):
del self._print_jobs[guid]
def _addPrintJobToQueue(self, stream, request:JobUploadRequest):
def _addPrintJobToQueue(self, stream, request: JobUploadRequest):
self.put("{}/jobs/upload".format(self.CURA_API_ROOT), data = json.dumps(request.__dict__),
on_finished = lambda reply: self._onAddPrintJobToQueueFinished(stream, reply))
def _onAddPrintJobToQueueFinished(self, stream, reply: QNetworkReply) -> None:
s = json.loads(bytes(reply.readAll()).decode("utf-8"))
status_code, response = self._parseReply(reply) # type: Tuple[int, dict]
if status_code > 204 or not isinstance(dict, response) or "data" not in response:
Logger.error()
return
self.put()
job_response = JobUploadResponse(**response.get("data"))
self.put(job_response.upload_url, data=stream.getvalue(), on_finished=self._onPrintJobUploaded)
# try:
# r = requests.put(self._job.output_url, data=data)
# if r.status_code == 200:
# Logger.log("d", "Finished writing %s to remote URL %s", "", self._job.output_url)
# self.onWriteSuccess.emit(r.text)
# else:
# Logger.log("d", "Error writing %s to remote URL %s", "", self._job.output_url)
# self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(r.text))
# except requests.ConnectionError as e:
# Logger.log("e", "There was a connection error when uploading the G-code to a remote URL: %s", e)
# self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(e))
# except requests.HTTPError as e:
# Logger.log("e", "There was an HTTP error when uploading the G-code to a remote URL: %s", e)
# self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(e))
def _onPrintJobUploaded(self, reply: QNetworkReply) -> None:
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
if status_code > 204:
self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(r.text))
Logger.logException("w", "Received unexpected response from the job upload: %s, %s.", status_code,
bytes(reply.readAll()).decode())
return
pass
self.onWriteSuccess.emit(r.text)

View file

@ -3,7 +3,7 @@
import json
from time import sleep
from threading import Thread
from typing import Dict, Optional, List
from typing import Dict, Optional
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply

View file

@ -1,5 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import List
from ..Models import BaseModel
@ -41,7 +43,7 @@ class CloudClusterPrinterConfiguration(BaseModel):
## Class representing a cluster printer
class CloudClusterPrinter(BaseModel):
def __init__(self, **kwargs):
self.configuration = None # type: CloudClusterPrinterConfiguration
self.configuration = None # type: List[CloudClusterPrinterConfiguration]
self.enabled = None # type: str
self.firmware_version = None # type: str
self.friendly_name = None # type: str
@ -56,7 +58,7 @@ class CloudClusterPrinter(BaseModel):
## Class representing a cloud cluster print job constraint
class CloudClusterPrintJobConstraint(BaseModel):
def __init__(self, **kwargs):
self.require_printer_name: None # type: str
self.require_printer_name = None # type: str
super().__init__(**kwargs)
## Class representing a print job