Changed SD filename generation, made SD handling more reliable as a whole (cherry picked from commit 717ec041)
This commit is contained in:
parent
df05a80201
commit
4b05f125a0
8 changed files with 141 additions and 36 deletions
|
|
@ -70,6 +70,7 @@ class Printer():
|
|||
# sd handling
|
||||
self._sdPrinting = False
|
||||
self._sdStreaming = False
|
||||
self._sdFilelistAvailable = threading.Event()
|
||||
|
||||
self._selectedFile = None
|
||||
|
||||
|
|
@ -434,6 +435,7 @@ class Printer():
|
|||
|
||||
def mcSdFiles(self, files):
|
||||
self._sendTriggerUpdateCallbacks("gcodeFiles")
|
||||
self._sdFilelistAvailable.set()
|
||||
|
||||
def mcFileSelected(self, filename, filesize, sd):
|
||||
self._setJobData(filename, filesize, sd)
|
||||
|
|
@ -467,15 +469,15 @@ 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, absolutePath):
|
||||
from octoprint.util import isGcodeFileName
|
||||
from octoprint.util import isSTLFileName
|
||||
|
||||
if not self._comm or self._comm.isBusy():
|
||||
if not self._comm or self._comm.isBusy() or not self._comm.isSdReady():
|
||||
logging.error("No connection to printer or printer is busy")
|
||||
return
|
||||
|
||||
|
|
@ -492,32 +494,42 @@ class Printer():
|
|||
absolutePath, callBack, callBackArgs)
|
||||
|
||||
def streamSdFile(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
|
||||
sdFilename = filename[:filename.find(".")].lower()
|
||||
if len(sdFilename) > 8:
|
||||
sdFilename = sdFilename[:8]
|
||||
self._comm.startFileTransfer(path, sdFilename + ".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
|
||||
|
||||
|
|
|
|||
|
|
@ -471,7 +471,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)
|
||||
|
|
|
|||
|
|
@ -84,23 +84,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"),
|
||||
|
|
@ -111,6 +117,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");
|
||||
|
|
@ -148,7 +210,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) {
|
||||
|
|
@ -203,7 +265,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) {
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@
|
|||
</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_wrapper">
|
||||
{% 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_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>
|
||||
{% else %}
|
||||
<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>
|
||||
<input id="gcode_upload" type="file" name="gcode_file" class="fileinput-button" data-url="/ajax/gcodefiles/upload" data-bind="enable: loginState.isUser()">
|
||||
</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>
|
||||
<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>
|
||||
{% else %}
|
||||
<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 sys
|
||||
import time
|
||||
import re
|
||||
|
||||
from octoprint.settings import settings
|
||||
|
||||
|
|
@ -151,3 +152,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")
|
||||
|
|
|
|||
|
|
@ -611,6 +611,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
|
||||
|
|
@ -645,7 +646,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:
|
||||
|
|
@ -804,6 +805,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)
|
||||
|
|
@ -1162,4 +1164,4 @@ class PrintingGcodeFileInformation(PrintingFileInformation):
|
|||
|
||||
class StreamingGcodeFileInformation(PrintingGcodeFileInformation):
|
||||
def __init__(self, filename):
|
||||
PrintingGcodeFileInformation.__init__(self, filename, None)
|
||||
PrintingGcodeFileInformation.__init__(self, filename, None)
|
||||
|
|
@ -76,6 +76,7 @@ class VirtualPrinter():
|
|||
return
|
||||
else:
|
||||
self.lastN = linenumber
|
||||
data = data.split(None, 1)[1].strip()
|
||||
|
||||
data += "\n"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue