Changed SD filename generation, made SD handling more reliable as a whole
This commit is contained in:
parent
63555d2c4b
commit
717ec0419f
8 changed files with 139 additions and 31 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue