Browse Source

Added missing (renamed) file

CURA-1339
Jaime van Kessel 9 years ago
parent
commit
b31106a666
1 changed files with 247 additions and 0 deletions
  1. 247 0
      plugins/USBPrinting/USBPrinterOutputDeviceManager.py

+ 247 - 0
plugins/USBPrinting/USBPrinterOutputDeviceManager.py

@@ -0,0 +1,247 @@
+# Copyright (c) 2015 Ultimaker B.V.
+# Cura is released under the terms of the AGPLv3 or higher.
+
+from UM.Signal import Signal, SignalEmitter
+from . import USBPrinterOutputDevice
+from UM.Application import Application
+from UM.Resources import Resources
+from UM.Logger import Logger
+from UM.PluginRegistry import PluginRegistry
+from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
+from cura.PrinterOutputDevice import ConnectionState
+from UM.Qt.ListModel import ListModel
+from UM.Message import Message
+
+from cura.CuraApplication import CuraApplication
+
+import threading
+import platform
+import glob
+import time
+import os.path
+from UM.Extension import Extension
+
+from PyQt5.QtQml import QQmlComponent, QQmlContext
+from PyQt5.QtCore import QUrl, QObject, pyqtSlot, pyqtProperty, pyqtSignal, Qt
+from UM.i18n import i18nCatalog
+i18n_catalog = i18nCatalog("cura")
+
+
+##  Manager class that ensures that a usbPrinteroutput device is created for every connected USB printer.
+class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
+    def __init__(self, parent = None):
+        QObject.__init__(self, parent)
+        SignalEmitter.__init__(self)
+        OutputDevicePlugin.__init__(self)
+        Extension.__init__(self)
+        self._serial_port_list = []
+        self._usb_output_devices = {}
+        self._usb_output_devices_model = None
+        self._update_thread = threading.Thread(target = self._updateThread)
+        self._update_thread.setDaemon(True)
+
+        self._check_updates = True
+        self._firmware_view = None
+
+        ## Add menu item to top menu of the application.
+        self.setMenuName(i18n_catalog.i18nc("@title:menu","Firmware"))
+        self.addMenuItem(i18n_catalog.i18nc("@item:inmenu", "Update Firmware"), self.updateAllFirmware)
+
+        Application.getInstance().applicationShuttingDown.connect(self.stop)
+        self.addUSBOutputDeviceSignal.connect(self.addOutputDevice) #Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
+
+    addUSBOutputDeviceSignal = Signal()
+    connectionStateChanged = pyqtSignal()
+
+    progressChanged = pyqtSignal()
+
+    @pyqtProperty(float, notify = progressChanged)
+    def progress(self):
+        progress = 0
+        for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
+            progress += device.progress
+
+        return progress / len(self._usb_output_devices)
+
+    def start(self):
+        self._check_updates = True
+        self._update_thread.start()
+
+    def stop(self):
+        self._check_updates = False
+        try:
+            self._update_thread.join()
+        except RuntimeError:
+            pass
+
+    def _updateThread(self):
+        while self._check_updates:
+            result = self.getSerialPortList(only_list_usb = True)
+            self._addRemovePorts(result)
+            time.sleep(5)
+
+    ##  Show firmware interface.
+    #   This will create the view if its not already created.
+    def spawnFirmwareInterface(self, serial_port):
+        if self._firmware_view is None:
+            path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "FirmwareUpdateWindow.qml"))
+            component = QQmlComponent(Application.getInstance()._engine, path)
+
+            self._firmware_context = QQmlContext(Application.getInstance()._engine.rootContext())
+            self._firmware_context.setContextProperty("manager", self)
+            self._firmware_view = component.create(self._firmware_context)
+
+        self._firmware_view.show()
+
+    @pyqtSlot()
+    def updateAllFirmware(self):
+        if not self._usb_output_devices:
+            Message(i18n_catalog.i18nc("@info","Cannot update firmware, there were no connected printers found.")).show()
+            return
+
+        self.spawnFirmwareInterface("")
+        for printer_connection in self._usb_output_devices:
+            try:
+                self._usb_output_devices[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
+            except FileNotFoundError:
+                self._usb_output_devices[printer_connection].setProgress(100, 100)
+                Logger.log("w", "No firmware found for printer %s", printer_connection)
+                continue
+
+    @pyqtSlot(str, result = bool)
+    def updateFirmwareBySerial(self, serial_port):
+        if serial_port in self._usb_output_devices:
+            self.spawnFirmwareInterface(self._usb_output_devices[serial_port].getSerialPort())
+            try:
+                self._usb_output_devices[serial_port].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
+            except FileNotFoundError:
+                self._firmware_view.close()
+                Logger.log("e", "Could not find firmware required for this machine")
+                return False
+            return True
+        return False
+
+    ##  Return the singleton instance of the USBPrinterManager
+    @classmethod
+    def getInstance(cls, engine = None, script_engine = None):
+        # Note: Explicit use of class name to prevent issues with inheritance.
+        if USBPrinterOutputDeviceManager._instance is None:
+            USBPrinterOutputDeviceManager._instance = cls()
+
+        return USBPrinterOutputDeviceManager._instance
+
+    def _getDefaultFirmwareName(self):
+        machine_instance = Application.getInstance().getMachineManager().getActiveMachineInstance()
+        machine_type = machine_instance.getMachineDefinition().getId()
+        if platform.system() == "Linux":
+            baudrate = 115200
+        else:
+            baudrate = 250000
+
+        # NOTE: The keyword used here is the id of the machine. You can find the id of your machine in the *.json file, eg.
+        # https://github.com/Ultimaker/Cura/blob/master/resources/machines/ultimaker_original.json#L2
+        # The *.hex files are stored at a seperate repository:
+        # https://github.com/Ultimaker/cura-binary-data/tree/master/cura/resources/firmware
+        machine_without_extras  = {"bq_witbox"                : "MarlinWitbox.hex",
+                                   "ultimaker_original"       : "MarlinUltimaker-{baudrate}.hex",
+                                   "ultimaker_original_plus"  : "MarlinUltimaker-UMOP-{baudrate}.hex",
+                                   "ultimaker2"               : "MarlinUltimaker2.hex",
+                                   "ultimaker2_go"            : "MarlinUltimaker2go.hex",
+                                   "ultimaker2plus"           : "MarlinUltimaker2plus.hex",
+                                   "ultimaker2_extended"      : "MarlinUltimaker2extended.hex",
+                                   "ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex",
+                                   }
+        machine_with_heated_bed = {"ultimaker_original"       : "MarlinUltimaker-HBK-{baudrate}.hex",
+                                   }
+
+        ##TODO: Add check for multiple extruders
+        hex_file = None
+        if machine_type in machine_without_extras.keys():  # The machine needs to be defined here!
+            if machine_type in machine_with_heated_bed.keys() and machine_instance.getMachineSettingValue("machine_heated_bed"):
+                Logger.log("d", "Choosing firmware with heated bed enabled for machine %s.", machine_type)
+                hex_file = machine_with_heated_bed[machine_type]  # Return firmware with heated bed enabled
+            else:
+                Logger.log("d", "Choosing basic firmware for machine %s.", machine_type)
+                hex_file = machine_without_extras[machine_type]  # Return "basic" firmware
+        else:
+            Logger.log("e", "There is no firmware for machine %s.", machine_type)
+
+        if hex_file:
+            return hex_file.format(baudrate=baudrate)
+        else:
+            Logger.log("e", "Could not find any firmware for machine %s.", machine_type)
+            raise FileNotFoundError()
+
+    ##  Helper to identify serial ports (and scan for them)
+    def _addRemovePorts(self, serial_ports):
+        # First, find and add all new or changed keys
+        for serial_port in list(serial_ports):
+            if serial_port not in self._serial_port_list:
+                self.addUSBOutputDeviceSignal.emit(serial_port)  # Hack to ensure its created in main thread
+                continue
+        self._serial_port_list = list(serial_ports)
+
+        devices_to_remove = []
+        for port, device in self._usb_output_devices.items():
+            if port not in self._serial_port_list:
+                device.close()
+                devices_to_remove.append(port)
+
+        for port in devices_to_remove:
+            del self._usb_output_devices[port]
+
+    ##  Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
+    def addOutputDevice(self, serial_port):
+        device = USBPrinterOutputDevice.USBPrinterOutputDevice(serial_port)
+        device.connect()
+        device.connectionStateChanged.connect(self._onConnectionStateChanged)
+        device.progressChanged.connect(self.progressChanged)
+        self._usb_output_devices[serial_port] = device
+
+    ##  If one of the states of the connected devices change, we might need to add / remove them from the global list.
+    def _onConnectionStateChanged(self, serial_port):
+        try:
+            if self._usb_output_devices[serial_port].connectionState == ConnectionState.CONNECTED:
+                self.getOutputDeviceManager().addOutputDevice(self._usb_output_devices[serial_port])
+            else:
+                self.getOutputDeviceManager().removeOutputDevice(serial_port)
+            self.connectionStateChanged.emit()
+        except KeyError:
+            pass  # no output device by this device_id found in connection list.
+
+
+    @pyqtProperty(QObject , notify = connectionStateChanged)
+    def connectedPrinterList(self):
+        self._usb_output_devices_model = ListModel()
+        self._usb_output_devices_model.addRoleName(Qt.UserRole + 1, "name")
+        self._usb_output_devices_model.addRoleName(Qt.UserRole + 2, "printer")
+        for connection in self._usb_output_devices:
+            if self._usb_output_devices[connection].connectionState == ConnectionState.CONNECTED:
+                self._usb_output_devices_model.appendItem({"name": connection, "printer": self._usb_output_devices[connection]})
+        return self._usb_output_devices_model
+
+    ##  Create a list of serial ports on the system.
+    #   \param only_list_usb If true, only usb ports are listed
+    def getSerialPortList(self, only_list_usb = False):
+        base_list = []
+        if platform.system() == "Windows":
+            import winreg
+            try:
+                key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM")
+                i = 0
+                while True:
+                    values = winreg.EnumValue(key, i)
+                    if not only_list_usb or "USBSER" in values[0]:
+                        base_list += [values[1]]
+                    i += 1
+            except Exception as e:
+                pass
+        else:
+            if only_list_usb:
+                base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.usb*")
+                base_list = filter(lambda s: "Bluetooth" not in s, base_list) # Filter because mac sometimes puts them in the list
+            else:
+                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/*")
+        return list(base_list)
+
+    _instance = None