diff --git a/cura/PrinterOutput/PrinterOutputModel.py b/cura/PrinterOutput/PrinterOutputModel.py index aaf9b48968..0c30d8d788 100644 --- a/cura/PrinterOutput/PrinterOutputModel.py +++ b/cura/PrinterOutput/PrinterOutputModel.py @@ -16,6 +16,7 @@ if MYPY: class PrinterOutputModel(QObject): bedTemperatureChanged = pyqtSignal() targetBedTemperatureChanged = pyqtSignal() + isPreheatingChanged = pyqtSignal() stateChanged = pyqtSignal() activePrintJobChanged = pyqtSignal() nameChanged = pyqtSignal() @@ -24,7 +25,7 @@ class PrinterOutputModel(QObject): typeChanged = pyqtSignal() cameraChanged = pyqtSignal() - def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None): + def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None, firmware_version = ""): super().__init__(parent) self._bed_temperature = -1 # Use -1 for no heated bed. self._target_bed_temperature = 0 @@ -34,18 +35,31 @@ class PrinterOutputModel(QObject): self._extruders = [ExtruderOutputModel(printer=self) for i in range(number_of_extruders)] self._head_position = Vector(0, 0, 0) self._active_print_job = None # type: Optional[PrintJobOutputModel] - + self._firmware_version = firmware_version self._printer_state = "unknown" - + self._is_preheating = False self._type = "" self._camera = None + @pyqtProperty(str, constant = True) + def firmwareVersion(self): + return self._firmware_version + def setCamera(self, camera): if self._camera is not camera: self._camera = camera self.cameraChanged.emit() + def updateIsPreheating(self, pre_heating): + if self._is_preheating != pre_heating: + self._is_preheating = pre_heating + self.isPreheatingChanged.emit() + + @pyqtProperty(bool, notify=isPreheatingChanged) + def isPreheating(self): + return self._is_preheating + @pyqtProperty(QObject, notify=cameraChanged) def camera(self): return self._camera diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index ad63e65943..f223ef1844 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -23,7 +23,7 @@ class MonitorStage(CuraStage): def _setActivePrintJob(self, print_job): if self._active_print_job != print_job: if self._active_print_job: - self._active_printer.stateChanged.disconnect(self._updateIconSource) + self._active_print_job.stateChanged.disconnect(self._updateIconSource) self._active_print_job = print_job if self._active_print_job: self._active_print_job.stateChanged.connect(self._updateIconSource) diff --git a/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py b/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py index c7fdf9bdc6..967c99995e 100644 --- a/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/LegacyUM3OutputDevice.py @@ -543,7 +543,9 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice): return if not self._printers: - self._printers = [PrinterOutputModel(output_controller=self._output_controller, number_of_extruders=self._number_of_extruders)] + # Quickest way to get the firmware version is to grab it from the zeroconf. + firmware_version = self._properties.get(b"firmware_version", b"").decode("utf-8") + self._printers = [PrinterOutputModel(output_controller=self._output_controller, number_of_extruders=self._number_of_extruders, firmware_version=firmware_version)] self._printers[0].setCamera(NetworkCamera("http://" + self._address + ":8080/?action=stream")) self.printersChanged.emit() @@ -553,6 +555,14 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice): printer.updateTargetBedTemperature(result["bed"]["temperature"]["target"]) printer.updateState(result["status"]) + try: + # If we're still handling the request, we should ignore remote for a bit. + if not printer.getController().isPreheatRequestInProgress(): + printer.updateIsPreheating(result["bed"]["pre_heat"]["active"]) + except KeyError: + # Older firmwares don't support preheating, so we need to fake it. + pass + head_position = result["heads"][0]["position"] printer.updateHeadPosition(head_position["x"], head_position["y"], head_position["z"]) diff --git a/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py b/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py index 54c126e5cc..c476673353 100644 --- a/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py +++ b/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py @@ -2,6 +2,8 @@ # Cura is released under the terms of the LGPLv3 or higher. from cura.PrinterOutput.PrinterOutputController import PrinterOutputController +from PyQt5.QtCore import QTimer +from UM.Version import Version MYPY = False if MYPY: @@ -12,11 +14,33 @@ if MYPY: class LegacyUM3PrinterOutputController(PrinterOutputController): def __init__(self, output_device): super().__init__(output_device) + self._preheat_bed_timer = QTimer() + self._preheat_bed_timer.setSingleShot(True) + self._preheat_bed_timer.timeout.connect(self._onPreheatBedTimerFinished) + self._preheat_printer = None + # Are we still waiting for a response about preheat? + # We need this so we can already update buttons, so it feels more snappy. + self._preheat_request_in_progress = False + + def isPreheatRequestInProgress(self): + return self._preheat_request_in_progress def setJobState(self, job: "PrintJobOutputModel", state: str): data = "{\"target\": \"%s\"}" % state self._output_device.put("print_job/state", data, onFinished=None) + def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: int): + data = str(temperature) + self._output_device.put("printer/bed/temperature/target", data, onFinished=self._onPutBedTemperatureCompleted) + + def _onPutBedTemperatureCompleted(self, reply): + if Version(self._preheat_printer.firmwareVersion) < Version("3.5.92"): + # If it was handling a preheat, it isn't anymore. + self._preheat_request_in_progress = False + + def _onPutPreheatBedCompleted(self, reply): + self._preheat_request_in_progress = False + def moveHead(self, printer: "PrinterOutputModel", x, y, z, speed): head_pos = printer._head_position new_x = head_pos.x + x @@ -27,3 +51,42 @@ class LegacyUM3PrinterOutputController(PrinterOutputController): def homeBed(self, printer): self._output_device.put("printer/heads/0/position/z", "0", onFinished=None) + + def _onPreheatBedTimerFinished(self): + self.setTargetBedTemperature(self._preheat_printer, 0) + self._preheat_printer.updateIsPreheating(False) + self._preheat_request_in_progress = True + + def cancelPreheatBed(self, printer: "PrinterOutputModel"): + self.preheatBed(printer, temperature=0, duration=0) + self._preheat_bed_timer.stop() + printer.updateIsPreheating(False) + + def preheatBed(self, printer: "PrinterOutputModel", temperature, duration): + try: + temperature = round(temperature) # The API doesn't allow floating point. + duration = round(duration) + except ValueError: + return # Got invalid values, can't pre-heat. + + if duration > 0: + data = """{"temperature": "%i", "timeout": "%i"}""" % (temperature, duration) + else: + data = """{"temperature": "%i"}""" % temperature + + # Real bed pre-heating support is implemented from 3.5.92 and up. + + if Version(printer.firmwareVersion) < Version("3.5.92"): + # No firmware-side duration support then, so just set target bed temp and set a timer. + self.setTargetBedTemperature(printer, temperature=temperature) + self._preheat_bed_timer.setInterval(duration * 1000) + self._preheat_bed_timer.start() + self._preheat_printer = printer + printer.updateIsPreheating(True) + return + + self._output_device.put("printer/bed/pre_heat", data, onFinished = self._onPutPreheatBedCompleted) + printer.updateIsPreheating(True) + self._preheat_request_in_progress = True + + diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 5f09160708..65c2a161bd 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -136,15 +136,6 @@ Item color: UM.Theme.getColor("setting_control_highlight") opacity: preheatTemperatureControl.hovered ? 1.0 : 0 } - Label //Maximum temperature indication. - { - text: (bedTemperature.properties.maximum_value != "None" ? bedTemperature.properties.maximum_value : "") + "°C" - color: UM.Theme.getColor("setting_unit") - font: UM.Theme.getFont("default") - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("setting_unit_margin").width - anchors.verticalCenter: parent.verticalCenter - } MouseArea //Change cursor on hovering. { id: preheatTemperatureInputMouseArea @@ -204,58 +195,6 @@ Item } } - UM.RecolorImage - { - id: preheatCountdownIcon - width: UM.Theme.getSize("save_button_specs_icons").width - height: UM.Theme.getSize("save_button_specs_icons").height - sourceSize.width: width - sourceSize.height: height - color: UM.Theme.getColor("text") - visible: preheatCountdown.visible - source: UM.Theme.getIcon("print_time") - anchors.right: preheatCountdown.left - anchors.rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2) - anchors.verticalCenter: preheatCountdown.verticalCenter - } - - Timer - { - id: preheatUpdateTimer - interval: 100 //Update every 100ms. You want to update every 1s, but then you have one timer for the updating running out of sync with the actual date timer and you might skip seconds. - running: printerModel != null && printerModel.preheatBedRemainingTime != "" - repeat: true - onTriggered: update() - property var endTime: new Date() //Set initial endTime to be the current date, so that the endTime has initially already passed and the timer text becomes invisible if you were to update. - function update() - { - if(printerModel != null && !printerModel.canPreHeatBed) - { - return // Nothing to do, printer cant preheat at all! - } - preheatCountdown.text = "" - if (printerModel != null && connectedPrinter.preheatBedRemainingTime != null) - { - preheatCountdown.text = connectedPrinter.preheatBedRemainingTime; - } - if (preheatCountdown.text == "") //Either time elapsed or not connected. - { - stop(); - } - } - } - Label - { - id: preheatCountdown - text: printerModel != null ? printerModel.preheatBedRemainingTime : "" - visible: text != "" //Has no direct effect, but just so that we can link visibility of clock icon to visibility of the countdown text. - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - anchors.right: preheatButton.left - anchors.rightMargin: UM.Theme.getSize("default_margin").width - anchors.verticalCenter: preheatButton.verticalCenter - } - Button //The pre-heat button. { id: preheatButton @@ -267,9 +206,9 @@ Item { return false; //Not connected, not authenticated or printer is busy. } - if (preheatUpdateTimer.running) + if (printerModel.isPreheating) { - return true; //Can always cancel if the timer is running. + return true; } if (bedTemperature.properties.minimum_value != "None" && Math.floor(preheatTemperatureInput.text) < Math.floor(bedTemperature.properties.minimum_value)) { @@ -363,23 +302,20 @@ Item } } font: UM.Theme.getFont("action_button") - text: preheatUpdateTimer.running ? catalog.i18nc("@button Cancel pre-heating", "Cancel") : catalog.i18nc("@button", "Pre-heat") + text: printerModel.isPreheating ? catalog.i18nc("@button Cancel pre-heating", "Cancel") : catalog.i18nc("@button", "Pre-heat") } } } onClicked: { - if (!preheatUpdateTimer.running) + if (!printerModel.isPreheating) { - printerModel.preheatBed(preheatTemperatureInput.text, printerModel.preheatBedTimeout); - preheatUpdateTimer.start(); - preheatUpdateTimer.update(); //Update once before the first timer is triggered. + printerModel.preheatBed(preheatTemperatureInput.text, 900); } else { printerModel.cancelPreheatBed(); - preheatUpdateTimer.update(); } }