From 63eb1793ad17282986996ff351629202dc4dfa6f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 12 Apr 2016 15:15:02 +0200 Subject: [PATCH 01/47] Renaming USBPrinterConnection CURA-1339 --- plugins/USBPrinting/USBPrinterManager.py | 4 ++-- .../{PrinterConnection.py => USBPrinterOutputDevice.py} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename plugins/USBPrinting/{PrinterConnection.py => USBPrinterOutputDevice.py} (99%) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 6dc88f59d5..1188fce28f 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the AGPLv3 or higher. from UM.Signal import Signal, SignalEmitter -from . import PrinterConnection +from . import USBPrinterOutputDevice from UM.Application import Application from UM.Resources import Resources from UM.Logger import Logger @@ -190,7 +190,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): ## Because the model needs to be created in the same thread as the QMLEngine, we use a signal. def addConnection(self, serial_port): - connection = PrinterConnection.PrinterConnection(serial_port) + connection = USBPrinterOutputDevice.USBPrinterOutputDevice(serial_port) connection.connect() connection.connectionStateChanged.connect(self._onPrinterConnectionStateChanged) connection.progressChanged.connect(self.progressChanged) diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/USBPrinterOutputDevice.py similarity index 99% rename from plugins/USBPrinting/PrinterConnection.py rename to plugins/USBPrinting/USBPrinterOutputDevice.py index 7d55952984..a2dcaa5dca 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -24,7 +24,7 @@ from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") -class PrinterConnection(OutputDevice, QObject, SignalEmitter): +class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): def __init__(self, serial_port, parent = None): QObject.__init__(self, parent) OutputDevice.__init__(self, serial_port) From 6dd0a0d61abd428c37bb8c23b48cac2da23b2ff6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 12 Apr 2016 16:45:58 +0200 Subject: [PATCH 02/47] Added printer specific output device CURA-1339 --- cura/PrinterOutputDevice.py | 91 +++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 cura/PrinterOutputDevice.py diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py new file mode 100644 index 0000000000..68b9e05aa6 --- /dev/null +++ b/cura/PrinterOutputDevice.py @@ -0,0 +1,91 @@ +from UM.OutputDevice.OutputDevice import OutputDevice +from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot + + +## Printer output device adds extra interface options on top of output device. +# +# Note that a number of settings are marked as "final". This is because decorators +# are not inherited by children. To fix this we use the private counter part of those +# functions to actually have the implementation. +# +# For all other uses it should be used in the same way as a "regular" OutputDevice. +class PrinterOutputDevice(OutputDevice): + def __init__(self, device_id): + super().__init__(device_id) + self._target_bed_temperature = 0 + self._target_hotend_temperatures = {} + + def requestWrite(self, node, file_name = None, filter_by_machine = False): + raise NotImplementedError("requestWrite needs to be implemented") + + bedTemperatureChanged = pyqtSignal() + targetBedTemperatureChanged = pyqtSignal() + + progressChanged = pyqtSignal() + + hotendTemperaturesChanged = pyqtSignal() + targetHotendTemperaturesChanged = pyqtSignal() + + ## Get the bed temperature of the bed (if any) + # This function is "final" (do not re-implement) + # /sa _getBedTemperature + @pyqtProperty(float, notify = bedTemperatureChanged) + def bedTemperature(self): + return self._getBedTemperature() + + def _getBedTemperature(self): + raise NotImplementedError("_getBedTemperature needs to be implemented") + + ## Get the temperature of a hot end as defined by index. + # /parameter index Index of the hotend to get a temperature from. + def getHotendTemperature(self, index): + raise NotImplementedError("getHotendTemperature needs to be implemented") + + ## Set the (target) bed temperature + # This function is "final" (do not re-implement) + # /sa _setBedTemperature + @pyqtSlot(int) + def setBedTemperature(self, temperature): + self._setBedTemperature(temperature) + self._target_bed_temperature = temperature + self.targetBedTemperatureChanged.emit() + + ## Set the bed temperature of the connected printer. + def _setBedTemperature(self, temperature): + raise NotImplementedError("_setBedTemperature needs to be implemented") + + def setHotendTemperature(self, index, temperature): + self._setTargetHotendTemperature(index, temperature) + self._target_hotend_temperatures[index] = temperature + self.targetHotendTemperaturesChanged.emit() + + def _setTargetHotendTemperature(self, index, temperature): + raise NotImplementedError("_setTargetHotendTemperature needs to be implemented") + + @pyqtProperty("QVariantMap", notify = targetHotendTemperaturesChanged) + def hotendTemperatures(self): + return self._getHotendTemperatures() + + def _getHotendTemperatures(self): + raise NotImplementedError("_getHotendTemperatures needs to be implemented") + + ## Attempt to establish connection + def connect(self): + pass + + def close(self): + pass + + ## Get the progress of any currently active process. + # This function is "final" (do not re-implement) + # /sa _getProgress + # /returns float progess of the process. -1 indicates that there is no process. + @pyqtProperty(float, notify = progressChanged) + def progress(self): + try: + return float(self._getProgress()) + except ValueError: + return -1 + + def _getProgress(self): + raise NotImplementedError("_getProgress needs to be implemented") \ No newline at end of file From de9721e5deb6ee47da2ac364a3c89f9f4bcbbfcc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 09:56:13 +0200 Subject: [PATCH 03/47] Added temperature & position stubs CURA-1339 --- cura/PrinterOutputDevice.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 68b9e05aa6..b00107b293 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -4,6 +4,8 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot ## Printer output device adds extra interface options on top of output device. # +# The assumption is made the printer is a FDM printer. +# # Note that a number of settings are marked as "final". This is because decorators # are not inherited by children. To fix this we use the private counter part of those # functions to actually have the implementation. @@ -14,6 +16,9 @@ class PrinterOutputDevice(OutputDevice): super().__init__(device_id) self._target_bed_temperature = 0 self._target_hotend_temperatures = {} + self._head_x = 0 + self._head_y = 0 + self._head_z = 0 def requestWrite(self, node, file_name = None, filter_by_machine = False): raise NotImplementedError("requestWrite needs to be implemented") @@ -26,6 +31,8 @@ class PrinterOutputDevice(OutputDevice): hotendTemperaturesChanged = pyqtSignal() targetHotendTemperaturesChanged = pyqtSignal() + headPositionChanged = pyqtSignal() + ## Get the bed temperature of the bed (if any) # This function is "final" (do not re-implement) # /sa _getBedTemperature @@ -63,6 +70,13 @@ class PrinterOutputDevice(OutputDevice): raise NotImplementedError("_setTargetHotendTemperature needs to be implemented") @pyqtProperty("QVariantMap", notify = targetHotendTemperaturesChanged) + def targetHotendTemperatures(self): + return self._getTargetHotendTemperatures() + + def _getTargetHotendTemperatures(self): + raise NotImplementedError("_getTargetHotendTemperatures needs to be implemented") + + @pyqtProperty("QVariantMap", notify = hotendTemperaturesChanged) def hotendTemperatures(self): return self._getHotendTemperatures() @@ -76,6 +90,32 @@ class PrinterOutputDevice(OutputDevice): def close(self): pass + @pyqtProperty(float, notify = headPositionChanged) + def headX(self): + return self._head_x + + @pyqtProperty(float, notify = headPositionChanged) + def headY(self): + return self._head_y + + @pyqtProperty(float, notify = headPositionChanged) + def headZ(self): + return self._head_z + + @pyqtSlot("long", "long", "long") + @pyqtSlot("long", "long", "long", "long") + def setHeadPosition(self, x, y ,z, speed = 3000): + pass + + def _setHeadX(self, x, speed): + pass + + def _setHeadY(self, y, speed): + pass + + def _setHeadZ(self, z, speed): + pass + ## Get the progress of any currently active process. # This function is "final" (do not re-implement) # /sa _getProgress From a4314638ab9825823419f88ac1cfab515d4b5ac2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 09:57:36 +0200 Subject: [PATCH 04/47] Added delete to printerOutput device to ensure it is properly closed CURA-1339 --- cura/PrinterOutputDevice.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index b00107b293..cc4cbb7094 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -90,6 +90,10 @@ class PrinterOutputDevice(OutputDevice): def close(self): pass + ## Ensure that close gets called when object is destroyed + def __del__(self): + self.close() + @pyqtProperty(float, notify = headPositionChanged) def headX(self): return self._head_x From 8e853ffecbc80096e62b179ba7d633f0fab319b9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 13:19:57 +0200 Subject: [PATCH 05/47] Added documentation CURA-1339 --- cura/PrinterOutputDevice.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index cc4cbb7094..a00069286b 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -94,21 +94,34 @@ class PrinterOutputDevice(OutputDevice): def __del__(self): self.close() + ## Get the x position of the head. + # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headX(self): return self._head_x + ## Get the y position of the head. + # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headY(self): return self._head_y + ## Get the z position of the head. + # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. + # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headZ(self): return self._head_z + ## Set the position of the head. + # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. + # This function is "final" (do not re-implement) @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") - def setHeadPosition(self, x, y ,z, speed = 3000): + def setHeadPosition(self, x, y, z, speed = 3000): + self._setHeadPosition(x, y , z, speed) + + def _setHeadPosition(self, x, y, z, speed): pass def _setHeadX(self, x, speed): From 70eb0428957fea192d34b128f40b25059ac6a4df Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 13:31:18 +0200 Subject: [PATCH 06/47] More fleshing out of the printerOutputDevice CURA-1339 --- cura/PrinterOutputDevice.py | 50 ++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index a00069286b..4d8bcce680 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -19,10 +19,12 @@ class PrinterOutputDevice(OutputDevice): self._head_x = 0 self._head_y = 0 self._head_z = 0 + self._connected = False def requestWrite(self, node, file_name = None, filter_by_machine = False): raise NotImplementedError("requestWrite needs to be implemented") + # Signals: bedTemperatureChanged = pyqtSignal() targetBedTemperatureChanged = pyqtSignal() @@ -33,6 +35,8 @@ class PrinterOutputDevice(OutputDevice): headPositionChanged = pyqtSignal() + conectedChanged = pyqtSignal() + ## Get the bed temperature of the bed (if any) # This function is "final" (do not re-implement) # /sa _getBedTemperature @@ -41,7 +45,7 @@ class PrinterOutputDevice(OutputDevice): return self._getBedTemperature() def _getBedTemperature(self): - raise NotImplementedError("_getBedTemperature needs to be implemented") + return None ## Get the temperature of a hot end as defined by index. # /parameter index Index of the hotend to get a temperature from. @@ -57,10 +61,17 @@ class PrinterOutputDevice(OutputDevice): self._target_bed_temperature = temperature self.targetBedTemperatureChanged.emit() - ## Set the bed temperature of the connected printer. + ## Set the bed temperature of the connected printer (if any). + # /parameter temperature Temperature bed needs to go to (in deg celsius) def _setBedTemperature(self, temperature): - raise NotImplementedError("_setBedTemperature needs to be implemented") + pass + ## Set the (target) hotend temperature + # This function is "final" (do not re-implement) + # /param index the index of the hotend that needs to change temperature + # /param temperature The temperature it needs to change to (in deg celsius). + # /sa _setTargetHotendTemperature + @pyqtSlot(int, int) def setHotendTemperature(self, index, temperature): self._setTargetHotendTemperature(index, temperature) self._target_hotend_temperatures[index] = temperature @@ -90,6 +101,10 @@ class PrinterOutputDevice(OutputDevice): def close(self): pass + @pyqtProperty(bool, notify = conectedChanged) + def connected(self): + return self._connected + ## Ensure that close gets called when object is destroyed def __del__(self): self.close() @@ -116,11 +131,40 @@ class PrinterOutputDevice(OutputDevice): ## Set the position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) + # /param speed Speed by which it needs to move (in mm/minute) @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def setHeadPosition(self, x, y, z, speed = 3000): self._setHeadPosition(x, y , z, speed) + ## Set the X position of the head. + # This function is "final" (do not re-implement) + # /param x x position head needs to move to. + # /param speed Speed by which it needs to move (in mm/minute) + @pyqtSlot("long") + @pyqtSlot("long", "long") + def setHeadX(self, x, speed = 3000): + self._setHeadX(x, speed) + + ## Set the Y position of the head. + # This function is "final" (do not re-implement) + # /param y y position head needs to move to. + # /param speed Speed by which it needs to move (in mm/minute) + @pyqtSlot("long") + @pyqtSlot("long", "long") + def setHeadY(self, y, speed = 3000): + self._setHeadY(y, speed) + + ## Set the Z position of the head. + # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. + # This function is "final" (do not re-implement) + # /param z z position head needs to move to. + # /param speed Speed by which it needs to move (in mm/minute) + @pyqtSlot("long") + @pyqtSlot("long", "long") + def setHeadZ(self, z, speed = 3000): + self._setHeadY(z, speed) + def _setHeadPosition(self, x, y, z, speed): pass From e5adea4ab2eb0c844d3df4c1eb409ff4b4c62580 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 13:36:22 +0200 Subject: [PATCH 07/47] PrinterOutputdevice is now also a QObject CURA-1339 --- cura/PrinterOutputDevice.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 4d8bcce680..ce236ed620 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -1,5 +1,5 @@ from UM.OutputDevice.OutputDevice import OutputDevice -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot +from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject ## Printer output device adds extra interface options on top of output device. @@ -11,9 +11,11 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot # functions to actually have the implementation. # # For all other uses it should be used in the same way as a "regular" OutputDevice. -class PrinterOutputDevice(OutputDevice): - def __init__(self, device_id): - super().__init__(device_id) +class PrinterOutputDevice(OutputDevice, QObject): + def __init__(self, device_id, parent = None): + QObject.__init__(self, parent) + OutputDevice.__init__(self, device_id) + self._target_bed_temperature = 0 self._target_hotend_temperatures = {} self._head_x = 0 From 623596ca59de0a117842d50d2b8649003251b0f2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 13:45:07 +0200 Subject: [PATCH 08/47] Added isConnecting CURA-1339 --- cura/PrinterOutputDevice.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index ce236ed620..d9f119bf83 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -22,6 +22,7 @@ class PrinterOutputDevice(OutputDevice, QObject): self._head_y = 0 self._head_z = 0 self._connected = False + self._connecting = False def requestWrite(self, node, file_name = None, filter_by_machine = False): raise NotImplementedError("requestWrite needs to be implemented") @@ -38,6 +39,7 @@ class PrinterOutputDevice(OutputDevice, QObject): headPositionChanged = pyqtSignal() conectedChanged = pyqtSignal() + connectingChanged = pyqtSignal() ## Get the bed temperature of the bed (if any) # This function is "final" (do not re-implement) @@ -107,6 +109,10 @@ class PrinterOutputDevice(OutputDevice, QObject): def connected(self): return self._connected + @pyqtProperty(bool, notify = connectingChanged) + def isConnecting(self): + return self._connecting + ## Ensure that close gets called when object is destroyed def __del__(self): self.close() From f963ad50217896f71528dca8d9e63488979f4921 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 13:46:10 +0200 Subject: [PATCH 09/47] Renamed _setBedTemperature to setTargetBedTemperature CURA-1339 --- cura/PrinterOutputDevice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index d9f119bf83..6c8c409fb7 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -61,13 +61,13 @@ class PrinterOutputDevice(OutputDevice, QObject): # /sa _setBedTemperature @pyqtSlot(int) def setBedTemperature(self, temperature): - self._setBedTemperature(temperature) + self._setTargetBedTemperature(temperature) self._target_bed_temperature = temperature self.targetBedTemperatureChanged.emit() ## Set the bed temperature of the connected printer (if any). # /parameter temperature Temperature bed needs to go to (in deg celsius) - def _setBedTemperature(self, temperature): + def _setTargetBedTemperature(self, temperature): pass ## Set the (target) hotend temperature From 0f097f0f641bea650c4b345f4f29780302ce29aa Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 13:48:32 +0200 Subject: [PATCH 10/47] Added property for target bed temp CURA-1339 --- cura/PrinterOutputDevice.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 6c8c409fb7..01ccd822cc 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -70,6 +70,11 @@ class PrinterOutputDevice(OutputDevice, QObject): def _setTargetBedTemperature(self, temperature): pass + ## Get the target bed temperature if connected printer (if any) + @pyqtProperty(int, notify = targetBedTemperatureChanged) + def targetBedTemperature(self): + return self._target_bed_temperature + ## Set the (target) hotend temperature # This function is "final" (do not re-implement) # /param index the index of the hotend that needs to change temperature From 598c5be9760c6b6da835531dfcabc1d87a6deed0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 14:08:10 +0200 Subject: [PATCH 11/47] Temperatures are now stored in the class CURA-1339 --- cura/PrinterOutputDevice.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 01ccd822cc..d09986020c 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -17,6 +17,8 @@ class PrinterOutputDevice(OutputDevice, QObject): OutputDevice.__init__(self, device_id) self._target_bed_temperature = 0 + self._bed_temperature = 0 + self._hotend_temperatures = {} self._target_hotend_temperatures = {} self._head_x = 0 self._head_y = 0 @@ -60,7 +62,7 @@ class PrinterOutputDevice(OutputDevice, QObject): # This function is "final" (do not re-implement) # /sa _setBedTemperature @pyqtSlot(int) - def setBedTemperature(self, temperature): + def setTargetBedTemperature(self, temperature): self._setTargetBedTemperature(temperature) self._target_bed_temperature = temperature self.targetBedTemperatureChanged.emit() @@ -70,6 +72,11 @@ class PrinterOutputDevice(OutputDevice, QObject): def _setTargetBedTemperature(self, temperature): pass + ## Get the bed temperature if connected printer (if any) + @pyqtProperty(int, notify = bedTemperatureChanged) + def bedTemperature(self): + return self._bed_temperature + ## Get the target bed temperature if connected printer (if any) @pyqtProperty(int, notify = targetBedTemperatureChanged) def targetBedTemperature(self): @@ -81,7 +88,7 @@ class PrinterOutputDevice(OutputDevice, QObject): # /param temperature The temperature it needs to change to (in deg celsius). # /sa _setTargetHotendTemperature @pyqtSlot(int, int) - def setHotendTemperature(self, index, temperature): + def setTargetHotendTemperature(self, index, temperature): self._setTargetHotendTemperature(index, temperature) self._target_hotend_temperatures[index] = temperature self.targetHotendTemperaturesChanged.emit() @@ -91,17 +98,11 @@ class PrinterOutputDevice(OutputDevice, QObject): @pyqtProperty("QVariantMap", notify = targetHotendTemperaturesChanged) def targetHotendTemperatures(self): - return self._getTargetHotendTemperatures() - - def _getTargetHotendTemperatures(self): - raise NotImplementedError("_getTargetHotendTemperatures needs to be implemented") + return self._target_hotend_temperatures @pyqtProperty("QVariantMap", notify = hotendTemperaturesChanged) def hotendTemperatures(self): - return self._getHotendTemperatures() - - def _getHotendTemperatures(self): - raise NotImplementedError("_getHotendTemperatures needs to be implemented") + return self._hotend_temperatures ## Attempt to establish connection def connect(self): From 583069010788a456b3a0db633180925505d4213c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 14:11:18 +0200 Subject: [PATCH 12/47] Added setter for progress CURA-1339 --- cura/PrinterOutputDevice.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index d09986020c..6d86cd8a91 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -20,6 +20,7 @@ class PrinterOutputDevice(OutputDevice, QObject): self._bed_temperature = 0 self._hotend_temperatures = {} self._target_hotend_temperatures = {} + self._progress = -1 self._head_x = 0 self._head_y = 0 self._head_z = 0 @@ -194,13 +195,12 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Get the progress of any currently active process. # This function is "final" (do not re-implement) # /sa _getProgress - # /returns float progess of the process. -1 indicates that there is no process. + # /returns float progress of the process. -1 indicates that there is no process. @pyqtProperty(float, notify = progressChanged) def progress(self): - try: - return float(self._getProgress()) - except ValueError: - return -1 + return self._progress - def _getProgress(self): - raise NotImplementedError("_getProgress needs to be implemented") \ No newline at end of file + ## Set the progress of any currently active process + def setProgress(self, progress): + self._progress = progress + self.progressChanged.emit() \ No newline at end of file From 2b9aa1dbb5d5a6afb00b3c0a719283e015bbfeee Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 15:09:25 +0200 Subject: [PATCH 13/47] PrinterOutput device now uses connection state This is instead of the multiple booleans that were used in USB printer CURA-1339 --- cura/PrinterOutputDevice.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 6d86cd8a91..54550e1aee 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -1,6 +1,6 @@ from UM.OutputDevice.OutputDevice import OutputDevice from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject - +from enum import IntEnum # For the connection state tracking. ## Printer output device adds extra interface options on top of output device. # @@ -20,12 +20,11 @@ class PrinterOutputDevice(OutputDevice, QObject): self._bed_temperature = 0 self._hotend_temperatures = {} self._target_hotend_temperatures = {} - self._progress = -1 + self._progress = 0 self._head_x = 0 self._head_y = 0 self._head_z = 0 - self._connected = False - self._connecting = False + self._connection_state = ConnectionState.CLOSED def requestWrite(self, node, file_name = None, filter_by_machine = False): raise NotImplementedError("requestWrite needs to be implemented") @@ -41,8 +40,7 @@ class PrinterOutputDevice(OutputDevice, QObject): headPositionChanged = pyqtSignal() - conectedChanged = pyqtSignal() - connectingChanged = pyqtSignal() + connectionStateChanged = pyqtSignal(str) ## Get the bed temperature of the bed (if any) # This function is "final" (do not re-implement) @@ -112,13 +110,13 @@ class PrinterOutputDevice(OutputDevice, QObject): def close(self): pass - @pyqtProperty(bool, notify = conectedChanged) - def connected(self): - return self._connected + @pyqtProperty(bool, notify = connectionStateChanged) + def connectionState(self): + return self._connection_state - @pyqtProperty(bool, notify = connectingChanged) - def isConnecting(self): - return self._connecting + def setConnectionState(self, connection_state): + self._connection_state = connection_state + self.connectionStateChanged.emit(self._id) ## Ensure that close gets called when object is destroyed def __del__(self): @@ -203,4 +201,10 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Set the progress of any currently active process def setProgress(self, progress): self._progress = progress - self.progressChanged.emit() \ No newline at end of file + self.progressChanged.emit() + +## The current processing state of the backend. +class ConnectionState(IntEnum): + CLOSED = 0 + CONNECTING = 1 + CONNECTED = 2 \ No newline at end of file From 0cd1031ec7f51356188672d657a02028a1c7afa0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 15:15:37 +0200 Subject: [PATCH 14/47] Changed USB printing to reflect changes in output device API CURA-1339 --- plugins/USBPrinting/USBPrinterManager.py | 6 +- plugins/USBPrinting/USBPrinterOutputDevice.py | 165 ++++-------------- 2 files changed, 36 insertions(+), 135 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 1188fce28f..1c2c96484f 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -8,6 +8,7 @@ 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 @@ -20,7 +21,6 @@ import time import os.path from UM.Extension import Extension -from PyQt5.QtQuick import QQuickView from PyQt5.QtQml import QQmlComponent, QQmlContext from PyQt5.QtCore import QUrl, QObject, pyqtSlot, pyqtProperty, pyqtSignal, Qt from UM.i18n import i18nCatalog @@ -197,7 +197,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): self._printer_connections[serial_port] = connection def _onPrinterConnectionStateChanged(self, serial_port): - if self._printer_connections[serial_port].isConnected(): + if self._printer_connections[serial_port].connectionState == ConnectionState.CLOSED: self.getOutputDeviceManager().addOutputDevice(self._printer_connections[serial_port]) else: self.getOutputDeviceManager().removeOutputDevice(serial_port) @@ -209,7 +209,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): 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].isConnected(): + if self._printer_connections[connection].isConnected: self._printer_connections_model.appendItem({"name":connection, "printer": self._printer_connections[connection]}) return self._printer_connections_model diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index a2dcaa5dca..4514d58ae3 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -11,25 +11,20 @@ import functools import os.path from UM.Application import Application -from UM.Signal import Signal, SignalEmitter from UM.Logger import Logger -from UM.OutputDevice.OutputDevice import OutputDevice from UM.PluginRegistry import PluginRegistry +from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState -from PyQt5.QtQuick import QQuickView from PyQt5.QtQml import QQmlComponent, QQmlContext -from PyQt5.QtCore import QUrl, QObject, pyqtSlot, pyqtProperty, pyqtSignal, Qt +from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") -class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): - def __init__(self, serial_port, parent = None): - QObject.__init__(self, parent) - OutputDevice.__init__(self, serial_port) - SignalEmitter.__init__(self) - #super().__init__(serial_port) +class USBPrinterOutputDevice(PrinterOutputDevice): + def __init__(self, serial_port): + super().__init__(serial_port) self.setName(catalog.i18nc("@item:inmenu", "USB printing")) self.setShortDescription(catalog.i18nc("@action:button", "Print with USB")) self.setDescription(catalog.i18nc("@info:tooltip", "Print with USB")) @@ -46,18 +41,10 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): self._end_stop_thread.daemon = True self._poll_endstop = -1 - # Printer is connected - self._is_connected = False - - # Printer is in the process of connecting - self._is_connecting = False - # The baud checking is done by sending a number of m105 commands to the printer and waiting for a readable # response. If the baudrate is correct, this should make sense, else we get giberish. self._required_responses_auto_baud = 3 - self._progress = 0 - self._listen_thread = threading.Thread(target=self._listen) self._listen_thread.daemon = True @@ -119,40 +106,26 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): self._control_view = None onError = pyqtSignal() - progressChanged = pyqtSignal() - extruderTemperatureChanged = pyqtSignal() - bedTemperatureChanged = pyqtSignal() + firmwareUpdateComplete = pyqtSignal() endstopStateChanged = pyqtSignal(str ,bool, arguments = ["key","state"]) - @pyqtProperty(float, notify = progressChanged) - def progress(self): - return self._progress + def _setTargetBedTemperature(self, temperature): + Logger.log("d", "Setting bed temperature to %s", temperature) + self._sendCommand("M140 S%s" % temperature) - @pyqtProperty(float, notify = extruderTemperatureChanged) - def extruderTemperature(self): - return self._extruder_temperatures[0] + def _setHeadPosition(self, x, y , z, speed): + self._sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed)) - @pyqtProperty(float, notify = bedTemperatureChanged) - def bedTemperature(self): - return self._bed_temperature + def _setHeadX(self, x, speed): + self._sendCommand("G0 X%s F%s" % (x, speed)) - @pyqtProperty(str, notify = onError) - def error(self): - return self._error_state + def _setHeadY(self, y, speed): + self._sendCommand("G0 Y%s F%s" % (y, speed)) - # TODO: Might need to add check that extruders can not be changed when it started printing or loading these settings from settings object - def setNumExtuders(self, num): - self._extruder_count = num - self._extruder_temperatures = [0] * self._extruder_count - self._target_extruder_temperatures = [0] * self._extruder_count - - ## Is the printer actively printing - def isPrinting(self): - if not self._is_connected or self._serial is None: - return False - return self._is_printing + def _setHeadZ(self, z, speed): + self._sendCommand("G0 Y%s F%s" % (z, speed)) @pyqtSlot() def startPrint(self): @@ -163,7 +136,7 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): ## Start a print based on a g-code. # \param gcode_list List with gcode (strings). def printGCode(self, gcode_list): - if self.isPrinting() or not self._is_connected: + if not self._progress or self._connection_state != ConnectionState.CONNECTED: Logger.log("d", "Printer is busy or not connected, aborting print") self.writeError.emit(self) return @@ -198,7 +171,7 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): def _updateFirmware(self): self.setProgress(0, 100) - if self._is_connecting or self._is_connected: + if self._connection_state != ConnectionState.CLOSED: self.close() hex_file = intelHex.readHex(self._firmware_file_name) @@ -253,14 +226,14 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): self._poll_endstop = False def _pollEndStop(self): - while self._is_connected and self._poll_endstop: + while self._connection_state == ConnectionState.CONNECTED and self._poll_endstop: self.sendCommand("M119") time.sleep(0.5) ## Private connect function run by thread. Can be started by calling connect. def _connect(self): Logger.log("d", "Attempting to connect to %s", self._serial_port) - self._is_connecting = True + self.setConnectionState(ConnectionState.CONNECTING) programmer = stk500v2.Stk500v2() try: programmer.connect(self._serial_port) # Connect with the serial, if this succeeds, it's an arduino based usb device. @@ -277,7 +250,7 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): try: self._serial = serial.Serial(str(self._serial_port), baud_rate, timeout = 3, writeTimeout = 10000) except serial.SerialException: - #Logger.log("i", "Could not open port %s" % self._serial_port) + Logger.log("d", "Could not open port %s" % self._serial_port) continue else: if not self.setBaudRate(baud_rate): @@ -291,15 +264,16 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): while timeout_time > time.time(): line = self._readline() if line is None: - self.setIsConnected(False) # 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 if b"T:" in line: self._serial.timeout = 0.5 sucesfull_responses += 1 if sucesfull_responses >= self._required_responses_auto_baud: - self._serial.timeout = 2 #Reset serial timeout - self.setIsConnected(True) + self._serial.timeout = 2 # Reset serial timeout + self.setConnectionState(ConnectionState.CONNECTED) Logger.log("i", "Established printer connection on port %s" % self._serial_port) return @@ -307,7 +281,7 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): Logger.log("e", "Baud rate detection for %s failed", self._serial_port) self.close() # Unable to connect, wrap up. - self.setIsConnected(False) + self.setConnectionState(ConnectionState.CLOSED) ## Set the baud rate of the serial. This can cause exceptions, but we simply want to ignore those. def setBaudRate(self, baud_rate): @@ -317,21 +291,9 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): except Exception as e: return False - def setIsConnected(self, state): - self._is_connecting = False - if self._is_connected != state: - self._is_connected = state - self.connectionStateChanged.emit(self._serial_port) - if self._is_connected: - self._listen_thread.start() #Start listening - else: - Logger.log("w", "Printer connection state was not changed") - - connectionStateChanged = Signal() - ## Close the printer connection def close(self): - Logger.log("d", "Closing the printer connection.") + Logger.log("d", "Closing the USB printer connection.") if self._connect_thread.isAlive(): try: self._connect_thread.join() @@ -339,10 +301,10 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): Logger.log("d", "PrinterConnection.close: %s (expected)", e) pass # This should work, but it does fail sometimes for some reason - self._connect_thread = threading.Thread(target=self._connect) + self._connect_thread = threading.Thread(target = self._connect) self._connect_thread.daemon = True - self.setIsConnected(False) + self.setConnectionState(ConnectionState.CLOSED) if self._serial is not None: try: self._listen_thread.join() @@ -350,49 +312,10 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): pass self._serial.close() - self._listen_thread = threading.Thread(target=self._listen) + self._listen_thread = threading.Thread(target = self._listen) self._listen_thread.daemon = True self._serial = None - def isConnected(self): - return self._is_connected - - @pyqtSlot(int) - def heatupNozzle(self, temperature): - Logger.log("d", "Setting nozzle temperature to %s", temperature) - self._sendCommand("M104 S%s" % temperature) - - @pyqtSlot(int) - def heatupBed(self, temperature): - Logger.log("d", "Setting bed temperature to %s", temperature) - self._sendCommand("M140 S%s" % temperature) - - @pyqtSlot() - def setMoveToRelative(self): - self._sendCommand("G91") - - @pyqtSlot() - def setMoveToAbsolute(self): - self._sendCommand("G90") - - @pyqtSlot("long", "long","long") - def moveHead(self, x, y, z): - Logger.log("d","Moving head to %s, %s , %s", x, y, z) - self._sendCommand("G0 X%s Y%s Z%s F3000" % (x, y, z)) - - @pyqtSlot("long", "long","long") - def moveHeadRelative(self, x, y, z): - self.setMoveToRelative() - self.moveHead(x,y,z) - self.setMoveToAbsolute() - - @pyqtSlot() - def homeHead(self): - self._sendCommand("G28") - - @pyqtSlot() - def homeBed(self): - self._sendCommand("G28 Z") ## Directly send the command, withouth checking connection state (eg; printing). # \param cmd string with g-code @@ -433,10 +356,6 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): self._setErrorState("Unexpected error while writing serial port %s " % e) self.close() - ## Ensure that close gets called when object is destroyed - def __del__(self): - self.close() - def createControlInterface(self): if self._control_view is None: Logger.log("d", "Creating control interface for printer connection") @@ -456,7 +375,7 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): ## Send a command to printer. # \param cmd string with g-code def sendCommand(self, cmd): - if self.isPrinting(): + if not self._progress: self._command_queue.put(cmd) elif self.isConnected(): self._sendCommand(cmd) @@ -467,24 +386,6 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): self._error_state = error self.onError.emit() - ## Private function to set the temperature of an extruder - # \param index index of the extruder - # \param temperature received temperature - def _setExtruderTemperature(self, index, temperature): - try: - self._extruder_temperatures[index] = temperature - self.extruderTemperatureChanged.emit() - except Exception as e: - Logger.log("d", "PrinterConnection._setExtruderTemperature: ", e) - pass - - ## Private function to set the temperature of the bed. - # As all printers (as of time of writing) only support a single heated bed, - # these are not indexed as with extruders. - def _setBedTemperature(self, temperature): - self._bed_temperature = temperature - self.bedTemperatureChanged.emit() - def requestWrite(self, node, file_name = None, filter_by_machine = False): self.showControlInterface() @@ -507,7 +408,7 @@ class USBPrinterOutputDevice(OutputDevice, QObject, SignalEmitter): Logger.log("i", "Printer connection listen thread started for %s" % self._serial_port) temperature_request_timeout = time.time() ok_timeout = time.time() - while self._is_connected: + while self._connected: line = self._readline() if line is None: From 5ff5a957ae50c666e0216780ff400d737631194f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 16:08:17 +0200 Subject: [PATCH 15/47] Added _setBedTemperature CURA-1339 --- cura/PrinterOutputDevice.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 54550e1aee..9b85b7f2ca 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -47,10 +47,7 @@ class PrinterOutputDevice(OutputDevice, QObject): # /sa _getBedTemperature @pyqtProperty(float, notify = bedTemperatureChanged) def bedTemperature(self): - return self._getBedTemperature() - - def _getBedTemperature(self): - return None + return self._bed_temperature ## Get the temperature of a hot end as defined by index. # /parameter index Index of the hotend to get a temperature from. @@ -59,7 +56,7 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Set the (target) bed temperature # This function is "final" (do not re-implement) - # /sa _setBedTemperature + # /sa _setTargetBedTemperature @pyqtSlot(int) def setTargetBedTemperature(self, temperature): self._setTargetBedTemperature(temperature) @@ -71,6 +68,10 @@ class PrinterOutputDevice(OutputDevice, QObject): def _setTargetBedTemperature(self, temperature): pass + def _setBedTemperature(self, temperature): + self._bed_temperature = temperature + self.bedTemperatureChanged.emit() + ## Get the bed temperature if connected printer (if any) @pyqtProperty(int, notify = bedTemperatureChanged) def bedTemperature(self): From 45b3e8fbd6b2b8487f5702d7e386b79d91d88d8b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 16:56:44 +0200 Subject: [PATCH 16/47] Hotend temperatures now follow same logic as bed CURA-1339 --- cura/PrinterOutputDevice.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 9b85b7f2ca..59d443d835 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -18,8 +18,9 @@ class PrinterOutputDevice(OutputDevice, QObject): self._target_bed_temperature = 0 self._bed_temperature = 0 - self._hotend_temperatures = {} - self._target_hotend_temperatures = {} + self._num_extruders = 1 + self._hotend_temperatures = [0] * self._num_extruders + self._target_hotend_temperatures = [0] * self._num_extruders self._progress = 0 self._head_x = 0 self._head_y = 0 @@ -49,11 +50,6 @@ class PrinterOutputDevice(OutputDevice, QObject): def bedTemperature(self): return self._bed_temperature - ## Get the temperature of a hot end as defined by index. - # /parameter index Index of the hotend to get a temperature from. - def getHotendTemperature(self, index): - raise NotImplementedError("getHotendTemperature needs to be implemented") - ## Set the (target) bed temperature # This function is "final" (do not re-implement) # /sa _setTargetBedTemperature @@ -96,14 +92,18 @@ class PrinterOutputDevice(OutputDevice, QObject): def _setTargetHotendTemperature(self, index, temperature): raise NotImplementedError("_setTargetHotendTemperature needs to be implemented") - @pyqtProperty("QVariantMap", notify = targetHotendTemperaturesChanged) + @pyqtProperty("QVariantList", notify = targetHotendTemperaturesChanged) def targetHotendTemperatures(self): return self._target_hotend_temperatures - @pyqtProperty("QVariantMap", notify = hotendTemperaturesChanged) + @pyqtProperty("QVariantList", notify = hotendTemperaturesChanged) def hotendTemperatures(self): return self._hotend_temperatures + def _setHotendTemperature(self, index, temperature): + self._hotend_temperatures[index] = temperature + self.hotendTemperaturesChanged.emit() + ## Attempt to establish connection def connect(self): pass From 0a46567c179c3b1c12fc4635a77d7c39a4d42cb2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 13 Apr 2016 16:57:08 +0200 Subject: [PATCH 17/47] USB printing now uses printeroutputdevice for temperatures CURA-1339 --- plugins/USBPrinting/ControlWindow.qml | 3 +- plugins/USBPrinting/USBPrinterManager.py | 4 +-- plugins/USBPrinting/USBPrinterOutputDevice.py | 29 +++++-------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/plugins/USBPrinting/ControlWindow.qml b/plugins/USBPrinting/ControlWindow.qml index 50dfe64f1f..5f6951bedc 100644 --- a/plugins/USBPrinting/ControlWindow.qml +++ b/plugins/USBPrinting/ControlWindow.qml @@ -25,7 +25,8 @@ UM.Dialog Label { //: USB Printing dialog label, %1 is head temperature - text: catalog.i18nc("@label","Extruder Temperature %1").arg(manager.extruderTemperature) + Component.onCompleted: console.log(manager.hotendTemperatures) + text: catalog.i18nc("@label","Extruder Temperature %1").arg(manager.hotendTemperatures[0]) } Label { diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 1c2c96484f..f49cafeb04 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -197,7 +197,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): self._printer_connections[serial_port] = connection def _onPrinterConnectionStateChanged(self, serial_port): - if self._printer_connections[serial_port].connectionState == ConnectionState.CLOSED: + if self._printer_connections[serial_port].connectionState == ConnectionState.CONNECTED: self.getOutputDeviceManager().addOutputDevice(self._printer_connections[serial_port]) else: self.getOutputDeviceManager().removeOutputDevice(serial_port) @@ -209,7 +209,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): 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].isConnected: + 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 diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 4514d58ae3..8de0a74f76 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -69,21 +69,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # List of gcode lines to be printed self._gcode = [] - # Number of extruders - self._extruder_count = 1 - - # Temperatures of all extruders - self._extruder_temperatures = [0] * self._extruder_count - - # Target temperatures of all extruders - self._target_extruder_temperatures = [0] * self._extruder_count - - #Target temperature of the bed - self._target_bed_temperature = 0 - - # Temperature of the bed - self._bed_temperature = 0 - # Current Z stage location self._current_z = 0 @@ -274,6 +259,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): if sucesfull_responses >= self._required_responses_auto_baud: self._serial.timeout = 2 # Reset serial timeout self.setConnectionState(ConnectionState.CONNECTED) + self._listen_thread.start() # Start listening Logger.log("i", "Established printer connection on port %s" % self._serial_port) return @@ -408,15 +394,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice): Logger.log("i", "Printer connection listen thread started for %s" % self._serial_port) temperature_request_timeout = time.time() ok_timeout = time.time() - while self._connected: + while self._connection_state == ConnectionState.CONNECTED: line = self._readline() - if line is None: break # None is only returned when something went wrong. Stop listening if time.time() > temperature_request_timeout: - if self._extruder_count > 0: - self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._extruder_count + if self._num_extruders > 0: + self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._num_extruders self.sendCommand("M105 T%d" % (self._temperature_requested_extruder_index)) else: self.sendCommand("M105") @@ -437,7 +422,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): elif b" T:" in line or line.startswith(b"T:"): #Temperature message try: - self._setExtruderTemperature(self._temperature_requested_extruder_index,float(re.search(b"T: *([0-9\.]*)", line).group(1))) + 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 @@ -469,8 +454,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): else: # Request the temperature on comm timeout (every 2 seconds) when we are not printing.) if line == b"": - if self._extruder_count > 0: - self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._extruder_count + if self._num_extruders > 0: + self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._num_extruders self.sendCommand("M105 T%d" % self._temperature_requested_extruder_index) else: self.sendCommand("M105") From ceb8ef86f80c3da630d70c0d6938d8cff41b2737 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 11:00:03 +0200 Subject: [PATCH 18/47] Added home bed & head functions CURA-1339 --- cura/PrinterOutputDevice.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 59d443d835..794d77d48a 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -1,6 +1,8 @@ from UM.OutputDevice.OutputDevice import OutputDevice from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from enum import IntEnum # For the connection state tracking. +from UM.Logger import Logger + ## Printer output device adds extra interface options on top of output device. # @@ -59,6 +61,27 @@ class PrinterOutputDevice(OutputDevice, QObject): self._target_bed_temperature = temperature self.targetBedTemperatureChanged.emit() + ## Home the head of the connected printer + # This function is "final" (do not re-implement) + @pyqtSlot() + def homeHead(self): + self._homeHead() + + ## Home the head of the connected printer + # This is an implementation function and should be overriden by children. + def _homeHead(self): + Logger.log("w", "_homeHead is not implemented by this output device") + + ## Home the bed of the connected printer + # This function is "final" (do not re-implement) + def homeBed(self): + self._homeBed() + + ## Home the bed of the connected printer + # This is an implementation function and should be overriden by children.. + def _homeBed(self): + Logger.log("w", "_homeBed is not implemented by this output device") + ## Set the bed temperature of the connected printer (if any). # /parameter temperature Temperature bed needs to go to (in deg celsius) def _setTargetBedTemperature(self, temperature): From 5f2e0ceec8ce484dea35122ec8fda1e5bd2f8a20 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 11:02:16 +0200 Subject: [PATCH 19/47] Fixes so the QML displays right temperatures again CURA-1339 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 4 ++-- resources/qml/WizardPages/UltimakerCheckup.qml | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 8de0a74f76..3c724007da 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -121,7 +121,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): ## Start a print based on a g-code. # \param gcode_list List with gcode (strings). def printGCode(self, gcode_list): - if not self._progress or self._connection_state != ConnectionState.CONNECTED: + if self._progress or self._connection_state != ConnectionState.CONNECTED: Logger.log("d", "Printer is busy or not connected, aborting print") self.writeError.emit(self) return @@ -363,7 +363,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def sendCommand(self, cmd): if not self._progress: self._command_queue.put(cmd) - elif self.isConnected(): + elif self._connection_state == ConnectionState.CONNECTED: self._sendCommand(cmd) ## Set the error state with a message. diff --git a/resources/qml/WizardPages/UltimakerCheckup.qml b/resources/qml/WizardPages/UltimakerCheckup.qml index b8c8aebe12..80024545c9 100644 --- a/resources/qml/WizardPages/UltimakerCheckup.qml +++ b/resources/qml/WizardPages/UltimakerCheckup.qml @@ -239,7 +239,7 @@ Item if(printer_connection != null) { nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking") - printer_connection.heatupNozzle(190) + printer_connection.setHotendTemperature(0, 190) wizardPage.extruder_target_temp = 190 } } @@ -253,7 +253,7 @@ Item anchors.leftMargin: UM.Theme.getSize("default_margin").width width: wizardPage.rightRow * 0.2 wrapMode: Text.WordWrap - text: printer_connection != null ? printer_connection.extruderTemperature + "°C" : "0°C" + text: printer_connection != null ? printer_connection.hotendTemperatures[0] + "°C" : "0°C" font.bold: true } ///////////////////////////////////////////////////////////////////////////// @@ -295,7 +295,7 @@ Item if(printer_connection != null) { bedTempStatus.text = catalog.i18nc("@info:progress","Checking") - printer_connection.heatupBed(60) + printer_connection.setBedTemperature(60) wizardPage.bed_target_temp = 60 } } @@ -348,16 +348,16 @@ Item } } - onExtruderTemperatureChanged: + onHotendTemperaturesChanged: { - if(printer_connection.extruderTemperature > wizardPage.extruder_target_temp - 10 && printer_connection.extruderTemperature < wizardPage.extruder_target_temp + 10) + if(printer_connection.extruderTemperatures[0] > wizardPage.extruder_target_temp - 10 && printer_connection.extruderTemperatures[0] < wizardPage.extruder_target_temp + 10) { if(printer_connection != null) { nozzleTempStatus.text = catalog.i18nc("@info:status","Works") wizardPage.checkupProgress.nozzleTemp = true checkTotalCheckUp() - printer_connection.heatupNozzle(0) + printer_connection.setTargetHotendTemperature(0, 0) } } } @@ -368,7 +368,7 @@ Item bedTempStatus.text = catalog.i18nc("@info:status","Works") wizardPage.checkupProgress.bedTemp = true checkTotalCheckUp() - printer_connection.heatupBed(0) + printer_connection.setBedTemperature(0) } } } From 4f3489233ce8942d577bb270851dd2d5fc9c1149 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 11:02:43 +0200 Subject: [PATCH 20/47] Added home head & bed implementations CURA-1339 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 3c724007da..db988b8b6b 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -112,6 +112,12 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def _setHeadZ(self, z, speed): self._sendCommand("G0 Y%s F%s" % (z, speed)) + def _homeHead(self): + self._sendCommand("G28") + + def _homeBed(self): + self._sendCommand("G28 Z") + @pyqtSlot() def startPrint(self): self.writeStarted.emit(self) From 58b216828a52f1c6b138a5d480753b0d83754803 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 11:06:11 +0200 Subject: [PATCH 21/47] Added handling for when connection was never correctly added to list CURA-1339 --- plugins/USBPrinting/USBPrinterManager.py | 14 +++++++++----- plugins/USBPrinting/USBPrinterOutputDevice.py | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index f49cafeb04..6e1ef07c0d 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -197,11 +197,15 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): self._printer_connections[serial_port] = connection def _onPrinterConnectionStateChanged(self, serial_port): - 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() + 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): diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index db988b8b6b..59402f4fa7 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -117,7 +117,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def _homeBed(self): self._sendCommand("G28 Z") - + @pyqtSlot() def startPrint(self): self.writeStarted.emit(self) From 0c09df71d9b69dde5bdbcdacdad19411c2f260d1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 11:09:30 +0200 Subject: [PATCH 22/47] wizard now sets Target temperature CURA-1339 --- plugins/USBPrinting/USBPrinterManager.py | 2 +- resources/qml/WizardPages/UltimakerCheckup.qml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 6e1ef07c0d..ced91982ea 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -204,7 +204,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): self.getOutputDeviceManager().removeOutputDevice(serial_port) self.printerConnectionStateChanged.emit() except KeyError: - pass # no output device by this device_id found in connection list. + pass # no output device by this device_id found in connection list. @pyqtProperty(QObject , notify = printerConnectionStateChanged) diff --git a/resources/qml/WizardPages/UltimakerCheckup.qml b/resources/qml/WizardPages/UltimakerCheckup.qml index 80024545c9..d59c8be6bd 100644 --- a/resources/qml/WizardPages/UltimakerCheckup.qml +++ b/resources/qml/WizardPages/UltimakerCheckup.qml @@ -239,7 +239,7 @@ Item if(printer_connection != null) { nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking") - printer_connection.setHotendTemperature(0, 190) + printer_connection.setTargetHotendTemperature(0, 190) wizardPage.extruder_target_temp = 190 } } @@ -295,7 +295,7 @@ Item if(printer_connection != null) { bedTempStatus.text = catalog.i18nc("@info:progress","Checking") - printer_connection.setBedTemperature(60) + printer_connection.setTargetBedTemperature(60) wizardPage.bed_target_temp = 60 } } From 9e8d61aea225db999df34c2d59718f4e788d5173 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 13:03:54 +0200 Subject: [PATCH 23/47] Added missing pyqtslot decorator CURA-1339 --- cura/PrinterOutputDevice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 794d77d48a..91eba34631 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -74,6 +74,7 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Home the bed of the connected printer # This function is "final" (do not re-implement) + @pyqtSlot() def homeBed(self): self._homeBed() From 34b76f3c1eab5da4487422836c2acdc9be6e84f1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 13:04:16 +0200 Subject: [PATCH 24/47] Implemented _setTargetHotendTemp CURA-1339 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 59402f4fa7..4cb85aa1d8 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -100,6 +100,9 @@ class USBPrinterOutputDevice(PrinterOutputDevice): Logger.log("d", "Setting bed temperature to %s", temperature) self._sendCommand("M140 S%s" % temperature) + def _setTargetHotendTemperature(self, index, temperature): + self._sendCommand("M140 T%s S%s" % (index, temperature)) + def _setHeadPosition(self, x, y , z, speed): self._sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed)) From 92c0c4b6375f5d8c884bfbaa755dc8aef668d45d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 13:18:05 +0200 Subject: [PATCH 25/47] Remove unusde code CURA-1339 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 4cb85aa1d8..69cdf8c6a6 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -320,19 +320,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): if "M109" in cmd or "M190" in cmd: self._heatup_wait_start_time = time.time() - if "M104" in cmd or "M109" in cmd: - try: - t = 0 - if "T" in cmd: - t = int(re.search("T([0-9]+)", cmd).group(1)) - self._target_extruder_temperatures[t] = float(re.search("S([0-9]+)", cmd).group(1)) - except: - pass - if "M140" in cmd or "M190" in cmd: - try: - self._target_bed_temperature = float(re.search("S([0-9]+)", cmd).group(1)) - except: - pass + try: command = (cmd + "\n").encode() self._serial.write(b"\n") @@ -370,7 +358,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): ## Send a command to printer. # \param cmd string with g-code def sendCommand(self, cmd): - if not self._progress: + if self._progress: self._command_queue.put(cmd) elif self._connection_state == ConnectionState.CONNECTED: self._sendCommand(cmd) From 6909ed8765a99f2f2a9fc6e7665b92bc44185c56 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 13:20:55 +0200 Subject: [PATCH 26/47] Changed setBed temp to target bed temp CURA-1339 --- resources/qml/WizardPages/UltimakerCheckup.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/WizardPages/UltimakerCheckup.qml b/resources/qml/WizardPages/UltimakerCheckup.qml index d59c8be6bd..188a7740f0 100644 --- a/resources/qml/WizardPages/UltimakerCheckup.qml +++ b/resources/qml/WizardPages/UltimakerCheckup.qml @@ -368,7 +368,7 @@ Item bedTempStatus.text = catalog.i18nc("@info:status","Works") wizardPage.checkupProgress.bedTemp = true checkTotalCheckUp() - printer_connection.setBedTemperature(0) + printer_connection.setTargetBedTemperature(0) } } } From 8c971d217dfaf03ff70721c30d876828a00719c0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 13:22:48 +0200 Subject: [PATCH 27/47] Fixed typo in command for setting nozzle temp CURA-1339 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 69cdf8c6a6..567ca664ce 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -101,7 +101,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._sendCommand("M140 S%s" % temperature) def _setTargetHotendTemperature(self, index, temperature): - self._sendCommand("M140 T%s S%s" % (index, temperature)) + Logger.log("d", "Setting hotend %s temperature to %s", index, temperature) + self._sendCommand("M104 T%s S%s" % (index, temperature)) def _setHeadPosition(self, x, y , z, speed): self._sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed)) From 9ee6323177d4cd6bf166996ace35bd02958dc72b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 13:26:38 +0200 Subject: [PATCH 28/47] wizard now uses correct property CURA-1339 --- resources/qml/WizardPages/UltimakerCheckup.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/WizardPages/UltimakerCheckup.qml b/resources/qml/WizardPages/UltimakerCheckup.qml index 188a7740f0..2aaf4c7543 100644 --- a/resources/qml/WizardPages/UltimakerCheckup.qml +++ b/resources/qml/WizardPages/UltimakerCheckup.qml @@ -350,7 +350,7 @@ Item onHotendTemperaturesChanged: { - if(printer_connection.extruderTemperatures[0] > wizardPage.extruder_target_temp - 10 && printer_connection.extruderTemperatures[0] < wizardPage.extruder_target_temp + 10) + if(printer_connection.hotendTemperatures[0] > wizardPage.extruder_target_temp - 10 && printer_connection.hotendTemperatures[0] < wizardPage.extruder_target_temp + 10) { if(printer_connection != null) { From 4b5c118ed2311840663f666a061cdc56a67d8e25 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 13:55:33 +0200 Subject: [PATCH 29/47] Refactoring & documentation CURA-1339 --- plugins/USBPrinting/USBPrinterManager.py | 244 ------------------ plugins/USBPrinting/USBPrinterOutputDevice.py | 48 ++-- plugins/USBPrinting/__init__.py | 6 +- 3 files changed, 28 insertions(+), 270 deletions(-) delete mode 100644 plugins/USBPrinting/USBPrinterManager.py diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py deleted file mode 100644 index ced91982ea..0000000000 --- a/plugins/USBPrinting/USBPrinterManager.py +++ /dev/null @@ -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 diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 567ca664ce..5536d6c74b 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/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() diff --git a/plugins/USBPrinting/__init__.py b/plugins/USBPrinting/__init__.py index 47f5de321f..281aecd682 100644 --- a/plugins/USBPrinting/__init__.py +++ b/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()} From fc88844cf87d0d7a895b21c1d9df50220af9d645 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 14:05:36 +0200 Subject: [PATCH 30/47] Added relative move option CURA-1339 --- cura/PrinterOutputDevice.py | 16 ++++++++++++++++ plugins/USBPrinting/USBPrinterOutputDevice.py | 3 --- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 91eba34631..1eff1b945a 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -203,6 +203,22 @@ class PrinterOutputDevice(OutputDevice, QObject): def setHeadZ(self, z, speed = 3000): self._setHeadY(z, speed) + ## Move the head of the printer. + # Note that this is a relative move. If you want to move the head to a specific position you can use + # setHeadPosition + # This function is "final" (do not re-implement) + # /param x distance in x to move + # /param y distance in y to move + # /param z distance in z to move + # /param speed Speed by which it needs to move (in mm/minute) + @pyqtSlot("long", "long", "long") + @pyqtSlot("long", "long", "long", "long") + def moveHead(self, x = 0, y = 0, z = 0, speed = 3000): + self._moveHead(x, y, z, speed) + + def _moveHead(self, x, y, z, speed): + pass + def _setHeadPosition(self, x, y, z, speed): pass diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 5536d6c74b..8262aaf3c4 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -69,9 +69,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # List of gcode lines to be printed self._gcode = [] - # 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 From 6b7fb894f8c634b3fa2d4cb05242638ec31c3cea Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 14:21:22 +0200 Subject: [PATCH 31/47] Added moveHead to usb printing And ensured that wizard page uses correct version again CURA-1339 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 5 +++++ resources/qml/WizardPages/Bedleveling.qml | 22 +++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 8262aaf3c4..fbf9e376b6 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -126,6 +126,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice): gcode_list = getattr( Application.getInstance().getController().getScene(), "gcode_list") self.printGCode(gcode_list) + def _moveHead(self, x, y, z, speed): + self._sendCommand("G91") + self._sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed)) + self._sendCommand("G90") + ## Start a print based on a g-code. # \param gcode_list List with gcode (strings). def printGCode(self, gcode_list): diff --git a/resources/qml/WizardPages/Bedleveling.qml b/resources/qml/WizardPages/Bedleveling.qml index 1721f0fd4a..256b858792 100644 --- a/resources/qml/WizardPages/Bedleveling.qml +++ b/resources/qml/WizardPages/Bedleveling.qml @@ -22,7 +22,7 @@ Item Component.onCompleted: { printer_connection.homeBed() - printer_connection.moveHeadRelative(0, 0, 3) + printer_connection.moveHead(0, 0, 3) printer_connection.homeHead() } UM.I18nCatalog { id: catalog; name:"cura"} @@ -84,23 +84,23 @@ Item { if(wizardPage.leveling_state == 0) { - printer_connection.moveHeadRelative(0, 0, 3) + printer_connection.moveHead(0, 0, 3) printer_connection.homeHead() - printer_connection.moveHeadRelative(0, 0, 3) - printer_connection.moveHeadRelative(platform_width - 10, 0, 0) - printer_connection.moveHeadRelative(0, 0, -3) + printer_connection.moveHead(0, 0, 3) + printer_connection.moveHead(platform_width - 10, 0, 0) + printer_connection.moveHead(0, 0, -3) } if(wizardPage.leveling_state == 1) { - printer_connection.moveHeadRelative(0, 0, 3) - printer_connection.moveHeadRelative(-platform_width/2, platform_height - 10, 0) - printer_connection.moveHeadRelative(0, 0, -3) + printer_connection.moveHead(0, 0, 3) + printer_connection.moveHead(-platform_width/2, platform_height - 10, 0) + printer_connection.moveHead(0, 0, -3) } if(wizardPage.leveling_state == 2) { - printer_connection.moveHeadRelative(0, 0, 3) - printer_connection.moveHeadRelative(-platform_width/2 + 10, -(platform_height + 10), 0) - printer_connection.moveHeadRelative(0, 0, -3) + printer_connection.moveHead(0, 0, 3) + printer_connection.moveHead(-platform_width/2 + 10, -(platform_height + 10), 0) + printer_connection.moveHead(0, 0, -3) } wizardPage.leveling_state++ if (wizardPage.leveling_state >= 3){ From 6f7b80299e2a8a901c840200d574308189ee0736 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 15:46:16 +0200 Subject: [PATCH 32/47] Added warning logging CURA-1339 --- cura/PrinterOutputDevice.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 1eff1b945a..e045b1912f 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -86,7 +86,7 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Set the bed temperature of the connected printer (if any). # /parameter temperature Temperature bed needs to go to (in deg celsius) def _setTargetBedTemperature(self, temperature): - pass + Logger.log("w", "_setTargetBedTemperature is not implemented by this output device") def _setBedTemperature(self, temperature): self._bed_temperature = temperature @@ -114,7 +114,7 @@ class PrinterOutputDevice(OutputDevice, QObject): self.targetHotendTemperaturesChanged.emit() def _setTargetHotendTemperature(self, index, temperature): - raise NotImplementedError("_setTargetHotendTemperature needs to be implemented") + Logger.log("w", "_setTargetHotendTemperature is not implemented by this output device") @pyqtProperty("QVariantList", notify = targetHotendTemperaturesChanged) def targetHotendTemperatures(self): @@ -217,19 +217,19 @@ class PrinterOutputDevice(OutputDevice, QObject): self._moveHead(x, y, z, speed) def _moveHead(self, x, y, z, speed): - pass + Logger.log("w", "_moveHead is not implemented by this output device") def _setHeadPosition(self, x, y, z, speed): - pass + Logger.log("w", "_setHeadPosition is not implemented by this output device") def _setHeadX(self, x, speed): - pass + Logger.log("w", "_setHeadX is not implemented by this output device") def _setHeadY(self, y, speed): - pass + Logger.log("w", "_setHeadY is not implemented by this output device") def _setHeadZ(self, z, speed): - pass + Logger.log("w", "_setHeadZ is not implemented by this output device") ## Get the progress of any currently active process. # This function is "final" (do not re-implement) From c2b1753d25573fb588d23420d1c1f6543fd481c4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 14 Apr 2016 15:59:10 +0200 Subject: [PATCH 33/47] Updated documentation CURA-1339 --- cura/PrinterOutputDevice.py | 62 ++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index e045b1912f..fb4a851676 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -47,14 +47,15 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Get the bed temperature of the bed (if any) # This function is "final" (do not re-implement) - # /sa _getBedTemperature + # /sa _getBedTemperature implementation function @pyqtProperty(float, notify = bedTemperatureChanged) def bedTemperature(self): return self._bed_temperature ## Set the (target) bed temperature # This function is "final" (do not re-implement) - # /sa _setTargetBedTemperature + # /param temperature new target temperature of the bed (in deg C) + # /sa _setTargetBedTemperature implementation function @pyqtSlot(int) def setTargetBedTemperature(self, temperature): self._setTargetBedTemperature(temperature) @@ -63,6 +64,7 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Home the head of the connected printer # This function is "final" (do not re-implement) + # /sa _homeHead implementation function @pyqtSlot() def homeHead(self): self._homeHead() @@ -74,20 +76,26 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Home the bed of the connected printer # This function is "final" (do not re-implement) + # /sa _homeBed implementation function @pyqtSlot() def homeBed(self): self._homeBed() ## Home the bed of the connected printer - # This is an implementation function and should be overriden by children.. + # This is an implementation function and should be overriden by children. + # /sa homeBed def _homeBed(self): Logger.log("w", "_homeBed is not implemented by this output device") - ## Set the bed temperature of the connected printer (if any). + ## Protected setter for the bed temperature of the connected printer (if any). # /parameter temperature Temperature bed needs to go to (in deg celsius) + # /sa setTargetBedTemperature def _setTargetBedTemperature(self, temperature): Logger.log("w", "_setTargetBedTemperature is not implemented by this output device") + ## Protected setter for the current bed temperature. + # This simply sets the bed temperature, but ensures that a signal is emitted. + # /param temperature temperature of the bed. def _setBedTemperature(self, temperature): self._bed_temperature = temperature self.bedTemperatureChanged.emit() @@ -106,13 +114,17 @@ class PrinterOutputDevice(OutputDevice, QObject): # This function is "final" (do not re-implement) # /param index the index of the hotend that needs to change temperature # /param temperature The temperature it needs to change to (in deg celsius). - # /sa _setTargetHotendTemperature + # /sa _setTargetHotendTemperature implementation function @pyqtSlot(int, int) def setTargetHotendTemperature(self, index, temperature): self._setTargetHotendTemperature(index, temperature) self._target_hotend_temperatures[index] = temperature self.targetHotendTemperaturesChanged.emit() + ## Implementation function of setTargetHotendTemperature. + # /param index Index of the hotend to set the temperature of + # /param temperature Temperature to set the hotend to (in deg C) + # /sa setTargetHotendTemperature def _setTargetHotendTemperature(self, index, temperature): Logger.log("w", "_setTargetHotendTemperature is not implemented by this output device") @@ -124,6 +136,10 @@ class PrinterOutputDevice(OutputDevice, QObject): def hotendTemperatures(self): return self._hotend_temperatures + ## Protected setter for the current hotend temperature. + # This simply sets the hotend temperature, but ensures that a signal is emitted. + # /param index Index of the hotend + # /param temperature temperature of the hotend (in deg C) def _setHotendTemperature(self, index, temperature): self._hotend_temperatures[index] = temperature self.hotendTemperaturesChanged.emit() @@ -132,6 +148,7 @@ class PrinterOutputDevice(OutputDevice, QObject): def connect(self): pass + ## Attempt to close the connection def close(self): pass @@ -139,6 +156,8 @@ class PrinterOutputDevice(OutputDevice, QObject): def connectionState(self): return self._connection_state + ## Set the connection state of this output device. + # /param connection_state ConnectionState enum. def setConnectionState(self, connection_state): self._connection_state = connection_state self.connectionStateChanged.emit(self._id) @@ -169,7 +188,11 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Set the position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) + # /param x new x location of the head. + # /param y new y location of the head. + # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) + # /sa _setHeadPosition implementation function @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def setHeadPosition(self, x, y, z, speed = 3000): @@ -179,6 +202,7 @@ class PrinterOutputDevice(OutputDevice, QObject): # This function is "final" (do not re-implement) # /param x x position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) + # /sa _setHeadx implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadX(self, x, speed = 3000): @@ -188,6 +212,7 @@ class PrinterOutputDevice(OutputDevice, QObject): # This function is "final" (do not re-implement) # /param y y position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) + # /sa _setHeadY implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadY(self, y, speed = 3000): @@ -198,6 +223,7 @@ class PrinterOutputDevice(OutputDevice, QObject): # This function is "final" (do not re-implement) # /param z z position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) + # /sa _setHeadZ implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadZ(self, z, speed = 3000): @@ -211,23 +237,48 @@ class PrinterOutputDevice(OutputDevice, QObject): # /param y distance in y to move # /param z distance in z to move # /param speed Speed by which it needs to move (in mm/minute) + # /sa _moveHead implementation function @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def moveHead(self, x = 0, y = 0, z = 0, speed = 3000): self._moveHead(x, y, z, speed) + ## Implementation function of moveHead. + # /param x distance in x to move + # /param y distance in y to move + # /param z distance in z to move + # /param speed Speed by which it needs to move (in mm/minute) + # /sa moveHead def _moveHead(self, x, y, z, speed): Logger.log("w", "_moveHead is not implemented by this output device") + ## Implementation function of setHeadPosition. + # /param x new x location of the head. + # /param y new y location of the head. + # /param z new z location of the head. + # /param speed Speed by which it needs to move (in mm/minute) + # /sa setHeadPosition def _setHeadPosition(self, x, y, z, speed): Logger.log("w", "_setHeadPosition is not implemented by this output device") + ## Implementation function of setHeadX. + # /param x new x location of the head. + # /param speed Speed by which it needs to move (in mm/minute) + # /sa setHeadX def _setHeadX(self, x, speed): Logger.log("w", "_setHeadX is not implemented by this output device") + ## Implementation function of setHeadY. + # /param y new y location of the head. + # /param speed Speed by which it needs to move (in mm/minute) + # /sa _setHeadY def _setHeadY(self, y, speed): Logger.log("w", "_setHeadY is not implemented by this output device") + ## Implementation function of setHeadZ. + # /param z new z location of the head. + # /param speed Speed by which it needs to move (in mm/minute) + # /sa _setHeadZ def _setHeadZ(self, z, speed): Logger.log("w", "_setHeadZ is not implemented by this output device") @@ -240,6 +291,7 @@ class PrinterOutputDevice(OutputDevice, QObject): return self._progress ## Set the progress of any currently active process + # /param progress Progress of the process. def setProgress(self, progress): self._progress = progress self.progressChanged.emit() From 94d3309a35dd1e4d6fa1302a086850cc77dea06b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 11:33:43 +0200 Subject: [PATCH 34/47] Removed duplicate bedTemperature declaration CURA-1339 --- cura/PrinterOutputDevice.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index fb4a851676..5509ae1b8c 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -100,11 +100,6 @@ class PrinterOutputDevice(OutputDevice, QObject): self._bed_temperature = temperature self.bedTemperatureChanged.emit() - ## Get the bed temperature if connected printer (if any) - @pyqtProperty(int, notify = bedTemperatureChanged) - def bedTemperature(self): - return self._bed_temperature - ## Get the target bed temperature if connected printer (if any) @pyqtProperty(int, notify = targetBedTemperatureChanged) def targetBedTemperature(self): From 0015d9d02732f8d29f19eb379a7a966bad0555ce Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 11:37:08 +0200 Subject: [PATCH 35/47] Documented signals CURA-1339 --- cura/PrinterOutputDevice.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 5509ae1b8c..d9c3a0d9f7 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -32,17 +32,28 @@ class PrinterOutputDevice(OutputDevice, QObject): def requestWrite(self, node, file_name = None, filter_by_machine = False): raise NotImplementedError("requestWrite needs to be implemented") - # Signals: + ## Signals + + # Signal to be emitted when bed temp is changed bedTemperatureChanged = pyqtSignal() + + # Signal to be emitted when target bed temp is changed targetBedTemperatureChanged = pyqtSignal() + # Signal when the progress is changed (usually when this output device is printing / sending lots of data) progressChanged = pyqtSignal() + # Signal to be emitted when hotend temp is changed hotendTemperaturesChanged = pyqtSignal() + + # Signal to be emitted when target hotend temp is changed targetHotendTemperaturesChanged = pyqtSignal() + # Signal to be emitted when head position is changed (x,y,z) headPositionChanged = pyqtSignal() + # Signal that is emitted every time connection state is changed. + # it also sends it's own device_id (for convenience sake) connectionStateChanged = pyqtSignal(str) ## Get the bed temperature of the bed (if any) From 6ad926b9b86ba2cd2ed52b33478daecaed8b53ca Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 11:38:57 +0200 Subject: [PATCH 36/47] connect & close are now pure virtual CURA-1339 --- cura/PrinterOutputDevice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index d9c3a0d9f7..6ab80f877e 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -152,11 +152,11 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Attempt to establish connection def connect(self): - pass + raise NotImplementedError("connect needs to be implemented") ## Attempt to close the connection def close(self): - pass + raise NotImplementedError("close needs to be implemented") @pyqtProperty(bool, notify = connectionStateChanged) def connectionState(self): From b31106a66635b5dcaafa4bcb1732d03b50a9ce5e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 11:45:14 +0200 Subject: [PATCH 37/47] Added missing (renamed) file CURA-1339 --- .../USBPrinterOutputDeviceManager.py | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 plugins/USBPrinting/USBPrinterOutputDeviceManager.py diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py new file mode 100644 index 0000000000..1a0a0c1d33 --- /dev/null +++ b/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 From 0939262ffee005d9c5269ee1c0c1de3603f52630 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 11:47:23 +0200 Subject: [PATCH 38/47] Removed stray debug code CURA-1339 --- plugins/USBPrinting/ControlWindow.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/USBPrinting/ControlWindow.qml b/plugins/USBPrinting/ControlWindow.qml index 5f6951bedc..0938e8e6d3 100644 --- a/plugins/USBPrinting/ControlWindow.qml +++ b/plugins/USBPrinting/ControlWindow.qml @@ -25,7 +25,6 @@ UM.Dialog Label { //: USB Printing dialog label, %1 is head temperature - Component.onCompleted: console.log(manager.hotendTemperatures) text: catalog.i18nc("@label","Extruder Temperature %1").arg(manager.hotendTemperatures[0]) } Label From 96e821cc3d4016c0e7cecfd9ebce36ef3b9e731d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 13:27:36 +0200 Subject: [PATCH 39/47] PrinterOutputDevice now uses safer super().__init CURA-1339 --- cura/PrinterOutputDevice.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 6ab80f877e..25ba91300e 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -15,8 +15,7 @@ from UM.Logger import Logger # For all other uses it should be used in the same way as a "regular" OutputDevice. class PrinterOutputDevice(OutputDevice, QObject): def __init__(self, device_id, parent = None): - QObject.__init__(self, parent) - OutputDevice.__init__(self, device_id) + super().__init__(device_id = device_id, parent = parent) self._target_bed_temperature = 0 self._bed_temperature = 0 From fb52d3993637cdcd5b9b1859998644f406408054 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 14:43:04 +0200 Subject: [PATCH 40/47] Changed enum values to lowerCamelCase CURA-1339 --- cura/PrinterOutputDevice.py | 9 +++++---- plugins/USBPrinting/USBPrinterOutputDevice.py | 20 +++++++++---------- .../USBPrinterOutputDeviceManager.py | 4 ++-- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 25ba91300e..7b79ad4aa4 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -26,7 +26,7 @@ class PrinterOutputDevice(OutputDevice, QObject): self._head_x = 0 self._head_y = 0 self._head_z = 0 - self._connection_state = ConnectionState.CLOSED + self._connection_state = ConnectionState.closed def requestWrite(self, node, file_name = None, filter_by_machine = False): raise NotImplementedError("requestWrite needs to be implemented") @@ -301,8 +301,9 @@ class PrinterOutputDevice(OutputDevice, QObject): self._progress = progress self.progressChanged.emit() + ## The current processing state of the backend. class ConnectionState(IntEnum): - CLOSED = 0 - CONNECTING = 1 - CONNECTED = 2 \ No newline at end of file + closed = 0 + connecting = 1 + connected = 2 \ No newline at end of file diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index fbf9e376b6..68c4567450 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -134,7 +134,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): ## Start a print based on a g-code. # \param gcode_list List with gcode (strings). def printGCode(self, gcode_list): - if self._progress or self._connection_state != ConnectionState.CONNECTED: + if self._progress or self._connection_state != ConnectionState.connected: Logger.log("d", "Printer is busy or not connected, aborting print") self.writeError.emit(self) return @@ -169,7 +169,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def _updateFirmware(self): self.setProgress(0, 100) - if self._connection_state != ConnectionState.CLOSED: + if self._connection_state != ConnectionState.closed: self.close() hex_file = intelHex.readHex(self._firmware_file_name) @@ -225,14 +225,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._poll_endstop = False def _pollEndStop(self): - while self._connection_state == ConnectionState.CONNECTED and self._poll_endstop: + while self._connection_state == ConnectionState.connected and self._poll_endstop: self.sendCommand("M119") time.sleep(0.5) ## Private connect function run by thread. Can be started by calling connect. def _connect(self): Logger.log("d", "Attempting to connect to %s", self._serial_port) - self.setConnectionState(ConnectionState.CONNECTING) + self.setConnectionState(ConnectionState.connecting) programmer = stk500v2.Stk500v2() try: programmer.connect(self._serial_port) # Connect with the serial, if this succeeds, it's an arduino based usb device. @@ -265,7 +265,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): line = self._readline() if line is None: # Something went wrong with reading, could be that close was called. - self.setConnectionState(ConnectionState.CLOSED) + self.setConnectionState(ConnectionState.closed) return if b"T:" in line: @@ -273,7 +273,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): sucesfull_responses += 1 if sucesfull_responses >= self._required_responses_auto_baud: self._serial.timeout = 2 # Reset serial timeout - self.setConnectionState(ConnectionState.CONNECTED) + self.setConnectionState(ConnectionState.connected) self._listen_thread.start() # Start listening Logger.log("i", "Established printer connection on port %s" % self._serial_port) return @@ -282,7 +282,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): Logger.log("e", "Baud rate detection for %s failed", self._serial_port) self.close() # Unable to connect, wrap up. - self.setConnectionState(ConnectionState.CLOSED) + self.setConnectionState(ConnectionState.closed) ## Set the baud rate of the serial. This can cause exceptions, but we simply want to ignore those. def setBaudRate(self, baud_rate): @@ -305,7 +305,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._connect_thread = threading.Thread(target = self._connect) self._connect_thread.daemon = True - self.setConnectionState(ConnectionState.CLOSED) + self.setConnectionState(ConnectionState.closed) if self._serial is not None: try: self._listen_thread.join() @@ -365,7 +365,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def sendCommand(self, cmd): if self._progress: self._command_queue.put(cmd) - elif self._connection_state == ConnectionState.CONNECTED: + elif self._connection_state == ConnectionState.connected: self._sendCommand(cmd) ## Set the error state with a message. @@ -396,7 +396,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): Logger.log("i", "Printer connection listen thread started for %s" % self._serial_port) temperature_request_timeout = time.time() ok_timeout = time.time() - while self._connection_state == ConnectionState.CONNECTED: + while self._connection_state == ConnectionState.connected: line = self._readline() if line is None: break # None is only returned when something went wrong. Stop listening diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 1a0a0c1d33..0b71de40e0 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -201,7 +201,7 @@ class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin, ## 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: + 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) @@ -216,7 +216,7 @@ class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin, 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: + 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 From a59c11388766ae4ad7781b357e6f0dc8d1271357 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 14:47:51 +0200 Subject: [PATCH 41/47] Added busy & error state to ConnectionState CURA-1339 --- cura/PrinterOutputDevice.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 7b79ad4aa4..7bf992ea83 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -306,4 +306,6 @@ class PrinterOutputDevice(OutputDevice, QObject): class ConnectionState(IntEnum): closed = 0 connecting = 1 - connected = 2 \ No newline at end of file + connected = 2 + busy = 3 + error = 4 \ No newline at end of file From 8a47903374c19525fcf9779c25eea89955c3a23c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 15:03:13 +0200 Subject: [PATCH 42/47] Changed namespace of usbprintermanager (QML) to Cura from UM CURA-1339 --- plugins/USBPrinting/USBPrinterOutputDeviceManager.py | 5 +---- plugins/USBPrinting/__init__.py | 2 +- resources/qml/WizardPages/Bedleveling.qml | 2 +- resources/qml/WizardPages/UltimakerCheckup.qml | 6 +++--- resources/qml/WizardPages/UpgradeFirmware.qml | 4 ++-- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 0b71de40e0..6d85a4cceb 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -30,10 +30,7 @@ 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) + super().__init__(parent = parent) self._serial_port_list = [] self._usb_output_devices = {} self._usb_output_devices_model = None diff --git a/plugins/USBPrinting/__init__.py b/plugins/USBPrinting/__init__.py index 281aecd682..4e8e332173 100644 --- a/plugins/USBPrinting/__init__.py +++ b/plugins/USBPrinting/__init__.py @@ -19,5 +19,5 @@ def getMetaData(): } def register(app): - qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "UM", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance) + qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "Cura", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance) return {"extension":USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance(), "output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance()} diff --git a/resources/qml/WizardPages/Bedleveling.qml b/resources/qml/WizardPages/Bedleveling.qml index 256b858792..5ed3b0ae58 100644 --- a/resources/qml/WizardPages/Bedleveling.qml +++ b/resources/qml/WizardPages/Bedleveling.qml @@ -18,7 +18,7 @@ Item property int platform_width: UM.MachineManager.getSettingValue("machine_width") property int platform_height: UM.MachineManager.getSettingValue("machine_depth") anchors.fill: parent; - property variant printer_connection: UM.USBPrinterManager.connectedPrinterList.getItem(0).printer + property variant printer_connection: Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer Component.onCompleted: { printer_connection.homeBed() diff --git a/resources/qml/WizardPages/UltimakerCheckup.qml b/resources/qml/WizardPages/UltimakerCheckup.qml index 2aaf4c7543..57f2146823 100644 --- a/resources/qml/WizardPages/UltimakerCheckup.qml +++ b/resources/qml/WizardPages/UltimakerCheckup.qml @@ -31,9 +31,9 @@ Item } property variant printer_connection: { - if (UM.USBPrinterManager.connectedPrinterList.rowCount() != 0){ + if (Cura.USBPrinterManager.connectedPrinterList.rowCount() != 0){ wizardPage.checkupProgress.connection = true - return UM.USBPrinterManager.connectedPrinterList.getItem(0).printer + return Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer } else { return null @@ -142,7 +142,7 @@ Item anchors.left: connectionLabel.right anchors.top: parent.top wrapMode: Text.WordWrap - text: UM.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete") + text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete") } ////////////////////////////////////////////////////////// Label diff --git a/resources/qml/WizardPages/UpgradeFirmware.qml b/resources/qml/WizardPages/UpgradeFirmware.qml index 4bbb049f20..a9a252ffe4 100644 --- a/resources/qml/WizardPages/UpgradeFirmware.qml +++ b/resources/qml/WizardPages/UpgradeFirmware.qml @@ -14,7 +14,7 @@ Item SystemPalette{id: palette} UM.I18nCatalog { id: catalog; name:"cura"} - property variant printer_connection: UM.USBPrinterManager.connectedPrinterList.rowCount() != 0 ? UM.USBPrinterManager.connectedPrinterList.getItem(0).printer : null + property variant printer_connection: Cura.USBPrinterManager.connectedPrinterList.rowCount() != 0 ? Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer : null Label { id: pageTitle @@ -62,7 +62,7 @@ Item anchors.top: parent.top anchors.left: parent.left text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware"); - onClicked: UM.USBPrinterManager.updateAllFirmware() + onClicked: Cura.USBPrinterManager.updateAllFirmware() } Button { id: skipUpgradeButton From 2c489ed054ef5eab52e4ce7a1e5bc450daf72df2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 15:05:21 +0200 Subject: [PATCH 43/47] Connection state signal is now connected before connect is called CURA-1339 --- plugins/USBPrinting/USBPrinterOutputDeviceManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 6d85a4cceb..636a134f2f 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -190,8 +190,8 @@ class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin, ## 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.connect() device.progressChanged.connect(self.progressChanged) self._usb_output_devices[serial_port] = device From 6abeac5c44dfeb9e57c65af466e630ec0398deeb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 15:26:24 +0200 Subject: [PATCH 44/47] Added comment about us violating the API CURA-1339 --- plugins/USBPrinting/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/USBPrinting/__init__.py b/plugins/USBPrinting/__init__.py index 4e8e332173..4fab439bad 100644 --- a/plugins/USBPrinting/__init__.py +++ b/plugins/USBPrinting/__init__.py @@ -19,5 +19,7 @@ def getMetaData(): } def register(app): + # We are violating the QT API here (as we use a factory, which is technically not allowed). + # but we don't really have another means for doing this (and it seems to you know -work-) qmlRegisterSingletonType(USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager, "Cura", 1, 0, "USBPrinterManager", USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance) return {"extension":USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance(), "output_device": USBPrinterOutputDeviceManager.USBPrinterOutputDeviceManager.getInstance()} From 232e447a745b52b1b54660594265cdaadcac7dd6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Apr 2016 15:48:20 +0200 Subject: [PATCH 45/47] Added bq firmware (which caused merge issue) --- plugins/USBPrinting/USBPrinterOutputDeviceManager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 636a134f2f..24e2148375 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -140,6 +140,7 @@ class USBPrinterOutputDeviceManager(QObject, SignalEmitter, OutputDevicePlugin, # 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", + "bq_hephestos_2" : "MarlinHephestos2.hex", "ultimaker_original" : "MarlinUltimaker-{baudrate}.hex", "ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex", "ultimaker2" : "MarlinUltimaker2.hex", From d33a75906a80d320c8d56d8a77dc86b62a84c217 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 19 Apr 2016 12:04:22 +0200 Subject: [PATCH 46/47] Progress is now only emitted if it changed CURA-1339 --- cura/PrinterOutputDevice.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 7bf992ea83..9bc46ea532 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -298,8 +298,9 @@ class PrinterOutputDevice(OutputDevice, QObject): ## Set the progress of any currently active process # /param progress Progress of the process. def setProgress(self, progress): - self._progress = progress - self.progressChanged.emit() + if self._progress != progress: + self._progress = progress + self.progressChanged.emit() ## The current processing state of the backend. From 02e02a8f98961a154403ea892024a9b0e0c008e4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 19 Apr 2016 15:30:34 +0200 Subject: [PATCH 47/47] Added way to update the head position CURA-1339 --- cura/PrinterOutputDevice.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 9bc46ea532..7025646294 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -190,6 +190,22 @@ class PrinterOutputDevice(OutputDevice, QObject): def headZ(self): return self._head_z + ## Update the saved position of the head + # This function should be called when a new position for the head is recieved. + def _updateHeadPosition(self, x, y ,z): + position_changed = False + if self._head_x != x: + self._head_x = x + position_changed = True + if self._head_y != y: + self._head_y = y + position_changed = True + if self._head_z != z: + self._head_z = z + position_changed = True + if position_changed: + self.headPositionChanged.emit() + ## Set the position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement)