X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=Cura%2Futil%2FgcodeInterpreter.py;h=1a9ee094916fa39adc62674f2a6dd331025b0816;hb=6208201bafdeaf82883181471c6da3a41283cfe7;hp=a4a3de4d71b14822fe76ea8328d375aca3c28f26;hpb=a4a02ec3d138c829f3f7c2a8de9801a46482a288;p=cura.git diff --git a/Cura/util/gcodeInterpreter.py b/Cura/util/gcodeInterpreter.py index a4a3de4d..1a9ee094 100644 --- a/Cura/util/gcodeInterpreter.py +++ b/Cura/util/gcodeInterpreter.py @@ -1,44 +1,50 @@ from __future__ import absolute_import +__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License" import sys import math -import re import os import time +import numpy +import types +import cStringIO as StringIO -from Cura.util import util3d from Cura.util import profile -class gcodePath(object): - def __init__(self, newType, pathType, layerThickness, startPoint): - self.type = newType - self.pathType = pathType - self.layerThickness = layerThickness - self.list = [startPoint] +def gcodePath(newType, pathType, layerThickness, startPoint): + return {'type': newType, + 'pathType': pathType, + 'layerThickness': layerThickness, + 'points': [startPoint], + 'extrusion': [0.0]} class gcode(object): def __init__(self): self.regMatch = {} - self.layerList = [] + self.layerList = None self.extrusionAmount = 0 - self.totalMoveTimeMinute = 0 + self.filename = None self.progressCallback = None - def load(self, filename): - if os.path.isfile(filename): - self._fileSize = os.stat(filename).st_size - gcodeFile = open(filename, 'r') + def load(self, data): + self.filename = None + if type(data) in types.StringTypes and os.path.isfile(data): + self.filename = data + self._fileSize = os.stat(data).st_size + gcodeFile = open(data, 'r') self._load(gcodeFile) gcodeFile.close() - - def loadList(self, l): - self._load(l) - + elif type(data) is list: + self._load(data) + else: + self._fileSize = len(data.getvalue()) + self._load(StringIO.StringIO(data.getvalue())) + def calculateWeight(self): #Calculates the weight of the filament in kg radius = float(profile.getProfileSetting('filament_diameter')) / 2 volumeM3 = (self.extrusionAmount * (math.pi * radius * radius)) / (1000*1000*1000) - return volumeM3 * profile.getPreferenceFloat('filament_density') + return volumeM3 * profile.getPreferenceFloat('filament_physical_density') def calculateCost(self): cost_kg = profile.getPreferenceFloat('filament_cost_kg') @@ -52,24 +58,23 @@ class gcode(object): return None def _load(self, gcodeFile): - pos = util3d.Vector3() - posOffset = util3d.Vector3() + self.layerList = [] + pos = [0.0,0.0,0.0] + posOffset = [0.0, 0.0, 0.0] currentE = 0.0 - totalExtrusion = 0.0 - maxExtrusion = 0.0 currentExtruder = 0 extrudeAmountMultiply = 1.0 - totalMoveTimeMinute = 0.0 absoluteE = True scale = 1.0 posAbs = True - feedRate = 3600 + feedRate = 3600.0 + moveType = 'move' layerThickness = 0.1 - pathType = 'CUSTOM'; + pathType = 'CUSTOM' currentLayer = [] - currentPath = gcodePath('move', pathType, layerThickness, pos.copy()) - currentPath.list[0].e = totalExtrusion - currentPath.list[0].extrudeAmountMultiply = extrudeAmountMultiply + currentPath = gcodePath('move', pathType, layerThickness, pos) + currentPath['extruder'] = currentExtruder + currentLayer.append(currentPath) for line in gcodeFile: if type(line) is tuple: @@ -89,131 +94,132 @@ class gcode(object): elif comment == 'skirt': pathType = 'SKIRT' if comment.startswith('LAYER:'): + currentPath = gcodePath(moveType, pathType, layerThickness, currentPath['points'][-1]) + currentPath['extruder'] = currentExtruder + for path in currentLayer: + path['points'] = numpy.array(path['points'], numpy.float32) + path['extrusion'] = numpy.array(path['extrusion'], numpy.float32) self.layerList.append(currentLayer) if self.progressCallback is not None: if self.progressCallback(float(gcodeFile.tell()) / float(self._fileSize)): #Abort the loading, we can safely return as the results here will be discarded gcodeFile.close() return - currentLayer = [] + currentLayer = [currentPath] line = line[0:line.find(';')] - T = self.getCodeInt(line, 'T') + T = getCodeInt(line, 'T') if T is not None: if currentExtruder > 0: - posOffset.x -= profile.getPreferenceFloat('extruder_offset_x%d' % (currentExtruder)) - posOffset.y -= profile.getPreferenceFloat('extruder_offset_y%d' % (currentExtruder)) + posOffset[0] -= profile.getMachineSettingFloat('extruder_offset_x%d' % (currentExtruder)) + posOffset[1] -= profile.getMachineSettingFloat('extruder_offset_y%d' % (currentExtruder)) currentExtruder = T if currentExtruder > 0: - posOffset.x += profile.getPreferenceFloat('extruder_offset_x%d' % (currentExtruder)) - posOffset.y += profile.getPreferenceFloat('extruder_offset_y%d' % (currentExtruder)) + posOffset[0] += profile.getMachineSettingFloat('extruder_offset_x%d' % (currentExtruder)) + posOffset[1] += profile.getMachineSettingFloat('extruder_offset_y%d' % (currentExtruder)) - G = self.getCodeInt(line, 'G') + G = getCodeInt(line, 'G') if G is not None: if G == 0 or G == 1: #Move - x = self.getCodeFloat(line, 'X') - y = self.getCodeFloat(line, 'Y') - z = self.getCodeFloat(line, 'Z') - e = self.getCodeFloat(line, 'E') - f = self.getCodeFloat(line, 'F') - oldPos = pos.copy() - if x is not None: - if posAbs: - pos.x = x * scale + posOffset.x - else: - pos.x += x * scale - if y is not None: - if posAbs: - pos.y = y * scale + posOffset.y - else: - pos.y += y * scale - if z is not None: - if posAbs: - pos.z = z * scale + posOffset.z - else: - pos.z += z * scale - if f is not None: - feedRate = f - if x is not None or y is not None or z is not None: - totalMoveTimeMinute += (oldPos - pos).vsize() / feedRate + x = getCodeFloat(line, 'X') + y = getCodeFloat(line, 'Y') + z = getCodeFloat(line, 'Z') + e = getCodeFloat(line, 'E') + #f = getCodeFloat(line, 'F') + oldPos = pos + pos = pos[:] + if posAbs: + if x is not None: + pos[0] = x * scale + posOffset[0] + if y is not None: + pos[1] = y * scale + posOffset[1] + if z is not None: + pos[2] = z * scale + posOffset[2] + else: + if x is not None: + pos[0] += x * scale + if y is not None: + pos[1] += y * scale + if z is not None: + pos[2] += z * scale moveType = 'move' if e is not None: - if not absoluteE: - e += currentE - if posAbs: - if e > currentE: - moveType = 'extrude' - if e < currentE: - moveType = 'retract' - else: - if e > 0: - moveType = 'extrude' - if e < 0: - moveType = 'retract' - totalExtrusion += e - currentE - currentE = e - if totalExtrusion > maxExtrusion: - maxExtrusion = totalExtrusion - if moveType == 'move' and oldPos.z != pos.z: - if oldPos.z > pos.z and abs(oldPos.z - pos.z) > 5.0 and pos.z < 1.0: - oldPos.z = 0.0 - layerThickness = abs(oldPos.z - pos.z) - if currentPath.type != moveType or currentPath.pathType != pathType: - currentPath = gcodePath(moveType, pathType, layerThickness, currentPath.list[-1]) + if absoluteE: + e -= currentE + if e > 0.0: + moveType = 'extrude' + if e < 0.0: + moveType = 'retract' + currentE += e + else: + e = 0.0 + if moveType == 'move' and oldPos[2] != pos[2]: + if oldPos[2] > pos[2] and abs(oldPos[2] - pos[2]) > 5.0 and pos[2] < 1.0: + oldPos[2] = 0.0 + layerThickness = abs(oldPos[2] - pos[2]) + if currentPath['type'] != moveType or currentPath['pathType'] != pathType: + currentPath = gcodePath(moveType, pathType, layerThickness, currentPath['points'][-1]) + currentPath['extruder'] = currentExtruder currentLayer.append(currentPath) - newPos = pos.copy() - newPos.e = totalExtrusion - newPos.extrudeAmountMultiply = extrudeAmountMultiply - currentPath.list.append(newPos) + + currentPath['points'].append(pos) + currentPath['extrusion'].append(e * extrudeAmountMultiply) elif G == 4: #Delay - S = self.getCodeFloat(line, 'S') - if S is not None: - totalMoveTimeMinute += S / 60 - P = self.getCodeFloat(line, 'P') - if P is not None: - totalMoveTimeMinute += P / 60 / 1000 + S = getCodeFloat(line, 'S') + P = getCodeFloat(line, 'P') + elif G == 10: #Retract + currentPath = gcodePath('retract', pathType, layerThickness, currentPath['points'][-1]) + currentPath['extruder'] = currentExtruder + currentLayer.append(currentPath) + currentPath['points'].append(currentPath['points'][0]) + elif G == 11: #Push back after retract + pass elif G == 20: #Units are inches scale = 25.4 elif G == 21: #Units are mm scale = 1.0 elif G == 28: #Home - x = self.getCodeFloat(line, 'X') - y = self.getCodeFloat(line, 'Y') - z = self.getCodeFloat(line, 'Z') + x = getCodeFloat(line, 'X') + y = getCodeFloat(line, 'Y') + z = getCodeFloat(line, 'Z') + center = [0.0,0.0,0.0] if x is None and y is None and z is None: - pos = util3d.Vector3() + pos = center else: + pos = pos[:] if x is not None: - pos.x = 0.0 + pos[0] = center[0] if y is not None: - pos.y = 0.0 + pos[1] = center[1] if z is not None: - pos.z = 0.0 + pos[2] = center[2] elif G == 90: #Absolute position posAbs = True elif G == 91: #Relative position posAbs = False elif G == 92: - x = self.getCodeFloat(line, 'X') - y = self.getCodeFloat(line, 'Y') - z = self.getCodeFloat(line, 'Z') - e = self.getCodeFloat(line, 'E') + x = getCodeFloat(line, 'X') + y = getCodeFloat(line, 'Y') + z = getCodeFloat(line, 'Z') + e = getCodeFloat(line, 'E') if e is not None: currentE = e if x is not None: - posOffset.x = pos.x - x + posOffset[0] = pos[0] - x if y is not None: - posOffset.y = pos.y - y + posOffset[1] = pos[1] - y if z is not None: - posOffset.z = pos.z - z + posOffset[2] = pos[2] - z else: print "Unknown G code:" + str(G) else: - M = self.getCodeInt(line, 'M') + M = getCodeInt(line, 'M') if M is not None: if M == 0: #Message with possible wait (ignored) pass elif M == 1: #Message with possible wait (ignored) pass + elif M == 25: #Stop SD printing + pass elif M == 80: #Enable power supply pass elif M == 81: #Suicide/disable power supply @@ -253,48 +259,46 @@ class gcode(object): elif M == 190: #Set bed temperature & wait pass elif M == 221: #Extrude amount multiplier - s = self.getCodeFloat(line, 'S') - if s != None: + s = getCodeFloat(line, 'S') + if s is not None: extrudeAmountMultiply = s / 100.0 else: print "Unknown M code:" + str(M) + for path in currentLayer: + path['points'] = numpy.array(path['points'], numpy.float32) + path['extrusion'] = numpy.array(path['extrusion'], numpy.float32) self.layerList.append(currentLayer) if self.progressCallback is not None and self._fileSize > 0: self.progressCallback(float(gcodeFile.tell()) / float(self._fileSize)) - self.extrusionAmount = maxExtrusion - self.totalMoveTimeMinute = totalMoveTimeMinute - #print "Extruded a total of: %d mm of filament" % (self.extrusionAmount) - #print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute) - def getCodeInt(self, line, code): - n = line.find(code) + 1 - if n < 1: - return None - m = line.find(' ', n) - try: - if m < 0: - return int(line[n:]) - return int(line[n:m]) - except: - return None +def getCodeInt(line, code): + n = line.find(code) + 1 + if n < 1: + return None + m = line.find(' ', n) + try: + if m < 0: + return int(line[n:]) + return int(line[n:m]) + except: + return None - def getCodeFloat(self, line, code): - n = line.find(code) + 1 - if n < 1: - return None - m = line.find(' ', n) - try: - if m < 0: - return float(line[n:]) - return float(line[n:m]) - except: - return None +def getCodeFloat(line, code): + n = line.find(code) + 1 + if n < 1: + return None + m = line.find(' ', n) + try: + if m < 0: + return float(line[n:]) + return float(line[n:m]) + except: + return None if __name__ == '__main__': t = time.time() for filename in sys.argv[1:]: g = gcode() g.load(filename) - print g.totalMoveTimeMinute print time.time() - t