Advertisement
Boelle

Untitled

Sep 16th, 2016
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.59 KB | None | 0 0
  1. # Copyright (c) 2015 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3.  
  4. from UM.Signal import Signal, signalemitter
  5. from . import USBPrinterOutputDevice
  6. from UM.Application import Application
  7. from UM.Resources import Resources
  8. from UM.Logger import Logger
  9. from UM.PluginRegistry import PluginRegistry
  10. from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
  11. from cura.PrinterOutputDevice import ConnectionState
  12. from UM.Qt.ListModel import ListModel
  13. from UM.Message import Message
  14.  
  15. from cura.CuraApplication import CuraApplication
  16.  
  17. import threading
  18. import platform
  19. import glob
  20. import time
  21. import os.path
  22. from UM.Extension import Extension
  23.  
  24. from PyQt5.QtQml import QQmlComponent, QQmlContext
  25. from PyQt5.QtCore import QUrl, QObject, pyqtSlot, pyqtProperty, pyqtSignal, Qt
  26. from UM.i18n import i18nCatalog
  27. i18n_catalog = i18nCatalog("cura")
  28.  
  29.  
  30. ## Manager class that ensures that a usbPrinteroutput device is created for every connected USB printer.
  31. @signalemitter
  32. class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
  33. def __init__(self, parent = None):
  34. super().__init__(parent = parent)
  35. self._serial_port_list = []
  36. self._usb_output_devices = {}
  37. self._usb_output_devices_model = None
  38. self._update_thread = threading.Thread(target = self._updateThread)
  39. self._update_thread.setDaemon(True)
  40.  
  41. self._check_updates = True
  42. self._firmware_view = None
  43.  
  44. Application.getInstance().applicationShuttingDown.connect(self.stop)
  45. self.addUSBOutputDeviceSignal.connect(self.addOutputDevice) #Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
  46.  
  47. addUSBOutputDeviceSignal = Signal()
  48. connectionStateChanged = pyqtSignal()
  49.  
  50. progressChanged = pyqtSignal()
  51. firmwareUpdateChange = pyqtSignal()
  52.  
  53. @pyqtProperty(float, notify = progressChanged)
  54. def progress(self):
  55. progress = 0
  56. for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
  57. progress += device.progress
  58. return progress / len(self._usb_output_devices)
  59.  
  60. @pyqtProperty(int, notify = progressChanged)
  61. def errorCode(self):
  62. for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
  63. if device._error_code:
  64. return device._error_code
  65. return 0
  66.  
  67. ## Return True if all printers finished firmware update
  68. @pyqtProperty(float, notify = firmwareUpdateChange)
  69. def firmwareUpdateCompleteStatus(self):
  70. complete = True
  71. for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
  72. if not device.firmwareUpdateFinished:
  73. complete = False
  74. return complete
  75.  
  76. def start(self):
  77. self._check_updates = True
  78. self._update_thread.start()
  79.  
  80. def stop(self):
  81. self._check_updates = False
  82. try:
  83. self._update_thread.join()
  84. except RuntimeError:
  85. pass
  86.  
  87. def _updateThread(self):
  88. while self._check_updates:
  89. result = self.getSerialPortList(only_list_usb = True)
  90. self._addRemovePorts(result)
  91. time.sleep(5)
  92.  
  93. ## Show firmware interface.
  94. # This will create the view if its not already created.
  95. def spawnFirmwareInterface(self, serial_port):
  96. if self._firmware_view is None:
  97. path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "FirmwareUpdateWindow.qml"))
  98. component = QQmlComponent(Application.getInstance()._engine, path)
  99.  
  100. self._firmware_context = QQmlContext(Application.getInstance()._engine.rootContext())
  101. self._firmware_context.setContextProperty("manager", self)
  102. self._firmware_view = component.create(self._firmware_context)
  103.  
  104. self._firmware_view.show()
  105.  
  106. @pyqtSlot(str)
  107. def updateAllFirmware(self, file_name):
  108. if file_name.startswith("file://"):
  109. file_name = QUrl(file_name).toLocalFile() # File dialogs prepend the path with file://, which we don't need / want
  110. if not self._usb_output_devices:
  111. Message(i18n_catalog.i18nc("@info", "Unable to update firmware because there are no printers connected.")).show()
  112. return
  113.  
  114. for printer_connection in self._usb_output_devices:
  115. self._usb_output_devices[printer_connection].resetFirmwareUpdate()
  116. self.spawnFirmwareInterface("")
  117. for printer_connection in self._usb_output_devices:
  118. try:
  119. self._usb_output_devices[printer_connection].updateFirmware(file_name)
  120. except FileNotFoundError:
  121. # Should only happen in dev environments where the resources/firmware folder is absent.
  122. self._usb_output_devices[printer_connection].setProgress(100, 100)
  123. Logger.log("w", "No firmware found for printer %s called '%s'", printer_connection, file_name)
  124. Message(i18n_catalog.i18nc("@info",
  125. "Could not find firmware required for the printer at %s.") % printer_connection).show()
  126. self._firmware_view.close()
  127.  
  128. continue
  129.  
  130. @pyqtSlot(str, str, result = bool)
  131. def updateFirmwareBySerial(self, serial_port, file_name):
  132. if serial_port in self._usb_output_devices:
  133. self.spawnFirmwareInterface(self._usb_output_devices[serial_port].getSerialPort())
  134. try:
  135. self._usb_output_devices[serial_port].updateFirmware(file_name)
  136. except FileNotFoundError:
  137. self._firmware_view.close()
  138. Logger.log("e", "Could not find firmware required for this machine called '%s'", file_name)
  139. return False
  140. return True
  141. return False
  142.  
  143. ## Return the singleton instance of the USBPrinterManager
  144. @classmethod
  145. def getInstance(cls, engine = None, script_engine = None):
  146. # Note: Explicit use of class name to prevent issues with inheritance.
  147. if USBPrinterOutputDeviceManager._instance is None:
  148. USBPrinterOutputDeviceManager._instance = cls()
  149.  
  150. return USBPrinterOutputDeviceManager._instance
  151.  
  152. @pyqtSlot(result = str)
  153. def getDefaultFirmwareName(self):
  154. # Check if there is a valid global container stack
  155. global_container_stack = Application.getInstance().getGlobalContainerStack()
  156. if not global_container_stack:
  157. Logger.log("e", "There is no global container stack. Can not update firmware.")
  158. self._firmware_view.close()
  159. return ""
  160.  
  161. # The bottom of the containerstack is the machine definition
  162. machine_id = global_container_stack.getBottom().id
  163.  
  164. machine_has_heated_bed = global_container_stack.getProperty("machine_heated_bed", "value")
  165.  
  166. if platform.system() == "Linux":
  167. baudrate = 115200
  168. else:
  169. baudrate = 250000
  170.  
  171. # NOTE: The keyword used here is the id of the machine. You can find the id of your machine in the *.json file, eg.
  172. # https://github.com/Ultimaker/Cura/blob/master/resources/machines/ultimaker_original.json#L2
  173. # The *.hex files are stored at a seperate repository:
  174. # https://github.com/Ultimaker/cura-binary-data/tree/master/cura/resources/firmware
  175. machine_without_extras = {"bq_witbox" : "MarlinWitbox.hex",
  176. "bq_hephestos_2" : "MarlinHephestos2.hex",
  177. "ultimaker_original" : "MarlinUltimaker-{baudrate}.hex",
  178. "ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex",
  179. "ultimaker_original_dual" : "MarlinUltimaker-{baudrate}-dual.hex",
  180. "ultimaker2" : "MarlinUltimaker2.hex",
  181. "ultimaker2_go" : "MarlinUltimaker2go.hex",
  182. "ultimaker2_plus" : "MarlinUltimaker2plus.hex",
  183. "ultimaker2_extended" : "MarlinUltimaker2extended.hex",
  184. "ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex",
  185. }
  186. machine_with_heated_bed = {"ultimaker_original" : "MarlinUltimaker-HBK-{baudrate}.hex",
  187. "ultimaker_original_dual" : "MarlinUltimaker-HBK-{baudrate}-dual.hex",
  188. }
  189. ##TODO: Add check for multiple extruders
  190. hex_file = None
  191. if machine_id in machine_without_extras.keys(): # The machine needs to be defined here!
  192. if machine_id in machine_with_heated_bed.keys() and machine_has_heated_bed:
  193. Logger.log("d", "Choosing firmware with heated bed enabled for machine %s.", machine_id)
  194. hex_file = machine_with_heated_bed[machine_id] # Return firmware with heated bed enabled
  195. else:
  196. Logger.log("d", "Choosing basic firmware for machine %s.", machine_id)
  197. hex_file = machine_without_extras[machine_id] # Return "basic" firmware
  198. else:
  199. Logger.log("w", "There is no firmware for machine %s.", machine_id)
  200.  
  201. if hex_file:
  202. return Resources.getPath(CuraApplication.ResourceTypes.Firmware, hex_file.format(baudrate=baudrate))
  203. else:
  204. Logger.log("w", "Could not find any firmware for machine %s.", machine_id)
  205. return ""
  206.  
  207. ## Helper to identify serial ports (and scan for them)
  208. def _addRemovePorts(self, serial_ports):
  209. # First, find and add all new or changed keys
  210. for serial_port in list(serial_ports):
  211. if serial_port not in self._serial_port_list:
  212. self.addUSBOutputDeviceSignal.emit(serial_port) # Hack to ensure its created in main thread
  213. continue
  214.  
  215. # TEMP HACK: Wait for all bootloaders to finish doing their thing and to reconnect if necessary
  216. time.sleep(10)
  217.  
  218. self._serial_port_list = list(serial_ports)
  219.  
  220. devices_to_remove = []
  221. for port, device in self._usb_output_devices.items():
  222. if port not in self._serial_port_list:
  223. device.close()
  224. devices_to_remove.append(port)
  225.  
  226. for port in devices_to_remove:
  227. del self._usb_output_devices[port]
  228.  
  229. ## Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
  230. def addOutputDevice(self, serial_port):
  231. device = USBPrinterOutputDevice.USBPrinterOutputDevice(serial_port)
  232. device.connectionStateChanged.connect(self._onConnectionStateChanged)
  233. device.connect()
  234. device.progressChanged.connect(self.progressChanged)
  235. device.firmwareUpdateChange.connect(self.firmwareUpdateChange)
  236. self._usb_output_devices[serial_port] = device
  237.  
  238. ## If one of the states of the connected devices change, we might need to add / remove them from the global list.
  239. def _onConnectionStateChanged(self, serial_port):
  240. try:
  241. if self._usb_output_devices[serial_port].connectionState == ConnectionState.connected:
  242. self.getOutputDeviceManager().addOutputDevice(self._usb_output_devices[serial_port])
  243. else:
  244. self.getOutputDeviceManager().removeOutputDevice(serial_port)
  245. self.connectionStateChanged.emit()
  246. except KeyError:
  247. pass # no output device by this device_id found in connection list.
  248.  
  249.  
  250. @pyqtProperty(QObject , notify = connectionStateChanged)
  251. def connectedPrinterList(self):
  252. self._usb_output_devices_model = ListModel()
  253. self._usb_output_devices_model.addRoleName(Qt.UserRole + 1, "name")
  254. self._usb_output_devices_model.addRoleName(Qt.UserRole + 2, "printer")
  255. for connection in self._usb_output_devices:
  256. if self._usb_output_devices[connection].connectionState == ConnectionState.connected:
  257. self._usb_output_devices_model.appendItem({"name": connection, "printer": self._usb_output_devices[connection]})
  258. return self._usb_output_devices_model
  259.  
  260. ## Create a list of serial ports on the system.
  261. # \param only_list_usb If true, only usb ports are listed
  262. def getSerialPortList(self, only_list_usb = False):
  263. base_list = []
  264. if platform.system() == "Windows":
  265. import winreg #@UnresolvedImport
  266. try:
  267. key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM")
  268. i = 0
  269. while True:
  270. values = winreg.EnumValue(key, i)
  271. if not only_list_usb or "USBSER" in values[0]:
  272. base_list += [values[1]]
  273. i += 1
  274. except Exception as e:
  275. pass
  276. else:
  277. if only_list_usb:
  278. base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.usb*")
  279. base_list = filter(lambda s: "Bluetooth" not in s, base_list) # Filter because mac sometimes puts them in the list
  280. else:
  281. base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*")
  282. return list(base_list)
  283.  
  284. _instance = None
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement