1# Copyright (c) 2018 Ultimaker B.V. 2# Cura is released under the terms of the LGPLv3 or higher. 3 4from typing import TYPE_CHECKING, Set, Union, Optional 5 6from PyQt5.QtCore import QTimer 7 8from .PrinterOutputController import PrinterOutputController 9 10if TYPE_CHECKING: 11 from .Models.PrintJobOutputModel import PrintJobOutputModel 12 from .Models.PrinterOutputModel import PrinterOutputModel 13 from .PrinterOutputDevice import PrinterOutputDevice 14 from .Models.ExtruderOutputModel import ExtruderOutputModel 15 16 17class GenericOutputController(PrinterOutputController): 18 def __init__(self, output_device: "PrinterOutputDevice") -> None: 19 super().__init__(output_device) 20 21 self._preheat_bed_timer = QTimer() 22 self._preheat_bed_timer.setSingleShot(True) 23 self._preheat_bed_timer.timeout.connect(self._onPreheatBedTimerFinished) 24 self._preheat_printer = None # type: Optional[PrinterOutputModel] 25 26 self._preheat_hotends_timer = QTimer() 27 self._preheat_hotends_timer.setSingleShot(True) 28 self._preheat_hotends_timer.timeout.connect(self._onPreheatHotendsTimerFinished) 29 self._preheat_hotends = set() # type: Set[ExtruderOutputModel] 30 31 self._output_device.printersChanged.connect(self._onPrintersChanged) 32 self._active_printer = None # type: Optional[PrinterOutputModel] 33 34 def _onPrintersChanged(self) -> None: 35 if self._active_printer: 36 self._active_printer.stateChanged.disconnect(self._onPrinterStateChanged) 37 self._active_printer.targetBedTemperatureChanged.disconnect(self._onTargetBedTemperatureChanged) 38 for extruder in self._active_printer.extruders: 39 extruder.targetHotendTemperatureChanged.disconnect(self._onTargetHotendTemperatureChanged) 40 41 self._active_printer = self._output_device.activePrinter 42 if self._active_printer: 43 self._active_printer.stateChanged.connect(self._onPrinterStateChanged) 44 self._active_printer.targetBedTemperatureChanged.connect(self._onTargetBedTemperatureChanged) 45 for extruder in self._active_printer.extruders: 46 extruder.targetHotendTemperatureChanged.connect(self._onTargetHotendTemperatureChanged) 47 48 def _onPrinterStateChanged(self) -> None: 49 if self._active_printer and self._active_printer.state != "idle": 50 if self._preheat_bed_timer.isActive(): 51 self._preheat_bed_timer.stop() 52 if self._preheat_printer: 53 self._preheat_printer.updateIsPreheating(False) 54 if self._preheat_hotends_timer.isActive(): 55 self._preheat_hotends_timer.stop() 56 for extruder in self._preheat_hotends: 57 extruder.updateIsPreheating(False) 58 self._preheat_hotends = set() 59 60 def moveHead(self, printer: "PrinterOutputModel", x, y, z, speed) -> None: 61 self._output_device.sendCommand("G91") 62 self._output_device.sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed)) 63 self._output_device.sendCommand("G90") 64 65 def homeHead(self, printer: "PrinterOutputModel") -> None: 66 self._output_device.sendCommand("G28 X Y") 67 68 def homeBed(self, printer: "PrinterOutputModel") -> None: 69 self._output_device.sendCommand("G28 Z") 70 71 def sendRawCommand(self, printer: "PrinterOutputModel", command: str) -> None: 72 self._output_device.sendCommand(command.upper()) #Most printers only understand uppercase g-code commands. 73 74 def setJobState(self, job: "PrintJobOutputModel", state: str) -> None: 75 if state == "pause": 76 self._output_device.pausePrint() 77 job.updateState("paused") 78 elif state == "print": 79 self._output_device.resumePrint() 80 job.updateState("printing") 81 elif state == "abort": 82 self._output_device.cancelPrint() 83 pass 84 85 def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: float) -> None: 86 self._output_device.sendCommand("M140 S%s" % round(temperature)) # The API doesn't allow floating point. 87 88 def _onTargetBedTemperatureChanged(self) -> None: 89 if self._preheat_bed_timer.isActive() and self._preheat_printer and self._preheat_printer.targetBedTemperature == 0: 90 self._preheat_bed_timer.stop() 91 self._preheat_printer.updateIsPreheating(False) 92 93 def preheatBed(self, printer: "PrinterOutputModel", temperature, duration) -> None: 94 try: 95 temperature = round(temperature) # The API doesn't allow floating point. 96 duration = round(duration) 97 except ValueError: 98 return # Got invalid values, can't pre-heat. 99 100 self.setTargetBedTemperature(printer, temperature = temperature) 101 self._preheat_bed_timer.setInterval(duration * 1000) 102 self._preheat_bed_timer.start() 103 self._preheat_printer = printer 104 printer.updateIsPreheating(True) 105 106 def cancelPreheatBed(self, printer: "PrinterOutputModel") -> None: 107 self.setTargetBedTemperature(printer, temperature = 0) 108 self._preheat_bed_timer.stop() 109 printer.updateIsPreheating(False) 110 111 def _onPreheatBedTimerFinished(self) -> None: 112 if not self._preheat_printer: 113 return 114 self.setTargetBedTemperature(self._preheat_printer, 0) 115 self._preheat_printer.updateIsPreheating(False) 116 117 def setTargetHotendTemperature(self, printer: "PrinterOutputModel", position: int, temperature: Union[int, float]) -> None: 118 self._output_device.sendCommand("M104 S%s T%s" % (temperature, position)) 119 120 def _onTargetHotendTemperatureChanged(self) -> None: 121 if not self._preheat_hotends_timer.isActive(): 122 return 123 if not self._active_printer: 124 return 125 126 for extruder in self._active_printer.extruders: 127 if extruder in self._preheat_hotends and extruder.targetHotendTemperature == 0: 128 extruder.updateIsPreheating(False) 129 self._preheat_hotends.remove(extruder) 130 if not self._preheat_hotends: 131 self._preheat_hotends_timer.stop() 132 133 def preheatHotend(self, extruder: "ExtruderOutputModel", temperature, duration) -> None: 134 position = extruder.getPosition() 135 number_of_extruders = len(extruder.getPrinter().extruders) 136 if position >= number_of_extruders: 137 return # Got invalid extruder nr, can't pre-heat. 138 139 try: 140 temperature = round(temperature) # The API doesn't allow floating point. 141 duration = round(duration) 142 except ValueError: 143 return # Got invalid values, can't pre-heat. 144 145 self.setTargetHotendTemperature(extruder.getPrinter(), position, temperature=temperature) 146 self._preheat_hotends_timer.setInterval(duration * 1000) 147 self._preheat_hotends_timer.start() 148 self._preheat_hotends.add(extruder) 149 extruder.updateIsPreheating(True) 150 151 def cancelPreheatHotend(self, extruder: "ExtruderOutputModel") -> None: 152 self.setTargetHotendTemperature(extruder.getPrinter(), extruder.getPosition(), temperature=0) 153 if extruder in self._preheat_hotends: 154 extruder.updateIsPreheating(False) 155 self._preheat_hotends.remove(extruder) 156 if not self._preheat_hotends and self._preheat_hotends_timer.isActive(): 157 self._preheat_hotends_timer.stop() 158 159 def _onPreheatHotendsTimerFinished(self) -> None: 160 for extruder in self._preheat_hotends: 161 self.setTargetHotendTemperature(extruder.getPrinter(), extruder.getPosition(), 0) 162 self._preheat_hotends = set() 163 164 # Cancel any ongoing preheating timers, without setting back the temperature to 0 165 # This can be used eg at the start of a print 166 def stopPreheatTimers(self) -> None: 167 if self._preheat_hotends_timer.isActive(): 168 for extruder in self._preheat_hotends: 169 extruder.updateIsPreheating(False) 170 self._preheat_hotends = set() 171 172 self._preheat_hotends_timer.stop() 173 174 if self._preheat_bed_timer.isActive(): 175 if self._preheat_printer: 176 self._preheat_printer.updateIsPreheating(False) 177 self._preheat_bed_timer.stop() 178