State is now sent in update bundles to the frontend if updates are available, but not more frequently than every 500ms.
parent
4983a00adf
commit
e8a633db8b
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue