State is now sent in update bundles to the frontend if updates are available, but not more frequently than every 500ms.

master
Gina Häußge 2013-01-12 00:00:58 +01:00
parent 4983a00adf
commit e8a633db8b
4 changed files with 276 additions and 159 deletions

View File

@ -3,7 +3,10 @@ __author__ = "Gina Häußge <osd@foosel.net>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
import time import time
from threading import Thread, Event, Lock import datetime
import threading
import copy
import os
import printer_webui.util.comm as comm import printer_webui.util.comm as comm
from printer_webui.util import gcodeInterpreter from printer_webui.util import gcodeInterpreter
@ -71,9 +74,19 @@ class Printer():
self._callbacks = [] self._callbacks = []
self._lastProgressReport = None self._lastProgressReport = None
self._stateMonitor = StateMonitor(ratelimit=0.5, updateCallback=self._sendCurrentDataCallbacks) self._stateMonitor = StateMonitor(
ratelimit=0.5,
updateCallback=self._sendCurrentDataCallbacks,
addTemperatureCallback=self._sendAddTemperatureCallbacks,
addLogCallback=self._sendAddLogCallbacks,
addMessageCallback=self._sendAddMessageCallbacks
)
self._stateMonitor.reset( self._stateMonitor.reset(
state={"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()} state={"state": None, "stateString": self.getStateString(), "flags": self._getStateFlags()},
jobData={"filename": None, "lines": None, "estimatedPrintTime": None, "filament": None},
gcodeData={"filename": None, "progress": None},
progress={"progress": None, "printTime": None, "printTimeLeft": None},
currentZ=None
) )
#~~ callback handling #~~ callback handling
@ -86,9 +99,24 @@ class Printer():
if callback in self._callbacks: if callback in self._callbacks:
self._callbacks.remove(callback) self._callbacks.remove(callback)
def _sendAddTemperatureCallbacks(self, data):
for callback in self._callbacks:
try: callback.addTemperature(data)
except: pass
def _sendAddLogCallbacks(self, data):
for callback in self._callbacks:
try: callback.addLog(data)
except: pass
def _sendAddMessageCallbacks(self, data):
for callback in self._callbacks:
try: callback.addMessage(data)
except: pass
def _sendCurrentDataCallbacks(self, data): def _sendCurrentDataCallbacks(self, data):
for callback in self._callbacks: for callback in self._callbacks:
try: callback.sendCurrentData(data) try: callback.sendCurrentData(copy.deepcopy(data))
except: pass except: pass
#~~ printer commands #~~ printer commands
@ -139,9 +167,11 @@ class Printer():
self._setJobData(None, None, None) self._setJobData(None, None, None)
self._gcodeLoader = GcodeLoader(file, self) self._gcodeLoader = GcodeLoader(file, self._onGcodeLoadingProgress, self._onGcodeLoaded)
self._gcodeLoader.start() self._gcodeLoader.start()
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
def startPrint(self): def startPrint(self):
""" """
Starts the currently loaded print job. Starts the currently loaded print job.
@ -192,11 +222,15 @@ class Printer():
def _setCurrentZ(self, currentZ): def _setCurrentZ(self, currentZ):
self._currentZ = currentZ self._currentZ = currentZ
self._stateMonitor.setCurrentZ(self._currentZ)
formattedCurrentZ = None
if self._currentZ:
formattedCurrentZ = "%.2f mm" % (self._currentZ)
self._stateMonitor.setCurrentZ(formattedCurrentZ)
def _setState(self, state): def _setState(self, state):
self._state = state self._state = state
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "stateFlags": self._getStateFlags()}) self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
def _addLog(self, log): def _addLog(self, log):
self._log.append(log) self._log.append(log)
@ -212,7 +246,16 @@ class Printer():
self._progress = progress self._progress = progress
self._printTime = printTime self._printTime = printTime
self._printTimeLeft = printTimeLeft self._printTimeLeft = printTimeLeft
self._stateMonitor.setProgress({"progress": self._progress, "printTime": self._printTime, "printTimeLeft": self._printTimeLeft})
formattedPrintTime = None
if (self._printTime):
formattedPrintTime = _getFormattedTimeDelta(datetime.timedelta(seconds=self._printTime))
formattedPrintTimeLeft = None
if (self._printTimeLeft):
formattedPrintTimeLeft = _getFormattedTimeDelta(datetime.timedelta(minutes=self._printTimeLeft))
self._stateMonitor.setProgress({"progress": self._progress, "printTime": formattedPrintTime, "printTimeLeft": formattedPrintTimeLeft})
def _addTemperatureData(self, temp, bedTemp, targetTemp, bedTargetTemp): def _addTemperatureData(self, temp, bedTemp, targetTemp, bedTargetTemp):
currentTime = int(time.time() * 1000) currentTime = int(time.time() * 1000)
@ -245,25 +288,21 @@ class Printer():
if self._gcodeList: if self._gcodeList:
lines = len(self._gcodeList) lines = len(self._gcodeList)
estimatedPrintTime = None formattedPrintTimeEstimation = None
filament = None formattedFilament = None
if self._gcode: if self._gcode:
estimatedPrintTime = self._gcode.totalMoveTimeMinute if self._gcode.totalMoveTimeMinute:
filament = self._gcode.extrusionAmount formattedPrintTimeEstimation = _getFormattedTimeDelta(datetime.timedelta(minutes=self._gcode.totalMoveTimeMinute))
if self._gcode.extrusionAmount:
formattedFilament = "%.2fm" % (self._gcode.extrusionAmount / 1000)
self._stateMonitor.setJobData({"filename": self._filename, "lines": lines, "estimatedPrintTime": estimatedPrintTime, "filament": filament}) formattedFilename = None
if self._filename:
formattedFilename = os.path.basename(self._filename)
self._stateMonitor.setJobData({"filename": formattedFilename, "lines": lines, "estimatedPrintTime": formattedPrintTimeEstimation, "filament": formattedFilament})
def _sendInitialStateUpdate(self, callback): def _sendInitialStateUpdate(self, callback):
lines = None
if self._gcodeList:
lines = len(self._gcodeList)
estimatedPrintTime = None
filament = None
if self._gcode:
estimatedPrintTime = self._gcode.totalMoveTimeMinute
filament = self._gcode.extrusionAmount
try: try:
data = self._stateMonitor.getCurrentData() data = self._stateMonitor.getCurrentData()
data.update({ data.update({
@ -346,16 +385,24 @@ class Printer():
#~~ callbacks triggered by gcodeLoader #~~ callbacks triggered by gcodeLoader
def onGcodeLoadingProgress(self, progress): def _onGcodeLoadingProgress(self, filename, progress):
self._stateMonitor.setGcodeData({"filename": self._gcodeLoader._filename, "progress": progress}) formattedFilename = None
if filename is not None:
formattedFilename = os.path.basename(filename)
def onGcodeLoaded(self): self._stateMonitor.setGcodeData({"filename": formattedFilename, "progress": progress})
self._setJobData(self._gcodeLoader._filename, self._gcodeLoader._gcode, self._gcodeLoader._gcodeList)
def _onGcodeLoaded(self, filename, gcode, gcodeList):
formattedFilename = None
if filename is not None:
formattedFilename = os.path.basename(filename)
self._setJobData(formattedFilename, gcode, gcodeList)
self._setCurrentZ(None) self._setCurrentZ(None)
self._setProgressData(None, None, None) self._setProgressData(None, None, None)
self._gcodeLoader = None self._gcodeLoader = None
self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "stateFlags": self._getStateFlags()}) self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()})
#~~ state reports #~~ state reports
@ -402,17 +449,18 @@ class Printer():
def isLoading(self): def isLoading(self):
return self._gcodeLoader is not None return self._gcodeLoader is not None
class GcodeLoader(Thread): class GcodeLoader(threading.Thread):
""" """
The GcodeLoader takes care of loading a gcode-File from disk and parsing it into a gcode object in a separate The GcodeLoader takes care of loading a gcode-File from disk and parsing it into a gcode object in a separate
thread while constantly notifying interested listeners about the current progress. thread while constantly notifying interested listeners about the current progress.
The progress is returned as a float value between 0 and 1 which is to be interpreted as the percentage of completion. The progress is returned as a float value between 0 and 1 which is to be interpreted as the percentage of completion.
""" """
def __init__(self, filename, printerCallback): def __init__(self, filename, progressCallback, loadedCallback):
Thread.__init__(self) threading.Thread.__init__(self)
self._printerCallback = printerCallback self._progressCallback = progressCallback
self._loadedCallback = loadedCallback
self._filename = filename self._filename = filename
self._progress = None self._progress = None
@ -443,11 +491,11 @@ class GcodeLoader(Thread):
self._gcode.progressCallback = self.onProgress self._gcode.progressCallback = self.onProgress
self._gcode.loadList(self._gcodeList) self._gcode.loadList(self._gcodeList)
self._printerCallback.onGcodeLoaded() self._loadedCallback(self._filename, self._gcode, self._gcodeList)
def onProgress(self, progress): def onProgress(self, progress):
self._progress = progress self._progress = progress
self._printerCallback.onGcodeLoadingProgress(progress) self._progressCallback(self._filename, self._progress)
class PrinterCallback(object): class PrinterCallback(object):
def sendCurrentData(self, data): def sendCurrentData(self, data):
@ -456,48 +504,52 @@ class PrinterCallback(object):
def sendHistoryData(self, data): def sendHistoryData(self, data):
pass pass
def addTemperature(self, data):
pass
def addLog(self, data):
pass
def addMessage(self, data):
pass
class StateMonitor(object): class StateMonitor(object):
def __init__(self, ratelimit, updateCallback): def __init__(self, ratelimit, updateCallback, addTemperatureCallback, addLogCallback, addMessageCallback):
self._ratelimit = ratelimit self._ratelimit = ratelimit
self._updateCallback = updateCallback self._updateCallback = updateCallback
self._addTemperatureCallback = addTemperatureCallback
self._addLogCallback = addLogCallback
self._addMessageCallback = addMessageCallback
self._state = None self._state = None
self._jobData = None self._jobData = None
self._gcodeData = None self._gcodeData = None
self._currentZ = None self._currentZ = None
self._progress = None self._progress = None
self._logBacklog = []
self._logHistory = []
self._messageBacklog = []
self._messageHistory = []
self._temperatureBacklog = []
self._temperatureHistory = []
self._temperatureBacklogMutex = Lock() self._changeEvent = threading.Event()
self._logBacklogMutex = Lock()
self._messageBacklogMutex = Lock()
self._changeEvent = Event()
self._lastUpdate = time.time() self._lastUpdate = time.time()
self._worker = Thread(target=self._work) self._worker = threading.Thread(target=self._work)
self._worker.start() self._worker.start()
def reset(self, state=None): def reset(self, state=None, jobData=None, gcodeData=None, progress=None, currentZ=None):
self.setState(state) self.setState(state)
self.setJobData(jobData)
self.setGcodeData(gcodeData)
self.setProgress(progress)
self.setCurrentZ(currentZ)
def addTemperature(self, temperature): def addTemperature(self, temperature):
with self._temperatureBacklogMutex: self._addTemperatureCallback(temperature)
self._temperatureBacklog.append(temperature)
self._changeEvent.set() self._changeEvent.set()
def addLog(self, log): def addLog(self, log):
with self._logBacklogMutex: self._addLogCallback(log)
self._logBacklog.append(log)
self._changeEvent.set() self._changeEvent.set()
def addMessage(self, message): def addMessage(self, message):
with self._messageBacklogMutex: self._addMessageCallback(message)
self._messageBacklog.append(message)
self._changeEvent.set() self._changeEvent.set()
def setCurrentZ(self, currentZ): def setCurrentZ(self, currentZ):
@ -523,28 +575,14 @@ class StateMonitor(object):
def _work(self): def _work(self):
while True: while True:
self._changeEvent.wait() self._changeEvent.wait()
additionalWaitTime = time.time() + self._ratelimit - self._lastUpdate
now = time.time()
delta = now - self._lastUpdate
additionalWaitTime = self._ratelimit - delta
if additionalWaitTime > 0: if additionalWaitTime > 0:
time.sleep(additionalWaitTime) time.sleep(additionalWaitTime)
with self._temperatureBacklogMutex:
temperatures = self._temperatureBacklog
self._temperatureBacklog = []
with self._logBacklogMutex:
logs = self._logBacklog
self._logBacklog = []
with self._messageBacklogMutex:
messages = self._messageBacklog
self._messageBacklog = []
data = self.getCurrentData() data = self.getCurrentData()
data.update({
"temperatures": temperatures,
"logs": logs,
"messages": messages
})
self._updateCallback(data) self._updateCallback(data)
self._lastUpdate = time.time() self._lastUpdate = time.time()
self._changeEvent.clear() self._changeEvent.clear()
@ -554,5 +592,14 @@ class StateMonitor(object):
"state": self._state, "state": self._state,
"job": self._jobData, "job": self._jobData,
"gcode": self._gcodeData, "gcode": self._gcodeData,
"currentZ": self._currentZ "currentZ": self._currentZ,
"progress": self._progress
} }
def _getFormattedTimeDelta(d):
if d is None:
return None
hours = d.seconds // 3600
minutes = (d.seconds % 3600) // 60
seconds = d.seconds % 60
return "%02d:%02d:%02d" % (hours, minutes, seconds)

