chiark / gitweb /
Add version 0.1 of serial communication with printerConnection.
authordaid <daid303@gmail.com>
Mon, 3 Feb 2014 13:33:42 +0000 (14:33 +0100)
committerdaid <daid303@gmail.com>
Mon, 3 Feb 2014 13:33:42 +0000 (14:33 +0100)
Cura/gui/newVersionDialog.py
Cura/gui/sceneView.py
Cura/serialCommunication.py [new file with mode: 0644]
Cura/util/machineCom.py
Cura/util/objectScene.py
Cura/util/printerConnection/doodle3dConnect.py
Cura/util/printerConnection/dummyConnection.py
Cura/util/printerConnection/printerConnectionBase.py
Cura/util/printerConnection/printerConnectionManager.py
Cura/util/printerConnection/serialConnection.py [new file with mode: 0644]
Cura/util/sliceEngine.py

index fa88460613754d75d5171958211bd73682668369..d154d358f1b38fecbb25c893c73df4993f980a7a 100644 (file)
@@ -26,14 +26,8 @@ class newVersionDialog(wx.Dialog):
                s.Add(wx.StaticText(p, -1, '(This dialog is only shown once)'))
                s.Add(wx.StaticLine(p), flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=10)
                s.Add(wx.StaticText(p, -1, 'New in this version:'))
-               s.Add(wx.StaticText(p, -1, '* Fixed a rare bug that caused the CuraEngine to crash on some models.'))
-               s.Add(wx.StaticText(p, -1, '* Added support for multiple Doodle3D boxes on the same network.'))
-               s.Add(wx.StaticText(p, -1, '* Made it possible to switch between "all at once" and "one at a time printing"'))
-               s.Add(wx.StaticText(p, -1, '* Improved USB communication stability.'))
-               s.Add(wx.StaticText(p, -1, '* Improved USB auto-detection for none-Ultimaker printers.'))
-               s.Add(wx.StaticText(p, -1, '* Set retraction enabled by default and in the quickprint profiles.'))
-               s.Add(wx.StaticText(p, -1, '* Fixed a bug that caused loading of really small objects to fail.'))
-               s.Add(wx.StaticText(p, -1, '* Made camera keyboard controls accessible in the GCode view, use shift+up/down for layer changes now.'))
+               s.Add(wx.StaticText(p, -1, '* Improved the LayerView rendering speed.'))
+               s.Add(wx.StaticText(p, -1, '* Made the LayerView update during slicing, so you can see the result before it is ready.'))
 
                self.hasUltimaker = None
                self.hasUltimaker2 = None
@@ -49,13 +43,10 @@ class newVersionDialog(wx.Dialog):
                        button = wx.Button(p, -1, 'Install now')
                        self.Bind(wx.EVT_BUTTON, self.OnUltimakerFirmware, button)
                        s.Add(button, flag=wx.TOP, border=5)
-               if self.hasUltimaker2 is not None:
+               if self.hasUltimaker2 is not None and False:
                        s.Add(wx.StaticLine(p), flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=10)
                        s.Add(wx.StaticText(p, -1, 'New firmware for your Ultimaker2:'))
-                       s.Add(wx.StaticText(p, -1, '* Fixed the bug where aborting a print caused massive retraction.'))
-                       s.Add(wx.StaticText(p, -1, '* Fixed a bug where going into move-material when the printer was still moving caused a bed-crash.'))
-                       s.Add(wx.StaticText(p, -1, '* Added bed temperature when cooling down the printer.'))
-                       s.Add(wx.StaticText(p, -1, '* Allow abort if bed-leveling is selected.'))
+                       s.Add(wx.StaticText(p, -1, '* .'))
                        button = wx.Button(p, -1, 'Install now')
                        self.Bind(wx.EVT_BUTTON, self.OnUltimaker2Firmware, button)
                        s.Add(button, flag=wx.TOP, border=5)
index 6eda047b73d21cc42e557faddc3ca07e08517e1f..80750c96a50a254da06def80f68bc0ed7d7e94d0 100644 (file)
@@ -217,9 +217,7 @@ class SceneView(openglGui.glGuiPanel):
        def OnPrintButton(self, button):
                if button == 1:
                        connectionGroup = self._printerConnectionManager.getAvailableGroup()
