Browse Source

Refactoring & documentation

CURA-1339
Jaime van Kessel 9 years ago
parent
commit
4b5c118ed2

+ 0 - 244
plugins/USBPrinting/USBPrinterManager.py

@@ -1,244 +0,0 @@
-# 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")
-
-class USBPrinterManager(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._printer_connections = {}
-        self._printer_connections_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.addConnectionSignal.connect(self.addConnection) #Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
-
-    addConnectionSignal = Signal()
-    printerConnectionStateChanged = pyqtSignal()
-
-    progressChanged = pyqtSignal()
-
-    @pyqtProperty(float, notify = progressChanged)
-    def progress(self):
-        progress = 0
-        for printer_name, connection in self._printer_connections.items(): # TODO: @UnusedVariable "printer_name"
-            progress += connection.progress
-
-        return progress / len(self._printer_connections)
-
-    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._printer_connections:
-            Message(i18n_catalog.i18nc("@info","Cannot update firmware, there were no connected printers found.")).show()
-            return
-
-        self.spawnFirmwareInterface("")
-        for printer_connection in self._printer_connections:
-            try:
-                self._printer_connections[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
-            except FileNotFoundError:
-                self._printer_connections[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._printer_connections:
-            self.spawnFirmwareInterface(self._printer_connections[serial_port].getSerialPort())
-            try:
-                self._printer_connections[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 USBPrinterManager._instance is None:
-            USBPrinterManager._instance = cls()
-
-        return USBPrinterManager._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()
-
-    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.addConnectionSignal.emit(serial_port) #Hack to ensure its created in main thread
-                continue
-        self._serial_port_list = list(serial_ports)
-
-        connections_to_remove = []
-        for port, connection in self._printer_connections.items():
-            if port not in self._serial_port_list:
-                connection.close()
-                connections_to_remove.append(port)
-
-        for port in connections_to_remove:
-            del self._printer_connections[port]
-
-
-    ##  Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
-    def addConnection(self, serial_port):
-        connection = USBPrinterOutputDevice.USBPrinterOutputDevice(serial_port)
-        connection.connect()
-        connection.connectionStateChanged.connect(self._onPrinterConnectionStateChanged)
-        connection.progressChanged.connect(self.progressChanged)
-        self._printer_connections[serial_port] = connection
-
-    def _onPrinterConnectionStateChanged(self, serial_port):
-        try:
-            if self._printer_connections[serial_port].connectionState == ConnectionState.CONNECTED:
-                self.getOutputDeviceManager().addOutputDevice(self._printer_connections[serial_port])
-            else:
-                self.getOutputDeviceManager().removeOutputDevice(serial_port)
-            self.printerConnectionStateChanged.emit()
-        except KeyError:
-            pass  # no output device by this device_id found in connection list.
-
-
-    @pyqtProperty(QObject , notify = printerConnectionStateChanged)
-    def connectedPrinterList(self):
-        self._printer_connections_model  = ListModel()
-        self._printer_connections_model.addRoleName(Qt.UserRole + 1,"name")
-        self._printer_connections_model.addRoleName(Qt.UserRole + 2, "printer")
-        for connection in self._printer_connections:
-            if self._printer_connections[connection].connectionState == ConnectionState.CONNECTED:
-                self._printer_connections_model.appendItem({"name":connection, "printer": self._printer_connections[connection]})
-        return self._printer_connections_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

+ 25 - 23
plugins/USBPrinting/USBPrinterOutputDevice.py

@@ -72,6 +72,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         # Current Z stage location 
         self._current_z = 0
 
+        # Check if endstops are ever pressed (used for first run)
         self._x_min_endstop_pressed = False
         self._y_min_endstop_pressed = False
         self._z_min_endstop_pressed = False
@@ -140,14 +141,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         for layer in gcode_list:
             self._gcode.extend(layer.split("\n"))
 
-        #Reset line number. If this is not done, first line is sometimes ignored
+        # Reset line number. If this is not done, first line is sometimes ignored
         self._gcode.insert(0, "M110")
         self._gcode_position = 0
         self._print_start_time_100 = None
         self._is_printing = True
         self._print_start_time = time.time()
 
-        for i in range(0, 4): #Push first 4 entries before accepting other inputs
+        for i in range(0, 4):  # Push first 4 entries before accepting other inputs
             self._sendNextGcodeLine()
 
         self.writeFinished.emit(self)
@@ -162,7 +163,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         if not self._updating_firmware and not self._connect_thread.isAlive():
             self._connect_thread.start()
 
-    ##  Private fuction (threaded) that actually uploads the firmware.
+    ##  Private function (threaded) that actually uploads the firmware.
     def _updateFirmware(self):
         self.setProgress(0, 100)
 
@@ -182,7 +183,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         except Exception:
             pass
 
-        time.sleep(1) # Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases.
+        # Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases.
+        time.sleep(1)
 
         if not programmer.isConnected():
             Logger.log("e", "Unable to connect with serial. Could not update firmware")
@@ -238,7 +240,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         except Exception as e:
             Logger.log("i", "Could not establish connection on %s, unknown reasons.  Device is not arduino based." % self._serial_port)
 
-        # If the programmer connected, we know its an atmega based version. Not all that useful, but it does give some debugging information.
+        # If the programmer connected, we know its an atmega based version.
+        # Not all that useful, but it does give some debugging information.
         for baud_rate in self._getBaudrateList(): # Cycle all baud rates (auto detect)
             Logger.log("d","Attempting to connect to printer with serial %s on baud rate %s", self._serial_port, baud_rate)
             if self._serial is None:
@@ -259,7 +262,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
             while timeout_time > time.time():
                 line = self._readline()
                 if line is None:
-                     # Something went wrong with reading, could be that close was called.
+                    # Something went wrong with reading, could be that close was called.
                     self.setConnectionState(ConnectionState.CLOSED)
                     return
 
@@ -273,10 +276,10 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
                         Logger.log("i", "Established printer connection on port %s" % self._serial_port)
                         return 
 
-                self._sendCommand("M105") # Send M105 as long as we are listening, otherwise we end up in an undefined state
+                self._sendCommand("M105")  # Send M105 as long as we are listening, otherwise we end up in an undefined state
 
         Logger.log("e", "Baud rate detection for %s failed", self._serial_port)
-        self.close() # Unable to connect, wrap up.
+        self.close()  # Unable to connect, wrap up.
         self.setConnectionState(ConnectionState.CLOSED)
 
     ##  Set the baud rate of the serial. This can cause exceptions, but we simply want to ignore those.
@@ -312,7 +315,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         self._listen_thread.daemon = True
         self._serial = None
 
-
     ##  Directly send the command, withouth checking connection state (eg; printing).
     #   \param cmd string with g-code
     def _sendCommand(self, cmd):
@@ -395,7 +397,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         while self._connection_state == ConnectionState.CONNECTED:
             line = self._readline()
             if line is None: 
-                break # None is only returned when something went wrong. Stop listening
+                break  # None is only returned when something went wrong. Stop listening
 
             if time.time() > temperature_request_timeout:
                 if self._num_extruders > 0:
@@ -408,8 +410,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
             if line.startswith(b"Error:"):
                 # Oh YEAH, consistency.
                 # Marlin reports a MIN/MAX temp error as "Error:x\n: Extruder switched off. MAXTEMP triggered !\n"
-                #       But a bed temp error is reported as "Error: Temperature heated bed switched off. MAXTEMP triggered !!"
-                #       So we can have an extra newline in the most common case. Awesome work people.
+                # But a bed temp error is reported as "Error: Temperature heated bed switched off. MAXTEMP triggered !!"
+                # So we can have an extra newline in the most common case. Awesome work people.
                 if re.match(b"Error:[0-9]\n", line):
                     line = line.rstrip() + self._readline()
 
@@ -418,12 +420,12 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
                     if not self.hasError():
                         self._setErrorState(line[6:])
 
-            elif b" T:" in line or line.startswith(b"T:"): #Temperature message
+            elif b" T:" in line or line.startswith(b"T:"):  # Temperature message
                 try: 
                     self._setHotendTemperature(self._temperature_requested_extruder_index, float(re.search(b"T: *([0-9\.]*)", line).group(1)))
                 except:
                     pass
-                if b"B:" in line: # Check if it's a bed temperature
+                if b"B:" in line:  # Check if it's a bed temperature
                     try:
                         self._setBedTemperature(float(re.search(b"B: *([0-9\.]*)", line).group(1)))
                     except Exception as e:
@@ -435,7 +437,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
 
             if self._is_printing:
                 if line == b"" and time.time() > ok_timeout:
-                    line = b"ok" # Force a timeout (basicly, send next command)
+                    line = b"ok"  # Force a timeout (basically, send next command)
 
                 if b"ok" in line:
                     ok_timeout = time.time() + 5
@@ -443,14 +445,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
                         self._sendCommand(self._command_queue.get())
                     else:
                         self._sendNextGcodeLine()
-                elif b"resend" in line.lower() or b"rs" in line: # Because a resend can be asked with "resend" and "rs"
+                elif b"resend" in line.lower() or b"rs" in line:  # Because a resend can be asked with "resend" and "rs"
                     try:
                         self._gcode_position = int(line.replace(b"N:",b" ").replace(b"N",b" ").replace(b":",b" ").split()[-1])
                     except:
                         if b"rs" in line:
                             self._gcode_position = int(line.split()[1])
 
-            else: # Request the temperature on comm timeout (every 2 seconds) when we are not printing.)
+            else:  # Request the temperature on comm timeout (every 2 seconds) when we are not printing.)
                 if line == b"":
                     if self._num_extruders > 0:
                         self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._num_extruders
@@ -472,7 +474,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         line = line.strip()
         try:
             if line == "M0" or line == "M1":
-                line = "M105"   #Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
+                line = "M105"  # Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
             if ("G0" in line or "G1" in line) and "Z" in line:
                 z = float(re.search("Z([0-9\.]*)", line).group(1))
                 if self._current_z != z:
@@ -484,13 +486,13 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
 
         self._sendCommand("N%d%s*%d" % (self._gcode_position, line, checksum))
         self._gcode_position += 1 
-        self.setProgress(( self._gcode_position / len(self._gcode)) * 100)
+        self.setProgress((self._gcode_position / len(self._gcode)) * 100)
         self.progressChanged.emit()
 
     ##  Set the progress of the print.
     #   It will be normalized (based on max_progress) to range 0 - 100
     def setProgress(self, progress, max_progress = 100):
-        self._progress  = (progress / max_progress) * 100 #Convert to scale of 0-100
+        self._progress = (progress / max_progress) * 100  # Convert to scale of 0-100
         self.progressChanged.emit()
 
     ##  Cancel the current print. Printer connection wil continue to listen.
@@ -507,7 +509,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
 
     ##  Check if the process did not encounter an error yet.
     def hasError(self):
-        return self._error_state != None
+        return self._error_state is not None
 
     ##  private read line used by printer connection to listen for data on serial port.
     def _readline(self):
@@ -516,7 +518,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         try:
             ret = self._serial.readline()
         except Exception as e:
-            Logger.log("e","Unexpected error while reading serial port. %s" %e)
+            Logger.log("e", "Unexpected error while reading serial port. %s" % e)
             self._setErrorState("Printer has been disconnected") 
             self.close()
             return None
@@ -530,7 +532,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
 
     def _onFirmwareUpdateComplete(self):
         self._update_firmware_thread.join()
-        self._update_firmware_thread = threading.Thread(target= self._updateFirmware)
+        self._update_firmware_thread = threading.Thread(target = self._updateFirmware)
         self._update_firmware_thread.daemon = True
 
         self.connect()

+ 3 - 3
plugins/USBPrinting/__init__.py

@@ -1,7 +1,7 @@
 # Copyright (c) 2015 Ultimaker B.V.
 # Cura is released under the terms of the AGPLv3 or higher.
 
-from . import USBPrinterManager
+from . import USBPrinterOutputDeviceManager
 from PyQt5.QtQml import qmlRegisterType, qmlRegisterSingletonType
 from UM.i18n import i18nCatalog
 i18n_catalog = i18nCatalog("cura")
@@ -19,5 +19,5 @@ def getMetaData():
     }
 
 def register(app):
-    qmlRegisterSingletonType(USBPrinterManager.USBPrinterManager, "UM", 1, 0, "USBPrinterManager", USBPrinterManager.USBPrinterManager.getInstance)
-    return {"extension":USBPrinterManager.USBPrinterManager.getInstance(),"output_device": USBPrinterManager.USBPrinterManager.getInstance() }
+    qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "UM", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance)
+    return {"extension":USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance(), "output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance()}