From 8f6f24d7a408cd39036d15fbe4ad18c8890b6cba Mon Sep 17 00:00:00 2001 From: Mikko Sivulainen Date: Fri, 9 Nov 2012 00:06:59 +0200 Subject: [PATCH 01/24] updated gcoder --- gcoder.py | 217 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 163 insertions(+), 54 deletions(-) diff --git a/gcoder.py b/gcoder.py index 9ed22c5..99e8e92 100755 --- a/gcoder.py +++ b/gcoder.py @@ -16,6 +16,17 @@ import sys import re +import math + +def deltalen(a,b): + d = object() + d.x = b.x - a.x + d.y = b.y - a.y + d.z = b.z - a.z + + return math.sqrt((d.x*d.x)+(d.y*d.y)+(d.z*d.z)) + + class Line(object): def __init__(self,l): @@ -77,8 +88,10 @@ class Line(object): return "" def _get_float(self,which): - return float(self.regex.findall(self.raw.split(which)[1])[0]) - + try: + return float(self.regex.findall(self.raw.split(which)[1])[0]) + except: + return None def _parse_coordinates(self): if "X" in self.raw: @@ -98,14 +111,76 @@ class Line(object): def is_move(self): - return "G1" in self.raw or "G0" in self.raw + return self.command() and ("G1" in self.raw or "G0" in self.raw) + + def __str__(self): + return self.raw + +class Layer(object): + def __init__(self,lines): + self.lines = lines + + + def measure(self): + xmin = 999999999 + ymin = 999999999 + zmin = 0 + xmax = -999999999 + ymax = -999999999 + zmax = -999999999 + relative = False + + current_x = 0 + current_y = 0 + current_z = 0 + + for line in self.lines: + if line.command() == "G92": + current_x = line.x or current_x + current_y = line.y or current_y + current_z = line.z or current_z + + if line.is_move(): + x = line.x + y = line.y + z = line.z + + if line.relative: + x = current_x + (x or 0) + y = current_y + (y or 0) + z = current_z + (z or 0) + + + if x and line.e: + if x < xmin: + xmin = x + if x > xmax: + xmax = x + if y and line.e: + if y < ymin: + ymin = y + if y > ymax: + ymax = y + if z: + if z < zmin: + zmin = z + if z > zmax: + zmax = z + + current_x = x or current_x + current_y = y or current_y + current_z = z or current_z + + return ( (xmin,xmax),(ymin,ymax),(zmin,zmax) ) + class GCode(object): def __init__(self,data): self.lines = [Line(i) for i in data] self._preprocess() - + self._create_layers() + def _preprocess(self): #checks for G20, G21, G90 and G91, sets imperial and relative flags imperial = False @@ -122,6 +197,63 @@ class GCode(object): elif line.is_move(): line.imperial = imperial line.relative = relative + + def _create_layers(self): + self.layers = [] + + prev_z = None + cur_z = 0 + cur_lines = [] + layer_index = [] + + temp_layers = {} + for line in self.lines: + if line.command() == "G92" and line.z != None: + cur_z = line.z + elif line.is_move(): + if line.z != None: + if line.relative: + cur_z += line.z + else: + cur_z = line.z + + if cur_z != prev_z: + old_lines = temp_layers.pop(prev_z,[]) + old_lines += cur_lines + temp_layers[prev_z] = old_lines + + if not prev_z in layer_index: + layer_index.append(prev_z) + + cur_lines = [] + + cur_lines.append(line) + prev_z = cur_z + + + old_lines = temp_layers.pop(prev_z,[]) + old_lines += cur_lines + temp_layers[prev_z] = old_lines + + if not prev_z in layer_index: + layer_index.append(prev_z) + + layer_index.sort() + + for idx in layer_index: + cur_lines = temp_layers[idx] + has_movement = False + for l in cur_lines: + if l.is_move() and l.e != None: + has_movement = True + break + + if has_movement: + self.layers.append(Layer(cur_lines)) + + + def num_layers(self): + return len(self.layers) def measure(self): @@ -131,60 +263,33 @@ class GCode(object): xmax = -999999999 ymax = -999999999 zmax = -999999999 - relative = False - - current_x = 0 - current_y = 0 - current_z = 0 - for line in self.lines: - if line.command() == "G92": - current_x = line.x or current_x - current_y = line.y or current_y - current_z = line.z or current_z + for l in self.layers: + xd,yd,zd = l.measure() + if xd[0] < xmin: + xmin = xd[0] + if xd[1] > xmax: + xmax = xd[1] + + if yd[0] < ymin: + ymin = yd[0] + if yd[1] > ymax: + ymax = yd[1] + + if zd[0] < zmin: + zmin = zd[0] + if zd[1] > zmax: + zmax = zd[1] - if line.is_move(): - x = line.x - y = line.y - z = line.z - - if line.relative: - x = current_x + (x or 0) - y = current_y + (y or 0) - z = current_z + (z or 0) - - - if x and line.e: - if x < xmin: - xmin = x - if x > xmax: - xmax = x - if y and line.e: - if y < ymin: - ymin = y - if y > ymax: - ymax = y - if z: - if z < zmin: - zmin = z - if z > zmax: - zmax = z - - current_x = x or current_x - current_y = y or current_y - current_z = z or current_z - self.xmin = xmin - self.ymin = ymin - self.zmin = zmin self.xmax = xmax + self.ymin = ymin self.ymax = ymax + self.zmin = zmin self.zmax = zmax - - self.width = xmax-xmin - self.depth = ymax-ymin - self.height = zmax-zmin - + self.width = xmax - xmin + self.depth = ymax - ymin + self.height = zmax - zmin def filament_length(self): total_e = 0 @@ -210,7 +315,8 @@ def main(): print "usage: %s filename.gcode" % sys.argv[0] return - gcode = GCode(sys.argv[1]) + d = [i.replace("\n","") for i in open(sys.argv[1])] + gcode = GCode(d) gcode.measure() @@ -219,6 +325,9 @@ def main(): print "\tY: %0.02f - %0.02f (%0.02f)" % (gcode.ymin,gcode.ymax,gcode.depth) print "\tZ: %0.02f - %0.02f (%0.02f)" % (gcode.zmin,gcode.zmax,gcode.height) print "Filament used: %0.02fmm" % gcode.filament_length() + print "Number of layers: %d" % gcode.num_layers() + if __name__ == '__main__': - main() \ No newline at end of file + main() + From 80403aa7eb56b23137b2797b866078a2b19c96fe Mon Sep 17 00:00:00 2001 From: Kliment Yanev Date: Tue, 13 Nov 2012 17:36:12 +0100 Subject: [PATCH 02/24] Make port textbox bigger --- printrun/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/printrun/gui.py b/printrun/gui.py index 09f7908..7e261d5 100644 --- a/printrun/gui.py +++ b/printrun/gui.py @@ -223,7 +223,7 @@ class MainToolbar(wx.BoxSizer): root.serialport = wx.ComboBox(root.panel, -1, choices = root.scanserial(), - style = wx.CB_DROPDOWN, size = (100, 25)) + style = wx.CB_DROPDOWN, size = (150, 25)) root.serialport.SetToolTip(wx.ToolTip("Select Port Printer is connected to")) root.rescanports() self.Add(root.serialport) From df0265d4c669ea66e094784d04c41f471a326950 Mon Sep 17 00:00:00 2001 From: Chris Olah Date: Wed, 9 Jan 2013 18:59:39 -0500 Subject: [PATCH 03/24] Make prompt dynamically generated. Prompt is now generated by a promptf() call for every cmdloop iteration. --- pronsole.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/pronsole.py b/pronsole.py index 982665b..c994502 100755 --- a/pronsole.py +++ b/pronsole.py @@ -192,6 +192,7 @@ class pronsole(cmd.Cmd): self.p.recvcb = self.recvcb self.recvlisteners = [] self.prompt = "PC>" + self.in_macro = False self.p.onlinecb = self.online self.f = None self.listing = 0 @@ -231,6 +232,19 @@ class pronsole(cmd.Cmd): self.web_config = None self.web_auth_config = None + def promptf(self): + """A function to generate prompts so that we can do dynamic prompts. """ + if self.in_macro: + return "..>" + else: + return "PC>" + + def postcmd(self, stop, line): + """ A hook we override to generate prompts after + each command is executed, for the next prompt.""" + self.prompt = self.promptf() + return stop + def set_temp_preset(self, key, value): if not key.startswith("bed"): self.temps["pla"] = str(self.settings.temperature_pla) @@ -258,7 +272,7 @@ class pronsole(cmd.Cmd): def online(self): print "Printer is now online" - sys.stdout.write(self.prompt) + sys.stdout.write(self.promptf()) sys.stdout.flush() def help_help(self, l): @@ -290,7 +304,8 @@ class pronsole(cmd.Cmd): def end_macro(self): if self.__dict__.has_key("onecmd"): del self.onecmd # remove override - self.prompt = "PC>" + self.in_macro = False + self.prompt = self.promptf() if self.cur_macro_def!="": self.macros[self.cur_macro_name] = self.cur_macro_def macro = self.compile_macro(self.cur_macro_name, self.cur_macro_def) @@ -342,7 +357,8 @@ class pronsole(cmd.Cmd): self.cur_macro_name = macro_name self.cur_macro_def = "" self.onecmd = self.hook_macro # override onecmd temporarily - self.prompt = "..>" + self.in_macro = False + self.prompt = self.promptf() def delete_macro(self, macro_name): if macro_name in self.macros.keys(): @@ -816,7 +832,7 @@ class pronsole(cmd.Cmd): tstring = l.rstrip() if(tstring!="ok" and not tstring.startswith("ok T") and not tstring.startswith("T:") and not self.listing and not self.monitoring): print tstring - sys.stdout.write(self.prompt) + sys.stdout.write(self.promptf()) sys.stdout.flush() for i in self.recvlisteners: i(l) From a224a10ce01c8065b969ee4e7304587739344f1a Mon Sep 17 00:00:00 2001 From: Christopher Olah Date: Fri, 11 Jan 2013 19:02:14 -0500 Subject: [PATCH 04/24] We make the prompt aware of extruder temperature. In order to do this, we consolidate handeling of printer status with a Status class. The status class is updated by recvcb. This has the side effect of simplifying the implementation of gettemp. We also detect whether there is a heated build platform or not, and don't display info about it if there isn't. --- pronsole.py | 59 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/pronsole.py b/pronsole.py index c994502..1e014db 100755 --- a/pronsole.py +++ b/pronsole.py @@ -183,15 +183,44 @@ class Settings: def _all_settings(self): return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")]) +class Status: + + def __init__(self): + self.extruder_temp = 0 + self.extruder_temp_target = 0 + self.bed_temp = 0 + self.bed_temp_target = 0 + self.print_job = None + self.print_job_progress = 1.0 + + def update_tempreading(self, tempstr): + r = tempstr.split() + # eg. r = ["ok", "T:20.5", "/0.0", "B:0.0", "/0.0", "@:0"] + if len(r) == 6: + self.extruder_temp = float(r[1][2:]) + self.extruder_temp_target = float(r[2][1:]) + self.bed_temp = float(r[3][2:]) + self.bed_temp_target = float(r[4][1:]) + + @property + def bed_enabled(self): + return self.bed_temp != 0 + + @property + def extruder_enabled(self): + return self.extruder_temp != 0 + + + class pronsole(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) if not READLINE: self.completekey = None + self.status = Status() self.p = printcore.printcore() self.p.recvcb = self.recvcb self.recvlisteners = [] - self.prompt = "PC>" self.in_macro = False self.p.onlinecb = self.online self.f = None @@ -236,12 +265,23 @@ class pronsole(cmd.Cmd): """A function to generate prompts so that we can do dynamic prompts. """ if self.in_macro: return "..>" + elif not self.p.online: + return "uninitialized>" + elif self.status.extruder_enabled:# and not self.status.bed_enabled: + if self.status.extruder_temp_target == 0: + return "T:%s>" % self.status.extruder_temp + else: + return "T:%s/%s>" % (self.status.extruder_temp, self.status.extruder_temp_target) else: - return "PC>" + return "printer>" def postcmd(self, stop, line): """ A hook we override to generate prompts after - each command is executed, for the next prompt.""" + each command is executed, for the next prompt. + We also use it to send M105 commands so that + temp info gets updated for the prompt.""" + if self.p.online: + self.p.send_now("M105") self.prompt = self.promptf() return stop @@ -536,6 +576,7 @@ class pronsole(cmd.Cmd): def preloop(self): print "Welcome to the printer console! Type \"help\" for a list of available commands." + self.prompt = self.promptf() cmd.Cmd.preloop(self) def do_connect(self, l): @@ -829,6 +870,7 @@ class pronsole(cmd.Cmd): def recvcb(self, l): if "T:" in l: self.tempreadings = l + self.status.update_tempreading(l) tstring = l.rstrip() if(tstring!="ok" and not tstring.startswith("ok T") and not tstring.startswith("T:") and not self.listing and not self.monitoring): print tstring @@ -864,16 +906,15 @@ class pronsole(cmd.Cmd): def help_help(self): self.do_help("") - def tempcb(self, l): - if "T:" in l: - print l.replace("\r", "").replace("T", "Hotend").replace("B", "Bed").replace("\n", "").replace("ok ", "") - def do_gettemp(self, l): if self.p.online: - self.recvlisteners+=[self.tempcb] self.p.send_now("M105") time.sleep(0.75) - self.recvlisteners.remove(self.tempcb) + if not self.status.bed_enabled: + print "Hotend: %s/%s" % (self.status.extruder_temp, self.status.extruder_temp_target) + else: + print "Hotend: %s/%s" % (self.status.extruder_temp, self.status.extruder_temp_target) + print "Bed: %s/%s" % (self.status.bed_temp, self.status.bed_temp_target) def help_gettemp(self): print "Read the extruder and bed temperature." From d3c1fbaa757c92d2c89cc6d053533d5e4f988886 Mon Sep 17 00:00:00 2001 From: Christopher Olah Date: Fri, 11 Jan 2013 19:26:04 -0500 Subject: [PATCH 05/24] Fix Ctr-C and Ctr-D behavior. We replace the cmdloop function with a slightly modified version to achieve standard unix shell behavior on keyboard interupts and EOF. --- pronsole.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/pronsole.py b/pronsole.py index 982665b..9bf8814 100755 --- a/pronsole.py +++ b/pronsole.py @@ -1193,6 +1193,68 @@ class pronsole(cmd.Cmd): self.onecmd(a) self.processing_args = False + + # We replace this function, defined in cmd.py . + # It's default behavior with reagrds to Ctr-C + # and Ctr-D doesn't make much sense... + + def cmdloop(self, intro=None): + """Repeatedly issue a prompt, accept input, parse an initial prefix + off the received input, and dispatch to action methods, passing them + the remainder of the line as argument. + + """ + + self.preloop() + if self.use_rawinput and self.completekey: + try: + import readline + self.old_completer = readline.get_completer() + readline.set_completer(self.complete) + readline.parse_and_bind(self.completekey+": complete") + except ImportError: + pass + try: + if intro is not None: + self.intro = intro + if self.intro: + self.stdout.write(str(self.intro)+"\n") + stop = None + while not stop: + if self.cmdqueue: + line = self.cmdqueue.pop(0) + else: + if self.use_rawinput: + try: + line = raw_input(self.prompt) + except EOFError: + print "" + self.do_exit("") + exit() + except KeyboardInterrupt: + print "" + line = "" + else: + self.stdout.write(self.prompt) + self.stdout.flush() + line = self.stdin.readline() + if not len(line): + line = "" + else: + line = line.rstrip('\r\n') + line = self.precmd(line) + stop = self.onecmd(line) + stop = self.postcmd(stop, line) + self.postloop() + finally: + if self.use_rawinput and self.completekey: + try: + import readline + readline.set_completer(self.old_completer) + except ImportError: + pass + + if __name__ == "__main__": interp = pronsole() From a377b85c8f3163f051c17486993c198d88864b10 Mon Sep 17 00:00:00 2001 From: Chris Olah Date: Sat, 12 Jan 2013 14:14:10 -0500 Subject: [PATCH 06/24] Make the dynamic prompt temperature stuff configurable. --- pronsole.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pronsole.py b/pronsole.py index 1e014db..b983b9b 100755 --- a/pronsole.py +++ b/pronsole.py @@ -218,6 +218,7 @@ class pronsole(cmd.Cmd): if not READLINE: self.completekey = None self.status = Status() + self.dynamic_temp = False self.p = printcore.printcore() self.p.recvcb = self.recvcb self.recvlisteners = [] @@ -267,7 +268,7 @@ class pronsole(cmd.Cmd): return "..>" elif not self.p.online: return "uninitialized>" - elif self.status.extruder_enabled:# and not self.status.bed_enabled: + elif self.status.extruder_enabled and self.dynamic_temp: if self.status.extruder_temp_target == 0: return "T:%s>" % self.status.extruder_temp else: @@ -280,7 +281,7 @@ class pronsole(cmd.Cmd): each command is executed, for the next prompt. We also use it to send M105 commands so that temp info gets updated for the prompt.""" - if self.p.online: + if self.p.online and self.dynamic_temp: self.p.send_now("M105") self.prompt = self.promptf() return stop @@ -907,6 +908,8 @@ class pronsole(cmd.Cmd): self.do_help("") def do_gettemp(self, l): + if "dynamic" in l: + self.dynamic_temp = True if self.p.online: self.p.send_now("M105") time.sleep(0.75) From aa1e576a1ecbd3abff15fea4c4828d5d3f1b272a Mon Sep 17 00:00:00 2001 From: Mikko Sivulainen Date: Sun, 13 Jan 2013 23:00:19 +0200 Subject: [PATCH 07/24] gcoder updated --- gcoder.py | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/gcoder.py b/gcoder.py index 99e8e92..f5bf28a 100755 --- a/gcoder.py +++ b/gcoder.py @@ -94,21 +94,36 @@ class Line(object): return None def _parse_coordinates(self): - if "X" in self.raw: - self._x = self._get_float("X") + try: + if "X" in self.raw: + self._x = self._get_float("X") + except: + pass + + try: + if "Y" in self.raw: + self._y = self._get_float("Y") + except: + pass + + try: + if "Z" in self.raw: + self._z = self._get_float("Z") + except: + pass + + try: + if "E" in self.raw: + self.e = self._get_float("E") + except: + pass + + try: + if "F" in self.raw: + self.f = self._get_float("F") + except: + pass - if "Y" in self.raw: - self._y = self._get_float("Y") - - if "Z" in self.raw: - self._z = self._get_float("Z") - - if "E" in self.raw: - self.e = self._get_float("E") - - if "F" in self.raw: - self.f = self._get_float("F") - def is_move(self): return self.command() and ("G1" in self.raw or "G0" in self.raw) From 29c9700324d650ee701c5d6976067d779c60a991 Mon Sep 17 00:00:00 2001 From: Christopher Olah Date: Sat, 19 Jan 2013 19:34:07 -0500 Subject: [PATCH 08/24] Fancy configurable prompts! * Prompts are now generated based off of string templates, for example: "%(bold)sT:%(extruder_temp_fancy)s %(progress_fancy)s >%(normal)s " * We have a dictionary of prompt string templates for different situations. * We have bold support for the prompt. * We have extruder temperature support for the prompt. * We have progress support for the prompt. --- pronsole.py | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/pronsole.py b/pronsole.py index 6fb37aa..f9dd1b6 100755 --- a/pronsole.py +++ b/pronsole.py @@ -261,20 +261,45 @@ class pronsole(cmd.Cmd): self.webrequested = False self.web_config = None self.web_auth_config = None + self.promptstrs = {"offline" : "%(bold)suninitialized>%(normal)s ", + "fallback" : "%(bold)sPC>%(normal)s ", + "macro" : "%(bold)s..>%(normal)s ", + "online" : "%(bold)sT:%(extruder_temp_fancy)s %(progress_fancy)s >%(normal)s "} def promptf(self): """A function to generate prompts so that we can do dynamic prompts. """ if self.in_macro: - return "..>" + promptstr = self.promptstrs["macro"] elif not self.p.online: - return "uninitialized>" - elif self.status.extruder_enabled and self.dynamic_temp: - if self.status.extruder_temp_target == 0: - return "T:%s>" % self.status.extruder_temp - else: - return "T:%s/%s>" % (self.status.extruder_temp, self.status.extruder_temp_target) + promptstr = self.promptstrs["offline"] + elif self.status.extruder_enabled: + promptstr = self.promptstrs["online"] else: - return "printer>" + promptstr = self.promptstrs["fallback"] + if not "%" in promptstr: + return promptstr + else: + specials = {} + specials["extruder_temp"] = str(int(self.status.extruder_temp)) + specials["extruder_temp_target"] = str(int(self.status.extruder_temp_target)) + if self.status.extruder_temp_target == 0: + specials["extruder_temp_fancy"] = str(int(self.status.extruder_temp)) + else: + specials["extruder_temp_fancy"] = "%s/%s" % (str(int(self.status.extruder_temp)), str(int(self.status.extruder_temp_target))) + if self.p.printing: + progress = int(1000*float(self.p.queueindex)/len(self.p.mainqueue)) / 10 + elif self.sdprinting: + progress = self.percentdone + else: + progress = 0.0 + specials["progress"] = str(progress) + if self.p.printing or self.sdprinting: + specials["progress_fancy"] = str(progress) +"%" + else: + specials["progress_fancy"] = "?%" + specials["bold"] = "\033[01m" + specials["normal"] = "\033[00m" + return promptstr % specials def postcmd(self, stop, line): """ A hook we override to generate prompts after From 3fb7a3e71c9d71974049909c539b699516830862 Mon Sep 17 00:00:00 2001 From: Christopher Olah Date: Sat, 19 Jan 2013 19:11:04 -0500 Subject: [PATCH 09/24] Clean up the monitor command. The monitor command now: * Has more pythonic code * Limits precision of progress elements (12.3% instead of 12.347812...%) * Uses a carriage return to have print progress replace the previous progress line. For example: Monitoring printer, use ^C to interrupt. Updating values every 5.000000 seconds. Print progress: 0.3% Previously, the line "Print progress: 0.3%" was "Print progress: 0.2%", etc. --- pronsole.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/pronsole.py b/pronsole.py index 6fb37aa..eabb0d3 100755 --- a/pronsole.py +++ b/pronsole.py @@ -1118,6 +1118,9 @@ class pronsole(cmd.Cmd): if not self.p.online: print "Printer is not online. Please connect first." return + if not (self.p.printing or self.sdprinting): + print "Printer not printing. Please print something before monitoring." + return print "Monitoring printer, use ^C to interrupt." if len(l): try: @@ -1126,22 +1129,27 @@ class pronsole(cmd.Cmd): print "Invalid period given." print "Updating values every %f seconds."%(interval,) self.monitoring = 1 + prev_msg_len = 0 try: - while(1): + while True: self.p.send_now("M105") if(self.sdprinting): self.p.send_now("M27") time.sleep(interval) #print (self.tempreadings.replace("\r", "").replace("T", "Hotend").replace("B", "Bed").replace("\n", "").replace("ok ", "")) - if(self.p.printing): - print "Print progress: ", 100*float(self.p.queueindex)/len(self.p.mainqueue), "%" - - if(self.sdprinting): - print "SD print progress: ", self.percentdone, "%" - - except: + if self.p.printing: + preface = "Print progress: " + progress = 100*float(self.p.queueindex)/len(self.p.mainqueue) + elif self.sdprinting: + preface = "Print progress: " + progress = self.percentdone + progress = int(progress*10)/10.0 #limit precision + prev_msg = preface + str(progress) + "%" + sys.stdout.write("\r" + prev_msg.ljust(prev_msg_len)) + sys.stdout.flush() + prev_msg_len = len(prev_msg) + except KeyboardInterrupt: print "Done monitoring." - pass self.monitoring = 0 def help_monitor(self): From 14602469607c9341bd1ed67f09e727e338bdde91 Mon Sep 17 00:00:00 2001 From: Christopher Olah Date: Sat, 19 Jan 2013 18:34:15 -0500 Subject: [PATCH 10/24] Clean up echoed firmware lines, connect info. Presently, when you connect to a printer using pronsole, one sees something like: uninitialized>start uninitialized>Printer is now online uninitialized>echo: External Reset printer>Marlin 1.0.0 RC2 ... With a few carriage returns and some string hackery, we clean up the output so that one sees: No port specified - connecting to /dev/ttyACM0 at 115200bps start Printer is now online External Reset Marlin 1.0.0 RC2 Much cleaner! --- pronsole.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pronsole.py b/pronsole.py index 6fb37aa..ba9fbfc 100755 --- a/pronsole.py +++ b/pronsole.py @@ -312,7 +312,7 @@ class pronsole(cmd.Cmd): return baselist+glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*') +glob.glob("/dev/tty.*")+glob.glob("/dev/cu.*")+glob.glob("/dev/rfcomm*") def online(self): - print "Printer is now online" + print "\rPrinter is now online" sys.stdout.write(self.promptf()) sys.stdout.flush() @@ -874,7 +874,9 @@ class pronsole(cmd.Cmd): self.status.update_tempreading(l) tstring = l.rstrip() if(tstring!="ok" and not tstring.startswith("ok T") and not tstring.startswith("T:") and not self.listing and not self.monitoring): - print tstring + if tstring[:5] == "echo:": + tstring = tstring[5:].lstrip() + print "\r" + tstring.ljust(15) sys.stdout.write(self.promptf()) sys.stdout.flush() for i in self.recvlisteners: From e2630df4bb010b0b727d8a736836ae6bf880ff95 Mon Sep 17 00:00:00 2001 From: Lenbok Date: Fri, 25 Jan 2013 07:29:45 +1300 Subject: [PATCH 11/24] Support M82/M83 for relative E --- gcoder.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/gcoder.py b/gcoder.py index f5bf28a..67128d8 100755 --- a/gcoder.py +++ b/gcoder.py @@ -40,6 +40,7 @@ class Line(object): self.raw = l.upper().lstrip() self.imperial = False self.relative = False + self.relative_e = False if ";" in self.raw: self.raw = self.raw.split(";")[0] @@ -145,6 +146,7 @@ class Layer(object): ymax = -999999999 zmax = -999999999 relative = False + relative_e = False current_x = 0 current_y = 0 @@ -200,6 +202,7 @@ class GCode(object): #checks for G20, G21, G90 and G91, sets imperial and relative flags imperial = False relative = False + relative_e = False for line in self.lines: if line.command() == "G20": imperial = True @@ -207,11 +210,18 @@ class GCode(object): imperial = False elif line.command() == "G90": relative = False + relative_e = False elif line.command() == "G91": relative = True + relative_e = True + elif line.command() == "M82": + relative_e = False + elif line.command() == "M83": + relative_e = True elif line.is_move(): line.imperial = imperial line.relative = relative + line.relative_e = relative_e def _create_layers(self): self.layers = [] @@ -316,7 +326,7 @@ class GCode(object): total_e += cur_e cur_e = line.e elif line.is_move() and line.e: - if line.relative: + if line.relative_e: cur_e += line.e else: cur_e = line.e From 786baebbf892e4367100cdf1af9d7363a1cba37c Mon Sep 17 00:00:00 2001 From: Christopher Olah Date: Sat, 2 Feb 2013 10:25:16 -0500 Subject: [PATCH 12/24] Confirm dangerous extruder target temperatures. Protect the user from typos in setting the extruder temp by confirming them with the user if they exceed 250. --- pronsole.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pronsole.py b/pronsole.py index e40a942..0a68beb 100755 --- a/pronsole.py +++ b/pronsole.py @@ -956,6 +956,17 @@ class pronsole(cmd.Cmd): l = l.replace(i, self.temps[i]) f = float(l) if f>=0: + if f > 250: + def confirm(): + print f, " is a high temperature to set your extruder to. Are you sure you want to do that?" + y_or_n = raw_input("y/n: ") + if y_or_n == "y": + return True + elif y_or_n != "n": + return confirm() + return False + if not confirm(): + return if self.p.online: self.p.send_now("M104 S"+l) print "Setting hotend temperature to ", f, " degrees Celsius." From c20b270b8437bfd5c62b4f51bff5f7394aa467ce Mon Sep 17 00:00:00 2001 From: Christopher Olah Date: Sat, 2 Feb 2013 21:24:07 -0500 Subject: [PATCH 13/24] Refactor and confirm exits while printing. Presently, one might accidentally terminate a print with a CTR-D while printing. Now we ask the user to confirm exiting if there is a print going when they try to exit. --- pronsole.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/pronsole.py b/pronsole.py index 0a68beb..57d41ff 100755 --- a/pronsole.py +++ b/pronsole.py @@ -131,6 +131,14 @@ def estimate_duration(g): #print "Total Duration: " #, time.strftime('%H:%M:%S', time.gmtime(totalduration)) return "{0:d} layers, ".format(int(layercount)) + str(datetime.timedelta(seconds = int(totalduration))) +def confirm(): + y_or_n = raw_input("y/n: ") + if y_or_n == "y": + return True + elif y_or_n != "n": + return confirm() + return False + class Settings: #def _temperature_alias(self): return {"pla":210, "abs":230, "off":0} #def _temperature_validate(self, v): @@ -957,14 +965,7 @@ class pronsole(cmd.Cmd): f = float(l) if f>=0: if f > 250: - def confirm(): - print f, " is a high temperature to set your extruder to. Are you sure you want to do that?" - y_or_n = raw_input("y/n: ") - if y_or_n == "y": - return True - elif y_or_n != "n": - return confirm() - return False + print f, " is a high temperature to set your extruder to. Are you sure you want to do that?" if not confirm(): return if self.p.online: @@ -1144,8 +1145,14 @@ class pronsole(cmd.Cmd): def do_exit(self, l): print "Disconnecting from printer..." - self.p.disconnect() + print self.p.printing + if self.p.printing: + print "Are you sure you want to exit while printing?" + print "(this will terminate the print)." + if not confirm(): + return False print "Exiting program. Goodbye!" + self.p.disconnect() return True def help_exit(self): @@ -1335,8 +1342,9 @@ class pronsole(cmd.Cmd): line = raw_input(self.prompt) except EOFError: print "" - self.do_exit("") - exit() + should_exit = self.do_exit("") + if should_exit: + exit() except KeyboardInterrupt: print "" line = "" From 29c3183168d25bf97d1a9ed20bb599c26c3083eb Mon Sep 17 00:00:00 2001 From: Christopher Olah Date: Fri, 8 Feb 2013 11:46:35 -0500 Subject: [PATCH 14/24] Make sure to set target temps to 0 before exiting. To ensure safety, we make sure we set target temps to 0 before we exit pronsole. --- pronsole.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pronsole.py b/pronsole.py index 6fb37aa..bfc2aad 100755 --- a/pronsole.py +++ b/pronsole.py @@ -1105,6 +1105,13 @@ class pronsole(cmd.Cmd): print "reverse -5 - EXTRUDES 5mm of filament at 300mm/min (5mm/s)" def do_exit(self, l): + if self.status.extruder_temp_target != 0: + print "Setting extruder temp to 0" + self.p.send_now("M104 S0.0") + if self.status.bed_enabled: + if self.status.bed_temp_taret != 0: + print "Setting bed temp to 0" + self.p.send_now("M140 S0.0") print "Disconnecting from printer..." self.p.disconnect() print "Exiting program. Goodbye!" From 63bd1ca9f658d81e460606b51d1dd5c87b5a81ef Mon Sep 17 00:00:00 2001 From: Kaz Walker Date: Sun, 17 Feb 2013 14:09:38 -0700 Subject: [PATCH 15/24] [printcore] Added checking to pause and resume function. --- printcore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/printcore.py b/printcore.py index bb6501c..b4b4bbc 100755 --- a/printcore.py +++ b/printcore.py @@ -222,6 +222,7 @@ class printcore(): def pause(self): """Pauses the print, saving the current position. """ + return False if !selft.printing self.paused = True self.printing = False self.print_thread.join() @@ -230,6 +231,7 @@ class printcore(): def resume(self): """Resumes a paused print. """ + return False if !self.paused self.paused = False self.printing = True self.print_thread = Thread(target = self._print) From b0cb2b07811a5428257bdf09189fd517131430ea Mon Sep 17 00:00:00 2001 From: Kaz Walker Date: Sun, 17 Feb 2013 14:11:01 -0700 Subject: [PATCH 16/24] Added icon files ad PNGs. --- P-face.png | Bin 0 -> 9063 bytes plater.png | Bin 0 -> 2090 bytes pronsole.png | Bin 0 -> 1090 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 P-face.png create mode 100644 plater.png create mode 100644 pronsole.png diff --git a/P-face.png b/P-face.png new file mode 100644 index 0000000000000000000000000000000000000000..1adcb1400fe3a13bb9db8d59c9dc8f0c67174e08 GIT binary patch literal 9063 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 Uq>L>plwY`vs6Y zmR`wcsg3{}biEspUK{kJc=sPdt}XUT-t|Y2Ym+^aF$V`{^BsWn+GZ=oNN{jg?IXQD zSW7Vy9Gpe-NUtxJQj7!#r)eGO^+_wm7iyzl-#2O#i+p}u#oEroKjXdWl@we9;oHuYqBDo%1x=n}1s%#eEc~?h_2w2`d;w_mi-Kr|BsYd-#CGNv2d^-`{@R4AI5^MTbQ^4%HC6fk&xxAb5c& zk4q3-5k+j_a=Sms&%)k`^2}RvyFX5;6C5qAEz${&n${)K2+k(95MFSGVR((pG(9Kd zXdkq)i!DFK5RbL2;mRj%vbss#P~6C5eU*%0}?Xz;}9lz=lEf`Bs{l7e%S0)TUq5`(h~ zorALrlY{ev9fR|O0gib03hNQ?1xU6*&)3P_-#bI{SbEnAFb4`~Jcc7HeTSC@ zAz7EB%ebVqUr5%0vPMr{+=paYs0A1-)f(Cr8qAiLonC)1NHQAP>&T-Mn7TUtS2(5A1=_R Date: Sun, 17 Feb 2013 14:14:02 -0700 Subject: [PATCH 17/24] [printcore] fixed typo. --- printcore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/printcore.py b/printcore.py index b4b4bbc..09e8574 100755 --- a/printcore.py +++ b/printcore.py @@ -222,7 +222,7 @@ class printcore(): def pause(self): """Pauses the print, saving the current position. """ - return False if !selft.printing + return False if !self.printing self.paused = True self.printing = False self.print_thread.join() From db88c702ff066052a5541efee143c6dcc6fe5a4c Mon Sep 17 00:00:00 2001 From: Kaz Walker Date: Sun, 17 Feb 2013 14:20:20 -0700 Subject: [PATCH 18/24] [printcore] Switched to use Python if syntax and not Ruby's. --- printcore.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/printcore.py b/printcore.py index 09e8574..e1cbd69 100755 --- a/printcore.py +++ b/printcore.py @@ -222,7 +222,7 @@ class printcore(): def pause(self): """Pauses the print, saving the current position. """ - return False if !self.printing + if !self.printing: return False self.paused = True self.printing = False self.print_thread.join() @@ -231,7 +231,7 @@ class printcore(): def resume(self): """Resumes a paused print. """ - return False if !self.paused + if !self.paused: return False self.paused = False self.printing = True self.print_thread = Thread(target = self._print) From 0f8d170f534ef2831e5516ec0e150a0eef8e4d12 Mon Sep 17 00:00:00 2001 From: Kaz Walker Date: Sun, 17 Feb 2013 14:24:25 -0700 Subject: [PATCH 19/24] Switched to using "not" instead of ! modifier. --- printcore.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/printcore.py b/printcore.py index e1cbd69..c5aa7e7 100755 --- a/printcore.py +++ b/printcore.py @@ -222,7 +222,7 @@ class printcore(): def pause(self): """Pauses the print, saving the current position. """ - if !self.printing: return False + if not self.printing: return False self.paused = True self.printing = False self.print_thread.join() @@ -231,7 +231,7 @@ class printcore(): def resume(self): """Resumes a paused print. """ - if !self.paused: return False + if not self.paused: return False self.paused = False self.printing = True self.print_thread = Thread(target = self._print) From c3f7e4c1aca0a05aab440e0310214f4c42c871b3 Mon Sep 17 00:00:00 2001 From: Kaz Walker Date: Sun, 17 Feb 2013 18:45:56 -0700 Subject: [PATCH 20/24] [printcore] Have pirnt_thread rejoin execution thread once print completes. --- printcore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/printcore.py b/printcore.py index c5aa7e7..3350761 100755 --- a/printcore.py +++ b/printcore.py @@ -289,6 +289,8 @@ class printcore(): self.sentlines = {} self.log = [] self.sent = [] + self.print_thread.join() + self.print_thread = None if self.endcb: #callback for printing done try: self.endcb() From bc478d1d427b88dd9f8055e85dc9eecf70d0402f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Corr=C3=AAa=20da=20Silva=20Sanches?= Date: Tue, 5 Mar 2013 14:53:35 -0300 Subject: [PATCH 21/24] updating download url for windows version of pyreadline --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c5588f..9a1837d 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Download the following, and install in this order: 1. http://python.org/ftp/python/2.7.2/python-2.7.2.msi 2. http://pypi.python.org/packages/any/p/pyserial/pyserial-2.5.win32.exe 3. http://downloads.sourceforge.net/wxpython/wxPython2.8-win32-unicode-2.8.12.0-py27.exe - 4. http://launchpad.net/pyreadline/1.7/1.7/+download/pyreadline-1.7.win32.exe + 4. https://pypi.python.org/packages/any/p/pyreadline/pyreadline-1.7.1.win32.exe 5. http://pyglet.googlecode.com/files/pyglet-1.1.4.zip For the last one, you will need to unpack it, open a command terminal, From c298228ce917a1c72df952e0f2db1246fc4676c7 Mon Sep 17 00:00:00 2001 From: N Oliver Date: Sat, 23 Mar 2013 14:36:15 -0700 Subject: [PATCH 22/24] attempting fix for issue 305 Encountered https://github.com/kliment/Printrun/issues/305#issuecomment-12306186 This edit removed the error, but I don't know what the original output was so I'm not sure if this really _fixes_ it, or is just a band-aid. --- printrun/webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/printrun/webinterface.py b/printrun/webinterface.py index bc0d564..3f51f37 100644 --- a/printrun/webinterface.py +++ b/printrun/webinterface.py @@ -291,7 +291,7 @@ class WebInterface(object): pageText+="
  • Pause
  • \n" for i in gPronterPtr.cpbuttons: - pageText+="
  • "+i[0]+"
  • \n" + pageText+="
  • "+i.label+"
  • \n" #for i in gPronterPtr.custombuttons: # print(str(i)); From 3cfe86f96e7f7425744fb1f927fec4cb9ff0610f Mon Sep 17 00:00:00 2001 From: OliverEngineer Date: Sat, 23 Mar 2013 18:02:18 -0700 Subject: [PATCH 23/24] Adding AJAX to the web interface need to explore CherryPy & websockets, but this quick'n'dirty is a big speed increase --- printrun/webinterface.py | 73 ++++++++++++++++++----------------- {css => web/css}/style.css | 0 web/js/asyncCommand.js | 79 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 35 deletions(-) rename {css => web/css}/style.css (100%) create mode 100644 web/js/asyncCommand.js diff --git a/printrun/webinterface.py b/printrun/webinterface.py index 3f51f37..72c6c59 100644 --- a/printrun/webinterface.py +++ b/printrun/webinterface.py @@ -23,7 +23,7 @@ from printrun.printrun_utils import configfile, imagefile, sharedfile users = {} def PrintHeader(): - return '\n\nPronterface-Web\n\n\n\n' + return '\n\nPronterface-Web\n\n\n\n\n' def PrintMenu(): return '' @@ -284,14 +284,14 @@ class WebInterface(object): pageText = PrintHeader()+self.name+PrintMenu() pageText+="
    \n" pageText+="
    \n" - pageText+="
    • Connect
    • \n" - pageText+="
    • Disconnect
    • \n" - pageText+="
    • Reset
    • \n" - pageText+="
    • Print
    • \n" - pageText+="
    • Pause
    • \n" + pageText+="
      • Connect
      • \n" + pageText+="
      • Disconnect
      • \n" + pageText+="
      • Reset
      • \n" + pageText+="
      • Print
      • \n" + pageText+="
      • Pause
      • \n" for i in gPronterPtr.cpbuttons: - pageText+="
      • "+i.label+"
      • \n" + pageText+="
      • "+i.label+"
      • \n" #for i in gPronterPtr.custombuttons: # print(str(i)); @@ -303,38 +303,38 @@ class WebInterface(object): pageText+="" pageText+='' - pageText+='X Home' - pageText+='Y Home' - pageText+='All Home' - pageText+='Z Home' - pageText+='Y 100' - pageText+='Y 10' - pageText+='Y 1' - pageText+='Y .1' - pageText+='Y -.1' - pageText+='Y -1' - pageText+='Y -10' - pageText+='Y -100' - pageText+='X -100' - pageText+='X 100' - pageText+='X -10' - pageText+='X 10' - pageText+='X -1' - pageText+='X 1' - pageText+='X -.1' - pageText+='X .1' + pageText+='X Home' + pageText+='Y Home' + pageText+='All Home' + pageText+='Z Home' + pageText+='Y 100' + pageText+='Y 10' + pageText+='Y 1' + pageText+='Y .1' + pageText+='Y -.1' + pageText+='Y -1' + pageText+='Y -10' + pageText+='Y -100' + pageText+='X -100' + pageText+='X 100' + pageText+='X -10' + pageText+='X 10' + pageText+='X -1' + pageText+='X 1' + pageText+='X -.1' + pageText+='X .1' pageText+="" pageText+="
    \n" #endxy pageText+="
    " pageText+="" pageText+='' - pageText+='Z 10' - pageText+='Z 1' - pageText+='Z .1' - pageText+='Z -.1' - pageText+='Z -1' - pageText+='Z -10' + pageText+='Z 10' + pageText+='Z 1' + pageText+='Z .1' + pageText+='Z -.1' + pageText+='Z -1' + pageText+='Z -10' pageText+="" #TODO Map Z Moves pageText+="
    \n" #endz @@ -374,11 +374,14 @@ def KillWebInterfaceThread(): cherrypy.engine.exit() def StartWebInterfaceThread(webInterface): - current_dir = os.path.dirname(os.path.abspath(__file__)) + current_dir = os.path.dirname(os.path.abspath(__file__)) cherrypy.config.update({'engine.autoreload_on':False}) cherrypy.config.update(configfile(webInterface.pface.web_config or "http.config")) conf = {'/css/style.css': {'tools.staticfile.on': True, - 'tools.staticfile.filename': sharedfile('css/style.css'), + 'tools.staticfile.filename': sharedfile('web/style.css'), + }, + '/js/asyncCommand.js': {'tools.staticfile.on': True, + 'tools.staticfile.filename': sharedfile('web/js/asyncCommand.js'), }, '/images/control_xy.png': {'tools.staticfile.on': True, 'tools.staticfile.filename': imagefile('control_xy.png'), diff --git a/css/style.css b/web/css/style.css similarity index 100% rename from css/style.css rename to web/css/style.css diff --git a/web/js/asyncCommand.js b/web/js/asyncCommand.js new file mode 100644 index 0000000..f37ec8c --- /dev/null +++ b/web/js/asyncCommand.js @@ -0,0 +1,79 @@ +function pronterfaceWebInterface_setup(){ + pronterfaceWebInterface_attachAsync(); +} + +function pronterfaceWebInterface_attachAsync(){ + + var list = []; + if(document.getElementsByClassName){ + list = document.getElementsByClassName('command'); + }else if(document.getElementsByTagName){ + list = document.getElementsByTagName('a'); + list.concat( document.getElementsByTagName('area') ); + //TODO filter list via checking the className attributes + }else{ + console && console.error && console.error('unable to gather list of elements'); + return false; + } + + for(var i=0; i < list.length; i++){ + list[i].addEventListener && list[i].addEventListener( 'click', function(e){return pronterfaceWebInterface_asyncCommand(null, e);}, true ); + list[i].attachEvent && list[i].attachEvent( 'onclick', function(e){return pronterfaceWebInterface_asyncCommand(null, e);} ); + } + + return true; +} + + +function pronterfaceWebInterface_asyncCommand( urlOrElement, event ){ + + if( ! urlOrElement && event.target) + urlOrElement = event.target; + + var url = null; + if( typeof urlOrElement == 'string' ){ + url = urlOrElement; + }else{ + url = urlOrElement&&urlOrElement.href; + } + + if( typeof url != 'string' ){ + console && console.error && console.error('url not a string', urlOrElement, url); + return true; + } + + var httpRequest; + if (window.XMLHttpRequest) { // Mozilla, Safari, ... + httpRequest = new XMLHttpRequest(); + } else if (window.ActiveXObject) { // IE 8 and older + httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); + } + + if( ! httpRequest ){ + alert('no AJAX available?'); + // follow link + return true; + } + + //onreadystatechange + //onerror + httpRequest.open( 'GET', url, true); + httpRequest.send(null); + + // don't follow link + if( event ){ + event.stopImmediatePropagation && event.stopImmediatePropagation(); + event.defaultPrevented = true; + event.preventDefault && event.preventDefault(); + } + return false; +} + + +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", pronterfaceWebInterface_setup, false); +} else if (document.attachEvent) { + document.attachEvent("onreadystatechange", pronterfaceWebInterface_setup); +} else { + document.onload = pronterfaceWebInterface_setup; +} From 06280e2ba802e301a3e4403ade886e668b8c29a2 Mon Sep 17 00:00:00 2001 From: N Oliver Date: Mon, 25 Mar 2013 16:38:13 -0700 Subject: [PATCH 24/24] Update webinterface.py oops! :'( web/css/style.css --- printrun/webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/printrun/webinterface.py b/printrun/webinterface.py index 72c6c59..bb9690b 100644 --- a/printrun/webinterface.py +++ b/printrun/webinterface.py @@ -378,7 +378,7 @@ def StartWebInterfaceThread(webInterface): cherrypy.config.update({'engine.autoreload_on':False}) cherrypy.config.update(configfile(webInterface.pface.web_config or "http.config")) conf = {'/css/style.css': {'tools.staticfile.on': True, - 'tools.staticfile.filename': sharedfile('web/style.css'), + 'tools.staticfile.filename': sharedfile('web/css/style.css'), }, '/js/asyncCommand.js': {'tools.staticfile.on': True, 'tools.staticfile.filename': sharedfile('web/js/asyncCommand.js'),