chiark / gitweb /
9b09c6dcd8bad96430c0c4d097f40b0b0cac429b
[cura.git] / Cura / util / sliceEngine.py
1 """
2 Slice engine communication.
3 This module handles all communication with the slicing engine.
4 """
5 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
6 import subprocess
7 import time
8 import math
9 import numpy
10 import os
11 import warnings
12 import threading
13 import traceback
14 import platform
15 import sys
16 import urllib
17 import urllib2
18 import hashlib
19 import socket
20 import struct
21 import cStringIO as StringIO
22
23 from Cura.util import profile
24 from Cura.util import pluginInfo
25 from Cura.util import version
26 from Cura.util import gcodeInterpreter
27
28 def getEngineFilename():
29         if platform.system() == 'Windows':
30                 if version.isDevVersion() and os.path.exists('C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'):
31                         return 'C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'
32                 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine.exe'))
33         if hasattr(sys, 'frozen'):
34                 return os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../..', 'CuraEngine'))
35         if os.path.isfile('/usr/bin/CuraEngine'):
36                 return '/usr/bin/CuraEngine'
37         if os.path.isfile('/usr/local/bin/CuraEngine'):
38                 return '/usr/local/bin/CuraEngine'
39         return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine'))
40
41 def getTempFilename():
42         warnings.simplefilter('ignore')
43         ret = os.tempnam(None, "Cura_Tmp")
44         warnings.simplefilter('default')
45         return ret
46
47 class EngineResult(object):
48         def __init__(self):
49                 self._engineLog = []
50                 self._gcodeData = StringIO.StringIO()
51                 self._polygons = []
52                 self._replaceInfo = {}
53                 self._success = False
54                 self._printTimeSeconds = None
55                 self._filamentMM = [0.0] * 4
56                 self._modelHash = None
57                 self._profileString = profile.getProfileString()
58                 self._preferencesString = profile.getPreferencesString()
59                 self._gcodeInterpreter = gcodeInterpreter.gcode()
60                 self._gcodeLoadThread = None
61                 self._finished = False
62
63         def getFilamentWeight(self, e=0):
64                 #Calculates the weight of the filament in kg
65                 radius = float(profile.getProfileSetting('filament_diameter')) / 2
66                 volumeM3 = (self._filamentMM[e] * (math.pi * radius * radius)) / (1000*1000*1000)
67                 return volumeM3 * profile.getPreferenceFloat('filament_physical_density')
68
69         def getFilamentCost(self, e=0):
70                 cost_kg = profile.getPreferenceFloat('filament_cost_kg')
71                 cost_meter = profile.getPreferenceFloat('filament_cost_meter')
72                 if cost_kg > 0.0 and cost_meter > 0.0:
73                         return "%.2f / %.2f" % (self.getFilamentWeight(e) * cost_kg, self._filamentMM[e] / 1000.0 * cost_meter)
74                 elif cost_kg > 0.0:
75                         return "%.2f" % (self.getFilamentWeight(e) * cost_kg)
76                 elif cost_meter > 0.0:
77                         return "%.2f" % (self._filamentMM[e] / 1000.0 * cost_meter)
78                 return None
79
80         def getPrintTime(self):
81                 if int(self._printTimeSeconds / 60 / 60) < 1:
82                         return '%d minutes' % (int(self._printTimeSeconds / 60) % 60)
83                 if int(self._printTimeSeconds / 60 / 60) == 1:
84                         return '%d hour %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
85                 return '%d hours %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
86
87         def getFilamentAmount(self, e=0):
88                 if self._filamentMM[e] == 0.0:
89                         return None
90                 return '%0.2f meter %0.0f gram' % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0)
91
92         def getLog(self):
93                 return self._engineLog
94
95         def getGCode(self):
96                 data = self._gcodeData.getvalue()
97                 if len(self._replaceInfo) > 0:
98                         block0 = data[0:2048]
99                         for k, v in self._replaceInfo.items():
100                                 v = (v + ' ' * len(k))[:len(k)]
101                                 block0 = block0.replace(k, v)
102                         return block0 + data[2048:]
103                 return data
104
105         def setGCode(self, gcode):
106                 self._gcodeData = StringIO.StringIO(gcode)
107                 self._replaceInfo = {}
108
109         def addLog(self, line):
110                 self._engineLog.append(line)
111
112         def setHash(self, hash):
113                 self._modelHash = hash
114
115         def setFinished(self, result):
116                 self._finished = result
117
118         def isFinished(self):
119                 return self._finished
120
121         def getGCodeLayers(self, loadCallback):
122                 if not self._finished:
123                         return None
124                 if self._gcodeInterpreter.layerList is None and self._gcodeLoadThread is None:
125                         self._gcodeInterpreter.progressCallback = self._gcodeInterpreterCallback
126                         self._gcodeLoadThread = threading.Thread(target=lambda : self._gcodeInterpreter.load(self._gcodeData))
127                         self._gcodeLoadCallback = loadCallback
128                         self._gcodeLoadThread.daemon = True
129                         self._gcodeLoadThread.start()
130                 return self._gcodeInterpreter.layerList
131
132         def _gcodeInterpreterCallback(self, progress):
133                 if len(self._gcodeInterpreter.layerList) % 5 == 0:
134                         time.sleep(0.1)
135                 return self._gcodeLoadCallback(self, progress)
136
137         def submitInfoOnline(self):
138                 if profile.getPreference('submit_slice_information') != 'True':
139                         return
140                 if version.isDevVersion():
141                         return
142                 data = {
143                         'processor': platform.processor(),
144                         'machine': platform.machine(),
145                         'platform': platform.platform(),
146                         'profile': self._profileString,
147                         'preferences': self._preferencesString,
148                         'modelhash': self._modelHash,
149                         'version': version.getVersion(),
150                 }
151                 try:
152                         f = urllib2.urlopen("http://www.youmagine.com/curastats/", data = urllib.urlencode(data), timeout = 1)
153                         f.read()
154                         f.close()
155                 except:
156                         pass
157
158 class Engine(object):
159         GUI_CMD_REQUEST_MESH = 0x01
160         GUI_CMD_SEND_POLYGONS = 0x02
161
162         def __init__(self, progressCallback):
163                 self._process = None
164                 self._thread = None
165                 self._callback = progressCallback
166                 self._progressSteps = ['inset', 'skin', 'export']
167                 self._objCount = 0
168                 self._result = None
169
170                 self._serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
171                 self._serverPortNr = 0xC20A
172                 while True:
173                         try:
174                                 self._serversocket.bind(('127.0.0.1', self._serverPortNr))
175                         except:
176                                 print "Failed to listen on port: %d" % (self._serverPortNr)
177                                 self._serverPortNr += 1
178                                 if self._serverPortNr > 0xFFFF:
179                                         print "Failed to listen on any port..."
180                                         break
181                         else:
182                                 break
183                 print 'Listening for engine communications on %d' % (self._serverPortNr)
184                 self._serversocket.listen(1)
185                 thread = threading.Thread(target=self._socketListenThread)
186                 thread.daemon = True
187                 thread.start()
188
189         def _socketListenThread(self):
190                 while True:
191                         sock, _ = self._serversocket.accept()
192                         thread = threading.Thread(target=self._socketConnectionThread, args=(sock,))
193                         thread.daemon = True
194                         thread.start()
195
196         def _socketConnectionThread(self, sock):
197                 while True:
198                         try:
199                                 data = sock.recv(4)
200                         except:
201                                 data = ''
202                         if len(data) == 0:
203                                 sock.close()
204                                 return
205                         cmd = struct.unpack('@i', data)[0]
206                         if cmd == self.GUI_CMD_REQUEST_MESH:
207                                 meshInfo = self._modelData[0]
208                                 self._modelData = self._modelData[1:]
209                                 sock.sendall(struct.pack('@i', meshInfo[0]))
210                                 sock.sendall(meshInfo[1].tostring())
211                         elif cmd == self.GUI_CMD_SEND_POLYGONS:
212                                 cnt = struct.unpack('@i', sock.recv(4))[0]
213                                 layerNr = struct.unpack('@i', sock.recv(4))[0]
214                                 z = struct.unpack('@i', sock.recv(4))[0]
215                                 z = float(z) / 1000.0
216                                 typeNameLen = struct.unpack('@i', sock.recv(4))[0]
217                                 typeName = sock.recv(typeNameLen)
218                                 while len(self._result._polygons) < layerNr + 1:
219                                         self._result._polygons.append({})
220                                 polygons = self._result._polygons[layerNr]
221                                 if typeName not in polygons:
222                                         polygons[typeName] = []
223                                 for n in xrange(0, cnt):
224                                         length = struct.unpack('@i', sock.recv(4))[0]
225                                         data = ''
226                                         while len(data) < length * 8 * 2:
227                                                 recvData = sock.recv(length * 8 * 2 - len(data))
228                                                 if len(recvData) < 1:
229                                                         return
230                                                 data += recvData
231                                         polygon2d = numpy.array(numpy.fromstring(data, numpy.int64), numpy.float32) / 1000.0
232                                         polygon2d = polygon2d.reshape((len(polygon2d) / 2, 2))
233                                         polygon = numpy.empty((len(polygon2d), 3), numpy.float32)
234                                         polygon[:,:-1] = polygon2d
235                                         polygon[:,2] = z
236                                         polygons[typeName].append(polygon)
237                         else:
238                                 print "Unknown command on socket: %x" % (cmd)
239
240         def cleanup(self):
241                 self.abortEngine()
242                 self._serversocket.close()
243
244         def abortEngine(self):
245                 if self._process is not None:
246                         try:
247                                 self._process.terminate()
248                         except:
249                                 pass
250                 if self._thread is not None:
251                         self._thread.join()
252                 self._thread = None
253
254         def wait(self):
255                 if self._thread is not None:
256                         self._thread.join()
257
258         def getResult(self):
259                 return self._result
260
261         def runEngine(self, scene):
262                 if len(scene.objects()) < 1:
263                         return
264                 extruderCount = 1
265                 for obj in scene.objects():
266                         if scene.checkPlatform(obj):
267                                 extruderCount = max(extruderCount, len(obj._meshList))
268
269                 extruderCount = max(extruderCount, profile.minimalExtruderCount())
270
271                 commandList = [getEngineFilename(), '-v', '-p']
272                 for k, v in self._engineSettings(extruderCount).iteritems():
273                         commandList += ['-s', '%s=%s' % (k, str(v))]
274                 commandList += ['-g', '%d' % (self._serverPortNr)]
275                 self._objCount = 0
276                 engineModelData = []
277                 hash = hashlib.sha512()
278                 order = scene.printOrder()
279                 if order is None:
280                         pos = numpy.array(profile.getMachineCenterCoords()) * 1000
281                         objMin = None
282                         objMax = None
283                         for obj in scene.objects():
284                                 if scene.checkPlatform(obj):
285                                         oMin = obj.getMinimum()[0:2] + obj.getPosition()
286                                         oMax = obj.getMaximum()[0:2] + obj.getPosition()
287                                         if objMin is None:
288                                                 objMin = oMin
289                                                 objMax = oMax
290                                         else:
291                                                 objMin[0] = min(oMin[0], objMin[0])
292                                                 objMin[1] = min(oMin[1], objMin[1])
293                                                 objMax[0] = max(oMax[0], objMax[0])
294                                                 objMax[1] = max(oMax[1], objMax[1])
295                         if objMin is None:
296                                 return
297                         pos += (objMin + objMax) / 2.0 * 1000
298                         commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
299
300                         vertexTotal = [0] * 4
301                         meshMax = 1
302                         for obj in scene.objects():
303                                 if scene.checkPlatform(obj):
304                                         meshMax = max(meshMax, len(obj._meshList))
305                                         for n in xrange(0, len(obj._meshList)):
306                                                 vertexTotal[n] += obj._meshList[n].vertexCount
307
308                         for n in xrange(0, meshMax):
309                                 verts = numpy.zeros((0, 3), numpy.float32)
310                                 for obj in scene.objects():
311                                         if scene.checkPlatform(obj):
312                                                 if n < len(obj._meshList):
313                                                         vertexes = (numpy.matrix(obj._meshList[n].vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
314                                                         vertexes -= obj._drawOffset
315                                                         vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
316                                                         verts = numpy.concatenate((verts, vertexes))
317                                                         hash.update(obj._meshList[n].vertexes.tostring())
318                                 engineModelData.append((vertexTotal[n], verts))
319
320                         commandList += ['$' * meshMax]
321                         self._objCount = 1
322                 else:
323                         for n in order:
324                                 obj = scene.objects()[n]
325                                 for mesh in obj._meshList:
326                                         engineModelData.append((mesh.vertexCount, mesh.vertexes))
327                                         hash.update(mesh.vertexes.tostring())
328                                 pos = obj.getPosition() * 1000
329                                 pos += numpy.array(profile.getMachineCenterCoords()) * 1000
330                                 commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
331                                 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
332                                 commandList += ['$' * len(obj._meshList)]
333                                 self._objCount += 1
334                 modelHash = hash.hexdigest()
335                 if self._objCount > 0:
336                         self._modelData = engineModelData
337                         self._thread = threading.Thread(target=self._watchProcess, args=(commandList, self._thread, modelHash))
338                         self._thread.daemon = True
339                         self._thread.start()
340
341         def _watchProcess(self, commandList, oldThread, modelHash):
342                 if oldThread is not None:
343                         if self._process is not None:
344                                 self._process.terminate()
345                         oldThread.join()
346                 self._callback(-1.0)
347                 try:
348                         self._process = self._runEngineProcess(commandList)
349                 except OSError:
350                         traceback.print_exc()
351                         return
352                 if self._thread != threading.currentThread():
353                         self._process.terminate()
354
355                 self._result = EngineResult()
356                 self._result.setHash(modelHash)
357                 self._callback(0.0)
358
359                 logThread = threading.Thread(target=self._watchStderr, args=(self._process.stderr,))
360                 logThread.daemon = True
361                 logThread.start()
362
363                 data = self._process.stdout.read(4096)
364                 while len(data) > 0:
365                         self._result._gcodeData.write(data)
366                         data = self._process.stdout.read(4096)
367
368                 returnCode = self._process.wait()
369                 logThread.join()
370                 if returnCode == 0:
371                         pluginError = pluginInfo.runPostProcessingPlugins(self._result)
372                         if pluginError is not None:
373                                 print pluginError
374                                 self._result.addLog(pluginError)
375                         self._result.setFinished(True)
376                         self._callback(1.0)
377                 else:
378                         for line in self._result.getLog():
379                                 print line
380                         self._callback(-1.0)
381                 self._process = None
382
383         def _watchStderr(self, stderr):
384                 objectNr = 0
385                 line = stderr.readline()
386                 while len(line) > 0:
387                         line = line.strip()
388                         if line.startswith('Progress:'):
389                                 line = line.split(':')
390                                 if line[1] == 'process':
391                                         objectNr += 1
392                                 elif line[1] in self._progressSteps:
393                                         progressValue = float(line[2]) / float(line[3])
394                                         progressValue /= len(self._progressSteps)
395                                         progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
396
397                                         progressValue /= self._objCount
398                                         progressValue += 1.0 / self._objCount * objectNr
399                                         try:
400                                                 self._callback(progressValue)
401                                         except:
402                                                 pass
403                         elif line.startswith('Print time:'):
404                                 self._result._printTimeSeconds = int(line.split(':')[1].strip())
405                         elif line.startswith('Filament:'):
406                                 self._result._filamentMM[0] = int(line.split(':')[1].strip())
407                                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
408                                         radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
409                                         self._result._filamentMM[0] /= (math.pi * radius * radius)
410                         elif line.startswith('Filament2:'):
411                                 self._result._filamentMM[1] = int(line.split(':')[1].strip())
412                                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
413                                         radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
414                                         self._result._filamentMM[1] /= (math.pi * radius * radius)
415                         elif line.startswith('Replace:'):
416                                 self._result._replaceInfo[line.split(':')[1].strip()] = line.split(':')[2].strip()
417                         else:
418                                 self._result.addLog(line)
419                         line = stderr.readline()
420
421         def _engineSettings(self, extruderCount):
422                 settings = {
423                         'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
424                         'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000) if profile.getProfileSettingFloat('bottom_thickness') > 0.0 else int(profile.getProfileSettingFloat('layer_height') * 1000),
425                         'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
426                         'filamentFlow': int(profile.getProfileSettingFloat('filament_flow')),
427                         'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
428                         'insetCount': int(profile.calculateLineCount()),
429                         'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
430                         'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
431                         'infillOverlap': int(profile.getProfileSettingFloat('fill_overlap')),
432                         'initialSpeedupLayers': int(4),
433                         'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
434                         'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
435                         'infillSpeed': int(profile.getProfileSettingFloat('infill_speed')) if int(profile.getProfileSettingFloat('infill_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
436                         'inset0Speed': int(profile.getProfileSettingFloat('inset0_speed')) if int(profile.getProfileSettingFloat('inset0_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
437                         'insetXSpeed': int(profile.getProfileSettingFloat('insetx_speed')) if int(profile.getProfileSettingFloat('insetx_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
438                         'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
439                         'fanSpeedMin': int(profile.getProfileSettingFloat('fan_speed')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
440                         'fanSpeedMax': int(profile.getProfileSettingFloat('fan_speed_max')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
441                         'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(profile.getProfileSettingFloat('support_angle')),
442                         'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
443                         'supportLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('support_fill_rate')) if profile.getProfileSettingFloat('support_fill_rate') > 0 else -1,
444                         'supportXYDistance': int(1000 * profile.getProfileSettingFloat('support_xy_distance')),
445                         'supportZDistance': int(1000 * profile.getProfileSettingFloat('support_z_distance')),
446                         'supportExtruder': 0 if profile.getProfileSetting('support_dual_extrusion') == 'First extruder' else (1 if profile.getProfileSetting('support_dual_extrusion') == 'Second extruder' and profile.minimalExtruderCount() > 1 else -1),
447                         'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000) if profile.getProfileSetting('retraction_enable') == 'True' else 0,
448                         'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
449                         'retractionMinimalDistance': int(profile.getProfileSettingFloat('retraction_min_travel') * 1000),
450                         'retractionAmountExtruderSwitch': int(profile.getProfileSettingFloat('retraction_dual_amount') * 1000),
451                         'retractionZHop': int(profile.getProfileSettingFloat('retraction_hop') * 1000),
452                         'minimalExtrusionBeforeRetraction': int(profile.getProfileSettingFloat('retraction_minimal_extrusion') * 1000),
453                         'enableCombing': 1 if profile.getProfileSetting('retraction_combing') == 'True' else 0,
454                         'multiVolumeOverlap': int(profile.getProfileSettingFloat('overlap_dual') * 1000),
455                         'objectSink': max(0, int(profile.getProfileSettingFloat('object_sink') * 1000)),
456                         'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
457                         'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
458                         'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
459                         'startCode': profile.getAlterationFileContents('start.gcode', extruderCount),
460                         'endCode': profile.getAlterationFileContents('end.gcode', extruderCount),
461
462                         'extruderOffset[1].X': int(profile.getMachineSettingFloat('extruder_offset_x1') * 1000),
463                         'extruderOffset[1].Y': int(profile.getMachineSettingFloat('extruder_offset_y1') * 1000),
464                         'extruderOffset[2].X': int(profile.getMachineSettingFloat('extruder_offset_x2') * 1000),
465                         'extruderOffset[2].Y': int(profile.getMachineSettingFloat('extruder_offset_y2') * 1000),
466                         'extruderOffset[3].X': int(profile.getMachineSettingFloat('extruder_offset_x3') * 1000),
467                         'extruderOffset[3].Y': int(profile.getMachineSettingFloat('extruder_offset_y3') * 1000),
468                         'fixHorrible': 0,
469                 }
470                 fanFullHeight = int(profile.getProfileSettingFloat('fan_full_height') * 1000)
471                 settings['fanFullOnLayerNr'] = (fanFullHeight - settings['initialLayerThickness'] - 1) / settings['layerThickness'] + 1
472                 if settings['fanFullOnLayerNr'] < 0:
473                         settings['fanFullOnLayerNr'] = 0
474
475                 if profile.getProfileSettingFloat('fill_density') == 0:
476                         settings['sparseInfillLineDistance'] = -1
477                 elif profile.getProfileSettingFloat('fill_density') == 100:
478                         settings['sparseInfillLineDistance'] = settings['extrusionWidth']
479                         #Set the up/down skins height to 10000 if we want a 100% filled object.
480                         # This gives better results then normal 100% infill as the sparse and up/down skin have some overlap.
481                         settings['downSkinCount'] = 10000
482                         settings['upSkinCount'] = 10000
483                 else:
484                         settings['sparseInfillLineDistance'] = int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density'))
485                 if profile.getProfileSetting('platform_adhesion') == 'Brim':
486                         settings['skirtDistance'] = 0
487                         settings['skirtLineCount'] = int(profile.getProfileSettingFloat('brim_line_count'))
488                 elif profile.getProfileSetting('platform_adhesion') == 'Raft':
489                         settings['skirtDistance'] = 0
490                         settings['skirtLineCount'] = 0
491                         settings['raftMargin'] = int(profile.getProfileSettingFloat('raft_margin') * 1000)
492                         settings['raftLineSpacing'] = int(profile.getProfileSettingFloat('raft_line_spacing') * 1000)
493                         settings['raftBaseThickness'] = int(profile.getProfileSettingFloat('raft_base_thickness') * 1000)
494                         settings['raftBaseLinewidth'] = int(profile.getProfileSettingFloat('raft_base_linewidth') * 1000)
495                         settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000)
496                         settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000)
497                 else:
498                         settings['skirtDistance'] = int(profile.getProfileSettingFloat('skirt_gap') * 1000)
499                         settings['skirtLineCount'] = int(profile.getProfileSettingFloat('skirt_line_count'))
500                         settings['skirtMinLength'] = int(profile.getProfileSettingFloat('skirt_minimal_length') * 1000)
501
502                 if profile.getProfileSetting('fix_horrible_union_all_type_a') == 'True':
503                         settings['fixHorrible'] |= 0x01
504                 if profile.getProfileSetting('fix_horrible_union_all_type_b') == 'True':
505                         settings['fixHorrible'] |= 0x02
506                 if profile.getProfileSetting('fix_horrible_use_open_bits') == 'True':
507                         settings['fixHorrible'] |= 0x10
508                 if profile.getProfileSetting('fix_horrible_extensive_stitching') == 'True':
509                         settings['fixHorrible'] |= 0x04
510
511                 if settings['layerThickness'] <= 0:
512                         settings['layerThickness'] = 1000
513                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
514                         settings['gcodeFlavor'] = 1
515                 if profile.getProfileSetting('spiralize') == 'True':
516                         settings['spiralizeMode'] = 1
517                 if profile.getProfileSetting('wipe_tower') == 'True' and extruderCount > 1:
518                         settings['wipeTowerSize'] = int(math.sqrt(profile.getProfileSettingFloat('wipe_tower_volume') * 1000 * 1000 * 1000 / settings['layerThickness']))
519                 if profile.getProfileSetting('ooze_shield') == 'True':
520                         settings['enableOozeShield'] = 1
521                 return settings
522
523         def _runEngineProcess(self, cmdList):
524                 kwargs = {}
525                 if subprocess.mswindows:
526                         su = subprocess.STARTUPINFO()
527                         su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
528                         su.wShowWindow = subprocess.SW_HIDE
529                         kwargs['startupinfo'] = su
530                         kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
531                 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)