From 6e62ecc8c11c1dc182fe8674ee42f020ccff1720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 21 Oct 2014 19:18:59 +0200 Subject: [PATCH] Keep track of files that are currently being used (file being printed, source and destination for slicing) so that they can't be deleted Also added logging to exception branches where the exception was formerly just swallowed. --- src/octoprint/filemanager/__init__.py | 24 +++++++++++++++---- src/octoprint/printer.py | 18 +++++++------- src/octoprint/server/api/files.py | 5 +++- src/octoprint/server/util/sockjs.py | 10 +++++++- src/octoprint/slicing/__init__.py | 2 +- .../static/js/app/viewmodels/files.js | 4 ++-- .../static/js/app/viewmodels/printerstate.js | 13 ++++++++++ src/octoprint/timelapse.py | 2 +- 8 files changed, 59 insertions(+), 19 deletions(-) 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):