From 717ec0419fedf4f7cf22d58c41f5edd6a6cea94b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 23 Sep 2013 17:59:22 +0200 Subject: [PATCH] Changed SD filename generation, made SD handling more reliable as a whole --- octoprint/printer.py | 33 ++++++++--- octoprint/server.py | 2 +- octoprint/static/js/app/main.js | 94 +++++++++++++++++++++++++----- octoprint/templates/dialogs.jinja2 | 4 +- octoprint/templates/index.jinja2 | 4 +- octoprint/util/__init__.py | 28 +++++++++ octoprint/util/comm.py | 4 +- octoprint/util/virtual.py | 1 + 8 files changed, 139 insertions(+), 31 deletions(-) diff --git a/octoprint/printer.py b/octoprint/printer.py index 323546ad..9429bbc1 100644 --- a/octoprint/printer.py +++ b/octoprint/printer.py @@ -69,6 +69,7 @@ class Printer(): # sd handling self._sdPrinting = False self._sdStreaming = False + self._sdFilelistAvailable = threading.Event() self._selectedFile = None @@ -432,6 +433,7 @@ class Printer(): def mcSdFiles(self, files): self._sendTriggerUpdateCallbacks("gcodeFiles") + self._sdFilelistAvailable.set() def mcFileSelected(self, filename, filesize, sd): self._setJobData(filename, filesize, sd) @@ -465,34 +467,47 @@ class Printer(): #~~ sd file handling def getSdFiles(self): - if self._comm is None: - return + if self._comm is None or not self._comm.isSdReady(): + return [] return self._comm.getSdFiles() def addSdFile(self, filename, path): - if not self._comm or self._comm.isBusy(): + if not self._comm or self._comm.isBusy() or not self._comm.isSdReady(): return - self._comm.startFileTransfer(path, filename[:8].lower() + ".gco") + + self.refreshSdFiles(blocking=True) + existingSdFiles = self._comm.getSdFiles() + + sdFilename = util.getDosFilename(filename, existingSdFiles) + self._comm.startFileTransfer(path, sdFilename) def deleteSdFile(self, filename): - if not self._comm: + if not self._comm or not self._comm.isSdReady(): return self._comm.deleteSdFile(filename) def initSdCard(self): - if not self._comm: + if not self._comm or self._comm.isSdReady(): return self._comm.initSdCard() def releaseSdCard(self): - if not self._comm: + if not self._comm or not self._comm.isSdReady(): return self._comm.releaseSdCard() - def refreshSdFiles(self): - if not self._comm: + def refreshSdFiles(self, blocking=False): + """ + Refreshs the list of file stored on the SD card attached to printer (if available and printer communication + available). Optional blocking parameter allows making the method block (max 10s) until the file list has been + received (and can be accessed via self._comm.getSdFiles()). Defaults to a asynchronous operation. + """ + if not self._comm or not self._comm.isSdReady(): return + self._sdFilelistAvailable.clear() self._comm.refreshSdFiles() + if blocking: + self._sdFilelistAvailable.wait(10000) #~~ state reports diff --git a/octoprint/server.py b/octoprint/server.py index 54ff8baa..730c8e46 100644 --- a/octoprint/server.py +++ b/octoprint/server.py @@ -451,7 +451,7 @@ def deleteGcodeFile(): printer.unselectFile() if not (currentFilename == filename and currentSd == sd and (printer.isPrinting() or printer.isPaused())): - if currentSd: + if sd: printer.deleteSdFile(filename) else: gcodeManager.removeFile(filename) diff --git a/octoprint/static/js/app/main.js b/octoprint/static/js/app/main.js index ee11483f..795be042 100644 --- a/octoprint/static/js/app/main.js +++ b/octoprint/static/js/app/main.js @@ -82,23 +82,29 @@ $(function() { } } - var localTarget; - if (CONFIG_SD_SUPPORT) { - localTarget = $("#drop_locally"); - } else { - localTarget = $("#drop"); + function enable_local_dropzone() { + $("#gcode_upload").fileupload({ + dataType: "json", + dropZone: localTarget, + formData: {target: "local"}, + done: gcode_upload_done, + fail: gcode_upload_fail, + progressall: gcode_upload_progress + }); } - $("#gcode_upload").fileupload({ - dataType: "json", - dropZone: localTarget, - formData: {target: "local"}, - done: gcode_upload_done, - fail: gcode_upload_fail, - progressall: gcode_upload_progress - }); + function disable_local_dropzone() { + $("#gcode_upload").fileupload({ + dataType: "json", + dropZone: null, + formData: {target: "local"}, + done: gcode_upload_done, + fail: gcode_upload_fail, + progressall: gcode_upload_progress + }); + } - if (CONFIG_SD_SUPPORT) { + function enable_sd_dropzone() { $("#gcode_upload_sd").fileupload({ dataType: "json", dropZone: $("#drop_sd"), @@ -109,6 +115,62 @@ $(function() { }); } + function disable_sd_dropzone() { + $("#gcode_upload_sd").fileupload({ + dataType: "json", + dropZone: null, + formData: {target: "sd"}, + done: gcode_upload_done, + fail: gcode_upload_fail, + progressall: gcode_upload_progress + }); + } + + var localTarget; + if (CONFIG_SD_SUPPORT) { + localTarget = $("#drop_locally"); + } else { + localTarget = $("#drop"); + } + + loginStateViewModel.isUser.subscribe(function(newValue) { + if (newValue === true) { + enable_local_dropzone(); + } else { + disable_local_dropzone(); + } + }); + + if (loginStateViewModel.isUser()) { + enable_local_dropzone(); + } else { + disable_local_dropzone(); + } + + if (CONFIG_SD_SUPPORT) { + printerStateViewModel.isSdReady.subscribe(function(newValue) { + if (newValue === true && loginStateViewModel.isUser()) { + enable_sd_dropzone(); + } else { + disable_sd_dropzone(); + } + }); + + loginStateViewModel.isUser.subscribe(function(newValue) { + if (newValue === true && printerStateViewModel.isSdReady()) { + enable_sd_dropzone(); + } else { + disable_sd_dropzone(); + } + }); + + if (printerStateViewModel.isSdReady() && loginStateViewModel.isUser()) { + enable_sd_dropzone(); + } else { + disable_sd_dropzone(); + } + } + $(document).bind("dragover", function (e) { var dropOverlay = $("#drop_overlay"); var dropZone = $("#drop"); @@ -146,7 +208,7 @@ $(function() { if (foundLocal) { dropZoneLocalBackground.addClass("hover"); dropZoneSdBackground.removeClass("hover"); - } else if (foundSd) { + } else if (foundSd && printerStateViewModel.isSdReady()) { dropZoneSdBackground.addClass("hover"); dropZoneLocalBackground.removeClass("hover"); } else if (found) { @@ -201,7 +263,7 @@ $(function() { ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog")); ko.applyBindings(navigationViewModel, document.getElementById("navbar")); ko.applyBindings(appearanceViewModel, document.getElementsByTagName("head")[0]); - ko.applyBindings(loginStateViewModel, document.getElementById("drop_overlay")); + ko.applyBindings(printerStateViewModel, document.getElementById("drop_overlay")); var timelapseElement = document.getElementById("timelapse"); if (timelapseElement) { diff --git a/octoprint/templates/dialogs.jinja2 b/octoprint/templates/dialogs.jinja2 index ca28988e..97a3ea09 100644 --- a/octoprint/templates/dialogs.jinja2 +++ b/octoprint/templates/dialogs.jinja2 @@ -17,13 +17,13 @@ -
+
{% if enableSdSupport %}