View File

@ -8,7 +8,7 @@ import tornadio2
import os import os
import fnmatch import fnmatch
import datetime import threading
from printer_webui.printer import Printer, getConnectionOptions, PrinterCallback from printer_webui.printer import Printer, getConnectionOptions, PrinterCallback
from printer_webui.settings import settings from printer_webui.settings import settings
@ -33,25 +33,63 @@ def index():
#~~ Printer state #~~ Printer state
class PrinterStateConnection(tornadio2.SocketConnection, PrinterCallback): class PrinterStateConnection(tornadio2.SocketConnection, PrinterCallback):
def __init__(self, session, endpoint=None):
tornadio2.SocketConnection.__init__(self, session, endpoint)
self._temperatureBacklog = []
self._temperatureBacklogMutex = threading.Lock()
self._logBacklog = []
self._logBacklogMutex = threading.Lock()
self._messageBacklog = []
self._messageBacklogMutex = threading.Lock()
def on_open(self, info): def on_open(self, info):
print("Opened socket") print("New connection from client")
printer.registerCallback(self) printer.registerCallback(self)
def on_close(self): def on_close(self):
print("Closed socket") print("Closed client connection")
printer.unregisterCallback(self) printer.unregisterCallback(self)
def on_message(self, message): def on_message(self, message):
pass pass
def sendCurrentData(self, data): def sendCurrentData(self, data):
print("Sending current data...") # add current temperature, log and message backlogs to sent data
with self._temperatureBacklogMutex:
temperatures = self._temperatureBacklog
self._temperatureBacklog = []
with self._logBacklogMutex:
logs = self._logBacklog
self._logBacklog = []
with self._messageBacklogMutex:
messages = self._messageBacklog
self._messageBacklog = []
data.update({
"temperatures": temperatures,
"logs": logs,
"messages": messages
})
self.emit("current", data) self.emit("current", data)
def sendHistoryData(self, data): def sendHistoryData(self, data):
print("Sending history...")
self.emit("history", data) self.emit("history", data)
def addLog(self, data):
with self._logBacklogMutex:
self._logBacklog.append(data)
def addMessage(self, data):
with self._messageBacklogMutex:
self._messageBacklog.append(data)
def addTemperature(self, data):
with self._temperatureBacklogMutex:
self._temperatureBacklog.append(data)
#~~ Printer control #~~ Printer control
@app.route(BASEURL + "control/connectionOptions", methods=["GET"]) @app.route(BASEURL + "control/connectionOptions", methods=["GET"])
@ -278,12 +316,6 @@ def setSettings():
#~~ helper functions #~~ helper functions
def _getFormattedTimeDelta(d):
hours = d.seconds // 3600
minutes = (d.seconds % 3600) // 60
seconds = d.seconds % 60
return "%02d:%02d:%02d" % (hours, minutes, seconds)
def sizeof_fmt(num): def sizeof_fmt(num):
""" """
Taken from http://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size Taken from http://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size

