chiark / gitweb /
Add layer slider. Solve the issue where loading a new file takes a long time and...
[cura.git] / Cura / util / gcodeInterpreter.py
1 from __future__ import absolute_import
2
3 import sys
4 import math
5 import re
6 import os
7
8 from Cura.util import util3d
9 from Cura.util import profile
10
11 class gcodePath(object):
12         def __init__(self, newType, pathType, layerThickness, startPoint):
13                 self.type = newType
14                 self.pathType = pathType
15                 self.layerThickness = layerThickness
16                 self.list = [startPoint]
17
18 class gcode(object):
19         def __init__(self):
20                 self.regMatch = {}
21                 self.layerList = []
22                 self.extrusionAmount = 0
23                 self.totalMoveTimeMinute = 0
24                 self.progressCallback = None
25         
26         def load(self, filename):
27                 if os.path.isfile(filename):
28                         self._fileSize = os.stat(filename).st_size
29                         gcodeFile = open(filename, 'r')
30                         self._load(gcodeFile)
31                         gcodeFile.close()
32         
33         def loadList(self, l):
34                 self._load(l)
35         
36         def calculateWeight(self):
37                 #Calculates the weight of the filament in kg
38                 radius = float(profile.getProfileSetting('filament_diameter')) / 2
39                 volumeM3 = (self.extrusionAmount * (math.pi * radius * radius)) / (1000*1000*1000)
40                 return volumeM3 * profile.getPreferenceFloat('filament_density')
41         
42         def calculateCost(self):
43                 cost_kg = profile.getPreferenceFloat('filament_cost_kg')
44                 cost_meter = profile.getPreferenceFloat('filament_cost_meter')
45                 if cost_kg > 0.0 and cost_meter > 0.0:
46                         return "%.2f / %.2f" % (self.calculateWeight() * cost_kg, self.extrusionAmount / 1000 * cost_meter)
47                 elif cost_kg > 0.0:
48                         return "%.2f" % (self.calculateWeight() * cost_kg)
49                 elif cost_meter > 0.0:
50                         return "%.2f" % (self.extrusionAmount / 1000 * cost_meter)
51                 return None
52         
53         def _load(self, gcodeFile):
54                 filePos = 0
55                 pos = util3d.Vector3()
56                 posOffset = util3d.Vector3()
57                 currentE = 0.0
58                 totalExtrusion = 0.0
59                 maxExtrusion = 0.0
60                 currentExtruder = 0
61                 extrudeAmountMultiply = 1.0
62                 totalMoveTimeMinute = 0.0
63                 absoluteE = True
64                 scale = 1.0
65                 posAbs = True
66                 feedRate = 3600
67                 layerThickness = 0.1
68                 pathType = 'CUSTOM';
69                 startCodeDone = False
70                 currentLayer = []
71                 currentPath = gcodePath('move', pathType, layerThickness, pos.copy())
72                 currentPath.list[0].e = totalExtrusion
73                 currentPath.list[0].extrudeAmountMultiply = extrudeAmountMultiply
74                 currentLayer.append(currentPath)
75                 for line in gcodeFile:
76                         if type(line) is tuple:
77                                 line = line[0]
78
79                         #Parse Cura_SF comments
80                         if line.startswith(';TYPE:'):
81                                 pathType = line[6:].strip()
82                                 if pathType != "CUSTOM":
83                                         startCodeDone = True
84                                         
85                         if ';' in line:
86                                 #Slic3r GCode comment parser
87                                 comment = line[line.find(';')+1:].strip()
88                                 if comment == 'fill':
89                                         pathType = 'FILL'
90                                 elif comment == 'perimeter':
91                                         pathType = 'WALL-INNER'
92                                 elif comment == 'skirt':
93                                         pathType = 'SKIRT'
94                                 if comment.startswith('LAYER:'):
95                                         self.layerList.append(currentLayer)
96                                         if self.progressCallback is not None:
97                                                 if filePos != gcodeFile.tell():
98                                                         filePos = gcodeFile.tell()
99                                                         if self.progressCallback(float(filePos) / float(self._fileSize)):
100                                                                 #Abort the loading, we can safely return as the results here will be discarded
101                                                                 gcodeFile.close()
102                                                                 return
103                                         currentLayer = []
104                                 if pathType != "CUSTOM":
105                                         startCodeDone = True
106                                 line = line[0:line.find(';')]
107                         T = self.getCodeInt(line, 'T')
108                         if T is not None:
109                                 if currentExtruder > 0:
110                                         posOffset.x -= profile.getPreferenceFloat('extruder_offset_x%d' % (currentExtruder))
111                                         posOffset.y -= profile.getPreferenceFloat('extruder_offset_y%d' % (currentExtruder))
112                                 currentExtruder = T
113                                 if currentExtruder > 0:
114                                         posOffset.x += profile.getPreferenceFloat('extruder_offset_x%d' % (currentExtruder))
115                                         posOffset.y += profile.getPreferenceFloat('extruder_offset_y%d' % (currentExtruder))
116                         
117                         G = self.getCodeInt(line, 'G')
118                         if G is not None:
119                                 if G == 0 or G == 1:    #Move
120                                         x = self.getCodeFloat(line, 'X')
121                                         y = self.getCodeFloat(line, 'Y')
122                                         z = self.getCodeFloat(line, 'Z')
123                                         e = self.getCodeFloat(line, 'E')
124                                         f = self.getCodeFloat(line, 'F')
125                                         oldPos = pos.copy()
126                                         if x is not None:
127                                                 if posAbs:
128                                                         pos.x = x * scale + posOffset.x
129                                                 else:
130                                                         pos.x += x * scale
131                                         if y is not None:
132                                                 if posAbs:
133                                                         pos.y = y * scale + posOffset.y
134                                                 else:
135                                                         pos.y += y * scale
136                                         if z is not None:
137                                                 if posAbs:
138                                                         pos.z = z * scale + posOffset.z
139                                                 else:
140                                                         pos.z += z * scale
141                                         if f is not None:
142                                                 feedRate = f
143                                         if x is not None or y is not None or z is not None:
144                                                 totalMoveTimeMinute += (oldPos - pos).vsize() / feedRate
145                                         moveType = 'move'
146                                         if e is not None:
147                                                 if not absoluteE:
148                                                         e += currentE
149                                                 if posAbs:
150                                                         if e > currentE:
151                                                                 moveType = 'extrude'
152                                                         if e < currentE:
153                                                                 moveType = 'retract'
154                                                 else:
155                                                         if e > 0:
156                                                                 moveType = 'extrude'
157                                                         if e < 0:
158                                                                 moveType = 'retract'
159                                                 totalExtrusion += e - currentE
160                                                 currentE = e
161                                                 if totalExtrusion > maxExtrusion:
162                                                         maxExtrusion = totalExtrusion
163                                         if moveType == 'move' and oldPos.z != pos.z:
164                                                 if oldPos.z > pos.z and abs(oldPos.z - pos.z) > 5.0 and pos.z < 1.0:
165                                                         oldPos.z = 0.0
166                                                 layerThickness = abs(oldPos.z - pos.z)
167                                         if currentPath.type != moveType or currentPath.pathType != pathType:
168                                                 currentPath = gcodePath(moveType, pathType, layerThickness, currentPath.list[-1])
169                                                 currentLayer.append(currentPath)
170                                         newPos = pos.copy()
171                                         newPos.e = totalExtrusion
172                                         newPos.extrudeAmountMultiply = extrudeAmountMultiply
173                                         currentPath.list.append(newPos)
174                                 elif G == 4:    #Delay
175                                         S = self.getCodeFloat(line, 'S')
176                                         if S is not None:
177                                                 totalMoveTimeMinute += S / 60
178                                         P = self.getCodeFloat(line, 'P')
179                                         if P is not None:
180                                                 totalMoveTimeMinute += P / 60 / 1000
181                                 elif G == 20:   #Units are inches
182                                         scale = 25.4
183                                 elif G == 21:   #Units are mm
184                                         scale = 1.0
185                                 elif G == 28:   #Home
186                                         x = self.getCodeFloat(line, 'X')
187                                         y = self.getCodeFloat(line, 'Y')
188                                         z = self.getCodeFloat(line, 'Z')
189                                         if x is None and y is None and z is None:
190                                                 pos = util3d.Vector3()
191                                         else:
192                                                 if x is not None:
193                                                         pos.x = 0.0
194                                                 if y is not None:
195                                                         pos.y = 0.0
196                                                 if z is not None:
197                                                         pos.z = 0.0
198                                 elif G == 90:   #Absolute position
199                                         posAbs = True
200                                 elif G == 91:   #Relative position
201                                         posAbs = False
202                                 elif G == 92:
203                                         x = self.getCodeFloat(line, 'X')
204                                         y = self.getCodeFloat(line, 'Y')
205                                         z = self.getCodeFloat(line, 'Z')
206                                         e = self.getCodeFloat(line, 'E')
207                                         if e is not None:
208                                                 currentE = e
209                                         if x is not None:
210                                                 posOffset.x = pos.x - x
211                                         if y is not None:
212                                                 posOffset.y = pos.y - y
213                                         if z is not None:
214                                                 posOffset.z = pos.z - z
215                                 else:
216                                         print "Unknown G code:" + str(G)
217                         else:
218                                 M = self.getCodeInt(line, 'M')
219                                 if M is not None:
220                                         if M == 1:      #Message with possible wait (ignored)
221                                                 pass
222                                         elif M == 80:   #Enable power supply
223                                                 pass
224                                         elif M == 81:   #Suicide/disable power supply
225                                                 pass
226                                         elif M == 82:   #Absolute E
227                                                 absoluteE = True
228                                         elif M == 83:   #Relative E
229                                                 absoluteE = False
230                                         elif M == 84:   #Disable step drivers
231                                                 pass
232                                         elif M == 92:   #Set steps per unit
233                                                 pass
234                                         elif M == 101:  #Enable extruder
235                                                 pass
236                                         elif M == 103:  #Disable extruder
237                                                 pass
238                                         elif M == 104:  #Set temperature, no wait
239                                                 pass
240                                         elif M == 105:  #Get temperature
241                                                 pass
242                                         elif M == 106:  #Enable fan
243                                                 pass
244                                         elif M == 107:  #Disable fan
245                                                 pass
246                                         elif M == 108:  #Extruder RPM (these should not be in the final GCode, but they are)
247                                                 pass
248                                         elif M == 109:  #Set temperature, wait
249                                                 pass
250                                         elif M == 110:  #Reset N counter
251                                                 pass
252                                         elif M == 113:  #Extruder PWM (these should not be in the final GCode, but they are)
253                                                 pass
254                                         elif M == 140:  #Set bed temperature
255                                                 pass
256                                         elif M == 190:  #Set bed temperature & wait
257                                                 pass
258                                         elif M == 221:  #Extrude amount multiplier
259                                                 s = self.getCodeFloat(line, 'S')
260                                                 if s != None:
261                                                         extrudeAmountMultiply = s / 100.0
262                                         else:
263                                                 print "Unknown M code:" + str(M)
264                 self.layerList.append(currentLayer)
265                 self.extrusionAmount = maxExtrusion
266                 self.totalMoveTimeMinute = totalMoveTimeMinute
267                 #print "Extruded a total of: %d mm of filament" % (self.extrusionAmount)
268                 #print "Estimated print duration: %.2f minutes" % (self.totalMoveTimeMinute)
269
270         def getCodeInt(self, line, code):
271                 if code not in self.regMatch:
272                         self.regMatch[code] = re.compile(code + '([^\s]+)')
273                 m = self.regMatch[code].search(line)
274                 if m == None:
275                         return None
276                 try:
277                         return int(m.group(1))
278                 except:
279                         return None
280
281         def getCodeFloat(self, line, code):
282                 if code not in self.regMatch:
283                         self.regMatch[code] = re.compile(code + '([^\s]+)')
284                 m = self.regMatch[code].search(line)
285                 if m == None:
286                         return None
287                 try:
288                         return float(m.group(1))
289                 except:
290                         return None
291
292 if __name__ == '__main__':
293         for filename in sys.argv[1:]:
294                 gcode().load(filename)
295