From: daid Date: Fri, 26 Sep 2014 05:48:58 +0000 (+0200) Subject: Fix slicing really big GCode files on windows. X-Git-Tag: lulzbot-14.12~83 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=1ef08b8b1720f7e66e8ea91d5356577df7ed8274;p=cura.git Fix slicing really big GCode files on windows. --- diff --git a/Cura/cura.py b/Cura/cura.py index 50e9fd92..76558068 100644 --- a/Cura/cura.py +++ b/Cura/cura.py @@ -72,7 +72,12 @@ def main(): if not options.output: options.output = args[0] + profile.getGCodeExtension() with open(options.output, "wb") as f: - f.write(engine.getResult().getGCode()) + gcode = engine.getResult().getGCode() + while True: + data = gcode.read() + if len(data) == 0: + break + f.write(data) print 'GCode file saved : %s' % options.output engine.cleanup() diff --git a/Cura/gui/sceneView.py b/Cura/gui/sceneView.py index 447b5838..a28dd189 100644 --- a/Cura/gui/sceneView.py +++ b/Cura/gui/sceneView.py @@ -294,7 +294,7 @@ class SceneView(openglGui.glGuiPanel): connection.window = printWindow.printWindowBasic(self, connection) connection.window.Show() connection.window.Raise() - if not connection.loadGCodeData(StringIO.StringIO(self._engine.getResult().getGCode())): + if not connection.loadGCodeData(self._engine.getResult().getGCode()): if connection.isPrinting(): self.notification.message("Cannot start print, because other print still running.") else: @@ -316,17 +316,18 @@ class SceneView(openglGui.glGuiPanel): threading.Thread(target=self._saveGCode,args=(filename,)).start() def _saveGCode(self, targetFilename, ejectDrive = False): - data = self._engine.getResult().getGCode() + gcode = self._engine.getResult().getGCode() try: - size = float(len(data)) - fsrc = StringIO.StringIO(data) + size = float(len(gcode)) + read_pos = 0 with open(targetFilename, 'wb') as fdst: while 1: - buf = fsrc.read(16*1024) - if not buf: + buf = gcode.read(16*1024) + if len(buf) < 1: break + read_pos += len(buf) fdst.write(buf) - self.printButton.setProgressBar(float(fsrc.tell()) / size) + self.printButton.setProgressBar(read_pos / size) self._queueRefresh() except: import sys, traceback diff --git a/Cura/util/bigDataStorage.py b/Cura/util/bigDataStorage.py new file mode 100644 index 00000000..9150455c --- /dev/null +++ b/Cura/util/bigDataStorage.py @@ -0,0 +1,81 @@ +import cStringIO as StringIO + +class BigDataStorage(object): + """ + The StringIO from python aborts with an out-of-memory error after 250MB. + So the BigDataStorage stores data in multiple StringIOs to prevent this issue. + """ + def __init__(self): + self._active = StringIO.StringIO() + self._list = [self._active] + self._read_index = None + + def write(self, data): + self._active.write(data) + if self._active.tell() > 1024 * 1024 * 100: + self._active = StringIO.StringIO() + self._list.append(self._active) + + def seekStart(self): + self._active = self._list[0] + self._active.seek(0) + self._read_index = 0 + + def read(self, size=None): + ret = self._active.read(size) + if ret == '': + if self._read_index + 1 < len(self._list): + self._read_index += 1 + self._active = self._list[self._read_index] + self._active.seek(0) + ret = self._active.read(size) + return ret + + def replaceAtStart(self, key, value): + data = self._list[0].getvalue() + block0 = data[0:2048] + value = (value + ' ' * len(key))[:len(key)] + block0 = block0.replace(key, value) + self._list[0] = StringIO.StringIO() + self._list[0].write(block0) + self._list[0].write(data[2048:]) + + def __len__(self): + ret = 0 + for data in self._list: + pos = data.tell() + data.seek(0, 2) + ret += data.tell() + data.seek(pos) + return ret + + def __iter__(self): + self._iter_index = 0 + return self + + def next(self): + if self._iter_index < len(self._list): + ret = self._list[self._iter_index].readline() + if ret == '': + self._iter_index += 1 + if self._iter_index < len(self._list): + self._list[self._iter_index].seek(0) + return self.next() + return ret + raise StopIteration + + def tell(self): + pos = 0 + for data in self._list[:self._iter_index]: + pos += data.tell() + if self._iter_index < len(self._list): + pos += self._list[self._iter_index].tell() + return pos + + def clone(self): + clone = BigDataStorage() + clone._list = [] + for item in self._list: + clone._list.append(StringIO.StringIO(item.getvalue())) + clone._active = clone._list[-1] + return clone \ No newline at end of file diff --git a/Cura/util/gcodeInterpreter.py b/Cura/util/gcodeInterpreter.py index b187ea31..95b42d75 100644 --- a/Cura/util/gcodeInterpreter.py +++ b/Cura/util/gcodeInterpreter.py @@ -51,9 +51,9 @@ class gcode(object): elif type(data) is list: self._load(data) else: - data = data.getvalue() self._fileSize = len(data) - self._load(StringIO.StringIO(data)) + data.seekStart() + self._load(data) def calculateWeight(self): #Calculates the weight of the filament in kg diff --git a/Cura/util/pluginInfo.py b/Cura/util/pluginInfo.py index 7290c8bc..05b50a4f 100644 --- a/Cura/util/pluginInfo.py +++ b/Cura/util/pluginInfo.py @@ -129,7 +129,12 @@ def runPostProcessingPlugins(engineResult): if tempfilename is None: f = tempfile.NamedTemporaryFile(prefix='CuraPluginTemp', delete=False) tempfilename = f.name - f.write(engineResult.getGCode()) + gcode = engineResult.getGCode() + while True: + data = gcode.read() + if len(data) == 0: + break + f.write(data) f.close() locals = {'filename': tempfilename} diff --git a/Cura/util/sliceEngine.py b/Cura/util/sliceEngine.py index 9759720c..ff33cdf8 100644 --- a/Cura/util/sliceEngine.py +++ b/Cura/util/sliceEngine.py @@ -19,8 +19,8 @@ import hashlib import socket import struct import errno -import cStringIO as StringIO +from Cura.util.bigDataStorage import BigDataStorage from Cura.util import profile from Cura.util import pluginInfo from Cura.util import version @@ -34,6 +34,8 @@ def getEngineFilename(): if platform.system() == 'Windows': 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' + if version.isDevVersion() and os.path.exists('C:/Program Files (x86)/Cura_14.09/CuraEngine.exe'): + return 'C:/Program Files (x86)/Cura_14.09/CuraEngine.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')) @@ -53,7 +55,7 @@ class EngineResult(object): """ def __init__(self): self._engineLog = [] - self._gcodeData = StringIO.StringIO() + self._gcodeData = BigDataStorage() self._polygons = [] self._replaceInfo = {} self._success = False @@ -101,17 +103,12 @@ class EngineResult(object): 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 +118,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 +131,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()