View File

@ -49,6 +49,10 @@ function ConnectionViewModel() {
self.saveSettings(false); self.saveSettings(false);
} }
self.fromHistoryData = function(data) {
self._processStateData(data.state);
}
self.fromCurrentData = function(data) { self.fromCurrentData = function(data) {
self._processStateData(data.state); self._processStateData(data.state);
} }
@ -102,7 +106,6 @@ function ConnectionViewModel() {
} }
} }
} }
var connectionViewModel = new ConnectionViewModel();
function PrinterStateViewModel() { function PrinterStateViewModel() {
var self = this; var self = this;
@ -144,17 +147,21 @@ function PrinterStateViewModel() {
}); });
self.fromCurrentData = function(data) { self.fromCurrentData = function(data) {
self._processStateData(data.state); self._fromData(data);
}
self.fromHistoryData = function(data) {
self._fromData(data);
}
self._fromData = function(data) {
self._processStateData(data.state)
self._processJobData(data.job); self._processJobData(data.job);
self._processGcodeData(data.gcode); self._processGcodeData(data.gcode);
self._processProgressData(data.progress); self._processProgressData(data.progress);
self._processZData(data.currentZ); self._processZData(data.currentZ);
} }
self.fromHistoryData = function(data) {
self._processStateData(data.state)
}
self._processStateData = function(data) { self._processStateData = function(data) {
self.stateString(data.stateString); self.stateString(data.stateString);
self.isErrorOrClosed(data.flags.closedOrError); self.isErrorOrClosed(data.flags.closedOrError);
@ -168,7 +175,7 @@ function PrinterStateViewModel() {
self._processJobData = function(data) { self._processJobData = function(data) {
self.filename(data.filename); self.filename(data.filename);
self.totalLines(data.lineCount); self.totalLines(data.lines);
self.estimatedPrintTime(data.estimatedPrintTime); self.estimatedPrintTime(data.estimatedPrintTime);
self.filament(data.filament); self.filament(data.filament);
} }
@ -180,16 +187,15 @@ function PrinterStateViewModel() {
} }
self._processProgressData = function(data) { self._processProgressData = function(data) {
self.currentLine(data.currentLine); self.currentLine(data.progress);
self.printTime(data.printTime); self.printTime(data.printTime);
self.printTimeLeft(data.printTimeLeft); self.printTimeLeft(data.printTimeLeft);
} }
self._processZData = function(data) { self._processZData = function(data) {
self.currentHeight(data.currentZ); self.currentHeight(data);
} }
} }
var printerStateViewModel = new PrinterStateViewModel();
function TemperatureViewModel() { function TemperatureViewModel() {
var self = this; var self = this;
@ -304,15 +310,15 @@ function TemperatureViewModel() {
self.temperatures.actualBed = self.temperatures.actualBed.slice(-300); self.temperatures.actualBed = self.temperatures.actualBed.slice(-300);
self.temperatures.targetBed = self.temperatures.targetBed.slice(-300); self.temperatures.targetBed = self.temperatures.targetBed.slice(-300);
self._updatePlot(); self.updatePlot();
} }
self._processTemperatureHistoryData = function(data) { self._processTemperatureHistoryData = function(data) {
self.temperatures = data; self.temperatures = data;
self._updatePlot(); self.updatePlot();
} }
self._updatePlot = function() { self.updatePlot = function() {
var data = [ var data = [
{label: "Actual", color: "#FF4040", data: self.temperatures.actual}, {label: "Actual", color: "#FF4040", data: self.temperatures.actual},
{label: "Target", color: "#FFA0A0", data: self.temperatures.target}, {label: "Target", color: "#FFA0A0", data: self.temperatures.target},
@ -322,7 +328,6 @@ function TemperatureViewModel() {
$.plot($("#temperature-graph"), data, self.plotOptions); $.plot($("#temperature-graph"), data, self.plotOptions);
} }
} }
var temperatureViewModel = new TemperatureViewModel();
function SpeedViewModel() { function SpeedViewModel() {
var self = this; var self = this;
@ -340,31 +345,38 @@ function SpeedViewModel() {
self.isReady = ko.observable(undefined); self.isReady = ko.observable(undefined);
self.isLoading = ko.observable(undefined); self.isLoading = ko.observable(undefined);
self.fromStateEvent = function(data) { self._fromCurrentData = function(data) {
self.isErrorOrClosed(data.closedOrError); self._processStateData(data.state);
self.isOperational(data.operational);
self.isPaused(data.paused);
self.isPrinting(data.printing);
self.isError(data.error);
self.isReady(data.ready);
self.isLoading(data.loading);
/*
if (response.feedrate) {
self.outerWall(response.feedrate.outerWall);
self.innerWall(response.feedrate.innerWall);
self.fill(response.feedrate.fill);
self.support(response.feedrate.support);
} else {
self.outerWall(undefined);
self.innerWall(undefined);
self.fill(undefined);
self.support(undefined);
}
*/
} }
self._fromHistoryData = function(data) {
self._processStateData(data.state);
}
self._processStateData = function(data) {
self.isErrorOrClosed(data.flags.closedOrError);
self.isOperational(data.flags.operational);
self.isPaused(data.flags.paused);
self.isPrinting(data.flags.printing);
self.isError(data.flags.error);
self.isReady(data.flags.ready);
self.isLoading(data.flags.loading);
}
/*
if (response.feedrate) {
self.outerWall(response.feedrate.outerWall);
self.innerWall(response.feedrate.innerWall);
self.fill(response.feedrate.fill);
self.support(response.feedrate.support);
} else {
self.outerWall(undefined);
self.innerWall(undefined);
self.fill(undefined);
self.support(undefined);
}
*/
} }
var speedViewModel = new SpeedViewModel();
function TerminalViewModel() { function TerminalViewModel() {
var self = this; var self = this;
@ -379,7 +391,29 @@ function TerminalViewModel() {
self.isReady = ko.observable(undefined); self.isReady = ko.observable(undefined);
self.isLoading = ko.observable(undefined); self.isLoading = ko.observable(undefined);
self.fromStateEvent = function(data) { self.fromCurrentData = function(data) {
self._processStateData(data.state);
self._processCurrentLogData(data.logs);
}
self.fromHistoryData = function(data) {
self._processStateData(data.state);
self._processHistoryLogData(data.logHistory);
}
self._processCurrentLogData = function(data) {
if (!self.log)
self.log = []
self.log = self.log.concat(data)
self.updateOutput();
}
self._processHistoryLogData = function(data) {
self.log = data;
self.updateOutput();
}
self._processStateData = function(data) {
self.isErrorOrClosed(data.flags.closedOrError); self.isErrorOrClosed(data.flags.closedOrError);
self.isOperational(data.flags.operational); self.isOperational(data.flags.operational);
self.isPaused(data.flags.paused); self.isPaused(data.flags.paused);
@ -389,18 +423,6 @@ function TerminalViewModel() {
self.isLoading(data.flags.loading); self.isLoading(data.flags.loading);
} }
self.fromLogEvent = function(data) {
if (!self.log)
self.log = []
self.log.concat(data.line)
self.updateOutput();
}
self.fromHistoryEvent = function(data) {
self.log = data;
self.updateOutput();
}
self.updateOutput = function() { self.updateOutput = function() {
if (!self.log) if (!self.log)
return; return;
@ -420,7 +442,6 @@ function TerminalViewModel() {
} }
} }
} }
var terminalViewModel = new TerminalViewModel();
function GcodeFilesViewModel() { function GcodeFilesViewModel() {
var self = this; var self = this;
@ -463,7 +484,6 @@ function GcodeFilesViewModel() {
}) })
} }
} }
var gcodeFilesViewModel = new GcodeFilesViewModel();
function WebcamViewModel() { function WebcamViewModel() {
var self = this; var self = this;
@ -508,7 +528,15 @@ function WebcamViewModel() {
} }
} }
self.fromStateEvent = function(data) { self.fromCurrentData = function(data) {
self._processStateData(data.state);
}
self.fromHistoryData = function(data) {
self._processStateData(data.state);
}
self._processStateData = function(data) {
self.isErrorOrClosed(data.flags.closedOrError); self.isErrorOrClosed(data.flags.closedOrError);
self.isOperational(data.flags.operational); self.isOperational(data.flags.operational);
self.isPaused(data.flags.paused); self.isPaused(data.flags.paused);
@ -546,7 +574,6 @@ function WebcamViewModel() {
}) })
} }
} }
var webcamViewModel = new WebcamViewModel();
function DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, speedViewModel, terminalViewModel, webcamViewModel) { function DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, speedViewModel, terminalViewModel, webcamViewModel) {
var self = this; var self = this;
@ -558,41 +585,50 @@ function DataUpdater(connectionViewModel, printerStateViewModel, temperatureView
self.speedViewModel = speedViewModel; self.speedViewModel = speedViewModel;
self.webcamViewModel = webcamViewModel; self.webcamViewModel = webcamViewModel;
self.socket = io.connect(); self._socket = io.connect();
self.socket.on("connect", function() { self._socket.on("connect", function() {
if ($("#offline_overlay").is(":visible")) { if ($("#offline_overlay").is(":visible")) {
$("#offline_overlay").hide(); $("#offline_overlay").hide();
self.webcamViewModel.requestData(); self.webcamViewModel.requestData();
} }
}) })
self.socket.on("disconnect", function() { self._socket.on("disconnect", function() {
// if the updated fails to communicate with the backend, we interpret this as a missing backend // if the updated fails to communicate with the backend, we interpret this as a missing backend
if (!$("#offline_overlay").is(":visible")) if (!$("#offline_overlay").is(":visible"))
$("#offline_overlay").show(); $("#offline_overlay").show();
}) })
self.socket.on("state", function(data) { self._socket.on("history", function(data) {
self.printerStateViewModel.fromStateEvent(data); self.connectionViewModel.fromHistoryData(data);
self.connectionViewModel.fromStateEvent(data);
self.temperatureViewModel.fromStateEvent(data);
self.terminalViewModel.fromStateEvent(data);
self.speedViewModel.fromStateEvent(data);
self.webcamViewModel.fromStateEvent(data);
})
self.socket.on("history", function(data) {
self.printerStateViewModel.fromHistoryData(data); self.printerStateViewModel.fromHistoryData(data);
self.temperatureViewModel.fromHistoryData(data); self.temperatureViewModel.fromHistoryData(data);
//self.terminalViewModel.fromHistoryData(data); self.terminalViewModel.fromHistoryData(data);
self.webcamViewModel.fromHistoryData(data);
}) })
self.socket.on("current", function(data) { self._socket.on("current", function(data) {
self.connectionViewModel.fromCurrentData(data); self.connectionViewModel.fromCurrentData(data);
self.printerStateViewModel.fromCurrentData(data); self.printerStateViewModel.fromCurrentData(data);
self.temperatureViewModel.fromCurrentData(data); self.temperatureViewModel.fromCurrentData(data);
self.terminalViewModel.fromCurrentData(data);
self.webcamViewModel.fromCurrentData(data);
}) })
self.reconnect = function() {
self._socket.socket.connect();
}
} }
var dataUpdater = new DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, speedViewModel, terminalViewModel, webcamViewModel);
$(function() { $(function() {
//~~ View models
var connectionViewModel = new ConnectionViewModel();
var printerStateViewModel = new PrinterStateViewModel();
var temperatureViewModel = new TemperatureViewModel();
var speedViewModel = new SpeedViewModel();
var terminalViewModel = new TerminalViewModel();
var gcodeFilesViewModel = new GcodeFilesViewModel();
var webcamViewModel = new WebcamViewModel();
var dataUpdater = new DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, speedViewModel, terminalViewModel, webcamViewModel);
//~~ Print job control //~~ Print job control
$("#job_print").click(function() { $("#job_print").click(function() {
@ -641,6 +677,9 @@ $(function() {
success: function() {$("#temp_newBedTemp").val("")} success: function() {$("#temp_newBedTemp").val("")}
}) })
}) })
$('#tabs a[data-toggle="tab"]').on('shown', function (e) {
temperatureViewModel.updatePlot();
});
//~~ Jog controls //~~ Jog controls
@ -719,7 +758,7 @@ $(function() {
}); });
//~~ Offline overlay //~~ Offline overlay
$("#offline_overlay_reconnect").click(function() {dataUpdater.requestData()}); $("#offline_overlay_reconnect").click(function() {dataUpdater.reconnect()});
//~~ knockout.js bindings //~~ knockout.js bindings
@ -738,7 +777,6 @@ $(function() {
//~~ startup commands //~~ startup commands
//dataUpdater.requestData();
connectionViewModel.requestData(); connectionViewModel.requestData();
gcodeFilesViewModel.requestData(); gcodeFilesViewModel.requestData();
webcamViewModel.requestData(); webcamViewModel.requestData();

View File

@ -105,11 +105,11 @@
</div> </div>
<div class="tabbable span8"> <div class="tabbable span8">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs" id="tabs">
<li class="active"><a href="#temp" data-toggle="tab">Temp</a></li> <li class="active"><a href="#temp" data-toggle="tab">Temperature</a></li>
<li><a href="#jog" data-toggle="tab">Jog</a></li> <li><a href="#jog" data-toggle="tab">Controls</a></li>
<li><a href="#speed" data-toggle="tab">Speed</a></li> <!--<li><a href="#speed" data-toggle="tab">Speed</a></li>-->
<li><a href="#term" data-toggle="tab">Term</a></li> <li><a href="#term" data-toggle="tab">Terminal</a></li>
{% if webcamStream %}<li><a href="#webcam" data-toggle="tab">Webcam</a></li>{% endif %} {% if webcamStream %}<li><a href="#webcam" data-toggle="tab">Webcam</a></li>{% endif %}
</ul> </ul>