2012-12-25 10:55:00 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# coding=utf-8
|
2012-12-28 19:37:40 +00:00
|
|
|
__author__ = "Gina Häußge <osd@foosel.net>"
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-26 23:04:12 +00:00
|
|
|
from flask import Flask, request, render_template, jsonify, make_response
|
|
|
|
|
from werkzeug import secure_filename
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
from printer import Printer, getConnectionOptions
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-26 23:04:12 +00:00
|
|
|
import sys
|
2012-12-25 19:49:10 +00:00
|
|
|
import os
|
|
|
|
|
import fnmatch
|
|
|
|
|
|
2012-12-26 23:04:12 +00:00
|
|
|
APPNAME="Cura"
|
|
|
|
|
BASEURL="/ajax/"
|
2012-12-25 19:49:10 +00:00
|
|
|
SUCCESS={}
|
2012-12-26 23:04:12 +00:00
|
|
|
|
|
|
|
|
# taken from http://stackoverflow.com/questions/1084697/how-do-i-store-desktop-application-data-in-a-cross-platform-way-for-python
|
2012-12-28 19:37:40 +00:00
|
|
|
if sys.platform == "darwin":
|
2012-12-26 23:04:12 +00:00
|
|
|
from AppKit import NSSearchPathForDirectoriesInDomains
|
|
|
|
|
# http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains
|
|
|
|
|
# NSApplicationSupportDirectory = 14
|
|
|
|
|
# NSUserDomainMask = 1
|
|
|
|
|
# True for expanding the tilde into a fully qualified path
|
|
|
|
|
appdata = os.path.join(NSSearchPathForDirectoriesInDomains(14, 1, True)[0], APPNAME)
|
2012-12-28 19:37:40 +00:00
|
|
|
elif sys.platform == "win32":
|
|
|
|
|
appdata = os.path.join(os.environ["APPDATA"], APPNAME)
|
2012-12-26 23:04:12 +00:00
|
|
|
else:
|
|
|
|
|
appdata = os.path.expanduser(os.path.join("~", "." + APPNAME.lower()))
|
|
|
|
|
|
|
|
|
|
UPLOAD_FOLDER = appdata + os.sep + "uploads"
|
|
|
|
|
if not os.path.isdir(UPLOAD_FOLDER):
|
|
|
|
|
os.makedirs(UPLOAD_FOLDER)
|
|
|
|
|
ALLOWED_EXTENSIONS = set(["gcode"])
|
2012-12-25 10:55:00 +00:00
|
|
|
|
|
|
|
|
app = Flask("Cura.webui")
|
|
|
|
|
printer = Printer()
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route("/")
|
2012-12-25 10:55:00 +00:00
|
|
|
def index():
|
2012-12-28 19:37:40 +00:00
|
|
|
return render_template("index.html")
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-25 19:49:10 +00:00
|
|
|
#~~ Printer state
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "state", methods=["GET"])
|
2012-12-25 10:55:00 +00:00
|
|
|
def printerState():
|
|
|
|
|
temp = printer.currentTemp
|
|
|
|
|
bedTemp = printer.currentBedTemp
|
2012-12-26 14:03:34 +00:00
|
|
|
targetTemp = printer.currentTargetTemp
|
|
|
|
|
bedTargetTemp = printer.currentBedTargetTemp
|
2012-12-25 10:55:00 +00:00
|
|
|
jobData = printer.jobData()
|
2012-12-28 19:37:40 +00:00
|
|
|
gcodeState = printer.gcodeState()
|
2012-12-29 14:41:23 +00:00
|
|
|
feedrateState = printer.feedrateState()
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-25 19:49:10 +00:00
|
|
|
result = {
|
2012-12-28 19:37:40 +00:00
|
|
|
"state": printer.getStateString(),
|
|
|
|
|
"temp": temp,
|
|
|
|
|
"bedTemp": bedTemp,
|
|
|
|
|
"targetTemp": targetTemp,
|
|
|
|
|
"targetBedTemp": bedTargetTemp,
|
|
|
|
|
"operational": printer.isOperational(),
|
|
|
|
|
"closedOrError": printer.isClosedOrError(),
|
|
|
|
|
"error": printer.isError(),
|
|
|
|
|
"printing": printer.isPrinting(),
|
|
|
|
|
"paused": printer.isPaused(),
|
|
|
|
|
"ready": printer.isReady(),
|
|
|
|
|
"loading": printer.isLoading()
|
2012-12-25 19:49:10 +00:00
|
|
|
}
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
if jobData is not None:
|
|
|
|
|
jobData["filename"] = jobData["filename"].replace(UPLOAD_FOLDER + os.sep, "")
|
|
|
|
|
result["job"] = jobData
|
2012-12-25 19:49:10 +00:00
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
if gcodeState is not None:
|
|
|
|
|
gcodeState["filename"] = gcodeState["filename"].replace(UPLOAD_FOLDER + os.sep, "")
|
|
|
|
|
result["gcode"] = gcodeState
|
2012-12-25 19:49:10 +00:00
|
|
|
|
2012-12-29 14:41:23 +00:00
|
|
|
if feedrateState is not None:
|
|
|
|
|
result["feedrate"] = feedrateState
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
if request.values.has_key("temperatures"):
|
|
|
|
|
result["temperatures"] = printer.temps
|
2012-12-25 19:49:10 +00:00
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
if request.values.has_key("log"):
|
|
|
|
|
result["log"] = printer.log
|
|
|
|
|
|
|
|
|
|
if request.values.has_key("messages"):
|
|
|
|
|
result["messages"] = printer.messages
|
2012-12-25 19:49:10 +00:00
|
|
|
|
|
|
|
|
return jsonify(result)
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "state/messages", methods=["GET"])
|
2012-12-25 10:55:00 +00:00
|
|
|
def printerMessages():
|
|
|
|
|
return jsonify(messages=printer.messages)
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "state/log", methods=["GET"])
|
2012-12-26 23:04:12 +00:00
|
|
|
def printerLogs():
|
2012-12-25 10:55:00 +00:00
|
|
|
return jsonify(log=printer.log)
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "state/temperatures", methods=["GET"])
|
2012-12-25 10:55:00 +00:00
|
|
|
def printerTemperatures():
|
|
|
|
|
return jsonify(temperatures = printer.temps)
|
|
|
|
|
|
2012-12-25 19:49:10 +00:00
|
|
|
#~~ Printer control
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "control/connectionOptions", methods=["GET"])
|
|
|
|
|
def connectionOptions():
|
|
|
|
|
return jsonify(getConnectionOptions())
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@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"])
|
2012-12-25 19:49:10 +00:00
|
|
|
def disconnect():
|
|
|
|
|
printer.disconnect()
|
2012-12-28 19:37:40 +00:00
|
|
|
return jsonify(state="Offline")
|
2012-12-25 19:49:10 +00:00
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "control/command", methods=["POST"])
|
2012-12-25 19:49:10 +00:00
|
|
|
def printerCommand():
|
2012-12-28 19:37:40 +00:00
|
|
|
command = request.form["command"]
|
2012-12-25 19:49:10 +00:00
|
|
|
printer.command(command)
|
|
|
|
|
return jsonify(SUCCESS)
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "control/print", methods=["POST"])
|
2012-12-25 10:55:00 +00:00
|
|
|
def printGcode():
|
|
|
|
|
printer.startPrint()
|
2012-12-25 19:49:10 +00:00
|
|
|
return jsonify(SUCCESS)
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "control/pause", methods=["POST"])
|
2012-12-25 10:55:00 +00:00
|
|
|
def pausePrint():
|
|
|
|
|
printer.togglePausePrint()
|
2012-12-25 19:49:10 +00:00
|
|
|
return jsonify(SUCCESS)
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "control/cancel", methods=["POST"])
|
2012-12-25 10:55:00 +00:00
|
|
|
def cancelPrint():
|
|
|
|
|
printer.cancelPrint()
|
2012-12-25 19:49:10 +00:00
|
|
|
return jsonify(SUCCESS)
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "control/temperature", methods=["POST"])
|
2012-12-26 14:03:34 +00:00
|
|
|
def setTargetTemperature():
|
|
|
|
|
if not printer.isOperational():
|
|
|
|
|
return jsonify(SUCCESS)
|
|
|
|
|
|
|
|
|
|
if request.values.has_key("temp"):
|
|
|
|
|
# set target temperature
|
|
|
|
|
temp = request.values["temp"];
|
|
|
|
|
printer.command("M104 S" + temp)
|
|
|
|
|
|
|
|
|
|
if request.values.has_key("bedTemp"):
|
|
|
|
|
# set target bed temperature
|
|
|
|
|
bedTemp = request.values["bedTemp"]
|
|
|
|
|
printer.command("M140 S" + bedTemp)
|
|
|
|
|
|
|
|
|
|
return jsonify(SUCCESS)
|
|
|
|
|
|
|
|
|
|
@app.route(BASEURL + "control/jog", methods=["POST"])
|
|
|
|
|
def jog():
|
|
|
|
|
if not printer.isOperational() or printer.isPrinting():
|
2012-12-28 19:37:40 +00:00
|
|
|
# do not jog when a print job is running or we don"t have a connection
|
2012-12-26 14:03:34 +00:00
|
|
|
return jsonify(SUCCESS)
|
|
|
|
|
|
|
|
|
|
if request.values.has_key("x"):
|
|
|
|
|
# jog x
|
|
|
|
|
x = request.values["x"]
|
|
|
|
|
printer.commands(["G91", "G1 X" + x + " F6000", "G90"])
|
|
|
|
|
if request.values.has_key("y"):
|
|
|
|
|
# jog y
|
|
|
|
|
y = request.values["y"]
|
|
|
|
|
printer.commands(["G91", "G1 Y" + y + " F6000", "G90"])
|
|
|
|
|
if request.values.has_key("z"):
|
|
|
|
|
# jog z
|
|
|
|
|
z = request.values["z"]
|
|
|
|
|
printer.commands(["G91", "G1 Z" + z + " F200", "G90"])
|
|
|
|
|
if request.values.has_key("homeXY"):
|
|
|
|
|
# home x/y
|
|
|
|
|
printer.command("G28 X0 Y0")
|
|
|
|
|
if request.values.has_key("homeZ"):
|
|
|
|
|
# home z
|
|
|
|
|
printer.command("G28 Z0")
|
|
|
|
|
|
|
|
|
|
return jsonify(SUCCESS)
|
|
|
|
|
|
2012-12-29 14:41:23 +00:00
|
|
|
@app.route(BASEURL + "control/speed", methods=["POST"])
|
|
|
|
|
def speed():
|
|
|
|
|
if not printer.isOperational():
|
|
|
|
|
return jsonify(SUCCESS)
|
|
|
|
|
|
|
|
|
|
for key in ["outerWall", "innerWall", "fill", "support"]:
|
|
|
|
|
if request.values.has_key(key):
|
|
|
|
|
value = int(request.values[key])
|
|
|
|
|
printer.setFeedrateModifier(key, value)
|
|
|
|
|
|
|
|
|
|
return jsonify(feedrate = printer.feedrateState())
|
|
|
|
|
|
2012-12-25 19:49:10 +00:00
|
|
|
#~~ GCODE file handling
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "gcodefiles", methods=["GET"])
|
2012-12-25 19:49:10 +00:00
|
|
|
def readGcodeFiles():
|
|
|
|
|
files = []
|
|
|
|
|
for osFile in os.listdir(UPLOAD_FOLDER):
|
|
|
|
|
if not fnmatch.fnmatch(osFile, "*.gcode"):
|
|
|
|
|
continue
|
|
|
|
|
files.append({
|
|
|
|
|
"name": osFile,
|
2012-12-29 14:41:23 +00:00
|
|
|
"size": sizeof_fmt(os.stat(os.path.join(UPLOAD_FOLDER, osFile)).st_size)
|
2012-12-25 19:49:10 +00:00
|
|
|
})
|
|
|
|
|
return jsonify(files=files)
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "gcodefiles/upload", methods=["POST"])
|
2012-12-25 19:49:10 +00:00
|
|
|
def uploadGcodeFile():
|
2012-12-28 19:37:40 +00:00
|
|
|
file = request.files["gcode_file"]
|
2012-12-26 23:04:12 +00:00
|
|
|
if file and allowed_file(file.filename):
|
|
|
|
|
secure = secure_filename(file.filename)
|
|
|
|
|
filename = os.path.join(UPLOAD_FOLDER, secure)
|
2012-12-25 19:49:10 +00:00
|
|
|
file.save(filename)
|
|
|
|
|
return readGcodeFiles()
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "gcodefiles/load", methods=["POST"])
|
2012-12-25 19:49:10 +00:00
|
|
|
def loadGcodeFile():
|
|
|
|
|
filename = request.values["filename"]
|
2012-12-29 14:41:23 +00:00
|
|
|
printer.loadGcode(os.path.join(UPLOAD_FOLDER, filename))
|
2012-12-25 19:49:10 +00:00
|
|
|
return jsonify(SUCCESS)
|
|
|
|
|
|
2012-12-28 19:37:40 +00:00
|
|
|
@app.route(BASEURL + "gcodefiles/delete", methods=["POST"])
|
2012-12-25 19:49:10 +00:00
|
|
|
def deleteGcodeFile():
|
2012-12-26 23:04:12 +00:00
|
|
|
if request.values.has_key("filename"):
|
|
|
|
|
filename = request.values["filename"]
|
|
|
|
|
if allowed_file(filename):
|
2012-12-29 14:41:23 +00:00
|
|
|
secure = os.path.join(UPLOAD_FOLDER, secure_filename(filename))
|
2012-12-26 23:04:12 +00:00
|
|
|
if os.path.exists(secure):
|
|
|
|
|
os.remove(secure)
|
2012-12-25 19:49:10 +00:00
|
|
|
return readGcodeFiles()
|
|
|
|
|
|
|
|
|
|
def sizeof_fmt(num):
|
|
|
|
|
"""
|
|
|
|
|
Taken from http://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size
|
|
|
|
|
"""
|
2012-12-28 19:37:40 +00:00
|
|
|
for x in ["bytes","KB","MB","GB"]:
|
2012-12-25 19:49:10 +00:00
|
|
|
if num < 1024.0:
|
|
|
|
|
return "%3.1f%s" % (num, x)
|
|
|
|
|
num /= 1024.0
|
2012-12-28 19:37:40 +00:00
|
|
|
return "%3.1f%s" % (num, "TB")
|
2012-12-25 10:55:00 +00:00
|
|
|
|
2012-12-26 23:04:12 +00:00
|
|
|
def allowed_file(filename):
|
|
|
|
|
return "." in filename and filename.rsplit(".", 1)[1] in ALLOWED_EXTENSIONS
|
|
|
|
|
|
2012-12-29 11:11:49 +00:00
|
|
|
def run(host = "0.0.0.0", port = 5000, debug = False):
|
|
|
|
|
app.debug = debug
|
|
|
|
|
app.run(host=host, port=port, use_reloader=False)
|