mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 23:17:32 -06:00
Added models to process the data from the api results
Added code to update the UI models
This commit is contained in:
parent
8a72ba3cfa
commit
763291821f
3 changed files with 246 additions and 31 deletions
|
@ -1,18 +1,23 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import json
|
||||
import os
|
||||
from typing import List, Optional, Dict
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QUrl
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty
|
||||
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 UM.Settings import ContainerRegistry
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.PrinterOutput import PrinterOutputController, PrintJobOutputModel
|
||||
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
|
||||
from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState
|
||||
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
||||
from .Models import CloudClusterPrinter, CloudClusterPrinterConfiguration, CloudClusterPrinterConfigurationMaterial, CloudClusterPrintJob, CloudClusterPrintJobConstraint
|
||||
|
||||
from .CloudOutputController import CloudOutputController
|
||||
from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel
|
||||
|
@ -38,17 +43,23 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
# Signal triggered when the print jobs in the queue were changed.
|
||||
printJobsChanged = pyqtSignal()
|
||||
|
||||
|
||||
def __init__(self, device_id: str, parent: QObject = None):
|
||||
super().__init__(device_id = device_id, address = "", properties = {}, parent = parent)
|
||||
self._setInterfaceElements()
|
||||
|
||||
self._device_id = device_id
|
||||
self._account = CuraApplication.getInstance().getCuraAPI().account
|
||||
|
||||
# We re-use the Cura Connect monitor tab to get the most functionality right away.
|
||||
self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
"../../resources/qml/ClusterMonitorItem.qml")
|
||||
self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
"../../resources/qml/ClusterControlItem.qml")
|
||||
|
||||
# Properties to populate later on with received cloud data.
|
||||
self._printers = []
|
||||
self._print_jobs = []
|
||||
self._printers = {} # type: Dict[str, PrinterOutputModel]
|
||||
self._print_jobs = {} # type: Dict[str, PrintJobOutputModel]
|
||||
self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines.
|
||||
|
||||
## We need to override _createEmptyRequest to work for the cloud.
|
||||
|
@ -90,8 +101,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
|
||||
## Get remote print jobs.
|
||||
@pyqtProperty("QVariantList", notify = printJobsChanged)
|
||||
def printJobs(self) -> List[UM3PrintJobOutputModel]:
|
||||
return self._print_jobs
|
||||
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"]
|
||||
|
||||
## Called when the connection to the cluster changes.
|
||||
def connect(self) -> None:
|
||||
|
@ -111,41 +122,182 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
|||
.format(status_code, reply.readAll()))
|
||||
return
|
||||
|
||||
data = self._parseStatusResponse(reply)
|
||||
if data is None:
|
||||
printers, print_jobs = self._parseStatusResponse(reply)
|
||||
if not printers and not print_jobs:
|
||||
return
|
||||
|
||||
# Update all data from the cluster.
|
||||
self._updatePrinters(data.get("printers", []))
|
||||
self._updatePrintJobs(data.get("print_jobs", []))
|
||||
self._updatePrinters(printers)
|
||||
self._updatePrintJobs(print_jobs)
|
||||
|
||||
@staticmethod
|
||||
def _parseStatusResponse(reply: QNetworkReply) -> Optional[dict]:
|
||||
def _parseStatusResponse(reply: QNetworkReply): # Optional[(CloudClusterPrinter, CloudClusterPrintJob)] doesn't work
|
||||
|
||||
printers = []
|
||||
print_jobs = []
|
||||
s = ''
|
||||
try:
|
||||
result = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
# TODO: use model or named tuple here.
|
||||
return result
|
||||
s = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
|
||||
for p in s["printers"]:
|
||||
printer = CloudClusterPrinter(**p)
|
||||
configuration = printer.configuration
|
||||
printer.configuration = []
|
||||
for c in configuration:
|
||||
extruder = CloudClusterPrinterConfiguration(**c)
|
||||
extruder.material = CloudClusterPrinterConfigurationMaterial(extruder.material)
|
||||
printer.configuration.append(extruder)
|
||||
|
||||
printers.append(printer)
|
||||
|
||||
for j in s["print_jobs"]:
|
||||
job = CloudClusterPrintJob(**j)
|
||||
constraints = job.constraints
|
||||
job.constraints = []
|
||||
for c in constraints:
|
||||
job.constraints.append(CloudClusterPrintJobConstraint(**c))
|
||||
|
||||
configuration = job.configuration
|
||||
job.configuration = []
|
||||
for c in configuration:
|
||||
configuration = CloudClusterPrinterConfiguration(**c)
|
||||
configuration.material = CloudClusterPrinterConfigurationMaterial(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
|
||||
|
||||
def _updatePrinters(self, remote_printers: List[Dict[str, any]]) -> None:
|
||||
# TODO: use model or tuple for remote_printers data
|
||||
for printer in remote_printers:
|
||||
|
||||
# If the printer does not exist yet, create it.
|
||||
if not self._getPrinterByKey(printer["uuid"]):
|
||||
self._printers.append(PrinterOutputModel(
|
||||
output_controller = CloudOutputController(self),
|
||||
number_of_extruders = self._number_of_extruders
|
||||
))
|
||||
|
||||
return printers, print_jobs
|
||||
|
||||
def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None:
|
||||
remote_printers = {p.uuid: p for p in printers}
|
||||
|
||||
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()))
|
||||
|
||||
for p in removed_printers:
|
||||
self._removePrinter(p)
|
||||
|
||||
for p in new_printers:
|
||||
self._addPrinter(printers[p])
|
||||
self._updatePrinter(printers[p])
|
||||
|
||||
for p in updated_printers:
|
||||
self._updatePrinter(printers[p])
|
||||
|
||||
# TODO: properly handle removed and updated printers
|
||||
self.printersChanged.emit()
|
||||
|
||||
def _updatePrintJobs(self, remote_print_jobs: List[Dict[str, any]]) -> None:
|
||||
# TODO: use model or tuple for remote_print_jobs data
|
||||
pass
|
||||
|
||||
def _addPrinter(self, printer):
|
||||
self._printers[printer.uuid] = self._createPrinterOutputModel(self, 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):
|
||||
model = self._printers[guid]
|
||||
self._printers[guid] = self._updatePrinterOutputModel(self, printer)
|
||||
|
||||
def _updatePrinterOutputModel(self, printer: CloudClusterPrinter, model : PrinterOutputModel) -> PrinterOutputModel:
|
||||
model.updateKey(printer.uuid)
|
||||
model.updateName(printer.friendly_name)
|
||||
model.updateType(printer.machine_variant)
|
||||
model.updateState(printer.status if printer.enabled else "disabled")
|
||||
|
||||
for index in range(0, len(printer.configuration)):
|
||||
try:
|
||||
extruder = model.extruders[index]
|
||||
extruder_data = printer.configuration[index]
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
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)
|
||||
extruder.updateActiveMaterial(material)
|
||||
|
||||
def _createMaterialOutputModel(self, material: CloudClusterPrinterConfigurationMaterial) -> MaterialOutputModel:
|
||||
material_manager = CuraApplication.getInstance().getMaterialManager()
|
||||
material_group_list = material_manager.getMaterialGroupListByGUID(material.guid) or []
|
||||
|
||||
# Sort the material groups by "is_read_only = True" first, and then the name alphabetically.
|
||||
read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list))
|
||||
non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list))
|
||||
material_group = None
|
||||
if read_only_material_group_list:
|
||||
read_only_material_group_list = sorted(read_only_material_group_list, key=lambda x: x.name)
|
||||
material_group = read_only_material_group_list[0]
|
||||
elif non_read_only_material_group_list:
|
||||
non_read_only_material_group_list = sorted(non_read_only_material_group_list, key=lambda x: x.name)
|
||||
material_group = non_read_only_material_group_list[0]
|
||||
|
||||
if material_group:
|
||||
container = material_group.root_material_node.getContainer()
|
||||
color = container.getMetaDataEntry("color_code")
|
||||
brand = container.getMetaDataEntry("brand")
|
||||
material_type = container.getMetaDataEntry("material")
|
||||
name = container.getName()
|
||||
else:
|
||||
Logger.log("w",
|
||||
"Unable to find material with guid {guid}. Using data as provided by cluster".format(
|
||||
guid=material.guid))
|
||||
color = material.color
|
||||
brand = material.brand
|
||||
material_type = material.material
|
||||
name = "Empty" if material.material == "empty" else "Unknown"
|
||||
|
||||
return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name)
|
||||
|
||||
|
||||
def _removePrinter(self, guid):
|
||||
del self._printers[guid]
|
||||
|
||||
def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None:
|
||||
remote_jobs = {j.uuid: j for j in jobs}
|
||||
|
||||
removed_jobs = set(self._print_jobs.keys()).difference(set(remote_jobs.keys()))
|
||||
new_jobs = set(remote_jobs.keys()).difference(set(self._print_jobs.keys()))
|
||||
updated_jobs = set(self._print_jobs.keys()).intersection(set(remote_jobs.keys()))
|
||||
|
||||
for j in removed_jobs:
|
||||
self._removePrintJob(j)
|
||||
|
||||
for j in new_jobs:
|
||||
self._addPrintJob(jobs[j])
|
||||
|
||||
for j in updated_jobs:
|
||||
self._updatePrintJob(jobs[j])
|
||||
|
||||
# TODO: properly handle removed and updated printers
|
||||
self.printJobsChanged()
|
||||
|
||||
def _addPrintJob(self, job: CloudClusterPrintJob):
|
||||
self._print_jobs[job.uuid] = self._createPrintJobOutputModel(job)
|
||||
|
||||
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)
|
||||
|
||||
def _updatePrintJobOutputModel(self, guid: str, job:CloudClusterPrintJob):
|
||||
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):
|
||||
del self._print_jobs[guid]
|
||||
|
||||
def _addPrintJobToQueue(self):
|
||||
# TODO: implement this
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue