From b5e19f067646e5a9873db1f9cf4583bf46c2d7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 21 Jul 2016 16:10:57 +0200 Subject: [PATCH] Synchronize access to selected file --- src/octoprint/printer/standard.py | 109 +++++++++++++++++------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/src/octoprint/printer/standard.py b/src/octoprint/printer/standard.py index 2400a12f..8a446036 100644 --- a/src/octoprint/printer/standard.py +++ b/src/octoprint/printer/standard.py @@ -71,6 +71,7 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): self._streamingFinishedCallback = None self._selectedFile = None + self._selectedFileLock = threading.RLock() self._timeEstimationData = None # comm @@ -111,8 +112,8 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): current_z=None ) - eventManager().subscribe(Events.METADATA_ANALYSIS_FINISHED, self._on_event_MetadataAnalysisFinished) - eventManager().subscribe(Events.METADATA_STATISTICS_UPDATED, self._on_event_MetadataStatisticsUpdated) + eventManager().subscribe(Events.METADATA_ANALYSIS_FINISHED, self._on_metadata_event) + eventManager().subscribe(Events.METADATA_STATISTICS_UPDATED, self._on_metadata_event) #~~ handling of PrinterCallbacks @@ -149,25 +150,23 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): #~~ callback from metadata analysis event - def _on_event_MetadataAnalysisFinished(self, event, data): - if self._selectedFile: - self._setJobData(self._selectedFile["filename"], - self._selectedFile["filesize"], - self._selectedFile["sd"]) - - def _on_event_MetadataStatisticsUpdated(self, event, data): - self._setJobData(self._selectedFile["filename"], - self._selectedFile["filesize"], - self._selectedFile["sd"]) + def _on_metadata_event(self, event, data): + with self._selectedFileLock: + if self._selectedFile and data and data.get("path", None) == self._selectedFile["filename"] \ + and data.get("storage", None) == FileDestinations.SDCARD if self._selectedFile["sd"] else FileDestinations.LOCAL: + self._setJobData(self._selectedFile["filename"], + self._selectedFile["filesize"], + self._selectedFile["sd"]) #~~ progress plugin reporting def _reportPrintProgressToPlugins(self, progress): - if not progress or not self._selectedFile or not "sd" in self._selectedFile or not "filename" in self._selectedFile: - return + with self._selectedFileLock: + if not progress or not self._selectedFile or not "sd" in self._selectedFile or not "filename" in self._selectedFile: + return - storage = "sdcard" if self._selectedFile["sd"] else "local" - filename = self._selectedFile["filename"] + storage = "sdcard" if self._selectedFile["sd"] else "local" + filename = self._selectedFile["filename"] def call_plugins(storage, filename, progress): for plugin in self._progressPlugins: @@ -378,13 +377,16 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): """ if self._comm is None or not self._comm.isOperational() or self._comm.isPrinting(): return - if self._selectedFile is None: - return + + with self._selectedFileLock: + if self._selectedFile is None: + return + sd = self._selectedFile["sd"] rolling_window = None threshold = None countdown = None - if self._selectedFile["sd"]: + if sd: # we are interesting in a rolling window of roughly the last 15s, so the number of entries has to be derived # by that divided by the sd status polling interval rolling_window = 15 / settings().get(["serial", "timeout", "sdStatus"]) @@ -441,15 +443,16 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): self._setProgressData() # mark print as failure - if self._selectedFile is not None: - self._fileManager.log_print(FileDestinations.SDCARD if self._selectedFile["sd"] else FileDestinations.LOCAL, self._selectedFile["filename"], time.time(), self._comm.getPrintTime(), False, self._printerProfileManager.get_current_or_default()["id"]) - payload = { - "file": self._selectedFile["filename"], - "origin": FileDestinations.LOCAL - } - if self._selectedFile["sd"]: - payload["origin"] = FileDestinations.SDCARD - eventManager().fire(Events.PRINT_FAILED, payload) + with self._selectedFileLock: + if self._selectedFile is not None: + self._fileManager.log_print(FileDestinations.SDCARD if self._selectedFile["sd"] else FileDestinations.LOCAL, self._selectedFile["filename"], time.time(), self._comm.getPrintTime(), False, self._printerProfileManager.get_current_or_default()["id"]) + payload = { + "file": self._selectedFile["filename"], + "origin": FileDestinations.LOCAL + } + if self._selectedFile["sd"]: + payload["origin"] = FileDestinations.SDCARD + eventManager().fire(Events.PRINT_FAILED, payload) def get_state_string(self): """ @@ -632,9 +635,13 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): estimatedTotalPrintTime = self._estimateTotalPrintTime(progress, cleanedPrintTime) totalPrintTime = estimatedTotalPrintTime - if self._selectedFile and "estimatedPrintTime" in self._selectedFile and self._selectedFile["estimatedPrintTime"]: - statisticalTotalPrintTime = self._selectedFile["estimatedPrintTime"] - if progress and cleanedPrintTime: + statisticalTotalPrintTime = None + with self._selectedFileLock: + if self._selectedFile and "estimatedPrintTime" in self._selectedFile and self._selectedFile[ + "estimatedPrintTime"]: + statisticalTotalPrintTime = self._selectedFile["estimatedPrintTime"] + + if statisticalTotalPrintTime is not None and progress and cleanedPrintTime: if estimatedTotalPrintTime is None: totalPrintTime = statisticalTotalPrintTime else: @@ -691,14 +698,16 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): else: path_in_storage = self._fileManager.path_in_storage(FileDestinations.LOCAL, filename) path_on_disk = self._fileManager.path_on_disk(FileDestinations.LOCAL, filename) - self._selectedFile = { - "filename": path_in_storage, - "filesize": filesize, - "sd": sd, - "estimatedPrintTime": None - } + with self._selectedFileLock: + self._selectedFile = { + "filename": path_in_storage, + "filesize": filesize, + "sd": sd, + "estimatedPrintTime": None + } else: - self._selectedFile = None + with self._selectedFileLock: + self._selectedFile = None self._stateMonitor.set_job_data({ "file": { "name": None, @@ -741,11 +750,13 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): if "lastPrintTime" in fileData["statistics"] and printer_profile in fileData["statistics"]["lastPrintTime"]: lastPrintTime = fileData["statistics"]["lastPrintTime"][printer_profile] - if averagePrintTime is not None: - self._selectedFile["estimatedPrintTime"] = averagePrintTime - elif estimatedPrintTime is not None: - # TODO apply factor which first needs to be tracked! - self._selectedFile["estimatedPrintTime"] = estimatedPrintTime + with self._selectedFileLock: + if self._selectedFile is not None: + if averagePrintTime is not None: + self._selectedFile["estimatedPrintTime"] = averagePrintTime + elif estimatedPrintTime is not None: + # TODO apply factor which first needs to be tracked! + self._selectedFile["estimatedPrintTime"] = estimatedPrintTime self._stateMonitor.set_job_data({ "file": { @@ -802,9 +813,10 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): # forward relevant state changes to gcode manager if oldState == comm.MachineCom.STATE_PRINTING: - if self._selectedFile is not None: - if state == comm.MachineCom.STATE_CLOSED or state == comm.MachineCom.STATE_ERROR or state == comm.MachineCom.STATE_CLOSED_WITH_ERROR: - self._fileManager.log_print(FileDestinations.SDCARD if self._selectedFile["sd"] else FileDestinations.LOCAL, self._selectedFile["filename"], time.time(), self._comm.getPrintTime(), False, self._printerProfileManager.get_current_or_default()["id"]) + with self._selectedFileLock: + if self._selectedFile is not None: + if state == comm.MachineCom.STATE_CLOSED or state == comm.MachineCom.STATE_ERROR or state == comm.MachineCom.STATE_CLOSED_WITH_ERROR: + self._fileManager.log_print(FileDestinations.SDCARD if self._selectedFile["sd"] else FileDestinations.LOCAL, self._selectedFile["filename"], time.time(), self._comm.getPrintTime(), False, self._printerProfileManager.get_current_or_default()["id"]) self._analysisQueue.resume() # printing done, put those cpu cycles to good use elif state == comm.MachineCom.STATE_PRINTING: self._analysisQueue.pause() # do not analyse files while printing @@ -862,8 +874,9 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback): self.start_print(pos=self._posAfterSelect) def on_comm_print_job_done(self): - self._fileManager.log_print(FileDestinations.SDCARD if self._selectedFile["sd"] else FileDestinations.LOCAL, self._selectedFile["filename"], time.time(), self._comm.getPrintTime(), True, self._printerProfileManager.get_current_or_default()["id"]) - self._setProgressData(completion=1.0, filepos=self._selectedFile["filesize"], printTime=self._comm.getPrintTime(), printTimeLeft=0) + with self._selectedFileLock: + self._fileManager.log_print(FileDestinations.SDCARD if self._selectedFile["sd"] else FileDestinations.LOCAL, self._selectedFile["filename"], time.time(), self._comm.getPrintTime(), True, self._printerProfileManager.get_current_or_default()["id"]) + self._setProgressData(completion=1.0, filepos=self._selectedFile["filesize"], printTime=self._comm.getPrintTime(), printTimeLeft=0) self._stateMonitor.set_state({"text": self.get_state_string(), "flags": self._getStateFlags()}) self._fileManager.delete_recovery_data()