Changed SD filename generation, made SD handling more reliable as a whole

This commit is contained in:
Gina Häußge 2013-09-23 17:59:22 +02:00
parent 63555d2c4b
commit 717ec0419f
8 changed files with 139 additions and 31 deletions

View file

@ -69,6 +69,7 @@ class Printer():
# sd handling # sd handling
self._sdPrinting = False self._sdPrinting = False
self._sdStreaming = False self._sdStreaming = False
self._sdFilelistAvailable = threading.Event()
self._selectedFile = None self._selectedFile = None
@ -432,6 +433,7 @@ class Printer():
def mcSdFiles(self, files): def mcSdFiles(self, files):
self._sendTriggerUpdateCallbacks("gcodeFiles") self._sendTriggerUpdateCallbacks("gcodeFiles")
self._sdFilelistAvailable.set()
def mcFileSelected(self, filename, filesize, sd): def mcFileSelected(self, filename, filesize, sd):
self._setJobData(filename, filesize, sd) self._setJobData(filename, filesize, sd)
@ -465,34 +467,47 @@ class Printer():
#~~ sd file handling #~~ sd file handling
def getSdFiles(self): def getSdFiles(self):
if self._comm is None: if self._comm is None or not self._comm.isSdReady():
return return []
return self._comm.getSdFiles() return self._comm.getSdFiles()
def addSdFile(self, filename, path): 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 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): def deleteSdFile(self, filename):
if not self._comm: if not self._comm or not self._comm.isSdReady():
return return
self._comm.deleteSdFile(filename) self._comm.deleteSdFile(filename)
def initSdCard(self): def initSdCard(self):
if not self._comm: if not self._comm or self._comm.isSdReady():
return return
self._comm.initSdCard() self._comm.initSdCard()
def releaseSdCard(self): def releaseSdCard(self):
if not self._comm: if not self._comm or not self._comm.isSdReady():
return return
self._comm.releaseSdCard() self._comm.releaseSdCard()
def refreshSdFiles(self): def refreshSdFiles(self, blocking=False):
if not self._comm: """
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 return
self._sdFilelistAvailable.clear()
self._comm.refreshSdFiles() self._comm.refreshSdFiles()
if blocking:
self._sdFilelistAvailable.wait(10000)
#~~ state reports #~~ state reports

View file

@ -451,7 +451,7 @@ def deleteGcodeFile():
printer.unselectFile() printer.unselectFile()
if not (currentFilename == filename and currentSd == sd and (printer.isPrinting() or printer.isPaused())): if not (currentFilename == filename and currentSd == sd and (printer.isPrinting() or printer.isPaused())):
if currentSd: if sd:
printer.deleteSdFile(filename) printer.deleteSdFile(filename)
else: else:
gcodeManager.removeFile(filename) gcodeManager.removeFile(filename)

View file

@ -82,23 +82,29 @@ $(function() {
} }
} }
var localTarget; function enable_local_dropzone() {
if (CONFIG_SD_SUPPORT) { $("#gcode_upload").fileupload({
localTarget = $("#drop_locally"); dataType: "json",
} else { dropZone: localTarget,
localTarget = $("#drop"); formData: {target: "local"},
done: gcode_upload_done,
fail: gcode_upload_fail,
progressall: gcode_upload_progress
});
} }
$("#gcode_upload").fileupload({ function disable_local_dropzone() {
dataType: "json", $("#gcode_upload").fileupload({
dropZone: localTarget, dataType: "json",
formData: {target: "local"}, dropZone: null,
done: gcode_upload_done, formData: {target: "local"},
fail: gcode_upload_fail, done: gcode_upload_done,
progressall: gcode_upload_progress fail: gcode_upload_fail,
}); progressall: gcode_upload_progress
});
}
if (CONFIG_SD_SUPPORT) { function enable_sd_dropzone() {
$("#gcode_upload_sd").fileupload({ $("#gcode_upload_sd").fileupload({
dataType: "json", dataType: "json",
dropZone: $("#drop_sd"), 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) { $(document).bind("dragover", function (e) {
var dropOverlay = $("#drop_overlay"); var dropOverlay = $("#drop_overlay");
var dropZone = $("#drop"); var dropZone = $("#drop");
@ -146,7 +208,7 @@ $(function() {
if (foundLocal) { if (foundLocal) {
dropZoneLocalBackground.addClass("hover"); dropZoneLocalBackground.addClass("hover");
dropZoneSdBackground.removeClass("hover"); dropZoneSdBackground.removeClass("hover");
} else if (foundSd) { } else if (foundSd && printerStateViewModel.isSdReady()) {
dropZoneSdBackground.addClass("hover"); dropZoneSdBackground.addClass("hover");
dropZoneLocalBackground.removeClass("hover"); dropZoneLocalBackground.removeClass("hover");
} else if (found) { } else if (found) {
@ -201,7 +263,7 @@ $(function() {
ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog")); ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog"));
ko.applyBindings(navigationViewModel, document.getElementById("navbar")); ko.applyBindings(navigationViewModel, document.getElementById("navbar"));
ko.applyBindings(appearanceViewModel, document.getElementsByTagName("head")[0]); 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"); var timelapseElement = document.getElementById("timelapse");
if (timelapseElement) { if (timelapseElement) {

View file

@ -17,13 +17,13 @@
</div> </div>
</div> </div>
<div id="drop_overlay" data-bind="visible: isUser()"> <div id="drop_overlay" data-bind="visible: loginState.isUser()">
<div id="drop_overlay_background"></div> <div id="drop_overlay_background"></div>
<div id="drop_overlay_wrapper"> <div id="drop_overlay_wrapper">
{% if enableSdSupport %} {% if enableSdSupport %}
<div class="dropzone" id="drop_locally"><span class="centered"><i class="icon-upload-alt"></i><br>Upload locally</span></div> <div class="dropzone" id="drop_locally"><span class="centered"><i class="icon-upload-alt"></i><br>Upload locally</span></div>
<div class="dropzone_background" id="drop_locally_background"></div> <div class="dropzone_background" id="drop_locally_background"></div>
<div class="dropzone" id="drop_sd"><span class="centered"><i class="icon-upload-alt"></i><br>Upload to SD</span></div> <div class="dropzone" id="drop_sd"><span class="centered"><i class="icon-upload-alt"></i><br>Upload to SD<br><small data-bind="visible: !isSdReady()">(SD not initialized)</small></span></div>
<div class="dropzone_background" id="drop_sd_background"></div> <div class="dropzone_background" id="drop_sd_background"></div>
{% else %} {% else %}
<div class="dropzone" id="drop"><span class="centered"><i class="icon-upload-alt"></i><br>Upload</span></div> <div class="dropzone" id="drop"><span class="centered"><i class="icon-upload-alt"></i><br>Upload</span></div>

View file

@ -211,10 +211,10 @@
<span>Upload</span> <span>Upload</span>
<input id="gcode_upload" type="file" name="gcode_file" class="fileinput-button" data-url="/ajax/gcodefiles/upload" data-bind="enable: loginState.isUser()"> <input id="gcode_upload" type="file" name="gcode_file" class="fileinput-button" data-url="/ajax/gcodefiles/upload" data-bind="enable: loginState.isUser()">
</span> </span>
<span class="btn btn-primary fileinput-button span6" data-bind="css: {disabled: !$root.loginState.isUser()}" style="margin-bottom: 10px"> <span class="btn btn-primary fileinput-button span6" data-bind="css: {disabled: !$root.loginState.isUser() || !$root.isSdReady()}" style="margin-bottom: 10px">
<i class="icon-upload-alt icon-white"></i> <i class="icon-upload-alt icon-white"></i>
<span>Upload to SD</span> <span>Upload to SD</span>
<input id="gcode_upload_sd" type="file" name="gcode_file" class="fileinput-button" data-url="/ajax/gcodefiles/upload" data-bind="enable: loginState.isUser()"> <input id="gcode_upload_sd" type="file" name="gcode_file" class="fileinput-button" data-url="/ajax/gcodefiles/upload" data-bind="enable: loginState.isUser() && isSdReady()">
</span> </span>
{% else %} {% else %}
<span class="btn btn-primary fileinput-button span12" data-bind="css: {disabled: !$root.loginState.isUser()}" style="margin-bottom: 10px"> <span class="btn btn-primary fileinput-button span12" data-bind="css: {disabled: !$root.loginState.isUser()}" style="margin-bottom: 10px">

View file

@ -6,6 +6,7 @@ import os
import traceback import traceback
import sys import sys
import time import time
import re
from octoprint.settings import settings from octoprint.settings import settings
@ -110,3 +111,30 @@ def getRemoteAddress(request):
if forwardedFor is not None: if forwardedFor is not None:
return forwardedFor.split(",")[0] return forwardedFor.split(",")[0]
return request.remote_addr 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")

View file

@ -609,6 +609,7 @@ class MachineCom(object):
# anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s" # anwer to M28, at least on Marlin, Repetier and Sprinter: "Writing to file: %s"
self._printSection = "CUSTOM" self._printSection = "CUSTOM"
self._changeState(self.STATE_PRINTING) self._changeState(self.STATE_PRINTING)
line = "ok"
elif 'Done printing file' in line: elif 'Done printing file' in line:
# printer is reporting file finished printing # printer is reporting file finished printing
self._sdFilePos = 0 self._sdFilePos = 0
@ -641,7 +642,7 @@ class MachineCom(object):
pass pass
##~~ Parsing for pause triggers ##~~ 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: if "enable" in pauseTriggers.keys() and pauseTriggers["enable"].search(line) is not None:
self.setPause(True) self.setPause(True)
elif "disable" in pauseTriggers.keys() and pauseTriggers["disable"].search(line) is not None: elif "disable" in pauseTriggers.keys() and pauseTriggers["disable"].search(line) is not None:
@ -800,6 +801,7 @@ class MachineCom(object):
self._callback.mcFileTransferDone() self._callback.mcFileTransferDone()
self._changeState(self.STATE_OPERATIONAL) self._changeState(self.STATE_OPERATIONAL)
eventManager().fire("TransferDone", filename) eventManager().fire("TransferDone", filename)
self.refreshSdFiles()
else: else:
self._callback.mcPrintjobDone() self._callback.mcPrintjobDone()
self._changeState(self.STATE_OPERATIONAL) self._changeState(self.STATE_OPERATIONAL)

View file

@ -76,6 +76,7 @@ class VirtualPrinter():
return return
else: else:
self.lastN = linenumber self.lastN = linenumber
data = data.split(None, 1)[1].strip()
data += "\n" data += "\n"