Getting the print queue api working with the printer, adding a job progress panel to inspector and adding job_finished and job_started events.
parent
5867975a10
commit
f01e5747e3
130
pronserve.py
130
pronserve.py
|
@ -26,6 +26,7 @@ import socket
|
|||
import mdns
|
||||
import uuid
|
||||
from operator import itemgetter, attrgetter
|
||||
from collections import deque
|
||||
|
||||
log = logging.getLogger("root")
|
||||
__UPLOADS__ = "./uploads"
|
||||
|
@ -68,6 +69,21 @@ class RootHandler(tornado.web.RequestHandler):
|
|||
def get(self):
|
||||
self.render("index.html")
|
||||
|
||||
class PrintHandler(tornado.web.RequestHandler):
|
||||
def put(self):
|
||||
pronserve.do_print()
|
||||
self.finish("ACK")
|
||||
|
||||
class PauseHandler(tornado.web.RequestHandler):
|
||||
def put(self):
|
||||
pronserve.do_pause()
|
||||
self.finish("ACK")
|
||||
|
||||
class StopHandler(tornado.web.RequestHandler):
|
||||
def put(self):
|
||||
pronserve.do_stop()
|
||||
self.finish("ACK")
|
||||
|
||||
class JobsHandler(tornado.web.RequestHandler):
|
||||
def post(self):
|
||||
fileinfo = self.request.files['job'][0]
|
||||
|
@ -106,8 +122,33 @@ class ConstructSocketHandler(tornado.websocket.WebSocketHandler):
|
|||
self.write_message({'connected': {'jobs': pronserve.jobs.public_list()}})
|
||||
print "WebSocket opened. %i sockets currently open." % len(pronserve.clients)
|
||||
|
||||
def on_sensor_change(self):
|
||||
self.write_message({'sensors': pronserve.sensors, 'timestamp': time.time()})
|
||||
def on_sensor_changed(self):
|
||||
print "sensor change"
|
||||
self.write_message({
|
||||
'sensor_changed': {'name': 'bed', 'value': pronserve.sensors['bed']},
|
||||
'timestamp': time.time()
|
||||
})
|
||||
self.write_message({
|
||||
'sensor_changed': {'name': 'extruder', 'value': pronserve.sensors['extruder']},
|
||||
'timestamp': time.time()
|
||||
})
|
||||
def on_job_progress_changed(self, progress):
|
||||
self.write_message({
|
||||
'job_progress_changed': progress,
|
||||
'timestamp': time.time()
|
||||
})
|
||||
|
||||
def on_job_started(self, job):
|
||||
self.write_message({
|
||||
'job_started': job,
|
||||
'timestamp': time.time()
|
||||
})
|
||||
|
||||
def on_job_finished(self, job):
|
||||
self.write_message({
|
||||
'job_finished': job,
|
||||
'timestamp': time.time()
|
||||
})
|
||||
|
||||
def on_job_added(self, job):
|
||||
self.write_message({'job_added': pronserve.jobs.sanitize(job)})
|
||||
|
@ -124,11 +165,12 @@ class ConstructSocketHandler(tornado.websocket.WebSocketHandler):
|
|||
|
||||
|
||||
def on_pronsole_log(self, msg):
|
||||
self.write_message({'log': {msg: msg, level: "debug"}})
|
||||
self.write_message({'log': {'msg': msg, 'level': "debug"}})
|
||||
|
||||
def on_message(self, msg):
|
||||
print "message received: %s"%(msg)
|
||||
# TODO: the read bit of repl!
|
||||
self.write_message("You said: " + msg)
|
||||
# self.write_message("You said: " + msg)
|
||||
|
||||
def on_close(self):
|
||||
pronserve.clients.remove(self)
|
||||
|
@ -146,7 +188,10 @@ application = tornado.web.Application([
|
|||
(r"/inspect", InspectHandler),
|
||||
(r"/socket", ConstructSocketHandler),
|
||||
(r"/jobs", JobsHandler),
|
||||
(r"/jobs/([0-9]*)", JobHandler)
|
||||
(r"/jobs/([0-9]*)", JobHandler),
|
||||
(r"/jobs/print", PrintHandler),
|
||||
(r"/jobs/pause", PauseHandler),
|
||||
(r"/stop", StopHandler)
|
||||
], **settings)
|
||||
|
||||
|
||||
|
@ -161,15 +206,53 @@ class Pronserve(pronsole.pronsole):
|
|||
self.stdout = sys.stdout
|
||||
self.ioloop = tornado.ioloop.IOLoop.instance()
|
||||
self.clients = set()
|
||||
self.settings.sensor_poll_rate = 0.3 # seconds
|
||||
self.settings.sensor_poll_rate = 1 # seconds
|
||||
self.sensors = {'extruder': -1, 'bed': -1}
|
||||
self.load_default_rc()
|
||||
self.jobs = PrintJobQueue()
|
||||
self.job_id_incr = 0;
|
||||
self.job_id_incr = 0
|
||||
self.printing_jobs = False
|
||||
self.current_job = None
|
||||
self.previous_job_progress = 0
|
||||
services = ({'type': '_construct._tcp', 'port': 8888, 'domain': "local."})
|
||||
self.mdns = mdns.publisher().save_group({'name': 'pronserve', 'services': services })
|
||||
self.jobs.listeners.append(self)
|
||||
|
||||
def do_print(self):
|
||||
if self.p.online:
|
||||
self.printing_jobs = True
|
||||
|
||||
def run_print_queue_loop(self):
|
||||
# This is a polling work around to the current lack of events in printcore
|
||||
# A better solution would be one in which a print_finised event could be
|
||||
# listend for asynchronously without polling.
|
||||
p = self.p
|
||||
if self.printing_jobs and p.printing == False and p.paused == False and p.online:
|
||||
if self.current_job != None:
|
||||
self.update_job_progress(100)
|
||||
self.fire("job_finished", self.jobs.sanitize(self.current_job))
|
||||
if len(self.jobs.list) > 0:
|
||||
print "Starting the next print job"
|
||||
self.current_job = self.jobs.list.popleft()
|
||||
self.p.startprint(self.current_job['body'].split("\n"))
|
||||
self.fire("job_started", self.jobs.sanitize(self.current_job))
|
||||
else:
|
||||
print "Finished all print jobs"
|
||||
self.current_job = None
|
||||
self.printing_jobs = False
|
||||
|
||||
# Updating the job progress
|
||||
self.update_job_progress(self.print_progress())
|
||||
|
||||
#print "print loop"
|
||||
next_timeout = time.time() + 0.3
|
||||
gen.Task(self.ioloop.add_timeout(next_timeout, self.run_print_queue_loop))
|
||||
|
||||
def update_job_progress(self, progress):
|
||||
if progress != self.previous_job_progress and self.current_job != None:
|
||||
self.previous_job_progress = progress
|
||||
self.fire("job_progress_changed", progress)
|
||||
|
||||
def run_sensor_loop(self):
|
||||
self.request_sensor_update()
|
||||
next_timeout = time.time() + self.settings.sensor_poll_rate
|
||||
|
@ -181,23 +264,34 @@ class Pronserve(pronsole.pronsole):
|
|||
def recvcb(self, l):
|
||||
""" Parses a line of output from the printer via printcore """
|
||||
l = l.rstrip()
|
||||
|
||||
print l
|
||||
if "T:" in l:
|
||||
self._receive_sensor_update(l)
|
||||
if l!="ok" and not l.startswith("ok T") and not l.startswith("T:"):
|
||||
self._receive_printer_error(l)
|
||||
|
||||
def print_progress(self):
|
||||
if(self.p.printing):
|
||||
return 100*float(self.p.queueindex)/len(self.p.mainqueue)
|
||||
if(self.sdprinting):
|
||||
return self.percentdone
|
||||
return 0
|
||||
|
||||
|
||||
def _receive_sensor_update(self, l):
|
||||
words = l.split(" ")
|
||||
words.pop(0)
|
||||
words = filter(lambda s: s.find(":") > 0, l.split(" "))
|
||||
d = dict([ s.split(":") for s in words])
|
||||
|
||||
print "sensor update received!"
|
||||
|
||||
for key, value in d.iteritems():
|
||||
self.__update_sensor(key, value)
|
||||
|
||||
self.fire("sensor_change")
|
||||
self.fire("sensor_changed")
|
||||
|
||||
def __update_sensor(self, key, value):
|
||||
if (key in self.settings.sensor_names) == False:
|
||||
return
|
||||
sensor_name = self.settings.sensor_names[key]
|
||||
self.sensors[sensor_name] = float(value)
|
||||
|
||||
|
@ -219,10 +313,11 @@ class Pronserve(pronsole.pronsole):
|
|||
def write_prompt(self):
|
||||
None
|
||||
|
||||
|
||||
class PrintJobQueue():
|
||||
|
||||
def __init__(self):
|
||||
self.list = []
|
||||
self.list = deque([])
|
||||
self.__last_id = 0
|
||||
self.listeners = []
|
||||
|
||||
|
@ -246,20 +341,16 @@ class PrintJobQueue():
|
|||
|
||||
def add(self, original_file_name, body):
|
||||
ext = os.path.splitext(original_file_name)[1]
|
||||
file_name = str(uuid.uuid4()) + ext
|
||||
job = dict(
|
||||
id = self.__last_id,
|
||||
rank = len(self.list),
|
||||
original_file_name=original_file_name,
|
||||
path= __UPLOADS__ + "/" + file_name,
|
||||
body= body,
|
||||
)
|
||||
self.__last_id += 1
|
||||
|
||||
fh = open(job['path'], 'w')
|
||||
fh.write(body)
|
||||
|
||||
self.list.append(job)
|
||||
print "Added %s as %s"%(original_file_name, file_name)
|
||||
print "Added %s"%(original_file_name)
|
||||
self.fire("job_added", job)
|
||||
|
||||
def display_summary(self):
|
||||
|
@ -305,8 +396,9 @@ print "Pronserve is starting..."
|
|||
pronserve = Pronserve()
|
||||
pronserve.do_connect("")
|
||||
|
||||
time.sleep(0.2)
|
||||
time.sleep(1)
|
||||
pronserve.run_sensor_loop()
|
||||
pronserve.run_print_queue_loop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
application.listen(8888)
|
||||
|
|
|
@ -38,4 +38,17 @@
|
|||
#temperature-graph
|
||||
{
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
#print-job-panel
|
||||
{
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.job-pogress
|
||||
{
|
||||
margin: 80px 0;
|
||||
font-size: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
}
|
|
@ -6,11 +6,15 @@
|
|||
$(window).focus(function() {
|
||||
windowFocus = true;
|
||||
//if ($console) $console.append("Window refocused, restarting graph.\n");
|
||||
$(".focus-lost-overlay").addClass("out").removeClass("in");
|
||||
$(".focus-lost-overlay").addClass("out").removeClass("in").delay(1000).hide();
|
||||
}).blur(function() {
|
||||
windowFocus = false;
|
||||
//if ($console) $console.append("Window's focus, lost stopping graph...\n");
|
||||
$(".focus-lost-overlay").addClass("in").removeClass("out");
|
||||
$(".focus-lost-overlay")
|
||||
.stop(true,true)
|
||||
.show()
|
||||
.addClass("in")
|
||||
.removeClass("out");
|
||||
}.debounce());
|
||||
|
||||
var connect = function() {
|
||||
|
@ -75,7 +79,6 @@
|
|||
if(windowFocus == false) return;
|
||||
updateSensorsUi();
|
||||
updateGraphUi();
|
||||
$consoleWrapper.scrollTop($console.innerHeight());
|
||||
}
|
||||
|
||||
var onConnect = function(ws) {
|
||||
|
@ -85,26 +88,42 @@
|
|||
// Web Socket is connected, send data using send()
|
||||
|
||||
};
|
||||
var nextGraphPoint = {};
|
||||
ws.onmessage = function (evt)
|
||||
{
|
||||
msg = JSON.parse(evt.data)
|
||||
if(msg.sensors != undefined)
|
||||
if(msg.sensor_changed != undefined)
|
||||
{
|
||||
var sensorNames = ["bed", "extruder"];
|
||||
var values = {timestamp: msg.timestamp};
|
||||
for (var i = 0; i < sensorNames.length; i++)
|
||||
{
|
||||
var name = sensorNames[i];
|
||||
var val = parseFloat(msg.sensors[name]);
|
||||
values[name] = val;
|
||||
var name = msg.sensor_changed.name;
|
||||
var val = parseFloat(msg.sensor_changed.value);
|
||||
nextGraphPoint[name] = val;
|
||||
$("."+name+" .val").data("val", val.format(1))
|
||||
}
|
||||
updateGraphData(values);
|
||||
if(nextGraphPoint.bed != undefined && nextGraphPoint.extruder != undefined)
|
||||
{
|
||||
nextGraphPoint.timestamp = msg.timestamp
|
||||
updateGraphData(nextGraphPoint);
|
||||
nextGraphPoint = {};
|
||||
}
|
||||
requestAnimationFrame(updateUi);
|
||||
}
|
||||
else if (msg.job_progress_changed != undefined)
|
||||
{
|
||||
val = Math.round(parseFloat(msg.job_progress_changed)*10)/10;
|
||||
$(".job-pogress .val").html(val);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log($consoleWrapper.scrollTop() - $console.innerHeight())
|
||||
var atBottom = $consoleWrapper.scrollTop() - $console.innerHeight() > -220;
|
||||
$console.append(evt.data + "\n");
|
||||
if (atBottom)
|
||||
{
|
||||
$consoleWrapper.scrollTop($console.innerHeight());
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onclose = function()
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
Connecting...
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span3" id="print-job-panel">
|
||||
<h2>Job Progress</h2>
|
||||
<div class="job-pogress"><span class="val">XX.X</span>%</div>
|
||||
</div>
|
||||
<div class="span9">
|
||||
<div class="sensors pull-right">
|
||||
<div class="extruder pull-right">
|
||||
Extruder: <span class="val">xx.x</span><span class="deg">°C</span>
|
||||
|
@ -38,7 +44,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div class="focus-lost-overlay modal-backdrop fade out"></div>
|
||||
<div class="focus-lost-overlay modal-backdrop fade out hide"></div>
|
||||
|
||||
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
||||
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
; THIS IS A TZST. DO NOT ATTZMPT TO PRINT THIS FILZ.
|
||||
; gZnZratZd by Slic3r 0.9.3-dZv on 2012-09-02 at 04:02:31
|
||||
|
||||
; layZr_hZight = 0.4
|
||||
; pZrimZtZrs = 3
|
||||
; solid_layZrs = 3
|
||||
; fill_dZnsity = 0.4
|
||||
; pZrimZtZr_spZZd = 30
|
||||
; infill_spZZd = 60
|
||||
; travZl_spZZd = 130
|
||||
; scalZ = 1
|
||||
; nozzlZ_diamZtZr = 0.5
|
||||
; filamZnt_diamZtZr = 3
|
||||
; Zxtrusion_multipliZr = 1
|
||||
; singlZ wall width = 0.53mm
|
||||
; first layZr singlZ wall width = 0.80mm
|
||||
|
||||
M104 S200 ; sZt tZmpZraturZ
|
||||
;G28 ; homZ all axZs
|
||||
;M109 S200 ; wait for tZmpZraturZ to bZ rZachZd
|
||||
G90 ; usZ absolutZ coordinatZs
|
||||
G21 ; sZt units to millimZtZrs
|
||||
G92 Z0
|
||||
M82 ; usZ absolutZ distancZs for Zxtrusion
|
||||
G1 Z0.400 F71800.000
|
||||
G1 X75.725 Y86.681
|
||||
G1 F1800.000 Z1.00000
|
||||
G1 X87.905 Y75.241 F1040.000 Z1.69560
|
||||
G1 X88.365 Y74.871 Z1.72017
|
||||
G1 X88.865 Y74.541 Z1.74511
|
||||
G1 X89.395 Y74.261 Z1.77006
|
||||
G1 X89.945 Y74.031 Z1.79488
|
||||
G1 X90.225 Y73.931 Z1.80726
|
||||
G1 X90.805 Y73.771 Z1.83230
|
||||
G1 X92.375 Y73.501 Z1.89862
|
||||
G1 X92.935 Y73.471 Z1.92196
|
||||
G1 X109.165 Y73.961 Z2.59789
|
||||
G1 X109.475 Y73.991 Z2.61085
|
||||
G1 X110.105 Y74.101 Z2.63747
|
||||
G1 X110.715 Y74.271 Z2.66383
|
||||
G1 X111.795 Y74.681 Z2.71192
|
||||
G1 X112.355 Y74.951 Z2.73780
|
||||
G1 X112.875 Y75.271 Z2.76322
|
||||
G1 X113.135 Y75.451 Z2.77638
|
||||
G1 X113.615 Y75.841 Z2.80213
|
||||
G1 X124.855 Y87.841 Z3.48656
|
||||
G1 X125.485 Y88.631 Z3.52863
|
||||
G1 X125.975 Y89.351 Z3.56488
|
||||
G1 X126.385 Y90.111 Z3.60083
|
||||
G1 X126.605 Y90.651 Z3.62510
|
||||
G1 X126.775 Y91.201 Z3.64906
|
||||
G1 X126.905 Y91.771 Z3.67340
|
||||
G1 X126.975 Y92.341 Z3.69731
|
||||
G1 X127.005 Y92.921 Z3.72148
|
||||
G1 X126.325 Y109.851 Z4.42681
|
||||
G1 X126.255 Y110.391 Z4.44947
|
||||
G1 X126.145 Y110.921 Z4.47201
|
||||
G1 X125.995 Y111.441 Z4.49453
|
||||
G1 X125.805 Y111.951 Z4.51719
|
||||
G1 X125.575 Y112.441 Z4.53972
|
||||
G1 X125.165 Y113.131 Z4.57313
|
||||
G1 X124.835 Y113.571 Z4.59603
|
||||
G1 X124.485 Y113.971 Z4.61815
|
||||
G1 X124.095 Y114.351 Z4.64082
|
||||
G1 X123.885 Y114.531 Z4.65233
|
||||
G1 X123.005 Y115.151 Z4.69715
|
||||
G1 X122.525 Y115.401 Z4.71967
|
||||
G1 X96.195 Y125.661 Z5.89600
|
||||
G1 X95.515 Y125.881 Z5.92575
|
||||
G1 X94.385 Y126.141 Z5.97402
|
||||
G1 X91.335 Y126.551 Z6.10213
|
||||
G1 X91.055 Y126.561 Z6.11379
|
||||
G1 X90.775 Y126.561 Z6.12545
|
||||
G1 X90.215 Y126.521 Z6.14882
|
||||
G1 X89.375 Y126.371 Z6.18434
|
||||
G1 X88.835 Y126.221 Z6.20767
|
||||
G1 X88.305 Y126.021 Z6.23125
|
||||
G1 X87.795 Y125.781 Z6.25471
|
||||
G1 X87.075 Y125.341 Z6.28984
|
||||
G1 X86.415 Y124.811 Z6.32507
|
||||
G1 X75.155 Y112.801 Z7.01038
|
||||
G1 X74.955 Y112.561 Z7.02339
|
||||
G1 X74.595 Y112.041 Z7.04972
|
||||
G1 X73.825 Y110.701 Z7.11405
|
||||
G1 X73.695 Y110.441 Z7.12615
|
||||
G1 X73.475 Y109.911 Z7.15004
|
||||
G1 X73.305 Y109.361 Z7.17400
|
||||
G1 X73.175 Y108.801 Z7.19794
|
||||
G1 X73.095 Y108.231 Z7.22190
|
||||
G1 X73.065 Y107.651 Z7.24607
|
||||
G1 X73.615 Y91.111 Z7.93497
|
||||
G1 X73.725 Y90.281 Z7.96982
|
||||
G1 X73.785 Y90.011 Z7.98134
|
||||
G1 X74.035 Y89.211 Z8.01623
|
||||
G1 X74.255 Y88.701 Z8.03935
|
||||
G1 X74.515 Y88.201 Z8.06281
|
||||
G1 X74.815 Y87.731 Z8.08602
|
||||
G1 X75.335 Y87.081 Z8.12067
|
||||
G1 X75.640 Y86.766 Z8.13893
|
||||
|
||||
|
Loading…
Reference in New Issue