PrinterOutputModel.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. # Copyright (c) 2019 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot, QUrl
  4. from typing import List, Dict, Optional, TYPE_CHECKING
  5. from UM.Math.Vector import Vector
  6. from cura.PrinterOutput.Peripheral import Peripheral
  7. from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel
  8. from cura.PrinterOutput.Models.ExtruderOutputModel import ExtruderOutputModel
  9. from UM.Logger import Logger
  10. if TYPE_CHECKING:
  11. from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel
  12. from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
  13. class PrinterOutputModel(QObject):
  14. bedTemperatureChanged = pyqtSignal()
  15. targetBedTemperatureChanged = pyqtSignal()
  16. isPreheatingChanged = pyqtSignal()
  17. stateChanged = pyqtSignal()
  18. activePrintJobChanged = pyqtSignal()
  19. nameChanged = pyqtSignal()
  20. headPositionChanged = pyqtSignal()
  21. keyChanged = pyqtSignal()
  22. typeChanged = pyqtSignal()
  23. buildplateChanged = pyqtSignal()
  24. cameraUrlChanged = pyqtSignal()
  25. configurationChanged = pyqtSignal()
  26. canUpdateFirmwareChanged = pyqtSignal()
  27. def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None, firmware_version = "") -> None:
  28. super().__init__(parent)
  29. self._bed_temperature = -1 # type: float # Use -1 for no heated bed.
  30. self._target_bed_temperature = 0 # type: float
  31. self._name = ""
  32. self._key = "" # Unique identifier
  33. self._controller = output_controller
  34. self._controller.canUpdateFirmwareChanged.connect(self._onControllerCanUpdateFirmwareChanged)
  35. self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)]
  36. self._printer_configuration = PrinterConfigurationModel() # Indicates the current configuration setup in this printer
  37. self._head_position = Vector(0, 0, 0)
  38. self._active_print_job = None # type: Optional[PrintJobOutputModel]
  39. self._firmware_version = firmware_version
  40. self._printer_state = "unknown"
  41. self._is_preheating = False
  42. self._printer_type = ""
  43. self._buildplate = ""
  44. self._peripherals = [] # type: List[Peripheral]
  45. self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in
  46. self._extruders]
  47. self._available_printer_configurations = [] # type: List[PrinterConfigurationModel]
  48. self._camera_url = QUrl() # type: QUrl
  49. @pyqtProperty(str, constant = True)
  50. def firmwareVersion(self) -> str:
  51. return self._firmware_version
  52. def setCameraUrl(self, camera_url: "QUrl") -> None:
  53. if self._camera_url != camera_url:
  54. self._camera_url = camera_url
  55. self.cameraUrlChanged.emit()
  56. @pyqtProperty(QUrl, fset = setCameraUrl, notify = cameraUrlChanged)
  57. def cameraUrl(self) -> "QUrl":
  58. return self._camera_url
  59. def updateIsPreheating(self, pre_heating: bool) -> None:
  60. if self._is_preheating != pre_heating:
  61. self._is_preheating = pre_heating
  62. self.isPreheatingChanged.emit()
  63. @pyqtProperty(bool, notify=isPreheatingChanged)
  64. def isPreheating(self) -> bool:
  65. return self._is_preheating
  66. @pyqtProperty(str, notify = typeChanged)
  67. def type(self) -> str:
  68. return self._printer_type
  69. def updateType(self, printer_type: str) -> None:
  70. if self._printer_type != printer_type:
  71. self._printer_type = printer_type
  72. self._printer_configuration.printerType = self._printer_type
  73. self.typeChanged.emit()
  74. self.configurationChanged.emit()
  75. @pyqtProperty(str, notify = buildplateChanged)
  76. def buildplate(self) -> str:
  77. return self._buildplate
  78. def updateBuildplate(self, buildplate: str) -> None:
  79. if self._buildplate != buildplate:
  80. self._buildplate = buildplate
  81. self._printer_configuration.buildplateConfiguration = self._buildplate
  82. self.buildplateChanged.emit()
  83. self.configurationChanged.emit()
  84. @pyqtProperty(str, notify=keyChanged)
  85. def key(self) -> str:
  86. return self._key
  87. def updateKey(self, key: str) -> None:
  88. if self._key != key:
  89. self._key = key
  90. self.keyChanged.emit()
  91. @pyqtSlot()
  92. def homeHead(self) -> None:
  93. self._controller.homeHead(self)
  94. @pyqtSlot()
  95. def homeBed(self) -> None:
  96. self._controller.homeBed(self)
  97. @pyqtSlot(str)
  98. def sendRawCommand(self, command: str) -> None:
  99. self._controller.sendRawCommand(self, command)
  100. @pyqtProperty("QVariantList", constant = True)
  101. def extruders(self) -> List["ExtruderOutputModel"]:
  102. return self._extruders
  103. @pyqtProperty(QVariant, notify = headPositionChanged)
  104. def headPosition(self) -> Dict[str, float]:
  105. return {"x": self._head_position.x, "y": self._head_position.y, "z": self.head_position.z}
  106. def updateHeadPosition(self, x: float, y: float, z: float) -> None:
  107. if self._head_position.x != x or self._head_position.y != y or self._head_position.z != z:
  108. self._head_position = Vector(x, y, z)
  109. self.headPositionChanged.emit()
  110. @pyqtProperty(float, float, float)
  111. @pyqtProperty(float, float, float, float)
  112. def setHeadPosition(self, x: float, y: float, z: float, speed: float = 3000) -> None:
  113. self.updateHeadPosition(x, y, z)
  114. self._controller.setHeadPosition(self, x, y, z, speed)
  115. @pyqtProperty(float)
  116. @pyqtProperty(float, float)
  117. def setHeadX(self, x: float, speed: float = 3000) -> None:
  118. self.updateHeadPosition(x, self._head_position.y, self._head_position.z)
  119. self._controller.setHeadPosition(self, x, self._head_position.y, self._head_position.z, speed)
  120. @pyqtProperty(float)
  121. @pyqtProperty(float, float)
  122. def setHeadY(self, y: float, speed: float = 3000) -> None:
  123. self.updateHeadPosition(self._head_position.x, y, self._head_position.z)
  124. self._controller.setHeadPosition(self, self._head_position.x, y, self._head_position.z, speed)
  125. @pyqtProperty(float)
  126. @pyqtProperty(float, float)
  127. def setHeadZ(self, z: float, speed:float = 3000) -> None:
  128. self.updateHeadPosition(self._head_position.x, self._head_position.y, z)
  129. self._controller.setHeadPosition(self, self._head_position.x, self._head_position.y, z, speed)
  130. @pyqtSlot(float, float, float)
  131. @pyqtSlot(float, float, float, float)
  132. def moveHead(self, x: float = 0, y: float = 0, z: float = 0, speed: float = 3000) -> None:
  133. self._controller.moveHead(self, x, y, z, speed)
  134. ## Pre-heats the heated bed of the printer.
  135. #
  136. # \param temperature The temperature to heat the bed to, in degrees
  137. # Celsius.
  138. # \param duration How long the bed should stay warm, in seconds.
  139. @pyqtSlot(float, float)
  140. def preheatBed(self, temperature: float, duration: float) -> None:
  141. self._controller.preheatBed(self, temperature, duration)
  142. @pyqtSlot()
  143. def cancelPreheatBed(self) -> None:
  144. self._controller.cancelPreheatBed(self)
  145. def getController(self) -> "PrinterOutputController":
  146. return self._controller
  147. @pyqtProperty(str, notify = nameChanged)
  148. def name(self) -> str:
  149. return self._name
  150. def setName(self, name: str) -> None:
  151. self.updateName(name)
  152. def updateName(self, name: str) -> None:
  153. if self._name != name:
  154. self._name = name
  155. self.nameChanged.emit()
  156. ## Update the bed temperature. This only changes it locally.
  157. def updateBedTemperature(self, temperature: float) -> None:
  158. if self._bed_temperature != temperature:
  159. self._bed_temperature = temperature
  160. self.bedTemperatureChanged.emit()
  161. def updateTargetBedTemperature(self, temperature: float) -> None:
  162. if self._target_bed_temperature != temperature:
  163. self._target_bed_temperature = temperature
  164. self.targetBedTemperatureChanged.emit()
  165. ## Set the target bed temperature. This ensures that it's actually sent to the remote.
  166. @pyqtSlot(float)
  167. def setTargetBedTemperature(self, temperature: float) -> None:
  168. self._controller.setTargetBedTemperature(self, temperature)
  169. self.updateTargetBedTemperature(temperature)
  170. def updateActivePrintJob(self, print_job: Optional["PrintJobOutputModel"]) -> None:
  171. if self._active_print_job != print_job:
  172. old_print_job = self._active_print_job
  173. if print_job is not None:
  174. print_job.updateAssignedPrinter(self)
  175. self._active_print_job = print_job
  176. if old_print_job is not None:
  177. old_print_job.updateAssignedPrinter(None)
  178. self.activePrintJobChanged.emit()
  179. def updateState(self, printer_state: str) -> None:
  180. if self._printer_state != printer_state:
  181. self._printer_state = printer_state
  182. self.stateChanged.emit()
  183. @pyqtProperty(QObject, notify = activePrintJobChanged)
  184. def activePrintJob(self) -> Optional["PrintJobOutputModel"]:
  185. return self._active_print_job
  186. @pyqtProperty(str, notify = stateChanged)
  187. def state(self) -> str:
  188. return self._printer_state
  189. @pyqtProperty(float, notify = bedTemperatureChanged)
  190. def bedTemperature(self) -> float:
  191. return self._bed_temperature
  192. @pyqtProperty(float, notify = targetBedTemperatureChanged)
  193. def targetBedTemperature(self) -> float:
  194. return self._target_bed_temperature
  195. # Does the printer support pre-heating the bed at all
  196. @pyqtProperty(bool, constant = True)
  197. def canPreHeatBed(self) -> bool:
  198. if self._controller:
  199. return self._controller.can_pre_heat_bed
  200. return False
  201. # Does the printer support pre-heating the bed at all
  202. @pyqtProperty(bool, constant = True)
  203. def canPreHeatHotends(self) -> bool:
  204. if self._controller:
  205. return self._controller.can_pre_heat_hotends
  206. return False
  207. # Does the printer support sending raw G-code at all
  208. @pyqtProperty(bool, constant = True)
  209. def canSendRawGcode(self) -> bool:
  210. if self._controller:
  211. return self._controller.can_send_raw_gcode
  212. return False
  213. # Does the printer support pause at all
  214. @pyqtProperty(bool, constant = True)
  215. def canPause(self) -> bool:
  216. if self._controller:
  217. return self._controller.can_pause
  218. return False
  219. # Does the printer support abort at all
  220. @pyqtProperty(bool, constant = True)
  221. def canAbort(self) -> bool:
  222. if self._controller:
  223. return self._controller.can_abort
  224. return False
  225. # Does the printer support manual control at all
  226. @pyqtProperty(bool, constant = True)
  227. def canControlManually(self) -> bool:
  228. if self._controller:
  229. return self._controller.can_control_manually
  230. return False
  231. # Does the printer support upgrading firmware
  232. @pyqtProperty(bool, notify = canUpdateFirmwareChanged)
  233. def canUpdateFirmware(self) -> bool:
  234. if self._controller:
  235. return self._controller.can_update_firmware
  236. return False
  237. # Stub to connect UM.Signal to pyqtSignal
  238. def _onControllerCanUpdateFirmwareChanged(self) -> None:
  239. self.canUpdateFirmwareChanged.emit()
  240. # Returns the active configuration (material, variant and buildplate) of the current printer
  241. @pyqtProperty(QObject, notify = configurationChanged)
  242. def printerConfiguration(self) -> Optional[PrinterConfigurationModel]:
  243. if self._printer_configuration.isValid():
  244. return self._printer_configuration
  245. return None
  246. peripheralsChanged = pyqtSignal()
  247. @pyqtProperty(str, notify = peripheralsChanged)
  248. def peripherals(self) -> str:
  249. return ", ".join([peripheral.name for peripheral in self._peripherals])
  250. def addPeripheral(self, peripheral: Peripheral) -> None:
  251. self._peripherals.append(peripheral)
  252. self.peripheralsChanged.emit()
  253. def removePeripheral(self, peripheral: Peripheral) -> None:
  254. self._peripherals.remove(peripheral)
  255. self.peripheralsChanged.emit()
  256. availableConfigurationsChanged = pyqtSignal()
  257. @pyqtProperty("QVariantList", notify = availableConfigurationsChanged)
  258. def availableConfigurations(self) -> List[PrinterConfigurationModel]:
  259. return self._available_printer_configurations
  260. def addAvailableConfiguration(self, new_configuration: PrinterConfigurationModel) -> None:
  261. if new_configuration not in self._available_printer_configurations:
  262. self._available_printer_configurations.append(new_configuration)
  263. self.availableConfigurationsChanged.emit()
  264. def removeAvailableConfiguration(self, config_to_remove: PrinterConfigurationModel) -> None:
  265. try:
  266. self._available_printer_configurations.remove(config_to_remove)
  267. except ValueError:
  268. Logger.log("w", "Unable to remove configuration that isn't in the list of available configurations")
  269. else:
  270. self.availableConfigurationsChanged.emit()
  271. def setAvailableConfigurations(self, new_configurations: List[PrinterConfigurationModel]) -> None:
  272. self._available_printer_configurations = new_configurations
  273. self.availableConfigurationsChanged.emit()