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
75 radius = float(profile.getProfileSetting('filament_diameter')) / 2
76 volumeM3 = (self._filamentMM[e] * (math.pi * radius * radius)) / (1000*1000*1000)
77 return volumeM3 * profile.getPreferenceFloat('filament_physical_density')
81 def getFilamentCost(self, e=0):
82 cost_kg = profile.getPreferenceFloat('filament_cost_kg')
83 cost_meter = profile.getPreferenceFloat('filament_cost_meter')
84 if cost_kg > 0.0 and cost_meter > 0.0:
85 return "%.2f / %.2f" % (self.getFilamentWeight(e) * cost_kg, self._filamentMM[e] / 1000.0 * cost_meter)
87 return "%.2f" % (self.getFilamentWeight(e) * cost_kg)
88 elif cost_meter > 0.0:
89 return "%.2f" % (self._filamentMM[e] / 1000.0 * cost_meter)
92 def getPrintTime(self):
93 if self._printTimeSeconds is None:
95 if int(self._printTimeSeconds / 60 / 60) < 1:
96 return _('%d minutes') % (int(self._printTimeSeconds / 60) % 60)
97 if int(self._printTimeSeconds / 60 / 60) == 1:
98 return _('%d hour %d minutes') % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
99 return _('%d hours %d minutes') % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
101 def getFilamentAmount(self, e=0):
102 if self._filamentMM[e] == 0.0:
104 return _('%0.2f meter %0.0f gram') % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0)
106 def getFilamentAmountMeters(self, e=0):
107 return float(self._filamentMM[e]) / 1000.0
110 return self._engineLog
113 self._gcodeData.seekStart()
114 return self._gcodeData
116 def setGCode(self, gcode):
117 self._gcodeData = BigDataStorage()
118 self._gcodeData.write(gcode)
119 self._replaceInfo = {}
121 def addLog(self, line):
122 self._engineLog.append(line)
124 def setHash(self, hash):
125 self._modelHash = hash
127 def addReplaceTag(self, key, value):
128 self._replaceInfo[key] = value
130 def applyReplaceTags(self):
131 self._gcodeData.replaceAtStart(self._replaceInfo)
133 def setFinished(self, result):
134 self._finished = result
136 def isFinished(self):
137 return self._finished
139 def getGCodeLayers(self, loadCallback):
140 if not self._finished:
142 if self._gcodeInterpreter.layerList is None and self._gcodeLoadThread is None:
143 self._gcodeInterpreter.progressCallback = self._gcodeInterpreterCallback
144 self._gcodeLoadThread = threading.Thread(target=lambda : self._gcodeInterpreter.load(self._gcodeData.clone()))
145 self._gcodeLoadCallback = loadCallback
146 self._gcodeLoadThread.daemon = True
147 self._gcodeLoadThread.start()
148 return self._gcodeInterpreter.layerList
150 def _gcodeInterpreterCallback(self, progress):
151 if len(self._gcodeInterpreter.layerList) % 5 == 0:
153 return self._gcodeLoadCallback(self, progress)
155 '''def submitInfoOnline(self):
156 if profile.getPreference('submit_slice_information') != 'True':
158 if version.isDevVersion():
161 'processor': platform.processor(),
162 'machine': platform.machine(),
163 'platform': platform.platform(),
164 'profile': self._profileString,
165 'preferences': self._preferencesString,
166 'modelhash': self._modelHash,
167 'version': version.getVersion(),
168 'printtime': self._printTimeSeconds,
169 'filament': ','.join(map(str, self._filamentMM)),
172 f = urllib2.urlopen("https://stats.youmagine.com/curastats/slice", data = urllib.urlencode(data), timeout = 1)
177 traceback.print_exc()'''
179 class Engine(object):
181 Class used to communicate with the CuraEngine.
182 The CuraEngine is ran as a 2nd process and reports back information trough stderr.
183 GCode trough stdout and has a socket connection for polygon information and loading the 3D model into the engine.
185 GUI_CMD_REQUEST_MESH = 0x01
186 GUI_CMD_SEND_POLYGONS = 0x02
187 GUI_CMD_FINISH_OBJECT = 0x03
189 def __init__(self, progressCallback):
192 self._callback = progressCallback
193 self._progressSteps = ['inset', 'skin', 'export']
197 self._engine_executable = getEngineFilename()
198 self._serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
199 self._serverPortNr = 0xC20A
200 for potential_port in xrange(0xC20A, 0xFFFF):
201 self._serverPortNr = potential_port
203 self._serversocket.bind(('127.0.0.1', self._serverPortNr))
206 print("Failed to listen on port: %d" % (self._serverPortNr))
208 print("Failed to listen on any port, this is a fatal error")
210 thread = threading.Thread(target=self._socketListenThread)
214 def _socketListenThread(self):
215 self._serversocket.listen(1)
216 print 'Listening for engine communications on %d' % (self._serverPortNr)
219 sock, _ = self._serversocket.accept()
220 thread = threading.Thread(target=self._socketConnectionThread, args=(sock,))
223 except socket.error, e:
224 if e.errno != errno.EINTR:
227 def _socketConnectionThread(self, sock):
237 cmd = struct.unpack('@i', data)[0]
238 if cmd == self.GUI_CMD_REQUEST_MESH:
239 meshInfo = self._modelData[0]
240 self._modelData = self._modelData[1:]
241 sock.sendall(struct.pack('@i', meshInfo[0]))
242 sock.sendall(meshInfo[1].tostring())
243 elif cmd == self.GUI_CMD_SEND_POLYGONS:
244 cnt = struct.unpack('@i', sock.recv(4))[0]
245 layerNr = struct.unpack('@i', sock.recv(4))[0]
246 layerNr += layerNrOffset
247 z = struct.unpack('@i', sock.recv(4))[0]
248 z = float(z) / 1000.0
249 typeNameLen = struct.unpack('@i', sock.recv(4))[0]
250 typeName = sock.recv(typeNameLen)
251 while len(self._result._polygons) < layerNr + 1:
252 self._result._polygons.append({})
253 polygons = self._result._polygons[layerNr]
254 if typeName not in polygons:
255 polygons[typeName] = []
256 for n in xrange(0, cnt):
257 length = struct.unpack('@i', sock.recv(4))[0]
259 while len(data) < length * 8 * 2:
260 recvData = sock.recv(length * 8 * 2 - len(data))
261 if len(recvData) < 1:
264 polygon2d = numpy.array(numpy.fromstring(data, numpy.int64), numpy.float32) / 1000.0
265 polygon2d = polygon2d.reshape((len(polygon2d) / 2, 2))
266 polygon = numpy.empty((len(polygon2d), 3), numpy.float32)
267 polygon[:,:-1] = polygon2d
269 polygons[typeName].append(polygon)
270 elif cmd == self.GUI_CMD_FINISH_OBJECT:
271 layerNrOffset = len(self._result._polygons)
273 print "Unknown command on socket: %x" % (cmd)
278 self._serversocket.close()
280 def abortEngine(self):
281 if self._process is not None:
283 self._process.terminate()
288 if self._thread is not None:
294 def runEngine(self, scene, overrides = None):
295 if len(scene.objects()) < 1:
297 self._thread = threading.Thread(target=self._runEngine, args=(scene, overrides, self._thread, pluginInfo.getPostProcessPluginConfig()))
298 self._thread.daemon = True
301 def _runEngine(self, scene, overrides, old_thread, pluginConfig):
302 if old_thread is not None:
303 if self._process is not None:
305 self._process.terminate()
312 for obj in scene.objects():
313 if scene.checkPlatform(obj):
314 extruderCount = max(extruderCount, len(obj._meshList))
316 extruderCount = max(extruderCount, profile.minimalExtruderCount())
318 if overrides is not None:
319 for k, v in overrides.items():
320 profile.setTempOverride(k, v)
321 commandList = [self._engine_executable, '-v', '-p']
323 engineSettings = self._engineSettings(extruderCount)
325 self._filamentMM = [0.0] * 4
329 for k, v in engineSettings.iteritems():
330 commandList += ['-s', '%s=%s' % (k, str(v))]
331 commandList += ['-g', '%d' % (self._serverPortNr)]
332 if overrides is not None:
333 profile.resetTempOverride()
336 hash = hashlib.sha512()
337 order = scene.printOrder()
339 pos = numpy.array(profile.getMachineCenterCoords()) * 1000
342 for obj in scene.objects():
343 if scene.checkPlatform(obj):
344 oMin = obj.getMinimum()[0:2] + obj.getPosition()
345 oMax = obj.getMaximum()[0:2] + obj.getPosition()
350 objMin[0] = min(oMin[0], objMin[0])
351 objMin[1] = min(oMin[1], objMin[1])
352 objMax[0] = max(oMax[0], objMax[0])
353 objMax[1] = max(oMax[1], objMax[1])
356 pos += (objMin + objMax) / 2.0 * 1000
357 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
359 vertexTotal = [0] * 4
361 for obj in scene.objects():
362 if scene.checkPlatform(obj):
363 meshMax = max(meshMax, len(obj._meshList))
364 for n in xrange(0, len(obj._meshList)):
365 vertexTotal[n] += obj._meshList[n].vertexCount
367 for n in xrange(0, meshMax):
368 verts = numpy.zeros((0, 3), numpy.float32)
369 for obj in scene.objects():
370 if scene.checkPlatform(obj):
371 if n < len(obj._meshList):
372 vertexes = (numpy.matrix(obj._meshList[n].vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
373 vertexes -= obj._drawOffset
374 vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
375 verts = numpy.concatenate((verts, vertexes))
376 hash.update(obj._meshList[n].vertexes.tostring())
377 engineModelData.append((vertexTotal[n], verts))
379 commandList += ['$' * meshMax]
383 obj = scene.objects()[n]
384 for mesh in obj._meshList:
385 engineModelData.append((mesh.vertexCount, mesh.vertexes))
386 hash.update(mesh.vertexes.tostring())
387 pos = obj.getPosition() * 1000
388 pos += numpy.array(profile.getMachineCenterCoords()) * 1000
389 commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
390 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
391 commandList += ['$' * len(obj._meshList)]
393 modelHash = hash.hexdigest()
394 if self._objCount < 1:
396 if self._thread != threading.currentThread():
399 self._modelData = engineModelData
401 self._process = self._runEngineProcess(commandList)
403 traceback.print_exc()
406 self._result = EngineResult()
407 self._result.addLog('Running: %s' % (' '.join(commandList)))
408 self._result.setHash(modelHash)
411 logThread = threading.Thread(target=self._watchStderr, args=(self._process.stderr,))
412 logThread.daemon = True
416 data = self._process.stdout.read(4096)
418 if self._thread != threading.currentThread():
419 self._process.terminate()
420 self._result._gcodeData.write(data)
421 data = self._process.stdout.read(4096)
423 returnCode = self._process.wait()
425 self._result.addLog("Slicer process returned : %d" % returnCode)
428 self._result.addReplaceTag('#P_TIME#', self._result.getPrintTime())
429 self._result.addReplaceTag('#F_AMNT#', self._result.getFilamentAmountMeters(0))
430 self._result.addReplaceTag('#F_WGHT#', math.floor(self._result.getFilamentWeight(0) * 1000.0))
431 self._result.addReplaceTag('#F_COST#', self._result.getFilamentCost(0))
432 self._result.applyReplaceTags()
435 plugin_error = pluginInfo.runPostProcessingPlugins(self._result, pluginConfig)
436 if plugin_error is not None:
437 self._result.addLog(plugin_error)
438 self._result.setFinished(True)
444 traceback.print_exc()
445 self._result.addLog("MemoryError")
449 with open(os.path.join(profile.getBasePath(), 'engine.log'), "w") as f:
450 for line in self._result.getLog():
455 def _watchStderr(self, stderr):
457 line = stderr.readline()
460 if line.startswith('Progress:'):
461 line = line.split(':')
462 if line[1] == 'process':
464 elif line[1] in self._progressSteps:
465 progressValue = float(line[2]) / float(line[3])
466 progressValue /= len(self._progressSteps)
467 progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
469 progressValue /= self._objCount
470 progressValue += 1.0 / self._objCount * objectNr
472 self._callback(progressValue)
475 elif line.startswith('Print time:'):
476 self._result._printTimeSeconds = int(line.split(':')[1].strip())
477 elif line.startswith('Filament:'):
478 self._result._filamentMM[0] = int(line.split(':')[1].strip())
479 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
480 radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
481 self._result._filamentMM[0] /= (math.pi * radius * radius)
482 elif line.startswith('Filament2:'):
483 self._result._filamentMM[1] = int(line.split(':')[1].strip())
484 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
485 radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
486 self._result._filamentMM[1] /= (math.pi * radius * radius)
487 elif line.startswith('Replace:'):
488 self._result.addReplaceTag(line.split(':')[1].strip(), line.split(':')[2].strip())
490 self._result.addLog(line)
491 line = stderr.readline()
493 def _engineSettings(self, extruderCount):
495 'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
496 'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000) if profile.getProfileSettingFloat('bottom_thickness') > 0.0 else int(profile.getProfileSettingFloat('layer_height') * 1000),
497 'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
498 'filamentFlow': int(profile.getProfileSettingFloat('filament_flow')),
499 'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
500 'layer0extrusionWidth': int(profile.calculateEdgeWidth() * profile.getProfileSettingFloat('layer0_width_factor') / 100 * 1000),
501 'insetCount': int(profile.calculateLineCount()),
502 'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
503 'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
504 'infillOverlap': int(profile.getProfileSettingFloat('fill_overlap')),
505 'initialSpeedupLayers': int(4),
506 'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
507 'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
508 'infillSpeed': int(profile.getProfileSettingFloat('infill_speed')) if int(profile.getProfileSettingFloat('infill_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
509 'inset0Speed': int(profile.getProfileSettingFloat('inset0_speed')) if int(profile.getProfileSettingFloat('inset0_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
510 'insetXSpeed': int(profile.getProfileSettingFloat('insetx_speed')) if int(profile.getProfileSettingFloat('insetx_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
511 'skinSpeed': int(profile.getProfileSettingFloat('solidarea_speed')) if int(profile.getProfileSettingFloat('solidarea_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
512 'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
513 'fanSpeedMin': int(profile.getProfileSettingFloat('fan_speed')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
514 'fanSpeedMax': int(profile.getProfileSettingFloat('fan_speed_max')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
515 'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(profile.getProfileSettingFloat('support_angle')),
516 'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
517 'supportLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('support_fill_rate')) if profile.getProfileSettingFloat('support_fill_rate') > 0 else -1,
518 'supportXYDistance': int(1000 * profile.getProfileSettingFloat('support_xy_distance')),
519 'supportZDistance': int(1000 * profile.getProfileSettingFloat('support_z_distance')),
520 '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),
521 'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000) if profile.getProfileSetting('retraction_enable') == 'True' else 0,
522 'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
523 'retractionMinimalDistance': int(profile.getProfileSettingFloat('retraction_min_travel') * 1000),
524 'retractionAmountExtruderSwitch': int(profile.getProfileSettingFloat('retraction_dual_amount') * 1000),
525 'retractionZHop': int(profile.getProfileSettingFloat('retraction_hop') * 1000),
526 'minimalExtrusionBeforeRetraction': int(profile.getProfileSettingFloat('retraction_minimal_extrusion') * 1000),
527 'multiVolumeOverlap': int(profile.getProfileSettingFloat('overlap_dual') * 1000),
528 'objectSink': max(0, int(profile.getProfileSettingFloat('object_sink') * 1000)),
529 'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
530 'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
531 'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
532 'startCode': profile.getAlterationFileContents('start.gcode', extruderCount),
533 'endCode': profile.getAlterationFileContents('end.gcode', extruderCount),
534 'preSwitchExtruderCode': profile.getAlterationFileContents('preSwitchExtruder.gcode', extruderCount),
535 'postSwitchExtruderCode': profile.getAlterationFileContents('postSwitchExtruder.gcode', extruderCount),
537 'extruderOffset[1].X': int(profile.getMachineSettingFloat('extruder_offset_x1') * 1000),
538 'extruderOffset[1].Y': int(profile.getMachineSettingFloat('extruder_offset_y1') * 1000),
539 'extruderOffset[2].X': int(profile.getMachineSettingFloat('extruder_offset_x2') * 1000),
540 'extruderOffset[2].Y': int(profile.getMachineSettingFloat('extruder_offset_y2') * 1000),
541 'extruderOffset[3].X': int(profile.getMachineSettingFloat('extruder_offset_x3') * 1000),
542 'extruderOffset[3].Y': int(profile.getMachineSettingFloat('extruder_offset_y3') * 1000),
543 'zOffset': int(profile.getMachineSettingFloat('extruder_z_offset') * 1000),
546 fanFullHeight = int(profile.getProfileSettingFloat('fan_full_height') * 1000)
547 settings['fanFullOnLayerNr'] = (fanFullHeight - settings['initialLayerThickness'] - 1) / settings['layerThickness'] + 1
548 if settings['fanFullOnLayerNr'] < 0:
549 settings['fanFullOnLayerNr'] = 0
550 if profile.getProfileSetting('retraction_combing') == 'All':
551 settings['enableCombing'] = 1
552 elif profile.getProfileSetting('retraction_combing') == 'No Skin':
553 settings['enableCombing'] = 2
555 settings['enableCombing'] = 0
556 if profile.getProfileSetting('support_type') == 'Lines':
557 settings['supportType'] = 1
559 if profile.getProfileSettingFloat('fill_density') == 0:
560 settings['sparseInfillLineDistance'] = -1
561 elif profile.getProfileSettingFloat('fill_density') == 100:
562 settings['sparseInfillLineDistance'] = settings['extrusionWidth']
563 #Set the up/down skins height to 10000 if we want a 100% filled object.
564 # This gives better results then normal 100% infill as the sparse and up/down skin have some overlap.
565 settings['downSkinCount'] = 10000
566 settings['upSkinCount'] = 10000
568 settings['sparseInfillLineDistance'] = int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density'))
569 if profile.getProfileSetting('platform_adhesion') == 'Brim':
570 settings['skirtDistance'] = 0
571 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('brim_line_count'))
572 elif profile.getProfileSetting('platform_adhesion') == 'Raft':
573 settings['skirtDistance'] = 0
574 settings['skirtLineCount'] = 0
575 settings['raftMargin'] = int(profile.getProfileSettingFloat('raft_margin') * 1000)
576 settings['raftLineSpacing'] = int(profile.getProfileSettingFloat('raft_line_spacing') * 1000)
577 settings['raftBaseThickness'] = int(profile.getProfileSettingFloat('raft_base_thickness') * 1000)
578 settings['raftBaseLinewidth'] = int(profile.getProfileSettingFloat('raft_base_linewidth') * 1000)
579 settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000)
580 settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000)
581 settings['raftInterfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000 * 2.0)
582 settings['raftAirGapLayer0'] = int(profile.getProfileSettingFloat('raft_airgap') * 1000 + profile.getProfileSettingFloat('raft_airgap_all') * 1000)
583 settings['raftAirGap'] = int(profile.getProfileSettingFloat('raft_airgap_all') * 1000)
584 settings['raftBaseSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
585 settings['raftFanSpeed'] = 0
586 settings['raftSurfaceThickness'] = int(profile.getProfileSettingFloat('raft_surface_thickness') * 1000)
587 settings['raftSurfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000)
588 settings['raftSurfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000)
589 settings['raftSurfaceLayers'] = int(profile.getProfileSettingFloat('raft_surface_layers'))
590 settings['raftSurfaceSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
592 settings['skirtDistance'] = int(profile.getProfileSettingFloat('skirt_gap') * 1000)
593 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('skirt_line_count'))
594 settings['skirtMinLength'] = int(profile.getProfileSettingFloat('skirt_minimal_length') * 1000)
596 if profile.getProfileSetting('fix_horrible_union_all_type_a') == 'True':
597 settings['fixHorrible'] |= 0x01
598 if profile.getProfileSetting('fix_horrible_union_all_type_b') == 'True':
599 settings['fixHorrible'] |= 0x02
600 if profile.getProfileSetting('fix_horrible_use_open_bits') == 'True':
601 settings['fixHorrible'] |= 0x10
602 if profile.getProfileSetting('fix_horrible_extensive_stitching') == 'True':
603 settings['fixHorrible'] |= 0x04
605 if settings['layerThickness'] <= 0:
606 settings['layerThickness'] = 1000
607 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
608 settings['gcodeFlavor'] = 1
609 elif profile.getMachineSetting('gcode_flavor') == 'MakerBot':
610 settings['gcodeFlavor'] = 2
611 elif profile.getMachineSetting('gcode_flavor') == 'BFB':
612 settings['gcodeFlavor'] = 3
613 elif profile.getMachineSetting('gcode_flavor') == 'Mach3/LinuxCNC':
614 settings['gcodeFlavor'] = 4
615 elif profile.getMachineSetting('gcode_flavor') == 'RepRap (Volumetric)':
616 settings['gcodeFlavor'] = 5
617 if profile.getProfileSetting('spiralize') == 'True':
618 settings['spiralizeMode'] = 1
619 if profile.getProfileSetting('simple_mode') == 'True':
620 settings['simpleMode'] = 1
621 if profile.getProfileSetting('wipe_tower') == 'True' and extruderCount > 1:
622 settings['wipeTowerSize'] = int(math.sqrt(profile.getProfileSettingFloat('wipe_tower_volume') * 1000 * 1000 * 1000 / settings['layerThickness']))
623 if profile.getProfileSetting('ooze_shield') == 'True':
624 settings['enableOozeShield'] = 1
627 def _runEngineProcess(self, cmdList):
629 if subprocess.mswindows:
630 su = subprocess.STARTUPINFO()
631 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
632 su.wShowWindow = subprocess.SW_HIDE
633 kwargs['startupinfo'] = su
634 kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
635 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)