2 Slice engine communication.
3 This module handles all communication with the slicing engine.
5 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
22 from Cura.util.bigDataStorage import BigDataStorage
23 from Cura.util import profile
24 from Cura.util import pluginInfo
25 from Cura.util import version
26 from Cura.util import gcodeInterpreter
28 def getEngineFilename():
30 Finds and returns the path to the current engine executable. This is OS depended.
31 :return: The full path to the engine executable.
33 base_search_path = os.path.dirname(inspect.getfile(getEngineFilename))
34 search_filename = 'CuraEngine'
35 if platform.system() == 'Windows':
36 search_filename += '.exe'
37 if version.isDevVersion() and os.path.exists('C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'):
38 return 'C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'
39 for n in xrange(0, 10):
40 full_filename = os.path.abspath(os.path.join(base_search_path, '/'.join(['..'] * n), search_filename))
41 if os.path.isfile(full_filename):
43 full_filename = os.path.abspath(os.path.join(base_search_path, '/'.join(['..'] * n), 'CuraEngine', search_filename))
44 if os.path.isfile(full_filename):
46 if os.path.isfile('/usr/bin/CuraEngine'):
47 return '/usr/bin/CuraEngine'
48 if os.path.isfile('/usr/local/bin/CuraEngine'):
49 return '/usr/local/bin/CuraEngine'
52 class EngineResult(object):
54 Result from running the CuraEngine.
55 Contains the engine log, polygons retrieved from the engine, the GCode and some meta-data.
59 self._gcodeData = BigDataStorage()
61 self._replaceInfo = {}
63 self._printTimeSeconds = None
64 self._filamentMM = [0.0] * 4
65 self._modelHash = None
66 self._profileString = profile.getProfileString()
67 self._preferencesString = profile.getPreferencesString()
68 self._gcodeInterpreter = gcodeInterpreter.gcode()
69 self._gcodeLoadThread = None
70 self._finished = False
72 def getFilamentWeight(self, e=0):
73 #Calculates the weight of the filament in kg
74 radius = float(profile.getProfileSetting('filament_diameter')) / 2
75 volumeM3 = (self._filamentMM[e] * (math.pi * radius * radius)) / (1000*1000*1000)
76 return volumeM3 * profile.getPreferenceFloat('filament_physical_density')
78 def getFilamentCost(self, e=0):
79 cost_kg = profile.getPreferenceFloat('filament_cost_kg')
80 cost_meter = profile.getPreferenceFloat('filament_cost_meter')
81 if cost_kg > 0.0 and cost_meter > 0.0:
82 return "%.2f / %.2f" % (self.getFilamentWeight(e) * cost_kg, self._filamentMM[e] / 1000.0 * cost_meter)
84 return "%.2f" % (self.getFilamentWeight(e) * cost_kg)
85 elif cost_meter > 0.0:
86 return "%.2f" % (self._filamentMM[e] / 1000.0 * cost_meter)
89 def getPrintTime(self):
90 if self._printTimeSeconds is None:
92 if int(self._printTimeSeconds / 60 / 60) < 1:
93 return _('%d minutes') % (int(self._printTimeSeconds / 60) % 60)
94 if int(self._printTimeSeconds / 60 / 60) == 1:
95 return _('%d hour %d minutes') % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
96 return _('%d hours %d minutes') % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
98 def getFilamentAmount(self, e=0):
99 if self._filamentMM[e] == 0.0:
101 return _('%0.2f meter %0.0f gram') % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0)
104 return self._engineLog
107 self._gcodeData.seekStart()
108 return self._gcodeData
110 def setGCode(self, gcode):
111 self._gcodeData = BigDataStorage()
112 self._gcodeData.write(gcode)
113 self._replaceInfo = {}
115 def addLog(self, line):
116 self._engineLog.append(line)
118 def setHash(self, hash):
119 self._modelHash = hash
121 def setFinished(self, result):
123 for k, v in self._replaceInfo.items():
124 self._gcodeData.replaceAtStart(k, v)
125 self._finished = result
127 def isFinished(self):
128 return self._finished
130 def getGCodeLayers(self, loadCallback):
131 if not self._finished:
133 if self._gcodeInterpreter.layerList is None and self._gcodeLoadThread is None:
134 self._gcodeInterpreter.progressCallback = self._gcodeInterpreterCallback
135 self._gcodeLoadThread = threading.Thread(target=lambda : self._gcodeInterpreter.load(self._gcodeData.clone()))
136 self._gcodeLoadCallback = loadCallback
137 self._gcodeLoadThread.daemon = True
138 self._gcodeLoadThread.start()
139 return self._gcodeInterpreter.layerList
141 def _gcodeInterpreterCallback(self, progress):
142 if len(self._gcodeInterpreter.layerList) % 5 == 0:
144 return self._gcodeLoadCallback(self, progress)
146 '''def submitInfoOnline(self):
147 if profile.getPreference('submit_slice_information') != 'True':
149 if version.isDevVersion():
152 'processor': platform.processor(),
153 'machine': platform.machine(),
154 'platform': platform.platform(),
155 'profile': self._profileString,
156 'preferences': self._preferencesString,
157 'modelhash': self._modelHash,
158 'version': version.getVersion(),
159 'printtime': self._printTimeSeconds,
160 'filament': ','.join(map(str, self._filamentMM)),
163 f = urllib2.urlopen("https://stats.youmagine.com/curastats/slice", data = urllib.urlencode(data), timeout = 1)
168 traceback.print_exc()'''
170 class Engine(object):
172 Class used to communicate with the CuraEngine.
173 The CuraEngine is ran as a 2nd process and reports back information trough stderr.
174 GCode trough stdout and has a socket connection for polygon information and loading the 3D model into the engine.
176 GUI_CMD_REQUEST_MESH = 0x01
177 GUI_CMD_SEND_POLYGONS = 0x02
178 GUI_CMD_FINISH_OBJECT = 0x03
180 def __init__(self, progressCallback):
183 self._callback = progressCallback
184 self._progressSteps = ['inset', 'skin', 'export']
188 self._engine_executable = getEngineFilename()
189 self._serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
190 self._serverPortNr = 0xC20A
191 for potential_port in xrange(0xC20A, 0xFFFF):
192 self._serverPortNr = potential_port
194 self._serversocket.bind(('127.0.0.1', self._serverPortNr))
197 print("Failed to listen on port: %d" % (self._serverPortNr))
199 print("Failed to listen on any port, this is a fatal error")
201 thread = threading.Thread(target=self._socketListenThread)
205 def _socketListenThread(self):
206 self._serversocket.listen(1)
207 print 'Listening for engine communications on %d' % (self._serverPortNr)
210 sock, _ = self._serversocket.accept()
211 thread = threading.Thread(target=self._socketConnectionThread, args=(sock,))
214 except socket.error, e:
215 if e.errno != errno.EINTR:
218 def _socketConnectionThread(self, sock):
228 cmd = struct.unpack('@i', data)[0]
229 if cmd == self.GUI_CMD_REQUEST_MESH:
230 meshInfo = self._modelData[0]
231 self._modelData = self._modelData[1:]
232 sock.sendall(struct.pack('@i', meshInfo[0]))
233 sock.sendall(meshInfo[1].tostring())
234 elif cmd == self.GUI_CMD_SEND_POLYGONS:
235 cnt = struct.unpack('@i', sock.recv(4))[0]
236 layerNr = struct.unpack('@i', sock.recv(4))[0]
237 layerNr += layerNrOffset
238 z = struct.unpack('@i', sock.recv(4))[0]
239 z = float(z) / 1000.0
240 typeNameLen = struct.unpack('@i', sock.recv(4))[0]
241 typeName = sock.recv(typeNameLen)
242 while len(self._result._polygons) < layerNr + 1:
243 self._result._polygons.append({})
244 polygons = self._result._polygons[layerNr]
245 if typeName not in polygons:
246 polygons[typeName] = []
247 for n in xrange(0, cnt):
248 length = struct.unpack('@i', sock.recv(4))[0]
250 while len(data) < length * 8 * 2:
251 recvData = sock.recv(length * 8 * 2 - len(data))
252 if len(recvData) < 1:
255 polygon2d = numpy.array(numpy.fromstring(data, numpy.int64), numpy.float32) / 1000.0
256 polygon2d = polygon2d.reshape((len(polygon2d) / 2, 2))
257 polygon = numpy.empty((len(polygon2d), 3), numpy.float32)
258 polygon[:,:-1] = polygon2d
260 polygons[typeName].append(polygon)
261 elif cmd == self.GUI_CMD_FINISH_OBJECT:
262 layerNrOffset = len(self._result._polygons)
264 print "Unknown command on socket: %x" % (cmd)
268 self._serversocket.close()
270 def abortEngine(self):
271 if self._process is not None:
273 self._process.terminate()
276 if self._thread is not None:
281 if self._thread is not None:
287 def runEngine(self, scene):
288 if len(scene.objects()) < 1:
290 self._thread = threading.Thread(target=self._runEngine, args=(scene, self._thread, pluginInfo.getPostProcessPluginConfig()))
291 self._thread.daemon = True
294 def _runEngine(self, scene, old_thread, pluginConfig):
295 if old_thread is not None:
296 if self._process is not None:
297 self._process.terminate()
302 for obj in scene.objects():
303 if scene.checkPlatform(obj):
304 extruderCount = max(extruderCount, len(obj._meshList))
306 extruderCount = max(extruderCount, profile.minimalExtruderCount())
308 commandList = [self._engine_executable, '-v', '-p']
309 for k, v in self._engineSettings(extruderCount).iteritems():
310 commandList += ['-s', '%s=%s' % (k, str(v))]
311 commandList += ['-g', '%d' % (self._serverPortNr)]
314 hash = hashlib.sha512()
315 order = scene.printOrder()
317 pos = numpy.array(profile.getMachineCenterCoords()) * 1000
320 for obj in scene.objects():
321 if scene.checkPlatform(obj):
322 oMin = obj.getMinimum()[0:2] + obj.getPosition()
323 oMax = obj.getMaximum()[0:2] + obj.getPosition()
328 objMin[0] = min(oMin[0], objMin[0])
329 objMin[1] = min(oMin[1], objMin[1])
330 objMax[0] = max(oMax[0], objMax[0])
331 objMax[1] = max(oMax[1], objMax[1])
334 pos += (objMin + objMax) / 2.0 * 1000
335 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
337 vertexTotal = [0] * 4
339 for obj in scene.objects():
340 if scene.checkPlatform(obj):
341 meshMax = max(meshMax, len(obj._meshList))
342 for n in xrange(0, len(obj._meshList)):
343 vertexTotal[n] += obj._meshList[n].vertexCount
345 for n in xrange(0, meshMax):
346 verts = numpy.zeros((0, 3), numpy.float32)
347 for obj in scene.objects():
348 if scene.checkPlatform(obj):
349 if n < len(obj._meshList):
350 vertexes = (numpy.matrix(obj._meshList[n].vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
351 vertexes -= obj._drawOffset
352 vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
353 verts = numpy.concatenate((verts, vertexes))
354 hash.update(obj._meshList[n].vertexes.tostring())
355 engineModelData.append((vertexTotal[n], verts))
357 commandList += ['$' * meshMax]
361 obj = scene.objects()[n]
362 for mesh in obj._meshList:
363 engineModelData.append((mesh.vertexCount, mesh.vertexes))
364 hash.update(mesh.vertexes.tostring())
365 pos = obj.getPosition() * 1000
366 pos += numpy.array(profile.getMachineCenterCoords()) * 1000
367 commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
368 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
369 commandList += ['$' * len(obj._meshList)]
371 modelHash = hash.hexdigest()
372 if self._objCount < 1:
374 if self._thread != threading.currentThread():
377 self._modelData = engineModelData
379 self._process = self._runEngineProcess(commandList)
381 traceback.print_exc()
384 self._result = EngineResult()
385 self._result.addLog('Running: %s' % (' '.join(commandList)))
386 self._result.setHash(modelHash)
389 logThread = threading.Thread(target=self._watchStderr, args=(self._process.stderr,))
390 logThread.daemon = True
394 data = self._process.stdout.read(4096)
396 if self._thread != threading.currentThread():
397 self._process.terminate()
398 self._result._gcodeData.write(data)
399 data = self._process.stdout.read(4096)
401 returnCode = self._process.wait()
403 self._result.addLog("Slicer process returned : %d" % returnCode)
405 plugin_error = pluginInfo.runPostProcessingPlugins(self._result, pluginConfig)
406 if plugin_error is not None:
407 self._result.addLog(plugin_error)
408 self._result.setFinished(True)
414 self._result.addLog("MemoryError")
418 with open(os.path.join(profile.getBasePath(), 'engine.log'), "w") as f:
419 for line in self._result.getLog():
424 def _watchStderr(self, stderr):
426 line = stderr.readline()
429 if line.startswith('Progress:'):
430 line = line.split(':')
431 if line[1] == 'process':
433 elif line[1] in self._progressSteps:
434 progressValue = float(line[2]) / float(line[3])
435 progressValue /= len(self._progressSteps)
436 progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
438 progressValue /= self._objCount
439 progressValue += 1.0 / self._objCount * objectNr
441 self._callback(progressValue)
444 elif line.startswith('Print time:'):
445 self._result._printTimeSeconds = int(line.split(':')[1].strip())
446 elif line.startswith('Filament:'):
447 self._result._filamentMM[0] = int(line.split(':')[1].strip())
448 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
449 radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
450 self._result._filamentMM[0] /= (math.pi * radius * radius)
451 elif line.startswith('Filament2:'):
452 self._result._filamentMM[1] = int(line.split(':')[1].strip())
453 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
454 radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
455 self._result._filamentMM[1] /= (math.pi * radius * radius)
456 elif line.startswith('Replace:'):
457 self._result._replaceInfo[line.split(':')[1].strip()] = line.split(':')[2].strip()
459 self._result.addLog(line)
460 line = stderr.readline()
462 def _engineSettings(self, extruderCount):
464 'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
465 'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000) if profile.getProfileSettingFloat('bottom_thickness') > 0.0 else int(profile.getProfileSettingFloat('layer_height') * 1000),
466 'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
467 'filamentFlow': int(profile.getProfileSettingFloat('filament_flow')),
468 'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
469 'layer0extrusionWidth': int(profile.calculateEdgeWidth() * profile.getProfileSettingFloat('layer0_width_factor') / 100 * 1000),
470 'insetCount': int(profile.calculateLineCount()),
471 'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
472 'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
473 'infillOverlap': int(profile.getProfileSettingFloat('fill_overlap')),
474 'initialSpeedupLayers': int(4),
475 'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
476 'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
477 'infillSpeed': int(profile.getProfileSettingFloat('infill_speed')) if int(profile.getProfileSettingFloat('infill_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
478 'inset0Speed': int(profile.getProfileSettingFloat('inset0_speed')) if int(profile.getProfileSettingFloat('inset0_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
479 'insetXSpeed': int(profile.getProfileSettingFloat('insetx_speed')) if int(profile.getProfileSettingFloat('insetx_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
480 'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
481 'fanSpeedMin': int(profile.getProfileSettingFloat('fan_speed')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
482 'fanSpeedMax': int(profile.getProfileSettingFloat('fan_speed_max')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
483 'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(profile.getProfileSettingFloat('support_angle')),
484 'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
485 'supportLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('support_fill_rate')) if profile.getProfileSettingFloat('support_fill_rate') > 0 else -1,
486 'supportXYDistance': int(1000 * profile.getProfileSettingFloat('support_xy_distance')),
487 'supportZDistance': int(1000 * profile.getProfileSettingFloat('support_z_distance')),
488 '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),
489 'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000) if profile.getProfileSetting('retraction_enable') == 'True' else 0,
490 'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
491 'retractionMinimalDistance': int(profile.getProfileSettingFloat('retraction_min_travel') * 1000),
492 'retractionAmountExtruderSwitch': int(profile.getProfileSettingFloat('retraction_dual_amount') * 1000),
493 'retractionZHop': int(profile.getProfileSettingFloat('retraction_hop') * 1000),
494 'minimalExtrusionBeforeRetraction': int(profile.getProfileSettingFloat('retraction_minimal_extrusion') * 1000),
495 'enableCombing': 1 if profile.getProfileSetting('retraction_combing') == 'True' else 0,
496 'multiVolumeOverlap': int(profile.getProfileSettingFloat('overlap_dual') * 1000),
497 'objectSink': max(0, int(profile.getProfileSettingFloat('object_sink') * 1000)),
498 'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
499 'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
500 'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
501 'startCode': profile.getAlterationFileContents('start.gcode', extruderCount),
502 'endCode': profile.getAlterationFileContents('end.gcode', extruderCount),
503 'preSwitchExtruderCode': profile.getAlterationFileContents('preSwitchExtruder.gcode', extruderCount),
504 'postSwitchExtruderCode': profile.getAlterationFileContents('postSwitchExtruder.gcode', extruderCount),
506 'extruderOffset[1].X': int(profile.getMachineSettingFloat('extruder_offset_x1') * 1000),
507 'extruderOffset[1].Y': int(profile.getMachineSettingFloat('extruder_offset_y1') * 1000),
508 'extruderOffset[2].X': int(profile.getMachineSettingFloat('extruder_offset_x2') * 1000),
509 'extruderOffset[2].Y': int(profile.getMachineSettingFloat('extruder_offset_y2') * 1000),
510 'extruderOffset[3].X': int(profile.getMachineSettingFloat('extruder_offset_x3') * 1000),
511 'extruderOffset[3].Y': int(profile.getMachineSettingFloat('extruder_offset_y3') * 1000),
512 'zOffset': int(profile.getMachineSettingFloat('extruder_z_offset') * 1000),
515 fanFullHeight = int(profile.getProfileSettingFloat('fan_full_height') * 1000)
516 settings['fanFullOnLayerNr'] = (fanFullHeight - settings['initialLayerThickness'] - 1) / settings['layerThickness'] + 1
517 if settings['fanFullOnLayerNr'] < 0:
518 settings['fanFullOnLayerNr'] = 0
519 if profile.getProfileSetting('support_type') == 'Lines':
520 settings['supportType'] = 1
522 if profile.getProfileSettingFloat('fill_density') == 0:
523 settings['sparseInfillLineDistance'] = -1
524 elif profile.getProfileSettingFloat('fill_density') == 100:
525 settings['sparseInfillLineDistance'] = settings['extrusionWidth']
526 #Set the up/down skins height to 10000 if we want a 100% filled object.
527 # This gives better results then normal 100% infill as the sparse and up/down skin have some overlap.
528 settings['downSkinCount'] = 10000
529 settings['upSkinCount'] = 10000
531 settings['sparseInfillLineDistance'] = int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density'))
532 if profile.getProfileSetting('platform_adhesion') == 'Brim':
533 settings['skirtDistance'] = 0
534 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('brim_line_count'))
535 elif profile.getProfileSetting('platform_adhesion') == 'Raft':
536 settings['skirtDistance'] = 0
537 settings['skirtLineCount'] = 0
538 settings['raftMargin'] = int(profile.getProfileSettingFloat('raft_margin') * 1000)
539 settings['raftLineSpacing'] = int(profile.getProfileSettingFloat('raft_line_spacing') * 1000)
540 settings['raftBaseThickness'] = int(profile.getProfileSettingFloat('raft_base_thickness') * 1000)
541 settings['raftBaseLinewidth'] = int(profile.getProfileSettingFloat('raft_base_linewidth') * 1000)
542 settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000)
543 settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000)
544 settings['raftInterfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000 * 2.0)
545 settings['raftAirGapLayer0'] = int(profile.getProfileSettingFloat('raft_airgap') * 1000 + profile.getProfileSettingFloat('raft_airgap_all') * 1000)
546 settings['raftAirGap'] = int(profile.getProfileSettingFloat('raft_airgap_all') * 1000)
547 settings['raftBaseSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
548 settings['raftFanSpeed'] = 0
549 settings['raftSurfaceThickness'] = int(profile.getProfileSettingFloat('raft_surface_thickness') * 1000)
550 settings['raftSurfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000)
551 settings['raftSurfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000)
552 settings['raftSurfaceLayers'] = int(profile.getProfileSettingFloat('raft_surface_layers'))
553 settings['raftSurfaceSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
555 settings['skirtDistance'] = int(profile.getProfileSettingFloat('skirt_gap') * 1000)
556 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('skirt_line_count'))
557 settings['skirtMinLength'] = int(profile.getProfileSettingFloat('skirt_minimal_length') * 1000)
559 if profile.getProfileSetting('fix_horrible_union_all_type_a') == 'True':
560 settings['fixHorrible'] |= 0x01
561 if profile.getProfileSetting('fix_horrible_union_all_type_b') == 'True':
562 settings['fixHorrible'] |= 0x02
563 if profile.getProfileSetting('fix_horrible_use_open_bits') == 'True':
564 settings['fixHorrible'] |= 0x10
565 if profile.getProfileSetting('fix_horrible_extensive_stitching') == 'True':
566 settings['fixHorrible'] |= 0x04
568 if settings['layerThickness'] <= 0:
569 settings['layerThickness'] = 1000
570 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
571 settings['gcodeFlavor'] = 1
572 elif profile.getMachineSetting('gcode_flavor') == 'MakerBot':
573 settings['gcodeFlavor'] = 2
574 elif profile.getMachineSetting('gcode_flavor') == 'BFB':
575 settings['gcodeFlavor'] = 3
576 elif profile.getMachineSetting('gcode_flavor') == 'Mach3':
577 settings['gcodeFlavor'] = 4
578 elif profile.getMachineSetting('gcode_flavor') == 'RepRap (Volumetric)':
579 settings['gcodeFlavor'] = 5
580 if profile.getProfileSetting('spiralize') == 'True':
581 settings['spiralizeMode'] = 1
582 if profile.getProfileSetting('simple_mode') == 'True':
583 settings['simpleMode'] = 1
584 if profile.getProfileSetting('wipe_tower') == 'True' and extruderCount > 1:
585 settings['wipeTowerSize'] = int(math.sqrt(profile.getProfileSettingFloat('wipe_tower_volume') * 1000 * 1000 * 1000 / settings['layerThickness']))
586 if profile.getProfileSetting('ooze_shield') == 'True':
587 settings['enableOozeShield'] = 1
590 def _runEngineProcess(self, cmdList):
592 if subprocess.mswindows:
593 su = subprocess.STARTUPINFO()
594 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
595 su.wShowWindow = subprocess.SW_HIDE
596 kwargs['startupinfo'] = su
597 kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
598 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)