- Gcode for printjob is now loaded asynchronously, including a progress indicator in the frontend
- New connection section which also allows selection of port and baudrate to use for connecting to printer (default values from profile file are pre-selected if available) - Connect button now switches to disconnect-functionality while connected - Bugfix: Jog controls are now also enabled if no job is loaded - Cleanups: More consistent quotes, some documentationmaster
parent
083498865d
commit
f40460d797
|
@ -1,11 +1,11 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
__author__ = 'Gina Häußge <osd@foosel.net>'
|
__author__ = "Gina Häußge <osd@foosel.net>"
|
||||||
|
|
||||||
from flask import Flask, request, render_template, jsonify, make_response
|
from flask import Flask, request, render_template, jsonify, make_response
|
||||||
from werkzeug import secure_filename
|
from werkzeug import secure_filename
|
||||||
|
|
||||||
from printer import Printer
|
from printer import Printer, getConnectionOptions
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
@ -16,15 +16,15 @@ BASEURL="/ajax/"
|
||||||
SUCCESS={}
|
SUCCESS={}
|
||||||
|
|
||||||
# taken from http://stackoverflow.com/questions/1084697/how-do-i-store-desktop-application-data-in-a-cross-platform-way-for-python
|
# taken from http://stackoverflow.com/questions/1084697/how-do-i-store-desktop-application-data-in-a-cross-platform-way-for-python
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == "darwin":
|
||||||
from AppKit import NSSearchPathForDirectoriesInDomains
|
from AppKit import NSSearchPathForDirectoriesInDomains
|
||||||
# http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains
|
# http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains
|
||||||
# NSApplicationSupportDirectory = 14
|
# NSApplicationSupportDirectory = 14
|
||||||
# NSUserDomainMask = 1
|
# NSUserDomainMask = 1
|
||||||
# True for expanding the tilde into a fully qualified path
|
# True for expanding the tilde into a fully qualified path
|
||||||
appdata = os.path.join(NSSearchPathForDirectoriesInDomains(14, 1, True)[0], APPNAME)
|
appdata = os.path.join(NSSearchPathForDirectoriesInDomains(14, 1, True)[0], APPNAME)
|
||||||
elif sys.platform == 'win32':
|
elif sys.platform == "win32":
|
||||||
appdata = os.path.join(os.environ['APPDATA'], APPNAME)
|
appdata = os.path.join(os.environ["APPDATA"], APPNAME)
|
||||||
else:
|
else:
|
||||||
appdata = os.path.expanduser(os.path.join("~", "." + APPNAME.lower()))
|
appdata = os.path.expanduser(os.path.join("~", "." + APPNAME.lower()))
|
||||||
|
|
||||||
|
@ -36,94 +36,111 @@ ALLOWED_EXTENSIONS = set(["gcode"])
|
||||||
app = Flask("Cura.webui")
|
app = Flask("Cura.webui")
|
||||||
printer = Printer()
|
printer = Printer()
|
||||||
|
|
||||||
@app.route('/')
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
return render_template('index.html')
|
return render_template("index.html")
|
||||||
|
|
||||||
#~~ Printer state
|
#~~ Printer state
|
||||||
|
|
||||||
@app.route(BASEURL + 'state', methods=['GET'])
|
@app.route(BASEURL + "state", methods=["GET"])
|
||||||
def printerState():
|
def printerState():
|
||||||
temp = printer.currentTemp
|
temp = printer.currentTemp
|
||||||
bedTemp = printer.currentBedTemp
|
bedTemp = printer.currentBedTemp
|
||||||
targetTemp = printer.currentTargetTemp
|
targetTemp = printer.currentTargetTemp
|
||||||
bedTargetTemp = printer.currentBedTargetTemp
|
bedTargetTemp = printer.currentBedTargetTemp
|
||||||
jobData = printer.jobData()
|
jobData = printer.jobData()
|
||||||
|
gcodeState = printer.gcodeState()
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
'state': printer.getStateString(),
|
"state": printer.getStateString(),
|
||||||
'temp': temp,
|
"temp": temp,
|
||||||
'bedTemp': bedTemp,
|
"bedTemp": bedTemp,
|
||||||
'targetTemp': targetTemp,
|
"targetTemp": targetTemp,
|
||||||
'targetBedTemp': bedTargetTemp,
|
"targetBedTemp": bedTargetTemp,
|
||||||
'operational': printer.isOperational(),
|
"operational": printer.isOperational(),
|
||||||
'closedOrError': printer.isClosedOrError(),
|
"closedOrError": printer.isClosedOrError(),
|
||||||
'error': printer.isError(),
|
"error": printer.isError(),
|
||||||
'printing': printer.isPrinting(),
|
"printing": printer.isPrinting(),
|
||||||
'paused': printer.isPaused(),
|
"paused": printer.isPaused(),
|
||||||
'ready': printer.isReady()
|
"ready": printer.isReady(),
|
||||||
|
"loading": printer.isLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jobData != None):
|
if jobData is not None:
|
||||||
result['job'] = jobData
|
jobData["filename"] = jobData["filename"].replace(UPLOAD_FOLDER + os.sep, "")
|
||||||
|
result["job"] = jobData
|
||||||
|
|
||||||
if (request.values.has_key('temperatures')):
|
if gcodeState is not None:
|
||||||
result['temperatures'] = printer.temps
|
gcodeState["filename"] = gcodeState["filename"].replace(UPLOAD_FOLDER + os.sep, "")
|
||||||
|
result["gcode"] = gcodeState
|
||||||
|
|
||||||
if (request.values.has_key('log')):
|
if request.values.has_key("temperatures"):
|
||||||
result['log'] = printer.log
|
result["temperatures"] = printer.temps
|
||||||
|
|
||||||
if (request.values.has_key('messages')):
|
if request.values.has_key("log"):
|
||||||
result['messages'] = printer.messages
|
result["log"] = printer.log
|
||||||
|
|
||||||
|
if request.values.has_key("messages"):
|
||||||
|
result["messages"] = printer.messages
|
||||||
|
|
||||||
return jsonify(result)
|
return jsonify(result)
|
||||||
|
|
||||||
@app.route(BASEURL + 'state/messages', methods=['GET'])
|
@app.route(BASEURL + "state/messages", methods=["GET"])
|
||||||
def printerMessages():
|
def printerMessages():
|
||||||
return jsonify(messages=printer.messages)
|
return jsonify(messages=printer.messages)
|
||||||
|
|
||||||
@app.route(BASEURL + 'state/log', methods=['GET'])
|
@app.route(BASEURL + "state/log", methods=["GET"])
|
||||||
def printerLogs():
|
def printerLogs():
|
||||||
return jsonify(log=printer.log)
|
return jsonify(log=printer.log)
|
||||||
|
|
||||||
@app.route(BASEURL + 'state/temperatures', methods=['GET'])
|
@app.route(BASEURL + "state/temperatures", methods=["GET"])
|
||||||
def printerTemperatures():
|
def printerTemperatures():
|
||||||
return jsonify(temperatures = printer.temps)
|
return jsonify(temperatures = printer.temps)
|
||||||
|
|
||||||
#~~ Printer control
|
#~~ Printer control
|
||||||
|
|
||||||
@app.route(BASEURL + 'control/connect', methods=['POST'])
|
@app.route(BASEURL + "control/connectionOptions", methods=["GET"])
|
||||||
def connect():
|
def connectionOptions():
|
||||||
printer.connect()
|
return jsonify(getConnectionOptions())
|
||||||
return jsonify(state='Connecting')
|
|
||||||
|
|
||||||
@app.route(BASEURL + 'control/disconnect', methods=['POST'])
|
@app.route(BASEURL + "control/connect", methods=["POST"])
|
||||||
|
def connect():
|
||||||
|
port = None
|
||||||
|
baudrate = None
|
||||||
|
if request.values.has_key("port"):
|
||||||
|
port = request.values["port"]
|
||||||
|
if request.values.has_key("baudrate"):
|
||||||
|
baudrate = request.values["baudrate"]
|
||||||
|
printer.connect(port=port, baudrate=baudrate)
|
||||||
|
return jsonify(state="Connecting")
|
||||||
|
|
||||||
|
@app.route(BASEURL + "control/disconnect", methods=["POST"])
|
||||||
def disconnect():
|
def disconnect():
|
||||||
printer.disconnect()
|
printer.disconnect()
|
||||||
return jsonify(state='Offline')
|
return jsonify(state="Offline")
|
||||||
|
|
||||||
@app.route(BASEURL + 'control/command', methods=['POST'])
|
@app.route(BASEURL + "control/command", methods=["POST"])
|
||||||
def printerCommand():
|
def printerCommand():
|
||||||
command = request.form['command']
|
command = request.form["command"]
|
||||||
printer.command(command)
|
printer.command(command)
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
@app.route(BASEURL + 'control/print', methods=['POST'])
|
@app.route(BASEURL + "control/print", methods=["POST"])
|
||||||
def printGcode():
|
def printGcode():
|
||||||
printer.startPrint()
|
printer.startPrint()
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
@app.route(BASEURL + 'control/pause', methods=['POST'])
|
@app.route(BASEURL + "control/pause", methods=["POST"])
|
||||||
def pausePrint():
|
def pausePrint():
|
||||||
printer.togglePausePrint()
|
printer.togglePausePrint()
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
@app.route(BASEURL + 'control/cancel', methods=['POST'])
|
@app.route(BASEURL + "control/cancel", methods=["POST"])
|
||||||
def cancelPrint():
|
def cancelPrint():
|
||||||
printer.cancelPrint()
|
printer.cancelPrint()
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
@app.route(BASEURL + 'control/temperature', methods=['POST'])
|
@app.route(BASEURL + "control/temperature", methods=["POST"])
|
||||||
def setTargetTemperature():
|
def setTargetTemperature():
|
||||||
if not printer.isOperational():
|
if not printer.isOperational():
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
@ -143,7 +160,7 @@ def setTargetTemperature():
|
||||||
@app.route(BASEURL + "control/jog", methods=["POST"])
|
@app.route(BASEURL + "control/jog", methods=["POST"])
|
||||||
def jog():
|
def jog():
|
||||||
if not printer.isOperational() or printer.isPrinting():
|
if not printer.isOperational() or printer.isPrinting():
|
||||||
# do not jog when a print job is running or we don't have a connection
|
# do not jog when a print job is running or we don"t have a connection
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
if request.values.has_key("x"):
|
if request.values.has_key("x"):
|
||||||
|
@ -169,7 +186,7 @@ def jog():
|
||||||
|
|
||||||
#~~ GCODE file handling
|
#~~ GCODE file handling
|
||||||
|
|
||||||
@app.route(BASEURL + 'gcodefiles', methods=['GET'])
|
@app.route(BASEURL + "gcodefiles", methods=["GET"])
|
||||||
def readGcodeFiles():
|
def readGcodeFiles():
|
||||||
files = []
|
files = []
|
||||||
for osFile in os.listdir(UPLOAD_FOLDER):
|
for osFile in os.listdir(UPLOAD_FOLDER):
|
||||||
|
@ -181,22 +198,22 @@ def readGcodeFiles():
|
||||||
})
|
})
|
||||||
return jsonify(files=files)
|
return jsonify(files=files)
|
||||||
|
|
||||||
@app.route(BASEURL + 'gcodefiles/upload', methods=['POST'])
|
@app.route(BASEURL + "gcodefiles/upload", methods=["POST"])
|
||||||
def uploadGcodeFile():
|
def uploadGcodeFile():
|
||||||
file = request.files['gcode_file']
|
file = request.files["gcode_file"]
|
||||||
if file and allowed_file(file.filename):
|
if file and allowed_file(file.filename):
|
||||||
secure = secure_filename(file.filename)
|
secure = secure_filename(file.filename)
|
||||||
filename = os.path.join(UPLOAD_FOLDER, secure)
|
filename = os.path.join(UPLOAD_FOLDER, secure)
|
||||||
file.save(filename)
|
file.save(filename)
|
||||||
return readGcodeFiles()
|
return readGcodeFiles()
|
||||||
|
|
||||||
@app.route(BASEURL + 'gcodefiles/load', methods=['POST'])
|
@app.route(BASEURL + "gcodefiles/load", methods=["POST"])
|
||||||
def loadGcodeFile():
|
def loadGcodeFile():
|
||||||
filename = request.values["filename"]
|
filename = request.values["filename"]
|
||||||
printer.loadGcode(UPLOAD_FOLDER + os.sep + filename)
|
printer.loadGcode(UPLOAD_FOLDER + os.sep + filename)
|
||||||
return jsonify(SUCCESS)
|
return jsonify(SUCCESS)
|
||||||
|
|
||||||
@app.route(BASEURL + 'gcodefiles/delete', methods=['POST'])
|
@app.route(BASEURL + "gcodefiles/delete", methods=["POST"])
|
||||||
def deleteGcodeFile():
|
def deleteGcodeFile():
|
||||||
if request.values.has_key("filename"):
|
if request.values.has_key("filename"):
|
||||||
filename = request.values["filename"]
|
filename = request.values["filename"]
|
||||||
|
@ -210,14 +227,15 @@ 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
|
||||||
"""
|
"""
|
||||||
for x in ['bytes','KB','MB','GB']:
|
for x in ["bytes","KB","MB","GB"]:
|
||||||
if num < 1024.0:
|
if num < 1024.0:
|
||||||
return "%3.1f%s" % (num, x)
|
return "%3.1f%s" % (num, x)
|
||||||
num /= 1024.0
|
num /= 1024.0
|
||||||
return "%3.1f%s" % (num, 'TB')
|
return "%3.1f%s" % (num, "TB")
|
||||||
|
|
||||||
def allowed_file(filename):
|
def allowed_file(filename):
|
||||||
return "." in filename and filename.rsplit(".", 1)[1] in ALLOWED_EXTENSIONS
|
return "." in filename and filename.rsplit(".", 1)[1] in ALLOWED_EXTENSIONS
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
|
app.debug = True
|
||||||
app.run(host="0.0.0.0", port=5000)
|
app.run(host="0.0.0.0", port=5000)
|
||||||
|
|
|
@ -1,20 +1,33 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
__author__ = 'Gina Häußge <osd@foosel.net>'
|
__author__ = "Gina Häußge <osd@foosel.net>"
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
import Cura.util.machineCom as machineCom
|
import Cura.util.machineCom as machineCom
|
||||||
from Cura.util import gcodeInterpreter
|
from Cura.util import gcodeInterpreter
|
||||||
|
from Cura.util import profile
|
||||||
|
|
||||||
|
def getConnectionOptions():
|
||||||
|
"""
|
||||||
|
Retrieves the available ports, baudrates, prefered port and baudrate for connecting to the printer.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"ports": sorted(machineCom.serialList(), key=str.lower),
|
||||||
|
"baudrates": sorted(machineCom.baudrateList(), key=int, reverse=True),
|
||||||
|
"portPreference": profile.getPreference('serial_port_auto'),
|
||||||
|
"baudratePreference": int(profile.getPreference('serial_baud_auto'))
|
||||||
|
}
|
||||||
|
|
||||||
class Printer():
|
class Printer():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# state
|
# state
|
||||||
self.temps = {
|
self.temps = {
|
||||||
'actual': [],
|
"actual": [],
|
||||||
'target': [],
|
"target": [],
|
||||||
'actualBed': [],
|
"actualBed": [],
|
||||||
'targetBed': []
|
"targetBed": []
|
||||||
}
|
}
|
||||||
self.messages = []
|
self.messages = []
|
||||||
self.log = []
|
self.log = []
|
||||||
|
@ -32,44 +45,68 @@ class Printer():
|
||||||
self.gcodeList = None
|
self.gcodeList = None
|
||||||
self.filename = None
|
self.filename = None
|
||||||
|
|
||||||
|
self.gcodeLoader = None
|
||||||
|
|
||||||
# comm
|
# comm
|
||||||
self.comm = None
|
self.comm = None
|
||||||
|
|
||||||
def connect(self):
|
def connect(self, port=None, baudrate=None):
|
||||||
if self.comm != None:
|
"""
|
||||||
|
Connects to the printer. If port and/or baudrate is provided, uses these settings, otherwise autodetection
|
||||||
|
will be attempted.
|
||||||
|
"""
|
||||||
|
if self.comm is not None:
|
||||||
self.comm.close()
|
self.comm.close()
|
||||||
self.comm = machineCom.MachineCom(callbackObject=self)
|
self.comm = machineCom.MachineCom(port, baudrate, callbackObject=self)
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
if self.comm != None:
|
"""
|
||||||
|
Closes the connection to the printer.
|
||||||
|
"""
|
||||||
|
if self.comm is not None:
|
||||||
self.comm.close()
|
self.comm.close()
|
||||||
self.comm = None
|
self.comm = None
|
||||||
|
|
||||||
def command(self, command):
|
def command(self, command):
|
||||||
|
"""
|
||||||
|
Sends a single gcode command to the printer.
|
||||||
|
"""
|
||||||
self.commands([command])
|
self.commands([command])
|
||||||
|
|
||||||
def commands(self, commands):
|
def commands(self, commands):
|
||||||
|
"""
|
||||||
|
Sends multiple gcode commands (provided as a list) to the printer.
|
||||||
|
"""
|
||||||
for command in commands:
|
for command in commands:
|
||||||
self.comm.sendCommand(command)
|
self.comm.sendCommand(command)
|
||||||
|
|
||||||
def mcLog(self, message):
|
def mcLog(self, message):
|
||||||
|
"""
|
||||||
|
Callback method for the comm object, called upon log output.
|
||||||
|
Log line is stored in internal buffer, which is truncated to the last 300 lines.
|
||||||
|
"""
|
||||||
self.log.append(message)
|
self.log.append(message)
|
||||||
self.log = self.log[-300:]
|
self.log = self.log[-300:]
|
||||||
|
|
||||||
def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
|
def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
|
||||||
|
"""
|
||||||
|
Callback method for the comm object, called upon receiving new temperature information.
|
||||||
|
Temperature information (actual and target) for print head and print bed is stored in corresponding
|
||||||
|
temperature history (including timestamp), history is truncated to 300 entries.
|
||||||
|
"""
|
||||||
currentTime = int(time.time() * 1000)
|
currentTime = int(time.time() * 1000)
|
||||||
|
|
||||||
self.temps['actual'].append((currentTime, temp))
|
self.temps["actual"].append((currentTime, temp))
|
||||||
self.temps['actual'] = self.temps['actual'][-300:]
|
self.temps["actual"] = self.temps["actual"][-300:]
|
||||||
|
|
||||||
self.temps['target'].append((currentTime, targetTemp))
|
self.temps["target"].append((currentTime, targetTemp))
|
||||||
self.temps['target'] = self.temps['target'][-300:]
|
self.temps["target"] = self.temps["target"][-300:]
|
||||||
|
|
||||||
self.temps['actualBed'].append((currentTime, bedTemp))
|
self.temps["actualBed"].append((currentTime, bedTemp))
|
||||||
self.temps['actualBed'] = self.temps['actualBed'][-300:]
|
self.temps["actualBed"] = self.temps["actualBed"][-300:]
|
||||||
|
|
||||||
self.temps['targetBed'].append((currentTime, bedTargetTemp))
|
self.temps["targetBed"].append((currentTime, bedTargetTemp))
|
||||||
self.temps['targetBed'] = self.temps['targetBed'][-300:]
|
self.temps["targetBed"] = self.temps["targetBed"][-300:]
|
||||||
|
|
||||||
self.currentTemp = temp
|
self.currentTemp = temp
|
||||||
self.currentTargetTemp = targetTemp
|
self.currentTargetTemp = targetTemp
|
||||||
|
@ -77,22 +114,55 @@ class Printer():
|
||||||
self.currentBedTargetTemp = bedTargetTemp
|
self.currentBedTargetTemp = bedTargetTemp
|
||||||
|
|
||||||
def mcStateChange(self, state):
|
def mcStateChange(self, state):
|
||||||
|
"""
|
||||||
|
Callback method for the comm object, called if the connection state changes.
|
||||||
|
New state is stored for retrieval by the frontend.
|
||||||
|
"""
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
def mcMessage(self, message):
|
def mcMessage(self, message):
|
||||||
|
"""
|
||||||
|
Callback method for the comm object, called upon message exchanges via serial.
|
||||||
|
Stores the message in the message buffer, truncates buffer to the last 300 lines.
|
||||||
|
"""
|
||||||
self.messages.append(message)
|
self.messages.append(message)
|
||||||
self.messages = self.messages[-300:]
|
self.messages = self.messages[-300:]
|
||||||
|
|
||||||
def mcProgress(self, lineNr):
|
def mcProgress(self, lineNr):
|
||||||
|
"""
|
||||||
|
Callback method for the comm object, called upon any change in progress of the printjob.
|
||||||
|
Triggers storage of new values for printTime, printTimeLeft and the current line.
|
||||||
|
"""
|
||||||
self.printTime = self.comm.getPrintTime()
|
self.printTime = self.comm.getPrintTime()
|
||||||
self.printTimeLeft = self.comm.getPrintTimeRemainingEstimate()
|
self.printTimeLeft = self.comm.getPrintTimeRemainingEstimate()
|
||||||
self.progress = self.comm.getPrintPos()
|
self.progress = self.comm.getPrintPos()
|
||||||
|
|
||||||
def mcZChange(self, newZ):
|
def mcZChange(self, newZ):
|
||||||
|
"""
|
||||||
|
Callback method for the comm object, called upon change of the z-layer.
|
||||||
|
"""
|
||||||
self.currentZ = newZ
|
self.currentZ = newZ
|
||||||
|
|
||||||
|
def onGcodeLoaded(self, gcodeLoader):
|
||||||
|
"""
|
||||||
|
Callback method for the gcode loader, gets called when the gcode for the new printjob has finished loading.
|
||||||
|
Takes care to set filename, gcode and commandlist from the gcode loader and reset print job progress.
|
||||||
|
"""
|
||||||
|
self.filename = gcodeLoader.filename
|
||||||
|
self.gcode = gcodeLoader.gcode
|
||||||
|
self.gcodeList = gcodeLoader.gcodeList
|
||||||
|
self.currentZ = None
|
||||||
|
self.progress = None
|
||||||
|
self.printTime = None
|
||||||
|
self.printTimeLeft = None
|
||||||
|
|
||||||
|
self.gcodeLoader = None
|
||||||
|
|
||||||
def jobData(self):
|
def jobData(self):
|
||||||
if self.gcode != None:
|
"""
|
||||||
|
Returns statistics regarding the currently loaded printjob, or None if no printjob is loaded.
|
||||||
|
"""
|
||||||
|
if self.gcode is not None:
|
||||||
formattedPrintTime = None
|
formattedPrintTime = None
|
||||||
if (self.printTime):
|
if (self.printTime):
|
||||||
formattedPrintTime = "%02d:%02d" % (int(self.printTime / 60), int(self.printTime % 60))
|
formattedPrintTime = "%02d:%02d" % (int(self.printTime / 60), int(self.printTime % 60))
|
||||||
|
@ -102,16 +172,17 @@ class Printer():
|
||||||
formattedPrintTimeLeft = "%02d:%02d" % (int(self.printTimeLeft / 60), int(self.printTimeLeft % 60))
|
formattedPrintTimeLeft = "%02d:%02d" % (int(self.printTimeLeft / 60), int(self.printTimeLeft % 60))
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'currentZ': self.currentZ,
|
"filename": self.filename,
|
||||||
'line': self.progress,
|
"currentZ": self.currentZ,
|
||||||
'totalLines': len(self.gcodeList),
|
"line": self.progress,
|
||||||
'printTime': formattedPrintTime,
|
"totalLines": len(self.gcodeList),
|
||||||
'printTimeLeft': formattedPrintTimeLeft,
|
"printTime": formattedPrintTime,
|
||||||
'filament': "%.2fm %.2fg" % (
|
"printTimeLeft": formattedPrintTimeLeft,
|
||||||
|
"filament": "%.2fm %.2fg" % (
|
||||||
self.gcode.extrusionAmount / 1000,
|
self.gcode.extrusionAmount / 1000,
|
||||||
self.gcode.calculateWeight() * 1000
|
self.gcode.calculateWeight() * 1000
|
||||||
),
|
),
|
||||||
'estimatedPrintTime': "%02d:%02d" % (
|
"estimatedPrintTime": "%02d:%02d" % (
|
||||||
int(self.gcode.totalMoveTimeMinute / 60),
|
int(self.gcode.totalMoveTimeMinute / 60),
|
||||||
int(self.gcode.totalMoveTimeMinute % 60)
|
int(self.gcode.totalMoveTimeMinute % 60)
|
||||||
)
|
)
|
||||||
|
@ -120,64 +191,68 @@ class Printer():
|
||||||
data = None
|
data = None
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def gcodeState(self):
|
||||||
|
if self.gcodeLoader is not None:
|
||||||
|
return {
|
||||||
|
"filename": self.gcodeLoader.filename,
|
||||||
|
"progress": self.gcodeLoader.progress
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def getStateString(self):
|
def getStateString(self):
|
||||||
if self.comm == None:
|
"""
|
||||||
return 'Offline'
|
Returns a human readable string corresponding to the current communication state.
|
||||||
|
"""
|
||||||
|
if self.comm is None:
|
||||||
|
return "Offline"
|
||||||
else:
|
else:
|
||||||
return self.comm.getStateString()
|
return self.comm.getStateString()
|
||||||
|
|
||||||
def isClosedOrError(self):
|
def isClosedOrError(self):
|
||||||
return self.comm == None or self.comm.isClosedOrError()
|
return self.comm is None or self.comm.isClosedOrError()
|
||||||
|
|
||||||
def isOperational(self):
|
def isOperational(self):
|
||||||
return self.comm != None and self.comm.isOperational()
|
return self.comm is not None and self.comm.isOperational()
|
||||||
|
|
||||||
def isPrinting(self):
|
def isPrinting(self):
|
||||||
return self.comm != None and self.comm.isPrinting()
|
return self.comm is not None and self.comm.isPrinting()
|
||||||
|
|
||||||
def isPaused(self):
|
def isPaused(self):
|
||||||
return self.comm != None and self.comm.isPaused()
|
return self.comm is not None and self.comm.isPaused()
|
||||||
|
|
||||||
def isError(self):
|
def isError(self):
|
||||||
return self.comm != None and self.comm.isError()
|
return self.comm is not None and self.comm.isError()
|
||||||
|
|
||||||
def isReady(self):
|
def isReady(self):
|
||||||
return self.gcodeList and len(self.gcodeList) > 0
|
return self.gcodeLoader is None and self.gcodeList and len(self.gcodeList) > 0
|
||||||
|
|
||||||
|
def isLoading(self):
|
||||||
|
return self.gcodeLoader is not None
|
||||||
|
|
||||||
def loadGcode(self, file):
|
def loadGcode(self, file):
|
||||||
if self.comm != None and self.comm.isPrinting():
|
"""
|
||||||
|
Loads the gcode from the given file as the new print job.
|
||||||
|
Aborts if the printer is currently printing or another gcode file is currently being loaded.
|
||||||
|
"""
|
||||||
|
if (self.comm is not None and self.comm.isPrinting()) or (self.gcodeLoader is not None):
|
||||||
return
|
return
|
||||||
|
|
||||||
#Send an initial M110 to reset the line counter to zero.
|
self.filename = None
|
||||||
prevLineType = lineType = 'CUSTOM'
|
self.gcode = None
|
||||||
gcodeList = ["M110"]
|
self.gcodeList = None
|
||||||
for line in open(file, 'r'):
|
|
||||||
if line.startswith(';TYPE:'):
|
self.gcodeLoader = GcodeLoader(file, self)
|
||||||
lineType = line[6:].strip()
|
self.gcodeLoader.start()
|
||||||
if ';' in line:
|
|
||||||
line = line[0:line.find(';')]
|
|
||||||
line = line.strip()
|
|
||||||
if len(line) > 0:
|
|
||||||
if prevLineType != lineType:
|
|
||||||
gcodeList.append((line, lineType, ))
|
|
||||||
else:
|
|
||||||
gcodeList.append(line)
|
|
||||||
prevLineType = lineType
|
|
||||||
gcode = gcodeInterpreter.gcode()
|
|
||||||
gcode.loadList(gcodeList)
|
|
||||||
#print "Loaded: %s (%d)" % (filename, len(gcodeList))
|
|
||||||
self.filename = file
|
|
||||||
self.gcode = gcode
|
|
||||||
self.gcodeList = gcodeList
|
|
||||||
self.currentZ = None
|
|
||||||
self.progress = None
|
|
||||||
self.printTime = None
|
|
||||||
self.printTimeLeft = None
|
|
||||||
|
|
||||||
def startPrint(self):
|
def startPrint(self):
|
||||||
if self.comm == None or not self.comm.isOperational():
|
"""
|
||||||
|
Starts the currently loaded print job.
|
||||||
|
Only starts if the printer is connected and operational, not currently printing and a printjob is loaded
|
||||||
|
"""
|
||||||
|
if self.comm is None or not self.comm.isOperational():
|
||||||
return
|
return
|
||||||
if self.gcodeList == None:
|
if self.gcodeList is None:
|
||||||
return
|
return
|
||||||
if self.comm.isPrinting():
|
if self.comm.isPrinting():
|
||||||
return
|
return
|
||||||
|
@ -185,13 +260,72 @@ class Printer():
|
||||||
self.comm.printGCode(self.gcodeList)
|
self.comm.printGCode(self.gcodeList)
|
||||||
|
|
||||||
def togglePausePrint(self):
|
def togglePausePrint(self):
|
||||||
if self.comm == None:
|
"""
|
||||||
|
Pause the current printjob.
|
||||||
|
"""
|
||||||
|
if self.comm is None:
|
||||||
return
|
return
|
||||||
self.comm.setPause(not self.comm.isPaused())
|
self.comm.setPause(not self.comm.isPaused())
|
||||||
|
|
||||||
def cancelPrint(self):
|
def cancelPrint(self, disableMotorsAndHeater=True):
|
||||||
if self.comm == None:
|
"""
|
||||||
|
Cancel the current printjob.
|
||||||
|
"""
|
||||||
|
if self.comm is None:
|
||||||
return
|
return
|
||||||
self.comm.cancelPrint()
|
self.comm.cancelPrint()
|
||||||
self.comm.sendCommands(["M84", "M104 S0", "M140 S0"]) # disable motors, switch off heaters
|
if disableMotorsAndHeater:
|
||||||
|
self.commands(["M84", "M104 S0", "M140 S0"]) # disable motors, switch off heaters
|
||||||
|
|
||||||
|
# reset line, height, print time
|
||||||
|
self.currentZ = None
|
||||||
|
self.progress = None
|
||||||
|
self.printTime = None
|
||||||
|
self.printTimeLeft = None
|
||||||
|
|
||||||
|
class GcodeLoader(Thread):
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
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):
|
||||||
|
Thread.__init__(self);
|
||||||
|
|
||||||
|
self.printerCallback = printerCallback;
|
||||||
|
|
||||||
|
self.filename = filename
|
||||||
|
self.progress = None
|
||||||
|
|
||||||
|
self.gcode = None
|
||||||
|
self.gcodeList = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
#Send an initial M110 to reset the line counter to zero.
|
||||||
|
prevLineType = lineType = "CUSTOM"
|
||||||
|
gcodeList = ["M110"]
|
||||||
|
with open(self.filename, "r") as file:
|
||||||
|
for line in file:
|
||||||
|
if line.startswith(";TYPE:"):
|
||||||
|
lineType = line[6:].strip()
|
||||||
|
if ";" in line:
|
||||||
|
line = line[0:line.find(";")]
|
||||||
|
line = line.strip()
|
||||||
|
if len(line) > 0:
|
||||||
|
if prevLineType != lineType:
|
||||||
|
gcodeList.append((line, lineType, ))
|
||||||
|
else:
|
||||||
|
gcodeList.append(line)
|
||||||
|
prevLineType = lineType
|
||||||
|
|
||||||
|
self.gcodeList = gcodeList
|
||||||
|
self.gcode = gcodeInterpreter.gcode()
|
||||||
|
self.gcode.progressCallback = self.onProgress
|
||||||
|
self.gcode.loadList(self.gcodeList)
|
||||||
|
|
||||||
|
self.printerCallback.onGcodeLoaded(self)
|
||||||
|
|
||||||
|
def onProgress(self, progress):
|
||||||
|
self.progress = progress
|
||||||
|
|
||||||
|
|
|
@ -51,3 +51,7 @@ table th.gcode_files_action, table td.gcode_files_action {
|
||||||
#temp_newTemp {
|
#temp_newTemp {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#connection_ports, #connection_baudrates {
|
||||||
|
width: 100%;
|
||||||
|
}
|
|
@ -1,3 +1,65 @@
|
||||||
|
function ConnectionViewModel() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.portOptions = ko.observableArray(undefined);
|
||||||
|
self.baudrateOptions = ko.observableArray(undefined);
|
||||||
|
self.selectedPort = ko.observable(undefined);
|
||||||
|
self.selectedBaudrate = ko.observable(undefined);
|
||||||
|
|
||||||
|
self.isErrorOrClosed = ko.observable(undefined);
|
||||||
|
self.isOperational = ko.observable(undefined);
|
||||||
|
self.isPrinting = ko.observable(undefined);
|
||||||
|
self.isPaused = ko.observable(undefined);
|
||||||
|
self.isError = ko.observable(undefined);
|
||||||
|
self.isReady = ko.observable(undefined);
|
||||||
|
self.isLoading = ko.observable(undefined);
|
||||||
|
|
||||||
|
self.buttonText = ko.computed(function() {
|
||||||
|
if (self.isErrorOrClosed())
|
||||||
|
return "Connect";
|
||||||
|
else
|
||||||
|
return "Disconnect";
|
||||||
|
})
|
||||||
|
|
||||||
|
self.fromResponse = function(response) {
|
||||||
|
self.portOptions(response.ports);
|
||||||
|
self.baudrateOptions(response.baudrates);
|
||||||
|
|
||||||
|
if (!self.selectedPort() && response.ports && response.ports.indexOf(response.portPreference) >= 0)
|
||||||
|
self.selectedPort(response.portPreference);
|
||||||
|
if (!self.selectedBaudrate() && response.baudrates && response.baudrates.indexOf(response.baudratePreference) >= 0)
|
||||||
|
self.selectedBaudrate(response.baudratePreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fromStateResponse = function(response) {
|
||||||
|
self.isErrorOrClosed(response.closedOrError);
|
||||||
|
self.isOperational(response.operational);
|
||||||
|
self.isPaused(response.paused);
|
||||||
|
self.isPrinting(response.printing);
|
||||||
|
self.isError(response.error);
|
||||||
|
self.isReady(response.ready);
|
||||||
|
self.isLoading(response.loading);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.connect = function() {
|
||||||
|
if (self.isErrorOrClosed()) {
|
||||||
|
$.ajax({
|
||||||
|
url: AJAX_BASEURL + "control/connect",
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
data: { "port": self.selectedPort(), "baudrate": self.selectedBaudrate() }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
$.ajax({
|
||||||
|
url: AJAX_BASEURL + "control/disconnect",
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var connectionViewModel = new ConnectionViewModel();
|
||||||
|
|
||||||
function PrinterStateViewModel() {
|
function PrinterStateViewModel() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -8,7 +70,9 @@ function PrinterStateViewModel() {
|
||||||
self.isPaused = ko.observable(undefined);
|
self.isPaused = ko.observable(undefined);
|
||||||
self.isError = ko.observable(undefined);
|
self.isError = ko.observable(undefined);
|
||||||
self.isReady = ko.observable(undefined);
|
self.isReady = ko.observable(undefined);
|
||||||
|
self.isLoading = ko.observable(undefined);
|
||||||
|
|
||||||
|
self.filename = ko.observable(undefined);
|
||||||
self.filament = ko.observable(undefined);
|
self.filament = ko.observable(undefined);
|
||||||
self.estimatedPrintTime = ko.observable(undefined);
|
self.estimatedPrintTime = ko.observable(undefined);
|
||||||
self.printTime = ko.observable(undefined);
|
self.printTime = ko.observable(undefined);
|
||||||
|
@ -35,14 +99,6 @@ function PrinterStateViewModel() {
|
||||||
return "Pause";
|
return "Pause";
|
||||||
});
|
});
|
||||||
|
|
||||||
self.connect = function() {
|
|
||||||
$.ajax({
|
|
||||||
url: AJAX_BASEURL + "control/connect",
|
|
||||||
type: 'POST',
|
|
||||||
dataType: 'json'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fromResponse = function(response) {
|
self.fromResponse = function(response) {
|
||||||
self.stateString(response.state);
|
self.stateString(response.state);
|
||||||
self.isErrorOrClosed(response.closedOrError);
|
self.isErrorOrClosed(response.closedOrError);
|
||||||
|
@ -51,8 +107,10 @@ function PrinterStateViewModel() {
|
||||||
self.isPrinting(response.printing);
|
self.isPrinting(response.printing);
|
||||||
self.isError(response.error);
|
self.isError(response.error);
|
||||||
self.isReady(response.ready);
|
self.isReady(response.ready);
|
||||||
|
self.isLoading(response.loading);
|
||||||
|
|
||||||
if (response.job) {
|
if (response.job) {
|
||||||
|
self.filename(response.job.filename);
|
||||||
self.filament(response.job.filament);
|
self.filament(response.job.filament);
|
||||||
self.estimatedPrintTime(response.job.estimatedPrintTime);
|
self.estimatedPrintTime(response.job.estimatedPrintTime);
|
||||||
self.printTime(response.job.printTime);
|
self.printTime(response.job.printTime);
|
||||||
|
@ -61,11 +119,17 @@ function PrinterStateViewModel() {
|
||||||
self.totalLines(response.job.totalLines ? response.job.totalLines : 0);
|
self.totalLines(response.job.totalLines ? response.job.totalLines : 0);
|
||||||
self.currentHeight(response.job.currentZ);
|
self.currentHeight(response.job.currentZ);
|
||||||
} else {
|
} else {
|
||||||
|
if (response.loading && response.gcode) {
|
||||||
|
self.filename("Loading... (" + Math.round(response.gcode.progress * 100) + "%)");
|
||||||
|
} else {
|
||||||
|
self.filename(undefined);
|
||||||
|
}
|
||||||
self.filament(undefined);
|
self.filament(undefined);
|
||||||
self.estimatedPrintTime(undefined);
|
self.estimatedPrintTime(undefined);
|
||||||
self.printTime(undefined);
|
self.printTime(undefined);
|
||||||
self.printTimeLeft(undefined);
|
self.printTimeLeft(undefined);
|
||||||
self.currentLine(undefined);
|
self.currentLine(undefined);
|
||||||
|
self.totalLines(undefined);
|
||||||
self.currentHeight(undefined);
|
self.currentHeight(undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,12 +143,14 @@ function TemperatureViewModel() {
|
||||||
self.bedTemp = ko.observable(undefined);
|
self.bedTemp = ko.observable(undefined);
|
||||||
self.targetTemp = ko.observable(undefined);
|
self.targetTemp = ko.observable(undefined);
|
||||||
self.bedTargetTemp = ko.observable(undefined);
|
self.bedTargetTemp = ko.observable(undefined);
|
||||||
|
|
||||||
self.isErrorOrClosed = ko.observable(undefined);
|
self.isErrorOrClosed = ko.observable(undefined);
|
||||||
self.isOperational = ko.observable(undefined);
|
self.isOperational = ko.observable(undefined);
|
||||||
self.isPrinting = ko.observable(undefined);
|
self.isPrinting = ko.observable(undefined);
|
||||||
self.isPaused = ko.observable(undefined);
|
self.isPaused = ko.observable(undefined);
|
||||||
self.isError = ko.observable(undefined);
|
self.isError = ko.observable(undefined);
|
||||||
self.isReady = ko.observable(undefined);
|
self.isReady = ko.observable(undefined);
|
||||||
|
self.isLoading = ko.observable(undefined);
|
||||||
|
|
||||||
self.tempString = ko.computed(function() {
|
self.tempString = ko.computed(function() {
|
||||||
if (!self.temp())
|
if (!self.temp())
|
||||||
|
@ -116,7 +182,6 @@ function TemperatureViewModel() {
|
||||||
},
|
},
|
||||||
xaxis: {
|
xaxis: {
|
||||||
mode: "time",
|
mode: "time",
|
||||||
timeformat: "%H:%M:%S",
|
|
||||||
minTickSize: [2, "minute"],
|
minTickSize: [2, "minute"],
|
||||||
tickFormatter: function(val, axis) {
|
tickFormatter: function(val, axis) {
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
|
@ -146,6 +211,7 @@ function TemperatureViewModel() {
|
||||||
self.isPrinting(response.printing);
|
self.isPrinting(response.printing);
|
||||||
self.isError(response.error);
|
self.isError(response.error);
|
||||||
self.isReady(response.ready);
|
self.isReady(response.ready);
|
||||||
|
self.isLoading(response.loading);
|
||||||
|
|
||||||
self.updatePlot();
|
self.updatePlot();
|
||||||
}
|
}
|
||||||
|
@ -174,9 +240,9 @@ function TerminalViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateOutput = function() {
|
self.updateOutput = function() {
|
||||||
var output = '';
|
var output = "";
|
||||||
for (var i = 0; i < self.log.length; i++) {
|
for (var i = 0; i < self.log.length; i++) {
|
||||||
output += self.log[i] + '\n';
|
output += self.log[i] + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
var container = $("#terminal-output");
|
var container = $("#terminal-output");
|
||||||
|
@ -227,13 +293,14 @@ function GcodeFilesViewModel() {
|
||||||
}
|
}
|
||||||
var gcodeFilesViewModel = new GcodeFilesViewModel();
|
var gcodeFilesViewModel = new GcodeFilesViewModel();
|
||||||
|
|
||||||
function DataUpdater(printerStateViewModel, temperatureViewModel, terminalViewModel) {
|
function DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, terminalViewModel) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.updateInterval = 500;
|
self.updateInterval = 500;
|
||||||
self.includeTemperatures = true;
|
self.includeTemperatures = true;
|
||||||
self.includeLogs = true;
|
self.includeLogs = true;
|
||||||
|
|
||||||
|
self.connectionViewModel = connectionViewModel;
|
||||||
self.printerStateViewModel = printerStateViewModel;
|
self.printerStateViewModel = printerStateViewModel;
|
||||||
self.temperatureViewModel = temperatureViewModel;
|
self.temperatureViewModel = temperatureViewModel;
|
||||||
self.terminalViewModel = terminalViewModel;
|
self.terminalViewModel = terminalViewModel;
|
||||||
|
@ -248,48 +315,51 @@ function DataUpdater(printerStateViewModel, temperatureViewModel, terminalViewMo
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: AJAX_BASEURL + "state",
|
url: AJAX_BASEURL + "state",
|
||||||
type: 'GET',
|
type: "GET",
|
||||||
dataType: 'json',
|
dataType: "json",
|
||||||
data: parameters,
|
data: parameters,
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
self.printerStateViewModel.fromResponse(response);
|
self.printerStateViewModel.fromResponse(response);
|
||||||
|
self.connectionViewModel.fromStateResponse(response);
|
||||||
|
|
||||||
if (response.temperatures)
|
if (response.temperatures)
|
||||||
self.temperatureViewModel.fromResponse(response);
|
self.temperatureViewModel.fromResponse(response);
|
||||||
|
|
||||||
if (response.log)
|
if (response.log)
|
||||||
self.terminalViewModel.fromResponse(response);
|
self.terminalViewModel.fromResponse(response);
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textState, errorThrows) {
|
||||||
|
//alert(textState);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(self.requestData, self.updateInterval);
|
setTimeout(self.requestData, self.updateInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var dataUpdater = new DataUpdater(printerStateViewModel, temperatureViewModel, terminalViewModel);
|
var dataUpdater = new DataUpdater(connectionViewModel, printerStateViewModel, temperatureViewModel, terminalViewModel);
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
$("#printer_connect").click(printerStateViewModel.connect);
|
|
||||||
$("#job_print").click(function() {
|
$("#job_print").click(function() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: AJAX_BASEURL + "control/print",
|
url: AJAX_BASEURL + "control/print",
|
||||||
type: 'POST',
|
type: "POST",
|
||||||
dataType: 'json',
|
dataType: "json",
|
||||||
success: function(){}
|
success: function(){}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
$("#job_pause").click(function() {
|
$("#job_pause").click(function() {
|
||||||
$("#job_pause").button('toggle');
|
$("#job_pause").button("toggle");
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: AJAX_BASEURL + "control/pause",
|
url: AJAX_BASEURL + "control/pause",
|
||||||
type: 'POST',
|
type: "POST",
|
||||||
dataType: 'json',
|
dataType: "json"
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
$("#job_cancel").click(function() {
|
$("#job_cancel").click(function() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: AJAX_BASEURL + "control/cancel",
|
url: AJAX_BASEURL + "control/cancel",
|
||||||
type: 'POST',
|
type: "POST",
|
||||||
dataType: 'json',
|
dataType: "json"
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -343,26 +413,24 @@ $(function() {
|
||||||
var command = $("#terminal-command").val();
|
var command = $("#terminal-command").val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: AJAX_BASEURL + "control/command",
|
url: AJAX_BASEURL + "control/command",
|
||||||
type: 'POST',
|
type: "POST",
|
||||||
dataType: 'json',
|
dataType: "json",
|
||||||
data: 'command=' + command,
|
data: "command=" + command
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#gcode_upload').fileupload({
|
$("#gcode_upload").fileupload({
|
||||||
dataType: 'json',
|
dataType: "json",
|
||||||
done: function (e, data) {
|
done: function (e, data) {
|
||||||
gcodeFilesViewModel.fromResponse(data.result);
|
gcodeFilesViewModel.fromResponse(data.result);
|
||||||
},
|
},
|
||||||
progressall: function (e, data) {
|
progressall: function (e, data) {
|
||||||
var progress = parseInt(data.loaded / data.total * 100, 10);
|
var progress = parseInt(data.loaded / data.total * 100, 10);
|
||||||
$('#gcode_upload_progress .bar').css(
|
$("#gcode_upload_progress .bar").css("width", progress + "%");
|
||||||
'width',
|
|
||||||
progress + '%'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ko.applyBindings(connectionViewModel, document.getElementById("connection"));
|
||||||
ko.applyBindings(printerStateViewModel, document.getElementById("state"));
|
ko.applyBindings(printerStateViewModel, document.getElementById("state"));
|
||||||
ko.applyBindings(gcodeFilesViewModel, document.getElementById("files"));
|
ko.applyBindings(gcodeFilesViewModel, document.getElementById("files"));
|
||||||
ko.applyBindings(temperatureViewModel, document.getElementById("temp"));
|
ko.applyBindings(temperatureViewModel, document.getElementById("temp"));
|
||||||
|
@ -372,12 +440,20 @@ $(function() {
|
||||||
dataUpdater.requestData();
|
dataUpdater.requestData();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: AJAX_BASEURL + "gcodefiles",
|
url: AJAX_BASEURL + "gcodefiles",
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
dataType: 'json',
|
dataType: "json",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
self.gcodeFilesViewModel.fromResponse(response);
|
self.gcodeFilesViewModel.fromResponse(response);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$.ajax({
|
||||||
|
url: AJAX_BASEURL + "control/connectionOptions",
|
||||||
|
method: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: function(response) {
|
||||||
|
connectionViewModel.fromResponse(response);
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -32,15 +32,28 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="accordion span4">
|
<div class="accordion span4">
|
||||||
|
<div class="accordion-group">
|
||||||
|
<div class="accordion-heading">
|
||||||
|
<a class="accordion-toggle" data-toggle="collapse" href="#connection"><i class="icon-signal"></i> Connection</a>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-body collapse in" id="connection">
|
||||||
|
<div class="accordion-inner">
|
||||||
|
<label for="connection_ports" data-bind="css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed">Serial Port</label>
|
||||||
|
<select id="connection_ports" data-bind="options: portOptions, optionsCaption: 'AUTO', value: selectedPort, css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed"></select>
|
||||||
|
<label for="connection_baudrates" data-bind="css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed">Baudrate</label>
|
||||||
|
<select id="connection_baudrates" data-bind="options: baudrateOptions, optionsCaption: 'AUTO', value: selectedBaudrate, css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed"></select>
|
||||||
|
<button class="btn btn-block" id="printer_connect" data-bind="click: connect, text: buttonText()">Connect</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a class="accordion-toggle" data-toggle="collapse" href="#state"><i class="icon-info-sign"></i> State</a>
|
<a class="accordion-toggle" data-toggle="collapse" href="#state"><i class="icon-info-sign"></i> State</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse in" id="state">
|
<div class="accordion-body collapse in" id="state">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<button class="btn btn-block" id="printer_connect" data-bind="css: {disabled: !isErrorOrClosed}, enable: isErrorOrClosed">Connect</button>
|
|
||||||
|
|
||||||
Machine State: <strong data-bind="text: stateString"></strong><br>
|
Machine State: <strong data-bind="text: stateString"></strong><br>
|
||||||
|
File: <strong data-bind="text: filename"></strong><br>
|
||||||
Filament: <strong data-bind="text: filament"></strong><br>
|
Filament: <strong data-bind="text: filament"></strong><br>
|
||||||
Estimated Print Time: <strong data-bind="text: estimatedPrintTime"></strong><br>
|
Estimated Print Time: <strong data-bind="text: estimatedPrintTime"></strong><br>
|
||||||
Line: <strong data-bind="text: lineString"></strong><br>
|
Line: <strong data-bind="text: lineString"></strong><br>
|
||||||
|
@ -144,21 +157,21 @@
|
||||||
<div class="tab-pane" id="jog">
|
<div class="tab-pane" id="jog">
|
||||||
<div style="width: 350px; height: 70px">
|
<div style="width: 350px; height: 70px">
|
||||||
<div style="width: 70px; float: left;"> </div>
|
<div style="width: 70px; float: left;"> </div>
|
||||||
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_y_inc" data-bind="enable: isOperational() && isReady() && !isPrinting()">Up</button></div>
|
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_y_inc" data-bind="enable: isOperational() && !isPrinting()">Up</button></div>
|
||||||
<div style="width: 70px; float: left;"> </div>
|
<div style="width: 70px; float: left;"> </div>
|
||||||
<div style="width: 70px; float: left; margin-left: 20px"><button class="btn btn-block" id="jog_z_inc" data-bind="enable: isOperational() && isReady() && !isPrinting()">Z+</button></div>
|
<div style="width: 70px; float: left; margin-left: 20px"><button class="btn btn-block" id="jog_z_inc" data-bind="enable: isOperational() && !isPrinting()">Z+</button></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 350px; height: 70px">
|
<div style="width: 350px; height: 70px">
|
||||||
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_x_dec" data-bind="enable: isOperational() && isReady() && !isPrinting()">Left</button></div>
|
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_x_dec" data-bind="enable: isOperational() && !isPrinting()">Left</button></div>
|
||||||
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_xy_home" data-bind="enable: isOperational() && isReady() && !isPrinting()">Home</button></div>
|
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_xy_home" data-bind="enable: isOperational() && !isPrinting()">Home</button></div>
|
||||||
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_x_inc" data-bind="enable: isOperational() && isReady() && !isPrinting()">Right</button></div>
|
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_x_inc" data-bind="enable: isOperational() && !isPrinting()">Right</button></div>
|
||||||
<div style="width: 70px; float: left; margin-left: 20px"><button class="btn btn-block" id="jog_z_home" data-bind="enable: isOperational() && isReady() && !isPrinting()">Home</button></div>
|
<div style="width: 70px; float: left; margin-left: 20px"><button class="btn btn-block" id="jog_z_home" data-bind="enable: isOperational() && !isPrinting()">Home</button></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 350px; height: 70px">
|
<div style="width: 350px; height: 70px">
|
||||||
<div style="width: 70px; float: left;"> </div>
|
<div style="width: 70px; float: left;"> </div>
|
||||||
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_y_dec" data-bind="enable: isOperational() && isReady() && !isPrinting()">Down</button></div>
|
<div style="width: 70px; float: left;"><button class="btn btn-block" id="jog_y_dec" data-bind="enable: isOperational() && !isPrinting()">Down</button></div>
|
||||||
<div style="width: 70px; float: left;"> </div>
|
<div style="width: 70px; float: left;"> </div>
|
||||||
<div style="width: 70px; float: left; margin-left: 20px"><button class="btn btn-block" id="jog_z_dec" data-bind="enable: isOperational() && isReady() && !isPrinting()">Z-</button></div>
|
<div style="width: 70px; float: left; margin-left: 20px"><button class="btn btn-block" id="jog_z_dec" data-bind="enable: isOperational() && !isPrinting()">Z-</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="speed">
|
<div class="tab-pane" id="speed">
|
||||||
|
|
Loading…
Reference in New Issue