From 7567734e1c8ea7b1269a7a1b92b93429e6c656de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Sun, 6 Jan 2013 21:19:39 +0100 Subject: [PATCH] Decoupled communication thread and frontend serving via update queue, extracted history to separate initial event sent upon connecting, making updates of temperatures, logs and messages smaller --- printer_webui/printer.py | 145 +++++++++++++++++++++------------- printer_webui/server.py | 34 +++++--- printer_webui/static/js/ui.js | 46 ++++++++++- 3 files changed, 157 insertions(+), 68 deletions(-) diff --git a/printer_webui/printer.py b/printer_webui/printer.py index 8ae4bec..226d920 100644 --- a/printer_webui/printer.py +++ b/printer_webui/printer.py @@ -4,6 +4,7 @@ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agp import time from threading import Thread +import Queue import printer_webui.util.comm as comm from printer_webui.util import gcodeInterpreter @@ -66,11 +67,13 @@ class Printer(): # callbacks self._callbacks = [] - - # callback throttling self._lastProgressReport = None - #~~ callback registration + self._updateQueue = Queue.Queue() + self._updateQueueWorker = Thread(target=self._processQueue) + self._updateQueueWorker.start() + + #~~ callback handling def registerCallback(self, callback): self._callbacks.append(callback) @@ -80,7 +83,57 @@ class Printer(): if callback in self._callbacks: self._callbacks.remove(callback) - #~~ printer commands + def _sendZChangeCallbacks(self, data): + for callback in self._callbacks: + try: callback.zChangeCB(data["currentZ"]) + except: pass + + def _sendStateCallbacks(self, data): + for callback in self._callbacks: + try: callback.stateChangeCB(data["state"], data["stateString"], data["stateFlags"]) + except: pass + + def _sendTemperatureCallbacks(self, data): + for callback in self._callbacks: + try: callback.temperatureChangeCB(data["currentTime"], data["temp"], data["bedTemp"], data["targetTemp"], data["targetBedTemp"]) + except: pass + + def _sendLogCallbacks(self, data): + for callback in self._callbacks: + try: callback.logChangeCB(data["log"]) + except: pass + + def _sendMessageCallbacks(self, data): + for callback in self._callbacks: + try: callback.messageChangeCB(data["message"]) + except: pass + + def _sendProgressCallbacks(self, data): + for callback in self._callbacks: + try: callback.progressChangeCB(data["progress"], data["printTime"], data["printTimeLeft"]) + except: pass + + def _sendJobCallbacks(self, data): + for callback in self._callbacks: + try: callback.jobDataChangeCB(data["filename"], data["lines"], data["estimatedPrintTime"], data["filament"]) + except: pass + + def _sendGcodeCallbacks(self, data): + for callback in self._callbacks: + try: callback.gcodeChangeCB(data["filename"], data["progress"]) + except: + pass + + def _addUpdate(self, target, data): + self._updateQueue.put((target, data)) + + def _processQueue(self): + while True: + (target, data) = self._updateQueue.get() + target(data) + self._updateQueue.task_done() + +#~~ printer commands def connect(self, port=None, baudrate=None): """ @@ -180,19 +233,12 @@ class Printer(): return self._timelapse def _setCurrentZ(self, currentZ): - print("Setting currentZ=%s" % str(currentZ)) self._currentZ = currentZ - - for callback in self._callbacks: - try: callback.zChangeCB(self._currentZ) - except: pass + self._addUpdate(self._sendZChangeCallbacks, {"currentZ": self._currentZ}) def _setState(self, state): self._state = state - - for callback in self._callbacks: - try: callback.stateChangeCB(self._state, self.getStateString(), self._getStateFlags()) - except: pass + self._addUpdate(self._sendStateCallbacks, {"state": self._state, "stateString": self.getStateString(), "stateFlags": self._getStateFlags()}) def _addLog(self, log): """ @@ -201,33 +247,22 @@ class Printer(): self._latestLog = log self._log.append(log) self._log = self._log[-300:] - - for callback in self._callbacks: - try: callback.logChangeCB(log, self._log) - except: pass + self._addUpdate(self._sendLogCallbacks, {"log": self._latestLog}) def _addMessage(self, message): self._latestMessage = message self._messages.append(message) self._messages = self._messages[-300:] - - for callback in self._callbacks: - try: callback.messageChangeCB(message, self._messages) - except: pass + self._addUpdate(self._sendLogCallbacks, {"message": self._latestLog}) def _setProgressData(self, progress, printTime, printTimeLeft): self._progress = progress self._printTime = printTime self._printTimeLeft = printTimeLeft - if self._lastProgressReport and self._lastProgressReport + 0.5 > time.time(): - return - - for callback in self._callbacks: - try: callback.progressChangeCB(self._progress, self._printTime, self._printTimeLeft) - except: pass - self._lastProgressReport = time.time() - + #if not self._lastProgressReport or self._lastProgressReport + 0.5 <= time.time(): + self._addUpdate(self._sendProgressCallbacks, {"progress": self._progress, "printTime": self._printTime, "printTimeLeft": self._printTimeLeft}) + # self._lastProgressReport = time.time() def _addTemperatureData(self, temp, bedTemp, targetTemp, bedTargetTemp): """ @@ -253,18 +288,24 @@ class Printer(): self._targetTemp = targetTemp self._targetBedTemp = bedTargetTemp - for callback in self._callbacks: - try: callback.temperatureChangeCB(self._temp, self._bedTemp, self._targetTemp, self._targetBedTemp, self._temps) - except: pass + self._addUpdate(self._sendTemperatureCallbacks, {"currentTime": currentTime, "temp": self._temp, "bedTemp": self._bedTemp, "targetTemp": self._targetTemp, "targetBedTemp": self._targetBedTemp, "history": self._temps}) def _setJobData(self, filename, gcode, gcodeList): self._filename = filename self._gcode = gcode self._gcodeList = gcodeList - for callback in self._callbacks: - try: callback.jobDataChangeCB(filename, len(gcodeList), self._gcode.totalMoveTimeMinute, self._gcode.extrusionAmount) - except: pass + lines = None + if self._gcodeList: + lines = len(self._gcodeList) + + estimatedPrintTime = None + filament = None + if self._gcode: + estimatedPrintTime = self._gcode.totalMoveTimeMinute + filament = self._gcode.extrusionAmount + + self._addUpdate(self._sendJobCallbacks, {"filename": self._filename, "lines": lines, "estimatedPrintTime": estimatedPrintTime, "filament": filament}) def _sendInitialStateUpdate(self, callback): lines = None @@ -280,11 +321,12 @@ class Printer(): try: callback.zChangeCB(self._currentZ) callback.stateChangeCB(self._state, self.getStateString(), self._getStateFlags()) - callback.logChangeCB(self._latestLog, self._log) - callback.messageChangeCB(self._latestMessage, self._messages) + callback.logChangeCB(self._latestLog) + callback.messageChangeCB(self._latestMessage) callback.progressChangeCB(self._progress, self._printTime, self._printTimeLeft) - callback.temperatureChangeCB(self._temp, self._bedTemp, self._targetTemp, self._targetBedTemp, self._temps) + callback.temperatureChangeCB(time.time() * 1000, self._temp, self._bedTemp, self._targetTemp, self._targetBedTemp) callback.jobDataChangeCB(self._filename, lines, estimatedPrintTime, filament) + callback.sendHistoryData(self._temps, self._log, self._messages) except Exception, err: import sys sys.stderr.write("ERROR: %s\n" % str(err)) @@ -347,12 +389,10 @@ class Printer(): self._setProgressData(self._comm.getPrintPos(), self._comm.getPrintTime(), self._comm.getPrintTimeRemainingEstimate()) - def mcZChange(self, newZ): """ Callback method for the comm object, called upon change of the z-layer. """ - print("Got callback for z change: " + str(newZ)) oldZ = self._currentZ if self._timelapse is not None: self._timelapse.onZChange(oldZ, newZ) @@ -362,23 +402,15 @@ class Printer(): #~~ callbacks triggered by gcodeLoader def onGcodeLoadingProgress(self, progress): - for callback in self._callbacks: - try: callback.gcodeChangeCB(self._gcodeLoader._filename, progress) - except Exception, err: - import sys - sys.stderr.write("ERROR: %s\n" % str(err)) - pass + self._addUpdate(self._sendGcodeCallbacks, {"filename": self._gcodeLoader._filename, "progress": progress}) def onGcodeLoaded(self): self._setJobData(self._gcodeLoader._filename, self._gcodeLoader._gcode, self._gcodeLoader._gcodeList) self._setCurrentZ(None) self._setProgressData(None, None, None) - self._gcodeLoader = None - for callback in self._callbacks: - try: callback.stateChangeCB(self._state, self.getStateString(), self._getStateFlags()) - except: pass + self._addUpdate(self._sendStateCallbacks, {"state": self._state, "stateString": self.getStateString(), "stateFlags": self._getStateFlags()}) #~~ state reports @@ -443,9 +475,9 @@ class GcodeLoader(Thread): """ def __init__(self, filename, printerCallback): - Thread.__init__(self); + Thread.__init__(self) - self._printerCallback = printerCallback; + self._printerCallback = printerCallback self._filename = filename self._progress = None @@ -489,20 +521,23 @@ class PrinterCallback(object): def progressChangeCB(self, currentLine, printTime, printTimeLeft): pass - def temperatureChangeCB(self, temp, bedTemp, targetTemp, bedTargetTemp, history): + def temperatureChangeCB(self, currentTime, temp, bedTemp, targetTemp, bedTargetTemp): pass def stateChangeCB(self, state, stateString, booleanStates): pass - def logChangeCB(self, line, history): + def logChangeCB(self, line): pass - def messageChangeCB(self, line, history): + def messageChangeCB(self, line): pass def gcodeChangeCB(self, filename, progress): pass def jobDataChangeCB(self, filename, lines, estimatedPrintTime, filamentLength): + pass + + def sendHistoryData(self, tempHistory, logHistory, messageHistory): pass \ No newline at end of file diff --git a/printer_webui/server.py b/printer_webui/server.py index b18a009..5567512 100644 --- a/printer_webui/server.py +++ b/printer_webui/server.py @@ -34,9 +34,11 @@ def index(): class PrinterStateConnection(tornadio2.SocketConnection, PrinterCallback): def on_open(self, info): + print("Opened socket") printer.registerCallback(self) def on_close(self): + print("Closed socket") printer.unregisterCallback(self) def zChangeCB(self, currentZ): @@ -44,6 +46,7 @@ class PrinterStateConnection(tornadio2.SocketConnection, PrinterCallback): if currentZ: formattedCurrentZ = "%.2f mm" % (currentZ) + print("Sending zChange...") self.emit("zChange", {"currentZ": formattedCurrentZ}) def progressChangeCB(self, currentLine, printTimeInSeconds, printTimeLeftInMinutes): @@ -55,31 +58,37 @@ class PrinterStateConnection(tornadio2.SocketConnection, PrinterCallback): if (printTimeLeftInMinutes): formattedPrintTimeLeft = _getFormattedTimeDelta(datetime.timedelta(minutes=printTimeLeftInMinutes)) + print("Sending progressChange...") self.emit("printProgress", { "currentLine": currentLine, "printTime": formattedPrintTime, "printTimeLeft": formattedPrintTimeLeft }) - def temperatureChangeCB(self, temp, bedTemp, targetTemp, bedTargetTemp, history): + def temperatureChangeCB(self, currentTime, temp, bedTemp, targetTemp, targetBedTemp): + print("Sending temperatureChange...") self.emit("temperature", { - "currentTemp": temp, - "currentBedTemp": bedTemp, - "currentTargetTemp": targetTemp, - "currentTargetBedTemp": bedTargetTemp, - "history": history + "currentTime": currentTime, + "temp": temp, + "bedTemp": bedTemp, + "targetTemp": targetTemp, + "targetBedTemp": targetBedTemp }) def stateChangeCB(self, state, stateString, booleanStates): + print("Sending stateChange...") self.emit("state", {"currentState": stateString, "flags": booleanStates}) - def logChangeCB(self, line, history): - self.emit("log", {"line": line, "history": history}) + def logChangeCB(self, line): + print("Sending logChange...") + self.emit("log", {"line": line}) - def messageChangeCB(self, line, history): - self.emit("message", {"line": line, "history": history}) + def messageChangeCB(self, line): + print("Sending messageChange...") + self.emit("message", {"line": line}) def gcodeChangeCB(self, filename, progress): + print("Sending gcodeChange...") self.emit("jobData", {"filename": "Loading... (%d%%)" % (round(progress * 100)), "lineCount": None, "estimatedPrintTime": None, "filament": None}) def jobDataChangeCB(self, filename, lines, estimatedPrintTimeInMinutes, filamentLengthInMillimeters): @@ -95,8 +104,13 @@ class PrinterStateConnection(tornadio2.SocketConnection, PrinterCallback): if filename: formattedFilename = filename.replace(UPLOAD_FOLDER + os.sep, "") + print("Sending jobDataChange...") self.emit("jobData", {"filename": formattedFilename, "lineCount": lines, "estimatedPrintTime": formattedPrintTimeEstimation, "filament": formattedFilament}) + def sendHistoryData(self, tempHistory, logHistory, messageHistory): + print("Sending history...") + self.emit("history", {"temperature": tempHistory, "log": logHistory, "message": messageHistory}) + #~~ Printer control @app.route(BASEURL + "control/connectionOptions", methods=["GET"]) diff --git a/printer_webui/static/js/ui.js b/printer_webui/static/js/ui.js index 1f83efd..ff4211e 100644 --- a/printer_webui/static/js/ui.js +++ b/printer_webui/static/js/ui.js @@ -250,8 +250,34 @@ function TemperatureViewModel() { self.bedTemp(data.bedTemp); self.targetTemp(data.targetTemp); self.bedTargetTemp(data.bedTargetTemp); - self.temperatures = (data.history); + // plot + if (!self.temperatures) + self.temperatures = []; + if (!self.temperatures.actual) + self.temperatures.actual = []; + if (!self.temperatures.target) + self.temperatures.target = []; + if (!self.temperatures.actualBed) + self.temperatures.actualBed = []; + if (!self.temperatures.targetBed) + self.temperatures.targetBed = []; + + self.temperatures.actual.push([data.currentTime, data.temp]) + self.temperatures.target.push([data.currentTime, data.targetTemp]) + self.temperatures.actualBed.push([data.currentTime, data.bedTemp]) + self.temperatures.targetBed.push([data.currentTime, data.bedTargetTemp]) + + self.temperatures.actual = self.temperatures.actual.slice(-300); + self.temperatures.target = self.temperatures.target.slice(-300); + self.temperatures.actualBed = self.temperatures.actualBed.slice(-300); + self.temperatures.targetBed = self.temperatures.targetBed.slice(-300); + + self.updatePlot(); + } + + self.fromHistoryEvent = function(data) { + self.temperatures = data; self.updatePlot(); } @@ -312,7 +338,7 @@ var speedViewModel = new SpeedViewModel(); function TerminalViewModel() { var self = this; - self.log = undefined; + self.log = []; self.isErrorOrClosed = ko.observable(undefined); self.isOperational = ko.observable(undefined); @@ -333,11 +359,21 @@ function TerminalViewModel() { } self.fromLogEvent = function(data) { - self.log = data.history; + if (!self.log) + self.log = [] + self.log.push(data.log) + self.updateOutput(); + } + + self.fromHistoryEvent = function(data) { + self.log = data; self.updateOutput(); } self.updateOutput = function() { + if (!self.log) + return; + var output = ""; for (var i = 0; i < self.log.length; i++) { output += self.log[i] + "\n"; @@ -516,6 +552,10 @@ function DataUpdater(connectionViewModel, printerStateViewModel, temperatureView self.socket.on("zChange", function(data) { self.printerStateViewModel.fromZChangeEvent(data); }) + self.socket.on("history", function(data) { + self.temperatureViewModel.fromHistoryEvent(data.temperature) + self.terminalViewModel.fromHistoryEvent(data.log) + }) self.requestData = function() { var parameters = {};