-                       if machineCom.machineIsConnected():
-                               self.showPrintWindow()
-                       elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
+                       if len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
                                drives = removableStorage.getPossibleSDcardDrives()
                                if len(drives) > 1:
                                        dlg = wx.SingleChoiceDialog(self, "Select SD drive", "Multiple removable drives have been found,\nplease select your SD card drive", map(lambda n: n[0], drives))
@@ -248,7 +246,6 @@ class SceneView(openglGui.glGuiPanel):
                                self.showSaveGCode()
                if button == 3:
                        menu = wx.Menu()
-                       self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
                        connections = self._printerConnectionManager.getAvailableConnections()
                        menu.connectionMap = {}
                        for connection in connections:
@@ -272,17 +269,6 @@ class SceneView(openglGui.glGuiPanel):
                        else:
                                self.notification.message("Failed to start print...")
 
-       def showPrintWindow(self):
-               if self._gcodeFilename is None:
-                       return
-               if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
-                       wx.MessageBox(_("USB printing on the Ultimaker2 is not supported."), _("USB Printing Error"), wx.OK | wx.ICON_WARNING)
-                       return
-               #TODO: Fix for _engine.getResult
-               self._usbPrintMonitor.loadFile(self._gcodeFilename, self._engine.getID())
-               if self._gcodeFilename is None:
-                       self._engine.submitInfoOnline()
-
        def showSaveGCode(self):
                if len(self._scene._objectList) < 1:
                        return
@@ -881,10 +867,7 @@ class SceneView(openglGui.glGuiPanel):
 
        def OnPaint(self,e):
                connectionGroup = self._printerConnectionManager.getAvailableGroup()
-               if machineCom.machineIsConnected():
-                       self.printButton._imageID = 6
-                       self.printButton._tooltip = _("Print")
-               elif len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
+               if len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
                        self.printButton._imageID = 2
                        self.printButton._tooltip = _("Toolpath to SD")
                elif connectionGroup is not None:
diff --git a/Cura/serialCommunication.py b/Cura/serialCommunication.py
new file mode 100644 (file)
index 0000000..27ae9fc
--- /dev/null
@@ -0,0 +1,63 @@
+__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
+
+# Serial communication with the printer for printing is done from a separate process,
+# this to ensure that the PIL does not block the serial printing.
+
+import sys
+import time
+import os
+
+from Cura.util import machineCom
+
+class serialComm(object):
+       def __init__(self, portName):
+               self._comm = None
+               self._gcodeList = []
+
+               self._comm = machineCom.MachineCom(portName, callbackObject=self)
+
+       def mcLog(self, message):
+               sys.stdout.write('log:%s\n' % (message))
+
+       def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
+               sys.stdout.write('temp:%s\n' % str(temp))
+
+       def mcStateChange(self, state):
+               if self._comm is None:
+                       return
+               sys.stdout.write('state:%d:%s\n' % (state, self._comm.getStateString()))
+
+       def mcMessage(self, message):
+               sys.stdout.write('message:%s\n' % (message))
+
+       def mcProgress(self, lineNr):
+               sys.stdout.write('progress:%d\n' % (lineNr))
+
+       def mcZChange(self, newZ):
+               sys.stdout.write('changeZ:%d\n' % (newZ))
+
+       def monitorStdin(self):
+
+               while not self._comm.isClosed():
+                       line = sys.stdin.readline().strip()
+                       line = line.split(':', 1)
+                       if line[0] == 'STOP':
+                               self._comm.cancelPrint()
+                               self._gcodeList = []
+                       elif line[0] == 'G':
+                               self._gcodeList.append(line[1])
+                       elif line[0] == 'START':
+                               self._comm.printGCode(self._gcodeList)
+                       else:
+                               sys.stderr.write(str(line))
+
+def main():
+       if len(sys.argv) != 2:
+               return
+       sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+       portName = sys.argv[1]
+       comm = serialComm(portName)
+       comm.monitorStdin()
+
+if __name__ == '__main__':
+       main()
index f04a21de213e06ac428af03480a1864a90530afb..0385f33e332b718138500331981edf8a2bdcd4a1 100644 (file)
@@ -230,13 +230,16 @@ class MachineCom(object):
                return "?%d?" % (self._state)
        
        def getShortErrorString(self):
-               if len(self._errorValue) < 30:
+               if len(self._errorValue) < 35:
                        return self._errorValue
-               return self._errorValue[:30] + "..."
+               return self._errorValue[:35] + "..."
 
        def getErrorString(self):
                return self._errorValue