Upload locally
-

Upload to SD
+

Upload to SD
(SD not initialized)
{% else %}

Upload
diff --git a/octoprint/templates/index.jinja2 b/octoprint/templates/index.jinja2 index 320252a3..8ffec32d 100644 --- a/octoprint/templates/index.jinja2 +++ b/octoprint/templates/index.jinja2 @@ -211,10 +211,10 @@ Upload - + Upload to SD - + {% else %} diff --git a/octoprint/util/__init__.py b/octoprint/util/__init__.py index 23d8a238..ff6e5924 100644 --- a/octoprint/util/__init__.py +++ b/octoprint/util/__init__.py @@ -6,6 +6,7 @@ import os import traceback import sys import time +import re from octoprint.settings import settings @@ -110,3 +111,30 @@ def getRemoteAddress(request): if forwardedFor is not None: return forwardedFor.split(",")[0] return request.remote_addr + + +def getDosFilename(input, existingFilenames, extension=None): + if input is None: + return None + + if extension is None: + extension = "gco" + + filename, ext = input.rsplit(".", 1) + return findCollisionfreeName(filename, extension, existingFilenames) + + +def findCollisionfreeName(input, extension, existingFilenames): + filename = re.sub(r"\s+", "_", input.lower().translate(None, ".\"/\\[]:;=,")) + + counter = 1 + power = 1 + while counter < (10 * power): + result = filename[:(6 - power + 1)] + "~" + str(counter) + "." + extension + if result not in existingFilenames: + return result + counter += 1 + if counter == 10 * power: + power += 1 + + raise ValueError("Can't create a collision free filename") diff --git a/octoprint/util/comm.py b/octoprint/util/comm.py index db555393..18cc3cd7 100644 --- a/octoprint/util/comm.py +++ b/octoprint/util/comm.py @@ -609,6 +609,7 @@ class MachineCom(object): # anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s" self._printSection = "CUSTOM" self._changeState(self.STATE_PRINTING) + line = "ok" elif 'Done printing file' in line: # printer is reporting file finished printing self._sdFilePos = 0 @@ -641,7 +642,7 @@ class MachineCom(object): pass ##~~ Parsing for pause triggers - if pauseTriggers: + if pauseTriggers and not self.isStreaming(): if "enable" in pauseTriggers.keys() and pauseTriggers["enable"].search(line) is not None: self.setPause(True) elif "disable" in pauseTriggers.keys() and pauseTriggers["disable"].search(line) is not None: @@ -800,6 +801,7 @@ class MachineCom(object): self._callback.mcFileTransferDone() self._changeState(self.STATE_OPERATIONAL) eventManager().fire("TransferDone", filename) + self.refreshSdFiles() else: self._callback.mcPrintjobDone() self._changeState(self.STATE_OPERATIONAL) diff --git a/octoprint/util/virtual.py b/octoprint/util/virtual.py index 6a445593..cc70578a 100644 --- a/octoprint/util/virtual.py +++ b/octoprint/util/virtual.py @@ -76,6 +76,7 @@ class VirtualPrinter(): return else: self.lastN = linenumber + data = data.split(None, 1)[1].strip() data += "\n"