diff --git a/src/octoprint/filemanager/__init__.py b/src/octoprint/filemanager/__init__.py index 65c51034..a6ee774b 100644 --- a/src/octoprint/filemanager/__init__.py +++ b/src/octoprint/filemanager/__init__.py @@ -187,6 +187,15 @@ class FileManager(object): finally: os.remove(tmp_path) + source_job_key = (source_location, source_path) + dest_job_key = (dest_location, dest_path) + + with self._slicing_jobs_mutex: + if source_job_key in self._slicing_jobs: + del self._slicing_jobs[source_job_key] + if dest_job_key in self._slicing_jobs: + del self._slicing_jobs[dest_job_key] + import time start_time = time.time() eventManager().fire(Events.SLICING_STARTED, {"stl": source_path, "gcode": dest_path}) @@ -197,13 +206,15 @@ class FileManager(object): f.close() with self._slicing_jobs_mutex: - if dest_location in self._slicing_jobs: - job_slicer_name, job_absolute_source_path, job_temp_path = self._slicing_jobs[dest_location] + source_job_key = (source_location, source_path) + dest_job_key = (dest_location, dest_path) + if dest_job_key in self._slicing_jobs: + job_slicer_name, job_absolute_source_path, job_temp_path = self._slicing_jobs[dest_job_key] self._slicing_manager.cancel_slicing(job_slicer_name, job_absolute_source_path, job_temp_path) - del self._slicing_jobs[dest_location] + del self._slicing_jobs[dest_job_key] - self._slicing_jobs[dest_location] = (slicer_name, absolute_source_path, temp_path) + self._slicing_jobs[dest_job_key] = self._slicing_jobs[source_job_key] = (slicer_name, absolute_source_path, temp_path) args = (source_location, source_path, temp_path, dest_location, dest_path, start_time, callback, callback_args) return self._slicing_manager.slice( @@ -229,7 +240,10 @@ class FileManager(object): for callback in self._slicing_progress_callbacks: try: callback.sendSlicingProgress(slicer, source_location, source_path, dest_location, dest_path, progress_int) - except: pass + except: self._logger.exception("Exception while pushing slicing progress") + + def get_busy_files(self): + return self._slicing_jobs.keys() def file_exists(self, destination, path): return self._storage(destination).file_exists(path) diff --git a/src/octoprint/printer.py b/src/octoprint/printer.py index 54fce774..05cdea62 100644 --- a/src/octoprint/printer.py +++ b/src/octoprint/printer.py @@ -33,6 +33,8 @@ class Printer(): def __init__(self, fileManager, analysisQueue): from collections import deque + self._logger = logging.getLogger(__name__) + self._analysisQueue = analysisQueue self._fileManager = fileManager @@ -120,32 +122,32 @@ class Printer(): def _sendAddTemperatureCallbacks(self, data): for callback in self._callbacks: try: callback.addTemperature(data) - except: pass + except: self._logger.exception("Exception while adding temperature data point") def _sendAddLogCallbacks(self, data): for callback in self._callbacks: try: callback.addLog(data) - except: pass + except: self._logger.exception("Exception while adding communication log entry") def _sendAddMessageCallbacks(self, data): for callback in self._callbacks: try: callback.addMessage(data) - except: pass + except: self._logger.exception("Exception while adding printer message") def _sendCurrentDataCallbacks(self, data): for callback in self._callbacks: try: callback.sendCurrentData(copy.deepcopy(data)) - except: pass + except: self._logger.exception("Exception while pushing current data") def _sendTriggerUpdateCallbacks(self, type): for callback in self._callbacks: try: callback.sendEvent(type) - except: pass + except: self._logger.exception("Exception while pushing trigger update") def _sendFeedbackCommandOutput(self, name, output): for callback in self._callbacks: try: callback.sendFeedbackCommandOutput(name, output) - except: pass + except: self._logger.exception("Exception while pushing feedback command output") #~~ callback from metadata analysis event @@ -248,7 +250,7 @@ class Printer(): def selectFile(self, filename, sd, printAfterSelect=False): if self._comm is None or (self._comm.isBusy() or self._comm.isStreaming()): - logging.info("Cannot load file: printer not connected or currently busy") + self._logger.info("Cannot load file: printer not connected or currently busy") return self._printAfterSelect = printAfterSelect @@ -559,7 +561,7 @@ class Printer(): def addSdFile(self, filename, absolutePath, streamingFinishedCallback): if not self._comm or self._comm.isBusy() or not self._comm.isSdReady(): - logging.error("No connection to printer or printer is busy") + self._logger.error("No connection to printer or printer is busy") return self._streamingFinishedCallback = streamingFinishedCallback diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index 2d5dcac1..30eec67e 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -370,11 +370,14 @@ def deleteGcodeFile(filename, target): if not _verifyFileExists(target, filename): return make_response("File not found on '%s': %s" % (target, filename), 404) - # prohibit deleting the file that is currently being printed + # prohibit deleting files that are currently in use currentOrigin, currentFilename = _getCurrentFile() if currentFilename == filename and currentOrigin == target and (printer.isPrinting() or printer.isPaused()): make_response("Trying to delete file that is currently being printed: %s" % filename, 409) + if (target, filename) in fileManager.get_busy_files(): + make_response("Trying to delete a file that is currently in use: %s" % filename, 409) + # deselect the file if it's currently selected if currentFilename is not None and filename == currentFilename: printer.unselectFile() diff --git a/src/octoprint/server/util/sockjs.py b/src/octoprint/server/util/sockjs.py index c5a561d0..22374eac 100644 --- a/src/octoprint/server/util/sockjs.py +++ b/src/octoprint/server/util/sockjs.py @@ -83,10 +83,18 @@ class PrinterStateConnection(sockjs.tornado.SockJSConnection): messages = self._messageBacklog self._messageBacklog = [] + busy_files = [dict(origin=v[0], name=v[1]) for v in self._fileManager.get_busy_files()] + if "job" in data and data["job"] is not None \ + and "file" in data["job"] and "name" in data["job"]["file"] and "origin" in data["job"]["file"] \ + and data["job"]["file"]["name"] is not None and data["job"]["file"]["origin"] is not None \ + and (self._printer.isPrinting() or self._printer.isPaused()): + busy_files.append(dict(origin=data["job"]["file"]["origin"], name=data["job"]["file"]["name"])) + data.update({ "temps": temperatures, "logs": logs, - "messages": messages + "messages": messages, + "busyFiles": busy_files, }) self._emit("current", data) diff --git a/src/octoprint/slicing/__init__.py b/src/octoprint/slicing/__init__.py index 66e56021..0cd4ae17 100644 --- a/src/octoprint/slicing/__init__.py +++ b/src/octoprint/slicing/__init__.py @@ -119,9 +119,9 @@ class SlicingManager(object): if not ok: callback_kwargs.update(dict(_error=result)) - callback(*callback_args, **callback_kwargs) except SlicingCancelled: callback_kwargs.update(dict(_cancelled=True)) + finally: callback(*callback_args, **callback_kwargs) import threading diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index 130f5260..e187ea70 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -239,7 +239,7 @@ function GcodeFilesViewModel(printerStateViewModel, loginStateViewModel, slicing }; self.getEntryId = function(data) { - return "gcode_file_" + md5(data["name"] + ":" + data["origin"]); + return "gcode_file_" + md5(data["origin"] + ":" + data["name"]); }; self.getEntryElement = function(data) { @@ -253,7 +253,7 @@ function GcodeFilesViewModel(printerStateViewModel, loginStateViewModel, slicing }; self.enableRemove = function(data) { - return self.loginState.isUser() && !(self.listHelper.isSelected(data) && (self.isPrinting() || self.isPaused())); + return self.loginState.isUser() && !_.contains(self.printerState.busyFiles(), data.origin + ":" + data.name); }; self.enableSelect = function(data, printAfterSelect) { diff --git a/src/octoprint/static/js/app/viewmodels/printerstate.js b/src/octoprint/static/js/app/viewmodels/printerstate.js index 9f81c62f..6dfbb0cf 100644 --- a/src/octoprint/static/js/app/viewmodels/printerstate.js +++ b/src/octoprint/static/js/app/viewmodels/printerstate.js @@ -22,6 +22,8 @@ function PrinterStateViewModel(loginStateViewModel) { self.sd = ko.observable(undefined); self.timelapse = ko.observable(undefined); + self.busyFiles = ko.observableArray([]); + self.filament = ko.observableArray([]); self.estimatedPrintTime = ko.observable(undefined); self.lastPrintTime = ko.observable(undefined); @@ -109,6 +111,7 @@ function PrinterStateViewModel(loginStateViewModel) { self._processJobData(data.job); self._processProgressData(data.progress); self._processZData(data.currentZ); + self._processBusyFiles(data.busyFiles); }; self._processStateData = function(data) { @@ -177,6 +180,16 @@ function PrinterStateViewModel(loginStateViewModel) { self.currentHeight(data); }; + self._processBusyFiles = function(data) { + var busyFiles = []; + _.each(data, function(entry) { + if (entry.hasOwnProperty("name") && entry.hasOwnProperty("origin")) { + busyFiles.push(entry.origin + ":" + entry.name); + } + }); + self.busyFiles(busyFiles); + }; + self.print = function() { var restartCommand = function() { self._jobCommand("restart"); diff --git a/src/octoprint/timelapse.py b/src/octoprint/timelapse.py index 0e35a291..ba5daf22 100644 --- a/src/octoprint/timelapse.py +++ b/src/octoprint/timelapse.py @@ -61,7 +61,7 @@ def notifyCallbacks(timelapse): config = timelapse.configData() for callback in updateCallbacks: try: callback.sendTimelapseConfig(config) - except: pass + except: logging.getLogger(__name__).exception("Exception while pushing timelapse configuration") def configureTimelapse(config=None, persist=False):