-       
+
+       def isClosed(self):
+               return self._state == self.STATE_CLOSED_WITH_ERROR or self._state == self.STATE_CLOSED
+
        def isClosedOrError(self):
                return self._state == self.STATE_ERROR or self._state == self.STATE_CLOSED_WITH_ERROR or self._state == self.STATE_CLOSED
 
index 6a0e93759d80372851a30cb2e22caf8cfc8cd281..757f063dbd374fbe57094cc714ee111e94a9373e 100644 (file)
@@ -241,6 +241,8 @@ class Scene(object):
 
        def checkPlatform(self, obj):
                area = obj._printAreaHull + obj.getPosition()
+               if obj.getSize()[2] > self._machineSize[2]:
+                       return False
                if not polygon.fullInside(area, self._machinePolygons[0]):
                        return False
                #Check the "no go zones"
index 918ee7a03b1ec006e4ac9765ccb31627d744be84..ad3ea51b27883800b6f2bcc45b2c2329262044af 100644 (file)
@@ -34,12 +34,6 @@ class doodle3dConnectionGroup(printerConnectionBase.printerConnectionGroup):
        def getPriority(self):
                return 100
 
-       def __cmp__(self, other):
-               return self.getPriority() - other.getPriority()
-
-       def __repr__(self):
-               return self.name
-
        def _doodle3DThread(self):
                self._waitDelay = 0
                while True:
index 98c7f94b97751b4f86241c3e40def8d9813851ff..be3c57ea2da2831407507f9cf95fc6b82399a81b 100644 (file)
@@ -16,6 +16,12 @@ class dummyConnectionGroup(printerConnectionBase.printerConnectionGroup):
        def getAvailableConnections(self):
                return self._list
 
+       def getIconID(self):
+               return 5
+
+       def getPriority(self):
+               return -100
+
 #Dummy printer class which is always
 class dummyConnection(printerConnectionBase.printerConnectionBase):
        def __init__(self, name):
index ab556a5feaf3928574642915747aa3f4fb61f143..e0870669377a58a7e7918d962b7adb13b38b49f6 100644 (file)
@@ -16,13 +16,13 @@ class printerConnectionGroup(object):
                return 5
 
        def getPriority(self):
-               return -1
+               return -100
 
        def __cmp__(self, other):
                return self.getPriority() - other.getPriority()
 
        def __repr__(self):
-               return self.name
+               return '%s %d' % (self._name, self.getPriority())
 
 #Base class for different printer connection implementations.
 # A printer connection can connect to printers in different ways, trough network, USB or carrier pigeons.
index 62f77df6add6f3c0b192f4d8418689df8f49ba1a..0e00c2cb1f7f7cc01642acf7e11c20fcc7758dd3 100644 (file)
@@ -2,6 +2,7 @@ __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AG
 
 from Cura.util import version
 from Cura.util.printerConnection import dummyConnection
+from Cura.util.printerConnection import serialConnection
 from Cura.util.printerConnection import doodle3dConnect
 
 class PrinterConnectionManager(object):
@@ -9,6 +10,7 @@ class PrinterConnectionManager(object):
                self._groupList = []
                if version.isDevVersion():
                        self._groupList.append(dummyConnection.dummyConnectionGroup())
+               self._groupList.append(serialConnection.serialConnectionGroup())
                self._groupList.append(doodle3dConnect.doodle3dConnectionGroup())
 
                #Sort the connections by highest priority first.
