X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=Cura%2Futil%2FsliceEngine.py;h=73d018941615aee7eb1b6ceeceba8a82823fa2cd;hb=fe5ccd83372d148b1519dd27dbb89ef11dedb1cf;hp=e6f8b16117412eeb96d823e50b3e46fd9add7a97;hpb=707f723f4bcc29dc4c076324c5d0573a3239285e;p=cura.git diff --git a/Cura/util/sliceEngine.py b/Cura/util/sliceEngine.py index e6f8b161..73d01894 100644 --- a/Cura/util/sliceEngine.py +++ b/Cura/util/sliceEngine.py @@ -8,19 +8,18 @@ import time import math import numpy import os -import warnings import threading import traceback import platform -import sys import urllib import urllib2 import hashlib import socket import struct import errno -import cStringIO as StringIO +import inspect +from Cura.util.bigDataStorage import BigDataStorage from Cura.util import profile from Cura.util import pluginInfo from Cura.util import version @@ -31,20 +30,24 @@ def getEngineFilename(): Finds and returns the path to the current engine executable. This is OS depended. :return: The full path to the engine executable. """ + base_search_path = os.path.dirname(inspect.getfile(getEngineFilename)) + search_filename = 'CuraEngine' if platform.system() == 'Windows': + search_filename += '.exe' if version.isDevVersion() and os.path.exists('C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe'): return 'C:/Software/Cura_SteamEngine/_bin/Release/Cura_SteamEngine.exe' - return os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine.exe')) - if hasattr(sys, 'frozen'): - return os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../..', 'CuraEngine')) + for n in xrange(0, 10): + full_filename = os.path.abspath(os.path.join(base_search_path, '/'.join(['..'] * n), search_filename)) + if os.path.isfile(full_filename): + return full_filename + full_filename = os.path.abspath(os.path.join(base_search_path, '/'.join(['..'] * n), 'CuraEngine', search_filename)) + if os.path.isfile(full_filename): + return full_filename if os.path.isfile('/usr/bin/CuraEngine'): return '/usr/bin/CuraEngine' if os.path.isfile('/usr/local/bin/CuraEngine'): return '/usr/local/bin/CuraEngine' - tempPath = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..', 'CuraEngine')) - if os.path.isdir(tempPath): - tempPath = os.path.join(tempPath,'CuraEngine') - return tempPath + return '' class EngineResult(object): """ @@ -53,7 +56,7 @@ class EngineResult(object): """ def __init__(self): self._engineLog = [] - self._gcodeData = StringIO.StringIO() + self._gcodeData = BigDataStorage() self._polygons = [] self._replaceInfo = {} self._success = False @@ -87,31 +90,26 @@ class EngineResult(object): if self._printTimeSeconds is None: return '' if int(self._printTimeSeconds / 60 / 60) < 1: - return '%d minutes' % (int(self._printTimeSeconds / 60) % 60) + return _('%d minutes') % (int(self._printTimeSeconds / 60) % 60) if int(self._printTimeSeconds / 60 / 60) == 1: - return '%d hour %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60) - return '%d hours %d minutes' % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60) + return _('%d hour %d minutes') % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60) + return _('%d hours %d minutes') % (int(self._printTimeSeconds / 60 / 60), int(self._printTimeSeconds / 60) % 60) def getFilamentAmount(self, e=0): if self._filamentMM[e] == 0.0: return None - return '%0.2f meter %0.0f gram' % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0) + return _('%0.2f meter %0.0f gram') % (float(self._filamentMM[e]) / 1000.0, self.getFilamentWeight(e) * 1000.0) def getLog(self): return self._engineLog def getGCode(self): - data = self._gcodeData.getvalue() - if len(self._replaceInfo) > 0: - block0 = data[0:2048] - for k, v in self._replaceInfo.items(): - v = (v + ' ' * len(k))[:len(k)] - block0 = block0.replace(k, v) - return block0 + data[2048:] - return data + self._gcodeData.seekStart() + return self._gcodeData def setGCode(self, gcode): - self._gcodeData = StringIO.StringIO(gcode) + self._gcodeData = BigDataStorage() + self._gcodeData.write(gcode) self._replaceInfo = {} def addLog(self, line): @@ -121,6 +119,9 @@ class EngineResult(object): self._modelHash = hash def setFinished(self, result): + if result: + for k, v in self._replaceInfo.items(): + self._gcodeData.replaceAtStart(k, v) self._finished = result def isFinished(self): @@ -131,7 +132,7 @@ class EngineResult(object): return None if self._gcodeInterpreter.layerList is None and self._gcodeLoadThread is None: self._gcodeInterpreter.progressCallback = self._gcodeInterpreterCallback - self._gcodeLoadThread = threading.Thread(target=lambda : self._gcodeInterpreter.load(self._gcodeData)) + self._gcodeLoadThread = threading.Thread(target=lambda : self._gcodeInterpreter.load(self._gcodeData.clone())) self._gcodeLoadCallback = loadCallback self._gcodeLoadThread.daemon = True self._gcodeLoadThread.start() @@ -142,7 +143,7 @@ class EngineResult(object): time.sleep(0.1) return self._gcodeLoadCallback(self, progress) - def submitInfoOnline(self): + '''def submitInfoOnline(self): if profile.getPreference('submit_slice_information') != 'True': return if version.isDevVersion(): @@ -155,14 +156,16 @@ class EngineResult(object): 'preferences': self._preferencesString, 'modelhash': self._modelHash, 'version': version.getVersion(), + 'printtime': self._printTimeSeconds, + 'filament': ','.join(map(str, self._filamentMM)), } try: - f = urllib2.urlopen("https://www.youmagine.com/curastats/", data = urllib.urlencode(data), timeout = 1) + f = urllib2.urlopen("https://stats.youmagine.com/curastats/slice", data = urllib.urlencode(data), timeout = 1) f.read() f.close() except: import traceback - traceback.print_exc() + traceback.print_exc()''' class Engine(object): """ @@ -182,19 +185,19 @@ class Engine(object): self._objCount = 0 self._result = None + self._engine_executable = getEngineFilename() self._serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._serverPortNr = 0xC20A - while True: + for potential_port in xrange(0xC20A, 0xFFFF): + self._serverPortNr = potential_port try: self._serversocket.bind(('127.0.0.1', self._serverPortNr)) - except: - print "Failed to listen on port: %d" % (self._serverPortNr) - self._serverPortNr += 1 - if self._serverPortNr > 0xFFFF: - print "Failed to listen on any port..." - break - else: break + except: + print("Failed to listen on port: %d" % (self._serverPortNr)) + else: + print("Failed to listen on any port, this is a fatal error") + exit(10) thread = threading.Thread(target=self._socketListenThread) thread.daemon = True thread.start() @@ -262,6 +265,7 @@ class Engine(object): def cleanup(self): self.abortEngine() + self.wait() self._serversocket.close() def abortEngine(self): @@ -270,9 +274,6 @@ class Engine(object): self._process.terminate() except: pass - if self._thread is not None: - self._thread.join() - self._thread = None def wait(self): if self._thread is not None: @@ -284,6 +285,17 @@ class Engine(object): def runEngine(self, scene): if len(scene.objects()) < 1: return + self._thread = threading.Thread(target=self._runEngine, args=(scene, self._thread, pluginInfo.getPostProcessPluginConfig())) + self._thread.daemon = True + self._thread.start() + + def _runEngine(self, scene, old_thread, pluginConfig): + if old_thread is not None: + if self._process is not None: + self._process.terminate() + old_thread.join() + self._callback(-1.0) + extruderCount = 1 for obj in scene.objects(): if scene.checkPlatform(obj): @@ -291,7 +303,7 @@ class Engine(object): extruderCount = max(extruderCount, profile.minimalExtruderCount()) - commandList = [getEngineFilename(), '-v', '-p'] + commandList = [self._engine_executable, '-v', '-p'] for k, v in self._engineSettings(extruderCount).iteritems(): commandList += ['-s', '%s=%s' % (k, str(v))] commandList += ['-g', '%d' % (self._serverPortNr)] @@ -355,25 +367,17 @@ class Engine(object): commandList += ['$' * len(obj._meshList)] self._objCount += 1 modelHash = hash.hexdigest() - if self._objCount > 0: - self._thread = threading.Thread(target=self._watchProcess, args=(commandList, self._thread, engineModelData, modelHash)) - self._thread.daemon = True - self._thread.start() + if self._objCount < 1: + return + if self._thread != threading.currentThread(): + return - def _watchProcess(self, commandList, oldThread, engineModelData, modelHash): - if oldThread is not None: - if self._process is not None: - self._process.terminate() - oldThread.join() - self._callback(-1.0) self._modelData = engineModelData try: self._process = self._runEngineProcess(commandList) except OSError: traceback.print_exc() return - if self._thread != threading.currentThread(): - self._process.terminate() self._result = EngineResult() self._result.addLog('Running: %s' % (' '.join(commandList))) @@ -384,25 +388,36 @@ class Engine(object): logThread.daemon = True logThread.start() - data = self._process.stdout.read(4096) - while len(data) > 0: - self._result._gcodeData.write(data) + try: data = self._process.stdout.read(4096) - - returnCode = self._process.wait() - logThread.join() - if returnCode == 0: - pluginError = pluginInfo.runPostProcessingPlugins(self._result) - if pluginError is not None: - print pluginError - self._result.addLog(pluginError) - self._result.setFinished(True) - self._callback(1.0) - else: - for line in self._result.getLog(): - print line + while len(data) > 0: + if self._thread != threading.currentThread(): + self._process.terminate() + self._result._gcodeData.write(data) + data = self._process.stdout.read(4096) + + returnCode = self._process.wait() + logThread.join() + self._result.addLog("Slicer process returned : %d" % returnCode) + if returnCode == 0: + plugin_error = pluginInfo.runPostProcessingPlugins(self._result, pluginConfig) + if plugin_error is not None: + self._result.addLog(plugin_error) + self._result.setFinished(True) + self._callback(1.0) + else: + self._callback(-1.0) + self._process = None + except MemoryError: + self._result.addLog("MemoryError") self._callback(-1.0) - self._process = None + finally: + try: + with open(os.path.join(profile.getBasePath(), 'engine.log'), "w") as f: + for line in self._result.getLog(): + f.write(line + "\n") + except: + pass def _watchStderr(self, stderr): objectNr = 0 @@ -492,6 +507,7 @@ class Engine(object): 'extruderOffset[2].Y': int(profile.getMachineSettingFloat('extruder_offset_y2') * 1000), 'extruderOffset[3].X': int(profile.getMachineSettingFloat('extruder_offset_x3') * 1000), 'extruderOffset[3].Y': int(profile.getMachineSettingFloat('extruder_offset_y3') * 1000), + 'zOffset': int(profile.getMachineSettingFloat('extruder_z_offset') * 1000), 'fixHorrible': 0, } fanFullHeight = int(profile.getProfileSettingFloat('fan_full_height') * 1000) @@ -524,12 +540,13 @@ class Engine(object): settings['raftInterfaceThickness'] = int(profile.getProfileSettingFloat('raft_interface_thickness') * 1000) settings['raftInterfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000) settings['raftInterfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_interface_linewidth') * 1000 * 2.0) - settings['raftAirGapLayer0'] = int(profile.getProfileSettingFloat('raft_airgap') * 1000) + settings['raftAirGapLayer0'] = int(profile.getProfileSettingFloat('raft_airgap') * 1000 + profile.getProfileSettingFloat('raft_airgap_all') * 1000) + settings['raftAirGap'] = int(profile.getProfileSettingFloat('raft_airgap_all') * 1000) settings['raftBaseSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed')) - settings['raftFanSpeed'] = 100 - settings['raftSurfaceThickness'] = settings['raftInterfaceThickness'] - settings['raftSurfaceLinewidth'] = int(profile.calculateEdgeWidth() * 1000) - settings['raftSurfaceLineSpacing'] = int(profile.calculateEdgeWidth() * 1000 * 0.9) + settings['raftFanSpeed'] = 0 + settings['raftSurfaceThickness'] = int(profile.getProfileSettingFloat('raft_surface_thickness') * 1000) + settings['raftSurfaceLinewidth'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000) + settings['raftSurfaceLineSpacing'] = int(profile.getProfileSettingFloat('raft_surface_linewidth') * 1000) settings['raftSurfaceLayers'] = int(profile.getProfileSettingFloat('raft_surface_layers')) settings['raftSurfaceSpeed'] = int(profile.getProfileSettingFloat('bottom_layer_speed')) else: