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))
291 self._thread.daemon = True
294 def _runEngine(self, scene, old_thread):
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()
404 self._result.setFinished(True)
405 plugin_error = pluginInfo.runPostProcessingPlugins(self._result)
406 if plugin_error is not None:
408 self._result.addLog(plugin_error)
411 for line in self._result.getLog():
416 self._result.addLog("MemoryError")
419 def _watchStderr(self, stderr):
421 line = stderr.readline()
424 if line.startswith('Progress:'):
425 line = line.split(':')
426 if line[1] == 'process':
428 elif line[1] in self._progressSteps:
429 progressValue = float(line[2]) / float(line[3])
430 progressValue /= len(self._progressSteps)
431 progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
433 progressValue /= self._objCount
434 progressValue += 1.0 / self._objCount * objectNr
436 self._callback(progressValue)
439 elif line.startswith('Print time:'):
440 self._result._printTimeSeconds = int(line.split(':')[1].strip())
441 elif line.startswith('Filament:'):
442 self._result._filamentMM[0] = int(line.split(':')[1].strip())
443 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
444 radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
445 self._result._filamentMM[0] /= (math.pi * radius * radius)
446 elif line.startswith('Filament2:'):
447 self._result._filamentMM[1] = int(line.split(':')[1].strip())
448 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
449 radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
450 self._result._filamentMM[1] /= (math.pi * radius * radius)
451 elif line.startswith('Replace:'):
452 self._result._replaceInfo[line.split(':')[1].strip()] = line.split(':')[2].strip()
454 self._result.addLog(line)
455 line = stderr.readline()
457 def _engineSettings(self, extruderCount):
459 'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
460 'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000) if profile.getProfileSettingFloat('bottom_thickness') > 0.0 else int(profile.getProfileSettingFloat('layer_height') * 1000),
461 'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
462 'filamentFlow': int(profile.getProfileSettingFloat('filament_flow')),
463 'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
464 'layer0extrusionWidth': int(profile.calculateEdgeWidth() * profile.getProfileSettingFloat('layer0_width_factor') / 100 * 1000),
465 'insetCount': int(profile.calculateLineCount()),
466 'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
467 'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
468 'infillOverlap': int(profile.getProfileSettingFloat('fill_overlap')),
469 'initialSpeedupLayers': int(4),
470 'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
471 'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
472 'infillSpeed': int(profile.getProfileSettingFloat('infill_speed')) if int(profile.getProfileSettingFloat('infill_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
473 'inset0Speed': int(profile.getProfileSettingFloat('inset0_speed')) if int(profile.getProfileSettingFloat('inset0_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
474 'insetXSpeed': int(profile.getProfileSettingFloat('insetx_speed')) if int(profile.getProfileSettingFloat('insetx_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
475 'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
476 'fanSpeedMin': int(profile.getProfileSettingFloat('fan_speed')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
477 'fanSpeedMax': int(profile.getProfileSettingFloat('fan_speed_max')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
478 'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(profile.getProfileSettingFloat('support_angle')),
479 'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
480 'supportLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('support_fill_rate')) if profile.getProfileSettingFloat('support_fill_rate') > 0 else -1,
481 'supportXYDistance': int(1000 * profile.getProfileSettingFloat('support_xy_distance')),
482 'supportZDistance': int(1000 * profile.getProfileSettingFloat('support_z_distance')),
483 '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),
484 'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000) if profile.getProfileSetting('retraction_enable') == 'True' else 0,
485 'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
486 'retractionMinimalDistance': int(profile.getProfileSettingFloat('retraction_min_travel') * 1000),
487 'retractionAmountExtruderSwitch': int(profile.getProfileSettingFloat('retraction_dual_amount') * 1000),
488 'retractionZHop': int(profile.getProfileSettingFloat('retraction_hop') * 1000),
489 'minimalExtrusionBeforeRetraction': int(profile.getProfileSettingFloat('retraction_minimal_extrusion') * 1000),
490 'enableCombing': 1 if profile.getProfileSetting('retraction_combing') == 'True' else 0,
491 'multiVolumeOverlap': int(profile.getProfileSettingFloat('overlap_dual') * 1000),
492 'objectSink': max(0, int(profile.getProfileSettingFloat('object_sink') * 1000)),
493 'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
494 'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
495 'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
496 'startCode': profile.getAlterationFileContents('start.gcode', extruderCount),
497 'endCode': profile.getAlterationFileContents('end.gcode', extruderCount),
498 'preSwitchExtruderCode': profile.getAlterationFileContents('preSwitchExtruder.gcode', extruderCount),
499 'postSwitchExtruderCode': profile.getAlterationFileContents('postSwitchExtruder.gcode', extruderCount),
501 'extruderOffset[1].X': int(profile.getMachineSettingFloat('extruder_offset_x1') * 1000),
502 'extruderOffset[1].Y': int(profile.getMachineSettingFloat('extruder_offset_y1') * 1000),
503 'extruderOffset[2].X': int(profile.getMachineSettingFloat('extruder_offset_x2') * 1000),
504 'extruderOffset[2].Y': int(profile.getMachineSettingFloat('extruder_offset_y2') * 1000),
505 'extruderOffset[3].X': int(profile.getMachineSettingFloat('extruder_offset_x3') * 1000),
506 'extruderOffset[3].Y': int(profile.getMachineSettingFloat('extruder_offset_y3') * 1000),
509 fanFullHeight = int(profile.getProfileSettingFloat('fan_full_height') * 1000)
510 settings['fanFullOnLayerNr'] = (fanFullHeight - settings['initialLayerThickness'] - 1) / settings['layerThickness'] + 1
511 if settings['fanFullOnLayerNr'] < 0:
512 settings['fanFullOnLayerNr'] = 0
513 if profile.getProfileSetting('support_type') == 'Lines':
514 settings['supportType'] = 1
516 if profile.getProfileSettingFloat('fill_density') == 0:
517 settings['sparseInfillLineDistance'] = -1
518 elif profile.getProfileSettingFloat('fill_density') == 100:
519 settings['sparseInfillLineDistance'] = settings['extrusionWidth']
520 #Set the up/down skins height to 10000 if we want a 100% filled object.
521 # This gives better results then normal 100% infill as the sparse and up/down skin have some overlap.
522 settings['downSkinCount'] = 10000
523 settings['upSkinCount'] = 10000
525 settings['sparseInfillLineDistance'] = int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density'))
526 if profile.getProfileSetting('platform_adhesion') == 'Brim':
527 settings['skirtDistance'] = 0
528 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('brim_line_count'))
529 elif profile.getProfileSetting('platform_adhesion') == 'Raft':
530 settings['skirtDistance'] = 0
531 settings['skirtLineCount'] = 0
532 settings['raftMargin'] = int(profile.getProfileSettingFloat('raft_margin') * 1000)
533 settings['raftLineSpacing'] = int(profile.getProfileSettingFloat('raft_line_spacing') * 1000)
534 settings['raftBaseThickness'] = int(profile.getProfileSettingFloat('raft_base_thickness') * 1000)
535 settings['raftBaseLinewidth'] = int(profile.getProfileSettingFloat('raft_base_linewidth') * 1000)
536 settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000)
537 settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000)
538 settings['raftInterfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000 * 2.0)
539 settings['raftAirGapLayer0'] = int(profile.getProfileSettingFloat('raft_airgap') * 1000 + profile.getProfileSettingFloat('raft_airgap_all') * 1000)
540 settings['raftAirGap'] = int(profile.getProfileSettingFloat('raft_airgap_all') * 1000)
541 settings['raftBaseSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
542 settings['raftFanSpeed'] = 0
543 settings['raftSurfaceThickness'] = int(profile.getProfileSettingFloat('raft_surface_thickness') * 1000)
544 settings['raftSurfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000)
545 settings['raftSurfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000)
546 settings['raftSurfaceLayers'] = int(profile.getProfileSettingFloat('raft_surface_layers'))
547 settings['raftSurfaceSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
549 settings['skirtDistance'] = int(profile.getProfileSettingFloat('skirt_gap') * 1000)
550 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('skirt_line_count'))
551 settings['skirtMinLength'] = int(profile.getProfileSettingFloat('skirt_minimal_length') * 1000)
553 if profile.getProfileSetting('fix_horrible_union_all_type_a') == 'True':
554 settings['fixHorrible'] |= 0x01
555 if profile.getProfileSetting('fix_horrible_union_all_type_b') == 'True':
556 settings['fixHorrible'] |= 0x02
557 if profile.getProfileSetting('fix_horrible_use_open_bits') == 'True':
558 settings['fixHorrible'] |= 0x10
559 if profile.getProfileSetting('fix_horrible_extensive_stitching') == 'True':
560 settings['fixHorrible'] |= 0x04
562 if settings['layerThickness'] <= 0:
563 settings['layerThickness'] = 1000
564 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
565 settings['gcodeFlavor'] = 1
566 elif profile.getMachineSetting('gcode_flavor') == 'MakerBot':
567 settings['gcodeFlavor'] = 2
568 elif profile.getMachineSetting('gcode_flavor') == 'BFB':
569 settings['gcodeFlavor'] = 3
570 elif profile.getMachineSetting('gcode_flavor') == 'Mach3':
571 settings['gcodeFlavor'] = 4
572 elif profile.getMachineSetting('gcode_flavor') == 'RepRap (Volumetric)':
573 settings['gcodeFlavor'] = 5
574 if profile.getProfileSetting('spiralize') == 'True':
575 settings['spiralizeMode'] = 1
576 if profile.getProfileSetting('simple_mode') == 'True':
577 settings['simpleMode'] = 1
578 if profile.getProfileSetting('wipe_tower') == 'True' and extruderCount > 1:
579 settings['wipeTowerSize'] = int(math.sqrt(profile.getProfileSettingFloat('wipe_tower_volume') * 1000 * 1000 * 1000 / settings['layerThickness']))
580 if profile.getProfileSetting('ooze_shield') == 'True':
581 settings['enableOozeShield'] = 1
584 def _runEngineProcess(self, cmdList):
586 if subprocess.mswindows:
587 su = subprocess.STARTUPINFO()
588 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
589 su.wShowWindow = subprocess.SW_HIDE
590 kwargs['startupinfo'] = su
591 kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
592 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)