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