chiark / gitweb /
Merge branch 'master' of github.com:daid/Cura
[cura.git] / Cura / util / gcodeInterpreter.py
1 from __future__ import absolute_import
2 import __init__
3
4 import sys
5 import math
6 import re
7 import os
8
9 from util import util3d
10
11 class gcodePath():
12         def __init__(self, newType, pathType, startPoint):
13                 self.type = newType
14                 self.pathType = pathType
15                 self.list = [startPoint]
16
17 class gcode():
18         def __init__(self):
19                 self.regMatch = {}
20                 self.layerList = []
21                 self.extrusionAmount = 0
22                 self.totalMoveTimeMinute = 0
23                 self.progressCallback = None
24         
25         def load(self, filename):
26                 fileSize = os.stat(filename).st_size
27                 filePos = 0
28                 gcodeFile = open(filename, 'r')
29                 pos = util3d.Vector3()
30                 posOffset = util3d.Vector3()
31                 currentE = 0.0
32                 totalExtrusion = 0.0
33                 maxExtrusion = 0.0
34                 totalMoveTimeMinute = 0.0
35                 scale = 1.0
36                 posAbs = True
37                 feedRate = 3600
38                 pathType = 'CUSTOM';
39                 startCodeDone = False
40                 currentLayer = []
41                 currentPath = gcodePath('move', pathType, pos.copy())
42                 currentPath.list[0].e = totalExtrusion
43                 currentLayer.append(currentPath)
44                 for line in gcodeFile:
45                         if filePos != gcodeFile.tell():
46                                 filePos = gcodeFile.tell()
47                                 if self.progressCallback != None:
48                                         self.progressCallback(float(filePos) / float(fileSize))
49                         
50                         #Parse Cura_SF comments
51                         if line.startswith(';TYPE:'):
52                                 pathType = line[6:].strip()
53                                 if pathType != "CUSTOM":
54                                         startCodeDone = True
55                                         
56                         if ';' in line:
57                                 #Slic3r GCode comment parser
58                                 comment = line[line.find(';')+1:].strip()
59                                 if comment == 'fill':
60                                         pathType = 'FILL'
61                                 elif comment == 'perimeter':
62                                         pathType = 'WALL-INNER'
63                                 elif comment == 'skirt':
64                                         pathType = 'SKIRT'
65                                 if pathType != "CUSTOM":
66                                         startCodeDone = True
67                                 line = line[0:line.find(';')]
68                         
69                         G = self.getCodeInt(line, 'G')
70                         if G is not None:
71                                 if G == 0 or G == 1:    #Move
72                                         x = self.getCodeFloat(line, 'X')
73                                         y = self.getCodeFloat(line, 'Y')
74                                         z = self.getCodeFloat(line, 'Z')
75                                         e = self.getCodeFloat(line, 'E')
76                                         f = self.getCodeFloat(line, 'F')
77                                         oldPos = pos.copy()
78                                         if x is not None:
79                                                 if posAbs:
80                                                         pos.x = x * scale
81                                                 else:
82                                                         pos.x += x * scale
83                                         if y is not None:
84                                                 if posAbs:
85                                                         pos.y = y * scale
86                                                 else:
87                                                         pos.y += y * scale
88                                         if z is not None:
89                                                 if posAbs:
90                                                         pos.z = z * scale
91                                                 else:
92                                                         pos.z += z * scale
93                                                 #Check if we have a new layer.
94                                                 if oldPos.z != pos.z and startCodeDone:
95                                                         self.layerList.append(currentLayer)
96                                                         currentLayer = []
97                                         if f is not None:
98                                                 feedRate = f
99                                         if x is not None or y is not None or z is not None:
100                                                 totalMoveTimeMinute += (oldPos - pos).vsize() / feedRate
101                                         moveType = 'move'
102                                         if e is not None:
103                                                 if posAbs:
104                                                         if e > currentE:
105                                                                 moveType = 'extrude'
106                                                         if e < currentE:
107                                                                 moveType = 'retract'
108                                                         totalExtrusion += e - currentE
109                                                         currentE = e
110                                                 else:
111                                                         if e > 0:
112                                                                 moveType = 'extrude'
113                                                         if e < 0:
114                                                                 moveType = 'retract'
115                                                         totalExtrusion += e
116                                                         currentE += e
117                                                 if totalExtrusion > maxExtrusion:
118                                                         maxExtrusion = totalExtrusion
119                                         if currentPath.type != moveType or currentPath.pathType != pathType:
120                                                 currentPath = gcodePath(moveType, pathType, currentPath.list[-1])
121                                                 currentLayer.append(currentPath)
122                                         newPos = pos.copy()
123                                         newPos.e = totalExtrusion
124                                         currentPath.list.append(newPos)
125                                 elif G == 20:   #Units are inches
126                                         scale = 25.4
127                                 elif G == 21:   #Units are mm
128                                         scale = 1.0
129                                 elif G == 28:   #Home
130                                         x = self.getCodeFloat(line, 'X')
131                                         y = self.getCodeFloat(line, 'Y')
132                                         z = self.getCodeFloat(line, 'Z')
133                                         if x is None and y is None and z is None:
134                                                 pos = util3d.Vector3()
135                                         else:
136                                                 if x is not None:
137                                                         pos.x = 0.0
138                                                 if y is not None:
139                                                         pos.y = 0.0
140                                                 if z is not None:
141                                                         pos.z = 0.0
142                                 elif G == 90:   #Absolute position
143                                         posAbs = True
144                                 elif G == 91:   #Relative position
145                                         posAbs = False
146                                 elif G == 92:
147                                         x = self.getCodeFloat(line, 'X')
148                                         y = self.getCodeFloat(line, 'Y')
149                                         z = self.getCodeFloat(line, 'Z')
150                                         e = self.getCodeFloat(line, 'E')
151                                         if e is not None:
152                                                 currentE = e
153                                         if x is not None:
154                                                 posOffset.x = pos.x + x
155                                         if y is not None:
156                                                 posOffset.y = pos.y + y
157                                         if z is not None:
158                                                 posOffset.z = pos.z + z
159                                 else:
160                                         print "Unknown G code:" + str(G)
161                         else:
162                                 M = self.getCodeInt(line, 'M')
163                                 if M is not None:
164                                         if M == 1:      #Message with possible wait (ignored)
165                                                 pass
166                                         elif M == 84:   #Disable step drivers
167                                                 pass
168                                         elif M == 92:   #Set steps per unit
169                                                 pass
170                                         elif M == 104:  #Set temperature, no wait
171                                                 pass
172                                         elif M == 105:  #Get temperature
173                                                 pass
174                                         elif M == 106:  #Enable fan
175                                                 pass
176                                         elif M == 107:  #Disable fan
177                                                 pass
178                                         elif M == 108:  #Extruder RPM (these should not be in the final GCode, but they are)
179                                                 pass
180                                         elif M == 109:  #Set temperature, wait
181                                                 pass
182                                         elif M == 113:  #Extruder PWM (these should not be in the final GCode, but they are)
183                                                 pass
184                                         else:
185                                                 print "Unknown M code:" + str(M)
186                 gcodeFile.close()
187                 self.layerList.append(currentLayer)
188                 self.extrusionAmount = maxExtrusion
189                 self.totalMoveTimeMinute = totalMoveTimeMinute
190                 print "Extruded a total of: %d mm of filament" % (self.extrusionAmount)
191                 print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute)
192
193         def getCodeInt(self, line, code):
194                 if code not in self.regMatch:
195                         self.regMatch[code] = re.compile(code + '([^\s]+)')
196                 m = self.regMatch[code].search(line)
197                 if m == None:
198                         return None
199                 try:
200                         return int(m.group(1))
201                 except:
202                         return None
203
204         def getCodeFloat(self, line, code):
205                 if code not in self.regMatch:
206                         self.regMatch[code] = re.compile(code + '([^\s]+)')
207                 m = self.regMatch[code].search(line)
208                 if m == None:
209                         return None
210                 try:
211                         return float(m.group(1))
212                 except:
213                         return None
214
215 if __name__ == '__main__':
216         for filename in sys.argv[1:]:
217                 gcode().load(filename)
218