diff --git a/octoprint/printer.py b/octoprint/printer.py index a82d45c..7a73674 100644 --- a/octoprint/printer.py +++ b/octoprint/printer.py @@ -73,9 +73,6 @@ class Printer(): self._selectedFile = None - # timelapse - self._timelapse = None - # comm self._comm = None @@ -215,15 +212,6 @@ class Printer(): self._gcodeManager.printFailed(self._selectedFile["filename"]) eventManager().fire("PrintFailed", self._filename) - def setTimelapse(self, timelapse): - if self._timelapse is not None and self.isPrinting(): - self._timelapse.stopTimelapse() - del self._timelapse - self._timelapse = timelapse - - def getTimelapse(self): - return self._timelapse - #~~ state monitoring def _setCurrentZ(self, currentZ): diff --git a/octoprint/server.py b/octoprint/server.py index c575d3e..c0de4e1 100644 --- a/octoprint/server.py +++ b/octoprint/server.py @@ -15,7 +15,7 @@ import subprocess from octoprint.printer import Printer, getConnectionOptions from octoprint.settings import settings, valid_boolean_trues -import octoprint.timelapse as timelapse +import octoprint.timelapse import octoprint.gcodefiles as gcodefiles import octoprint.util as util import octoprint.users as users @@ -28,7 +28,9 @@ BASEURL = "/ajax/" app = Flask("octoprint") # Only instantiated by the Server().run() method # In order that threads don't start too early when running as a Daemon -printer = None +printer = None +timelapse = None + gcodeManager = None userManager = None eventManager = None @@ -60,8 +62,8 @@ class PrinterStateConnection(tornadio2.SocketConnection): def on_open(self, info): self._logger.info("New connection from client") # Use of global here is smelly - printer.registerCallback(self) - gcodeManager.registerCallback(self) + self._printer.registerCallback(self) + self._gcodeManager.registerCallback(self) self._eventManager.fire("ClientOpened") self._eventManager.subscribe("MovieDone", self._onMovieDone) @@ -69,8 +71,8 @@ class PrinterStateConnection(tornadio2.SocketConnection): def on_close(self): self._logger.info("Closed client connection") # Use of global here is smelly - printer.unregisterCallback(self) - gcodeManager.unregisterCallback(self) + self._printer.unregisterCallback(self) + self._gcodeManager.unregisterCallback(self) self._eventManager.fire("ClientClosed") self._eventManager.unsubscribe("MovieDone", self._onMovieDone) @@ -352,19 +354,19 @@ def refreshFiles(): @app.route(BASEURL + "timelapse", methods=["GET"]) def getTimelapseData(): - lapse = printer.getTimelapse() + global timelapse type = "off" additionalConfig = {} - if lapse is not None and isinstance(lapse, timelapse.ZTimelapse): + if timelapse is not None and isinstance(timelapse, octoprint.timelapse.ZTimelapse): type = "zchange" - elif lapse is not None and isinstance(lapse, timelapse.TimedTimelapse): + elif timelapse is not None and isinstance(timelapse, octoprint.timelapse.TimedTimelapse): type = "timed" additionalConfig = { - "interval": lapse.interval() + "interval": timelapse.interval() } - files = timelapse.getFinishedTimelapses() + files = octoprint.timelapse.getFinishedTimelapses() for file in files: file["url"] = url_for("downloadTimelapse", filename=file["name"]) @@ -391,11 +393,17 @@ def deleteTimelapse(filename): @app.route(BASEURL + "timelapse", methods=["POST"]) @login_required def setTimelapseConfig(): + global timelapse + if request.values.has_key("type"): type = request.values["type"] - lapse = None + if type in ["zchange", "timed"]: + # valid timelapse type, check if there is an old one we need to stop first + if timelapse is not None: + timelapse.unload() + timelapse = None if "zchange" == type: - lapse = timelapse.ZTimelapse() + timelapse = octoprint.timelapse.ZTimelapse() elif "timed" == type: interval = 10 if request.values.has_key("interval"): @@ -403,8 +411,7 @@ def setTimelapseConfig(): interval = int(request.values["interval"]) except ValueError: pass - lapse = timelapse.TimedTimelapse(interval) - printer.setTimelapse(lapse) + timelapse = octoprint.timelapse.TimedTimelapse(interval) return getTimelapseData() @@ -802,6 +809,12 @@ class Server(): } }, "loggers": { + #"octoprint.timelapse": { + # "level": "DEBUG" + #}, + #"octoprint.events": { + # "level": "DEBUG" + #} }, "root": { "level": "INFO", diff --git a/octoprint/timelapse.py b/octoprint/timelapse.py index 0730546..7038738 100644 --- a/octoprint/timelapse.py +++ b/octoprint/timelapse.py @@ -47,10 +47,23 @@ class Timelapse(object): self._renderThread = None self._captureMutex = threading.Lock() + # subscribe events eventManager().subscribe("PrintStarted", self.onPrintStarted) eventManager().subscribe("PrintFailed", self.onPrintDone) eventManager().subscribe("PrintDone", self.onPrintDone) - self.subscribeToEvents() + for (event, callback) in self.eventSubscriptions(): + eventManager().subscribe(event, callback) + + def unload(self): + if self._inTimelapse: + self.stopTimelapse(doCreateMovie=False) + + # unsubscribe events + eventManager().unsubscribe("PrintStarted", self.onPrintStarted) + eventManager().unsubscribe("PrintFailed", self.onPrintDone) + eventManager().unsubscribe("PrintDone", self.onPrintDone) + for (event, callback) in self.eventSubscriptions(): + eventManager().unsubscribe(event, callback) def onPrintStarted(self, event, payload): """ @@ -64,14 +77,16 @@ class Timelapse(object): """ self.stopTimelapse() - def subscribeToEvents(self): + def eventSubscriptions(self): """ - Override this method to subscribe to additional events. Events that are already subscribed: + Override this method to subscribe to additional events by returning an array of (event, callback) tuples. + + Events that are already subscribed: * PrintStarted - self.onPrintStarted * PrintFailed - self.onPrintDone * PrintDone - self.onPrintDone """ - pass + return [] def startTimelapse(self, gcodeFile): self._logger.debug("Starting timelapse for %s" % gcodeFile) @@ -81,11 +96,13 @@ class Timelapse(object): self._inTimelapse = True self._gcodeFile = os.path.basename(gcodeFile) - def stopTimelapse(self): + def stopTimelapse(self, doCreateMovie=True): self._logger.debug("Stopping timelapse") - self._renderThread = threading.Thread(target=self._createMovie) - self._renderThread.daemon = True - self._renderThread.start() + + if doCreateMovie: + self._renderThread = threading.Thread(target=self._createMovie) + self._renderThread.daemon = True + self._renderThread.start() self._imageNumber = None self._inTimelapse = False @@ -157,8 +174,10 @@ class ZTimelapse(Timelapse): Timelapse.__init__(self) self._logger.debug("ZTimelapse initialized") - def subscribeToEvents(self): - eventManager().subscribe("ZChange", self._onZChange) + def eventSubscriptions(self): + return [ + ("ZChange", self._onZChange) + ] def _onZChange(self, event, payload): self.captureImage()