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 full_filename = os.path.abspath(os.path.join(base_search_path, '/'.join(['..'] * n), 'CuraEngine', 'build', search_filename))
47 if os.path.isfile(full_filename):
49 if os.path.isfile('/usr/bin/CuraEngine'):
50 return '/usr/bin/CuraEngine'
51 if os.path.isfile('/usr/local/bin/CuraEngine'):
52 return '/usr/local/bin/CuraEngine'
55 class EngineResult(object):
57 Result from running the CuraEngine.
58 Contains the engine log, polygons retrieved from the engine, the GCode and some meta-data.
62 self._gcodeData = BigDataStorage()
64 self._replaceInfo = {}
66 self._printTimeSeconds = None
67 self._filamentMM = [0.0] * 4
68 self._modelHash = None
69 self._profileString = profile.getProfileString()
70 self._preferencesString = profile.getPreferencesString()
71 self._gcodeInterpreter = gcodeInterpreter.gcode()
72 self._gcodeLoadThread = None
73 self._finished = False
75 def getFilamentWeight(self, e=0):
76 #Calculates the weight of the filament in kg
78 radius = float(profile.getProfileSetting('filament_diameter')) / 2
79 volumeM3 = (self._filamentMM[e] * (math.pi * radius * radius)) / (1000*1000*1000)
80 return volumeM3 * profile.getPreferenceFloat('filament_physical_density')
84 def getFilamentCost(self, e=0):
85 cost_kg = profile.getPreferenceFloat('filament_cost_kg')
86 cost_meter = profile.getPreferenceFloat('filament_cost_meter')
87 if cost_kg > 0.0 and cost_meter > 0.0:
88 return "%.2f / %.2f" % (self.getFilamentWeight(e) * cost_kg, self._filamentMM[e] / 1000.0 * cost_meter)
90 return "%.2f" % (self.getFilamentWeight(e) * cost_kg)
91 elif cost_meter > 0.0:
92 return "%.2f" % (self._filamentMM[e] / 1000.0 * cost_meter)
95 def getPrintTime(self):
96 if self._printTimeSeconds is None:
98 if int(self._printTimeSeconds / 60 / 60) < 1:
99 return _('%d minutes') % (int(self._printTimeSeconds / 60) % 60)
100 if int(self._printTimeSeconds / 60 / 60) == 1:
101 return _('%d hour %d minutes') % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
102 return _('%d hours %d minutes') % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60)
104 def getFilamentAmount(self, e=0):
105 if self._filamentMM[e] == 0.0:
107 return _('%0.2f meter %0.0f gram') % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0)
109 def getFilamentAmountMeters(self, e=0):
110 return float(self._filamentMM[e]) / 1000.0
113 return self._engineLog
116 self._gcodeData.seekStart()
117 return self._gcodeData
119 def setGCode(self, gcode):
120 self._gcodeData = BigDataStorage()
121 self._gcodeData.write(gcode)
122 self._replaceInfo = {}
124 def addLog(self, line):
125 self._engineLog.append(line)
127 def setHash(self, hash):
128 self._modelHash = hash
130 def addReplaceTag(self, key, value):
131 self._replaceInfo[key] = value
133 def applyReplaceTags(self):
134 self._gcodeData.replaceAtStart(self._replaceInfo)
136 def setFinished(self, result):
137 self._finished = result
139 def isFinished(self):
140 return self._finished
142 def getGCodeLayers(self, loadCallback):
143 if not self._finished:
145 if self._gcodeInterpreter.layerList is None and self._gcodeLoadThread is None:
146 self._gcodeInterpreter.progressCallback = self._gcodeInterpreterCallback
147 self._gcodeLoadThread = threading.Thread(target=lambda : self._gcodeInterpreter.load(self._gcodeData.clone()))
148 self._gcodeLoadCallback = loadCallback
149 self._gcodeLoadThread.daemon = True
150 self._gcodeLoadThread.start()
151 return self._gcodeInterpreter.layerList
153 def _gcodeInterpreterCallback(self, progress):
154 if len(self._gcodeInterpreter.layerList) % 5 == 0:
156 return self._gcodeLoadCallback(self, progress)
158 '''def submitInfoOnline(self):
159 if profile.getPreference('submit_slice_information') != 'True':
161 if version.isDevVersion():
164 'processor': platform.processor(),
165 'machine': platform.machine(),
166 'platform': platform.platform(),
167 'profile': self._profileString,
168 'preferences': self._preferencesString,
169 'modelhash': self._modelHash,
170 'version': version.getVersion(),
171 'printtime': self._printTimeSeconds,
172 'filament': ','.join(map(str, self._filamentMM)),
175 f = urllib2.urlopen("https://stats.youmagine.com/curastats/slice", data = urllib.urlencode(data), timeout = 1)
180 traceback.print_exc()'''
182 class Engine(object):
184 Class used to communicate with the CuraEngine.
185 The CuraEngine is ran as a 2nd process and reports back information trough stderr.
186 GCode trough stdout and has a socket connection for polygon information and loading the 3D model into the engine.
188 GUI_CMD_REQUEST_MESH = 0x01
189 GUI_CMD_SEND_POLYGONS = 0x02
190 GUI_CMD_FINISH_OBJECT = 0x03
192 def __init__(self, progressCallback):
195 self._callback = progressCallback
196 self._progressSteps = ['inset', 'skin', 'export']
200 self._engine_executable = getEngineFilename()
201 self._serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
202 self._serverPortNr = 0xC20A
203 for potential_port in xrange(0xC20A, 0xFFFF):
204 self._serverPortNr = potential_port
206 self._serversocket.bind(('127.0.0.1', self._serverPortNr))
209 print("Failed to listen on port: %d" % (self._serverPortNr))
211 print("Failed to listen on any port, this is a fatal error")
213 thread = threading.Thread(target=self._socketListenThread)
217 def _socketListenThread(self):
218 self._serversocket.listen(1)
219 print 'Listening for engine communications on %d' % (self._serverPortNr)
222 sock, _ = self._serversocket.accept()
223 thread = threading.Thread(target=self._socketConnectionThread, args=(sock,))
226 except socket.error, e:
227 if e.errno != errno.EINTR:
230 def _socketConnectionThread(self, sock):
240 cmd = struct.unpack('@i', data)[0]
241 if cmd == self.GUI_CMD_REQUEST_MESH:
242 meshInfo = self._modelData[0]
243 self._modelData = self._modelData[1:]
244 sock.sendall(struct.pack('@i', meshInfo[0]))
245 sock.sendall(meshInfo[1].tostring())
246 elif cmd == self.GUI_CMD_SEND_POLYGONS:
247 cnt = struct.unpack('@i', sock.recv(4))[0]
248 layerNr = struct.unpack('@i', sock.recv(4))[0]
249 layerNr += layerNrOffset
250 z = struct.unpack('@i', sock.recv(4))[0]
251 z = float(z) / 1000.0
252 typeNameLen = struct.unpack('@i', sock.recv(4))[0]
253 typeName = sock.recv(typeNameLen)
254 while len(self._result._polygons) < layerNr + 1:
255 self._result._polygons.append({})
256 polygons = self._result._polygons[layerNr]
257 if typeName not in polygons:
258 polygons[typeName] = []
259 for n in xrange(0, cnt):
260 length = struct.unpack('@i', sock.recv(4))[0]
262 while len(data) < length * 8 * 2:
263 recvData = sock.recv(length * 8 * 2 - len(data))
264 if len(recvData) < 1:
267 polygon2d = numpy.array(numpy.fromstring(data, numpy.int64), numpy.float32) / 1000.0
268 polygon2d = polygon2d.reshape((len(polygon2d) / 2, 2))
269 polygon = numpy.empty((len(polygon2d), 3), numpy.float32)
270 polygon[:,:-1] = polygon2d
272 polygons[typeName].append(polygon)
273 elif cmd == self.GUI_CMD_FINISH_OBJECT:
274 layerNrOffset = len(self._result._polygons)
276 print "Unknown command on socket: %x" % (cmd)
281 self._serversocket.close()
283 def abortEngine(self):
284 if self._process is not None:
286 self._process.terminate()
291 if self._thread is not None:
297 def runEngine(self, scene, overrides = None):
298 if len(scene.objects()) < 1:
300 self._thread = threading.Thread(target=self._runEngine, args=(scene, overrides, self._thread, pluginInfo.getPostProcessPluginConfig()))
301 self._thread.daemon = True
304 def _runEngine(self, scene, overrides, old_thread, pluginConfig):
305 if old_thread is not None:
306 if self._process is not None:
308 self._process.terminate()
315 for obj in scene.objects():
316 if scene.checkPlatform(obj):
317 extruderCount = max(extruderCount, len(obj._meshList))
319 extruderCount = max(extruderCount, profile.minimalExtruderCount())
321 if overrides is not None:
322 for k, v in overrides.items():
323 profile.setTempOverride(k, v)
324 commandList = [self._engine_executable, '-v', '-p']
326 engineSettings = self._engineSettings(extruderCount)
328 self._filamentMM = [0.0] * 4
332 for k, v in engineSettings.iteritems():
333 commandList += ['-s', '%s=%s' % (k, str(v))]
334 commandList += ['-g', '%d' % (self._serverPortNr)]
335 if overrides is not None:
336 profile.resetTempOverride()
339 hash = hashlib.sha512()
340 order = scene.printOrder()
342 pos = numpy.array(profile.getMachineCenterCoords()) * 1000
345 for obj in scene.objects():
346 if scene.checkPlatform(obj):
347 oMin = obj.getMinimum()[0:2] + obj.getPosition()
348 oMax = obj.getMaximum()[0:2] + obj.getPosition()
353 objMin[0] = min(oMin[0], objMin[0])
354 objMin[1] = min(oMin[1], objMin[1])
355 objMax[0] = max(oMax[0], objMax[0])
356 objMax[1] = max(oMax[1], objMax[1])
359 pos += (objMin + objMax) / 2.0 * 1000
360 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
362 vertexTotal = [0] * 4
364 for obj in scene.objects():
365 if scene.checkPlatform(obj):
366 meshMax = max(meshMax, len(obj._meshList))
367 for n in xrange(0, len(obj._meshList)):
368 vertexTotal[n] += obj._meshList[n].vertexCount
370 for n in xrange(0, meshMax):
371 verts = numpy.zeros((0, 3), numpy.float32)
372 for obj in scene.objects():
373 if scene.checkPlatform(obj):
374 if n < len(obj._meshList):
375 vertexes = (numpy.matrix(obj._meshList[n].vertexes, copy = False) * numpy.matrix(obj._matrix, numpy.float32)).getA()
376 vertexes -= obj._drawOffset
377 vertexes += numpy.array([obj.getPosition()[0], obj.getPosition()[1], 0.0])
378 verts = numpy.concatenate((verts, vertexes))
379 hash.update(obj._meshList[n].vertexes.tostring())
380 engineModelData.append((vertexTotal[n], verts))
382 commandList += ['$' * meshMax]
386 obj = scene.objects()[n]
387 for mesh in obj._meshList:
388 engineModelData.append((mesh.vertexCount, mesh.vertexes))
389 hash.update(mesh.vertexes.tostring())
390 pos = obj.getPosition() * 1000
391 pos += numpy.array(profile.getMachineCenterCoords()) * 1000
392 commandList += ['-m', ','.join(map(str, obj._matrix.getA().flatten()))]
393 commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
394 commandList += ['$' * len(obj._meshList)]
396 modelHash = hash.hexdigest()
397 if self._objCount < 1:
399 if self._thread != threading.currentThread():
402 self._modelData = engineModelData
404 self._process = self._runEngineProcess(commandList)
406 traceback.print_exc()
409 self._result = EngineResult()
410 self._result.addLog('Running: %s' % (' '.join(commandList)))
411 self._result.setHash(modelHash)
414 logThread = threading.Thread(target=self._watchStderr, args=(self._process.stderr,))
415 logThread.daemon = True
419 data = self._process.stdout.read(4096)
421 if self._thread != threading.currentThread():
422 self._process.terminate()
423 self._result._gcodeData.write(data)
424 data = self._process.stdout.read(4096)
426 returnCode = self._process.wait()
428 self._result.addLog("Slicer process returned : %d" % returnCode)
431 self._result.addReplaceTag('#P_TIME#', self._result.getPrintTime())
432 self._result.addReplaceTag('#F_AMNT#', self._result.getFilamentAmountMeters(0))
433 self._result.addReplaceTag('#F_WGHT#', math.floor(self._result.getFilamentWeight(0) * 1000.0))
434 self._result.addReplaceTag('#F_COST#', self._result.getFilamentCost(0))
435 self._result.applyReplaceTags()
438 plugin_error = pluginInfo.runPostProcessingPlugins(self._result, pluginConfig)
439 if plugin_error is not None:
440 self._result.addLog(plugin_error)
441 self._result.setFinished(True)
447 traceback.print_exc()
448 self._result.addLog("MemoryError")
452 with open(os.path.join(profile.getBasePath(), 'engine.log'), "w") as f:
453 for line in self._result.getLog():
458 def _watchStderr(self, stderr):
460 line = stderr.readline()
463 if line.startswith('Progress:'):
464 line = line.split(':')
465 if line[1] == 'process':
467 elif line[1] in self._progressSteps:
468 progressValue = float(line[2]) / float(line[3])
469 progressValue /= len(self._progressSteps)
470 progressValue += 1.0 / len(self._progressSteps) * self._progressSteps.index(line[1])
472 progressValue /= self._objCount
473 progressValue += 1.0 / self._objCount * objectNr
475 self._callback(progressValue)
478 elif line.startswith('Print time:'):
479 self._result._printTimeSeconds = int(line.split(':')[1].strip())
480 elif line.startswith('Filament:'):
481 self._result._filamentMM[0] = int(line.split(':')[1].strip())
482 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
483 radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
484 self._result._filamentMM[0] /= (math.pi * radius * radius)
485 elif line.startswith('Filament2:'):
486 self._result._filamentMM[1] = int(line.split(':')[1].strip())
487 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
488 radius = profile.getProfileSettingFloat('filament_diameter') / 2.0
489 self._result._filamentMM[1] /= (math.pi * radius * radius)
490 elif line.startswith('Replace:'):
491 self._result.addReplaceTag(line.split(':')[1].strip(), line.split(':')[2].strip())
493 self._result.addLog(line)
494 line = stderr.readline()
496 def _engineSettings(self, extruderCount):
498 'layerThickness': int(profile.getProfileSettingFloat('layer_height') * 1000),
499 'initialLayerThickness': int(profile.getProfileSettingFloat('bottom_thickness') * 1000) if profile.getProfileSettingFloat('bottom_thickness') > 0.0 else int(profile.getProfileSettingFloat('layer_height') * 1000),
500 'filamentDiameter': int(profile.getProfileSettingFloat('filament_diameter') * 1000),
501 'filamentFlow': int(profile.getProfileSettingFloat('filament_flow')),
502 'extrusionWidth': int(profile.calculateEdgeWidth() * 1000),
503 'layer0extrusionWidth': int(profile.calculateEdgeWidth() * profile.getProfileSettingFloat('layer0_width_factor') / 100 * 1000),
504 'insetCount': int(profile.calculateLineCount()),
505 'downSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_bottom') == 'True' else 0,
506 'upSkinCount': int(profile.calculateSolidLayerCount()) if profile.getProfileSetting('solid_top') == 'True' else 0,
507 'infillOverlap': int(profile.getProfileSettingFloat('fill_overlap')),
508 'initialSpeedupLayers': int(4),
509 'initialLayerSpeed': int(profile.getProfileSettingFloat('bottom_layer_speed')),
510 'printSpeed': int(profile.getProfileSettingFloat('print_speed')),
511 'infillSpeed': int(profile.getProfileSettingFloat('infill_speed')) if int(profile.getProfileSettingFloat('infill_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
512 'inset0Speed': int(profile.getProfileSettingFloat('inset0_speed')) if int(profile.getProfileSettingFloat('inset0_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
513 'insetXSpeed': int(profile.getProfileSettingFloat('insetx_speed')) if int(profile.getProfileSettingFloat('insetx_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
514 'skinSpeed': int(profile.getProfileSettingFloat('solidarea_speed')) if int(profile.getProfileSettingFloat('solidarea_speed')) > 0 else int(profile.getProfileSettingFloat('print_speed')),
515 'moveSpeed': int(profile.getProfileSettingFloat('travel_speed')),
516 'fanSpeedMin': int(profile.getProfileSettingFloat('fan_speed')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
517 'fanSpeedMax': int(profile.getProfileSettingFloat('fan_speed_max')) if profile.getProfileSetting('fan_enabled') == 'True' else 0,
518 'supportAngle': int(-1) if profile.getProfileSetting('support') == 'None' else int(profile.getProfileSettingFloat('support_angle')),
519 'supportEverywhere': int(1) if profile.getProfileSetting('support') == 'Everywhere' else int(0),
520 'supportLineDistance': int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('support_fill_rate')) if profile.getProfileSettingFloat('support_fill_rate') > 0 else -1,
521 'supportXYDistance': int(1000 * profile.getProfileSettingFloat('support_xy_distance')),
522 'supportZDistance': int(1000 * profile.getProfileSettingFloat('support_z_distance')),
523 '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),
524 'retractionAmount': int(profile.getProfileSettingFloat('retraction_amount') * 1000) if profile.getProfileSetting('retraction_enable') == 'True' else 0,
525 'retractionSpeed': int(profile.getProfileSettingFloat('retraction_speed')),
526 'retractionMinimalDistance': int(profile.getProfileSettingFloat('retraction_min_travel') * 1000),
527 'retractionAmountExtruderSwitch': int(profile.getProfileSettingFloat('retraction_dual_amount') * 1000),
528 'retractionZHop': int(profile.getProfileSettingFloat('retraction_hop') * 1000),
529 'minimalExtrusionBeforeRetraction': int(profile.getProfileSettingFloat('retraction_minimal_extrusion') * 1000),
530 'multiVolumeOverlap': int(profile.getProfileSettingFloat('overlap_dual') * 1000),
531 'objectSink': max(0, int(profile.getProfileSettingFloat('object_sink') * 1000)),
532 'minimalLayerTime': int(profile.getProfileSettingFloat('cool_min_layer_time')),
533 'minimalFeedrate': int(profile.getProfileSettingFloat('cool_min_feedrate')),
534 'coolHeadLift': 1 if profile.getProfileSetting('cool_head_lift') == 'True' else 0,
535 'startCode': profile.getAlterationFileContents('start.gcode', extruderCount),
536 'endCode': profile.getAlterationFileContents('end.gcode', extruderCount),
537 'preSwitchExtruderCode': profile.getAlterationFileContents('preSwitchExtruder.gcode', extruderCount),
538 'postSwitchExtruderCode': profile.getAlterationFileContents('postSwitchExtruder.gcode', extruderCount),
540 'extruderOffset[1].X': int(profile.getMachineSettingFloat('extruder_offset_x1') * 1000),
541 'extruderOffset[1].Y': int(profile.getMachineSettingFloat('extruder_offset_y1') * 1000),
542 'extruderOffset[2].X': int(profile.getMachineSettingFloat('extruder_offset_x2') * 1000),
543 'extruderOffset[2].Y': int(profile.getMachineSettingFloat('extruder_offset_y2') * 1000),
544 'extruderOffset[3].X': int(profile.getMachineSettingFloat('extruder_offset_x3') * 1000),
545 'extruderOffset[3].Y': int(profile.getMachineSettingFloat('extruder_offset_y3') * 1000),
546 'zOffset': int(profile.getMachineSettingFloat('extruder_z_offset') * 1000),
549 fanFullHeight = int(profile.getProfileSettingFloat('fan_full_height') * 1000)
550 settings['fanFullOnLayerNr'] = (fanFullHeight - settings['initialLayerThickness'] - 1) / settings['layerThickness'] + 1
551 if settings['fanFullOnLayerNr'] < 0:
552 settings['fanFullOnLayerNr'] = 0
553 if profile.getProfileSetting('retraction_combing') == 'All':
554 settings['enableCombing'] = 1
555 elif profile.getProfileSetting('retraction_combing') == 'No Skin':
556 settings['enableCombing'] = 2
558 settings['enableCombing'] = 0
559 if profile.getProfileSetting('support_type') == 'Lines':
560 settings['supportType'] = 1
562 if profile.getProfileSettingFloat('fill_density') == 0:
563 settings['sparseInfillLineDistance'] = -1
564 elif profile.getProfileSettingFloat('fill_density') == 100:
565 settings['sparseInfillLineDistance'] = settings['extrusionWidth']
566 #Set the up/down skins height to 10000 if we want a 100% filled object.
567 # This gives better results then normal 100% infill as the sparse and up/down skin have some overlap.
568 settings['downSkinCount'] = 10000
569 settings['upSkinCount'] = 10000
571 settings['sparseInfillLineDistance'] = int(100 * profile.calculateEdgeWidth() * 1000 / profile.getProfileSettingFloat('fill_density'))
572 if profile.getProfileSetting('perimeter_before_infill') == 'True':
573 settings['perimeterBeforeInfill'] = 1
575 settings['perimeterBeforeInfill'] = 0
576 if profile.getProfileSetting('platform_adhesion') == 'Brim':
577 settings['skirtDistance'] = 0
578 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('brim_line_count'))
579 elif profile.getProfileSetting('platform_adhesion') == 'Raft':
580 settings['skirtDistance'] = 0
581 settings['skirtLineCount'] = 0
582 settings['raftMargin'] = int(profile.getProfileSettingFloat('raft_margin') * 1000)
583 settings['raftLineSpacing'] = int(profile.getProfileSettingFloat('raft_line_spacing') * 1000)
584 settings['raftBaseThickness'] = int(profile.getProfileSettingFloat('raft_base_thickness') * 1000)
585 settings['raftBaseLinewidth'] = int(profile.getProfileSettingFloat('raft_base_linewidth') * 1000)
586 settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000)
587 settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000)
588 settings['raftInterfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000 * 2.0)
589 settings['raftAirGapLayer0'] = int(profile.getProfileSettingFloat('raft_airgap') * 1000 + profile.getProfileSettingFloat('raft_airgap_all') * 1000)
590 settings['raftAirGap'] = int(profile.getProfileSettingFloat('raft_airgap_all') * 1000)
591 settings['raftBaseSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
592 settings['raftFanSpeed'] = 0
593 settings['raftSurfaceThickness'] = int(profile.getProfileSettingFloat('raft_surface_thickness') * 1000)
594 settings['raftSurfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000)
595 settings['raftSurfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000)
596 settings['raftSurfaceLayers'] = int(profile.getProfileSettingFloat('raft_surface_layers'))
597 settings['raftSurfaceSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed'))
599 settings['skirtDistance'] = int(profile.getProfileSettingFloat('skirt_gap') * 1000)
600 settings['skirtLineCount'] = int(profile.getProfileSettingFloat('skirt_line_count'))
601 settings['skirtMinLength'] = int(profile.getProfileSettingFloat('skirt_minimal_length') * 1000)
603 if profile.getProfileSetting('fix_horrible_union_all_type_a') == 'True':
604 settings['fixHorrible'] |= 0x01
605 if profile.getProfileSetting('fix_horrible_union_all_type_b') == 'True':
606 settings['fixHorrible'] |= 0x02
607 if profile.getProfileSetting('fix_horrible_use_open_bits') == 'True':
608 settings['fixHorrible'] |= 0x10
609 if profile.getProfileSetting('fix_horrible_extensive_stitching') == 'True':
610 settings['fixHorrible'] |= 0x04
612 if settings['layerThickness'] <= 0:
613 settings['layerThickness'] = 1000
614 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
615 settings['gcodeFlavor'] = 1
616 elif profile.getMachineSetting('gcode_flavor') == 'MakerBot':
617 settings['gcodeFlavor'] = 2
618 elif profile.getMachineSetting('gcode_flavor') == 'BFB':
619 settings['gcodeFlavor'] = 3
620 elif profile.getMachineSetting('gcode_flavor') == 'Mach3/LinuxCNC':
621 settings['gcodeFlavor'] = 4
622 elif profile.getMachineSetting('gcode_flavor') == 'RepRap (Volumetric)':
623 settings['gcodeFlavor'] = 5
624 if profile.getProfileSetting('spiralize') == 'True':
625 settings['spiralizeMode'] = 1
626 if profile.getProfileSetting('simple_mode') == 'True':
627 settings['simpleMode'] = 1
628 if profile.getProfileSetting('wipe_tower') == 'True' and extruderCount > 1:
629 settings['wipeTowerSize'] = int(math.sqrt(profile.getProfileSettingFloat('wipe_tower_volume') * 1000 * 1000 * 1000 / settings['layerThickness']))
630 if profile.getProfileSetting('ooze_shield') == 'True':
631 settings['enableOozeShield'] = 1
634 def _runEngineProcess(self, cmdList):
636 if subprocess.mswindows:
637 su = subprocess.STARTUPINFO()
638 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
639 su.wShowWindow = subprocess.SW_HIDE
640 kwargs['startupinfo'] = su
641 kwargs['creationflags'] = 0x00004000 #BELOW_NORMAL_PRIORITY_CLASS
642 return subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)