From 60be6349f575213dfee92246c96ae88aa2313f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 28 Jul 2017 13:08:28 +0200 Subject: [PATCH] Allow cancelling file transfers Cancelling also deletes the incomplete file on the printer's SD. --- src/octoprint/events.py | 1 + src/octoprint/printer/standard.py | 23 ++++--- .../static/js/app/viewmodels/files.js | 17 ++++++ src/octoprint/util/comm.py | 60 ++++++++++++++----- 4 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/octoprint/events.py b/src/octoprint/events.py index 7f9b818f..03a7daf2 100644 --- a/src/octoprint/events.py +++ b/src/octoprint/events.py @@ -62,6 +62,7 @@ class Events(object): # SD Upload TRANSFER_STARTED = "TransferStarted" TRANSFER_DONE = "TransferDone" + TRANSFER_FAILED = "TransferFailed" # print job PRINT_STARTED = "PrintStarted" diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 6f2cddab..873dfec2 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -71,6 +71,7 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): self._sdStreaming = False self._sdFilelistAvailable = threading.Event() self._streamingFinishedCallback = None + self._streamingFailedCallback = None self._selectedFileMutex = threading.RLock() self._selectedFile = None @@ -579,12 +580,13 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): return [] return map(lambda x: (x[0][1:], x[1]), self._comm.getSdFiles()) - def add_sd_file(self, filename, absolutePath, streamingFinishedCallback): + def add_sd_file(self, filename, absolutePath, on_success=None, on_failure=None): if not self._comm or self._comm.isBusy() or not self._comm.isSdReady(): self._logger.error("No connection to printer or printer is busy") return - self._streamingFinishedCallback = streamingFinishedCallback + self._streamingFinishedCallback = on_success + self._streamingFailedCallback = on_failure self.refresh_sd_files(blocking=True) existingSdFiles = map(lambda x: x[0], self._comm.getSdFiles()) @@ -1171,19 +1173,26 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): self._updateProgressData(completion=0.0, filepos=0, printTime=0) self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) - def on_comm_file_transfer_done(self, filename): + def on_comm_file_transfer_done(self, filename, failed=False): self._sdStreaming = False - if self._streamingFinishedCallback is not None: - # in case of SD files, both filename and absolutePath are the same, so we set the (remote) filename for - # both parameters - self._streamingFinishedCallback(filename, filename, FileDestinations.SDCARD) + # in case of SD files, both filename and absolutePath are the same, so we set the (remote) filename for + # both parameters + if failed: + if self._streamingFailedCallback is not None: + self._streamingFailedCallback(filename, filename, FileDestinations.SDCARD) + else: + if self._streamingFinishedCallback is not None: + self._streamingFinishedCallback(filename, filename, FileDestinations.SDCARD) self._setCurrentZ(None) self._setJobData(None, None, None) self._updateProgressData() self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) + def on_comm_file_transfer_failed(self, filename): + self.on_comm_file_transfer_done(filename, failed=True) + def on_comm_force_disconnect(self): self.disconnect() diff --git a/src/octoprint/static/js/app/viewmodels/files.js b/src/octoprint/static/js/app/viewmodels/files.js index f74b93f4..159bfc3c 100644 --- a/src/octoprint/static/js/app/viewmodels/files.js +++ b/src/octoprint/static/js/app/viewmodels/files.js @@ -954,6 +954,23 @@ $(function() { self.requestData({focus: {location: "sdcard", path: payload.remote}}); }; + self.onEventTransferFailed = function(payload) { + self.uploadProgress + .removeClass("progress-striped") + .removeClass("active"); + self.uploadProgressBar + .css("width", "0"); + self.uploadProgressText(""); + + new PNotify({ + title: gettext("Streaming failed"), + text: _.sprintf(gettext("Did not finish streaming %(local)s to %(remote)s on SD"), payload), + type: "error" + }); + + self.requestData(); + }; + self.onServerConnect = self.onServerReconnect = function(payload) { self._enableDragNDrop(true); self.requestData(); diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 299efc19..1cadcd34 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -877,7 +877,7 @@ class MachineCom(object): def startFileTransfer(self, filename, localFilename, remoteFilename): if not self.isOperational() or self.isBusy(): - logging.info("Printer is not operational or busy") + self._logger.info("Printer is not operational or busy") return with self._jobLock: @@ -890,6 +890,39 @@ class MachineCom(object): eventManager().fire(Events.TRANSFER_STARTED, {"local": localFilename, "remote": remoteFilename}) self._callback.on_comm_file_transfer_started(remoteFilename, self._currentFile.getFilesize()) + def cancelFileTransfer(self): + if not self.isOperational() or not self.isStreaming(): + self._logger.info("Printer is not operational or not streaming") + return + + self._finishFileTransfer(failed=True) + + def _finishFileTransfer(self, failed=False): + with self._jobLock: + remote = self._currentFile.getRemoteFilename() + + self._sendCommand("M29") + if failed: + self.deleteSdFile(remote) + + payload = { + "local": self._currentFile.getLocalFilename(), + "remote": remote, + "time": self.getPrintTime() + } + + self._currentFile = None + self._changeState(self.STATE_OPERATIONAL) + + if failed: + self._callback.on_comm_file_transfer_failed(remote) + eventManager().fire(Events.TRANSFER_FAILED, payload) + else: + self._callback.on_comm_file_transfer_done(remote) + eventManager().fire(Events.TRANSFER_DONE, payload) + + self.refreshSdFiles() + def selectFile(self, filename, sd): if self.isBusy(): return @@ -920,13 +953,18 @@ class MachineCom(object): self._callback.on_comm_print_job_cancelled() def cancelPrint(self, firmware_error=None): - if not self.isOperational() or self.isStreaming(): + if not self.isOperational(): return if not self.isBusy() or self._currentFile is None: # we aren't even printing, nothing to cancel... return + if self.isStreaming(): + # we are streaming, we handle cancelling that differently... + self.cancelFileTransfer() + return + def _on_M400_sent(): # we don't call on_print_job_cancelled on our callback here # because we do this only after our M114 has been answered @@ -1964,20 +2002,7 @@ class MachineCom(object): line = self._currentFile.getNext() if line is None: if self.isStreaming(): - self._sendCommand("M29") - - remote = self._currentFile.getRemoteFilename() - payload = { - "local": self._currentFile.getLocalFilename(), - "remote": remote, - "time": self.getPrintTime() - } - - self._currentFile = None - self._changeState(self.STATE_OPERATIONAL) - self._callback.on_comm_file_transfer_done(remote) - eventManager().fire(Events.TRANSFER_DONE, payload) - self.refreshSdFiles() + self._finishFileTransfer() else: self._callback.on_comm_print_job_done() self._changeState(self.STATE_OPERATIONAL) @@ -2726,6 +2751,9 @@ class MachineComPrintCallback(object): def on_comm_file_transfer_done(self, filename): pass + def on_comm_file_transfer_failed(self, filename): + pass + def on_comm_force_disconnect(self): pass