diff --git a/Cura/util/printerConnection/serialConnection.py b/Cura/util/printerConnection/serialConnection.py
new file mode 100644 (file)
index 0000000..83cce9c
--- /dev/null
@@ -0,0 +1,144 @@
+__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
+
+import threading
+import time
+import platform
+import os
+import sys
+import subprocess
+
+from Cura.util import machineCom
+from Cura.util.printerConnection import printerConnectionBase
+
+class serialConnectionGroup(printerConnectionBase.printerConnectionGroup):
+       def __init__(self):
+               super(serialConnectionGroup, self).__init__("USB")
+               self._connectionMap = {}
+
+       def getAvailableConnections(self):
+               serialList = machineCom.serialList(True)
+               for port in machineCom.serialList(True):
+                       if port not in self._connectionMap:
+                               self._connectionMap[port] = serialConnection(port)
+               for key in self._connectionMap.keys():
+                       if key not in serialList and not self._connectionMap[key].isActiveConnectionOpen():
+                               self._connectionMap[key] = None
+               return self._connectionMap.values()
+
+       def getIconID(self):
+               return 6
+
+       def getPriority(self):
+               return 50
+
+class serialConnection(printerConnectionBase.printerConnectionBase):
+       def __init__(self, port):
+               super(serialConnection, self).__init__(port)
+               self._portName = port
+
+               self._process = None
+               self._thread = None
+
+               self._lineCount = 0
+               self._commState = None
+               self._commStateString = None
+               self._gcodeData = []
+
+       #Load the data into memory for printing, returns True on success
+       def loadGCodeData(self, dataStream):
+               if self.isPrinting() is None:
+                       return False
+               self._gcodeData = []
+               for line in dataStream:
+                       #Strip out comments, we do not need to send comments
+                       if ';' in line:
+                               line = line[:line.index(';')]
+                       #Strip out whitespace at the beginning/end this saves data to send.
+                       line = line.strip()
+
+                       if len(line) < 1:
+                               continue
+                       self._gcodeData.append(line)
+               return True
+
+       #Start printing the previously loaded file
+       def startPrint(self):
+               if self.isPrinting() or len(self._gcodeData) < 1 or self._process is None:
+                       return
+               self._process.stdin.write('STOP\n')
+               for line in self._gcodeData:
+                       self._process.stdin.write('G:%s\n' % (line))
+               self._process.stdin.write('START\n')
+
+       #Abort the previously loaded print file
+       def cancelPrint(self):
+               pass
+
+       def isPrinting(self):
+               return self._commState == machineCom.MachineCom.STATE_PRINTING
+
+       #Amount of progression of the current print file. 0.0 to 1.0
+       def getPrintProgress(self):
+               if self._lineCount < 1:
+                       return 0.0
+               return float(self._progressLine) / float(self._lineCount)
+
+       # Return if the printer with this connection type is available
+       def isAvailable(self):
+               return True
+
+       # Get the connection status string. This is displayed to the user and can be used to communicate
+       #  various information to the user.
+       def getStatusString(self):
+               return "%s" % (self._commStateString)
+
+       #Returns true if we need to establish an active connection. True for serial connections.
+       def hasActiveConnection(self):
+               return True
+
+       #Open the active connection to the printer so we can send commands
+       def openActiveConnection(self):
+               self.closeActiveConnection()
+               self._thread = threading.Thread(target=self._serialCommunicationThread)
+               self._thread.daemon = True
+               self._thread.start()
+
+       #Close the active connection to the printer
+       def closeActiveConnection(self):
+               if self._process is not None:
+                       self._process.terminate()
+                       self._thread.join()
+
+       #Is the active connection open right now.
+       def isActiveConnectionOpen(self):
+               if self._process is None:
+                       return False
+               return self._commState == machineCom.MachineCom.STATE_OPERATIONAL or self._commState == machineCom.MachineCom.STATE_PRINTING or self._commState == machineCom.MachineCom.STATE_PAUSED
+
+       def _serialCommunicationThread(self):
+               if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
+                       cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura')]
+               else:
+                       cmdList = [sys.executable, '-m', 'Cura.serialCommunication']
+               cmdList += [self._portName]
+               if platform.system() == "Darwin":
+                       if platform.machine() == 'i386':
+                               cmdList = ['arch', '-i386'] + cmdList
+               self._process = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+               line = self._process.stdout.readline()
+               while len(line) > 0:
+                       line = line.strip()
+                       line = line.split(':', 1)
+                       if line[0] == 'log':
+                               pass
+                       elif line[0] == 'temp':
+                               pass
+                       elif line[0] == 'state':
+                               line = line[1].split(':', 1)
+                               self._commState = int(line[0])
+                               self._commStateString = line[1]
+                               self._doCallback()
+                       else:
+                               print line
+                       line = self._process.stdout.readline()
+               self._process = None
index c35bad02585fb368f4889287cd84ae39a5da65ea..12388e76ca2d5f30bbcdf2d0a2087c872b40d782 100644 (file)
@@ -257,7 +257,7 @@ class Engine(object):
 
                extruderCount = max(extruderCount, profile.minimalExtruderCount())
 
-               commandList = [getEngineFilename(), '-vvv']
+               commandList = [getEngineFilename(), '-v', '-p']
                for k, v in self._engineSettings(extruderCount).iteritems():
                        commandList += ['-s', '%s=%s' % (k, str(v))]
                commandList += ['-g', '%d' % self._serverPortNr]