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 mdns
|
||||||
import uuid
|
import uuid
|
||||||
from operator import itemgetter, attrgetter
|
from operator import itemgetter, attrgetter
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
log = logging.getLogger("root")
|
log = logging.getLogger("root")
|
||||||
__UPLOADS__ = "./uploads"
|
__UPLOADS__ = "./uploads"
|
||||||
|
@ -68,6 +69,21 @@ class RootHandler(tornado.web.RequestHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
self.render("index.html")
|
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):
|
class JobsHandler(tornado.web.RequestHandler):
|
||||||
def post(self):
|
def post(self):
|
||||||
fileinfo = self.request.files['job'][0]
|
fileinfo = self.request.files['job'][0]
|
||||||
|
@ -106,8 +122,33 @@ class ConstructSocketHandler(tornado.websocket.WebSocketHandler):
|
||||||
self.write_message({'connected': {'jobs': pronserve.jobs.public_list()}})
|
self.write_message({'connected': {'jobs': pronserve.jobs.public_list()}})
|
||||||
print "WebSocket opened. %i sockets currently open." % len(pronserve.clients)
|
print "WebSocket opened. %i sockets currently open." % len(pronserve.clients)
|
||||||
|
|
||||||
def on_sensor_change(self):
|
def on_sensor_changed(self):
|
||||||
self.write_message({'sensors': pronserve.sensors, 'timestamp': time.time()})
|
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):
|
def on_job_added(self, job):
|
||||||
self.write_message({'job_added': pronserve.jobs.sanitize(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):
|
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):
|
def on_message(self, msg):
|
||||||
|
print "message received: %s"%(msg)
|
||||||
# TODO: the read bit of repl!
|
# TODO: the read bit of repl!
|
||||||
self.write_message("You said: " + msg)
|
# self.write_message("You said: " + msg)
|
||||||
|
|
||||||
def on_close(self):
|
def on_close(self):
|
||||||
pronserve.clients.remove(self)
|
pronserve.clients.remove(self)
|
||||||
|
@ -146,7 +188,10 @@ application = tornado.web.Application([
|
||||||
(r"/inspect", InspectHandler),
|
(r"/inspect", InspectHandler),
|
||||||
(r"/socket", ConstructSocketHandler),
|
(r"/socket", ConstructSocketHandler),
|
||||||
(r"/jobs", JobsHandler),
|
(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)
|
], **settings)
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,15 +206,53 @@ class Pronserve(pronsole.pronsole):
|
||||||
self.stdout = sys.stdout
|
self.stdout = sys.stdout
|
||||||
self.ioloop = tornado.ioloop.IOLoop.instance()
|
self.ioloop = tornado.ioloop.IOLoop.instance()
|
||||||
self.clients = set()
|
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.sensors = {'extruder': -1, 'bed': -1}
|
||||||
self.load_default_rc()
|
self.load_default_rc()
|
||||||
self.jobs = PrintJobQueue()
|
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."})
|
services = ({'type': '_construct._tcp', 'port': 8888, 'domain': "local."})
|
||||||
self.mdns = mdns.publisher().save_group({'name': 'pronserve', 'services': services })
|
self.mdns = mdns.publisher().save_group({'name': 'pronserve', 'services': services })
|
||||||
self.jobs.listeners.append(self)
|
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):
|
def run_sensor_loop(self):
|
||||||
self.request_sensor_update()
|
self.request_sensor_update()
|
||||||
next_timeout = time.time() + self.settings.sensor_poll_rate
|
next_timeout = time.time() + self.settings.sensor_poll_rate
|
||||||
|
@ -181,23 +264,34 @@ class Pronserve(pronsole.pronsole):
|
||||||
def recvcb(self, l):
|
def recvcb(self, l):
|
||||||
""" Parses a line of output from the printer via printcore """
|
""" Parses a line of output from the printer via printcore """
|
||||||
l = l.rstrip()
|
l = l.rstrip()
|
||||||
|
print l
|
||||||
if "T:" in l:
|
if "T:" in l:
|
||||||
self._receive_sensor_update(l)
|
self._receive_sensor_update(l)
|
||||||
if l!="ok" and not l.startswith("ok T") and not l.startswith("T:"):
|
if l!="ok" and not l.startswith("ok T") and not l.startswith("T:"):
|
||||||
self._receive_printer_error(l)
|
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):
|
def _receive_sensor_update(self, l):
|
||||||
words = l.split(" ")
|
words = filter(lambda s: s.find(":") > 0, l.split(" "))
|
||||||
words.pop(0)
|
|
||||||
d = dict([ s.split(":") for s in words])
|
d = dict([ s.split(":") for s in words])
|
||||||
|
|
||||||
|
print "sensor update received!"
|
||||||
|
|
||||||
for key, value in d.iteritems():
|
for key, value in d.iteritems():
|
||||||
self.__update_sensor(key, value)
|
self.__update_sensor(key, value)
|
||||||
|
|
||||||
self.fire("sensor_change")
|
self.fire("sensor_changed")
|
||||||
|
|
||||||
def __update_sensor(self, key, value):
|
def __update_sensor(self, key, value):
|
||||||
|
if (key in self.settings.sensor_names) == False:
|
||||||
|
return
|
||||||
sensor_name = self.settings.sensor_names[key]
|
sensor_name = self.settings.sensor_names[key]
|
||||||
self.sensors[sensor_name] = float(value)
|
self.sensors[sensor_name] = float(value)
|
||||||
|
|
||||||
|
@ -219,10 +313,11 @@ class Pronserve(pronsole.pronsole):
|
||||||
def write_prompt(self):
|
def write_prompt(self):
|
||||||
None
|
None
|
||||||
|
|
||||||
|
|
||||||
class PrintJobQueue():
|
class PrintJobQueue():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.list = []
|
self.list = deque([])
|
||||||
self.__last_id = 0
|
self.__last_id = 0
|
||||||
self.listeners = []
|
self.listeners = []
|
||||||
|
|
||||||
|
@ -246,20 +341,16 @@ class PrintJobQueue():
|
||||||
|
|
||||||
def add(self, original_file_name, body):
|
def add(self, original_file_name, body):
|
||||||
ext = os.path.splitext(original_file_name)[1]
|
ext = os.path.splitext(original_file_name)[1]
|
||||||
file_name = str(uuid.uuid4()) + ext
|
|
||||||
job = dict(
|
job = dict(
|
||||||
id = self.__last_id,
|
id = self.__last_id,
|
||||||
rank = len(self.list),
|
rank = len(self.list),
|
||||||
original_file_name=original_file_name,
|
original_file_name=original_file_name,
|
||||||
path= __UPLOADS__ + "/" + file_name,
|
body= body,
|
||||||
)
|
)
|
||||||
self.__last_id += 1
|
self.__last_id += 1
|
||||||
|
|
||||||
fh = open(job['path'], 'w')
|
|
||||||
fh.write(body)
|
|
||||||
|
|
||||||
self.list.append(job)
|
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)
|
self.fire("job_added", job)
|
||||||
|
|
||||||
def display_summary(self):
|
def display_summary(self):
|
||||||
|
@ -305,8 +396,9 @@ print "Pronserve is starting..."
|
||||||
pronserve = Pronserve()
|
pronserve = Pronserve()
|
||||||
pronserve.do_connect("")
|
pronserve.do_connect("")
|
||||||
|
|
||||||
time.sleep(0.2)
|
time.sleep(1)
|
||||||
pronserve.run_sensor_loop()
|
pronserve.run_sensor_loop()
|
||||||
|
pronserve.run_print_queue_loop()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
application.listen(8888)
|
application.listen(8888)
|
||||||
|
|
|
@ -38,4 +38,17 @@
|
||||||
#temperature-graph
|
#temperature-graph
|
||||||
{
|
{
|
||||||
height: 200px;
|
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() {
|
$(window).focus(function() {
|
||||||
windowFocus = true;
|
windowFocus = true;
|
||||||
//if ($console) $console.append("Window refocused, restarting graph.\n");
|
//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() {
|
}).blur(function() {
|
||||||
windowFocus = false;
|
windowFocus = false;
|
||||||
//if ($console) $console.append("Window's focus, lost stopping graph...\n");
|
//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());
|
}.debounce());
|
||||||
|
|
||||||
var connect = function() {
|
var connect = function() {
|
||||||
|
@ -75,7 +79,6 @@
|
||||||
if(windowFocus == false) return;
|
if(windowFocus == false) return;
|
||||||
updateSensorsUi();
|
updateSensorsUi();
|
||||||
updateGraphUi();
|
updateGraphUi();
|
||||||
$consoleWrapper.scrollTop($console.innerHeight());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var onConnect = function(ws) {
|
var onConnect = function(ws) {
|
||||||
|
@ -85,26 +88,42 @@
|
||||||
// Web Socket is connected, send data using send()
|
// Web Socket is connected, send data using send()
|
||||||
|
|
||||||
};
|
};
|
||||||
|
var nextGraphPoint = {};
|
||||||
ws.onmessage = function (evt)
|
ws.onmessage = function (evt)
|
||||||
{
|
{
|
||||||
msg = JSON.parse(evt.data)
|
msg = JSON.parse(evt.data)
|
||||||
if(msg.sensors != undefined)
|
if(msg.sensor_changed != undefined)
|
||||||
{
|
{
|
||||||
var sensorNames = ["bed", "extruder"];
|
var sensorNames = ["bed", "extruder"];
|
||||||
var values = {timestamp: msg.timestamp};
|
|
||||||
for (var i = 0; i < sensorNames.length; i++)
|
for (var i = 0; i < sensorNames.length; i++)
|
||||||
{
|
{
|
||||||
var name = sensorNames[i];
|
var name = msg.sensor_changed.name;
|
||||||
var val = parseFloat(msg.sensors[name]);
|
var val = parseFloat(msg.sensor_changed.value);
|
||||||
values[name] = val;
|
nextGraphPoint[name] = val;
|
||||||
$("."+name+" .val").data("val", val.format(1))
|
$("."+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);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
console.log($consoleWrapper.scrollTop() - $console.innerHeight())
|
||||||
|
var atBottom = $consoleWrapper.scrollTop() - $console.innerHeight() > -220;
|
||||||
$console.append(evt.data + "\n");
|
$console.append(evt.data + "\n");
|
||||||
|
if (atBottom)
|
||||||
|
{
|
||||||
|
$consoleWrapper.scrollTop($console.innerHeight());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ws.onclose = function()
|
ws.onclose = function()
|
||||||
|
|
|
@ -21,6 +21,12 @@
|
||||||
Connecting...
|
Connecting...
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</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="sensors pull-right">
|
||||||
<div class="extruder pull-right">
|
<div class="extruder pull-right">
|
||||||
Extruder: <span class="val">xx.x</span><span class="deg">°C</span>
|
Extruder: <span class="val">xx.x</span><span class="deg">°C</span>
|
||||||
|
@ -38,7 +44,7 @@
|
||||||
</div>
|
</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="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>
|
<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