From d1442a49008d1a3a7030b6b32898f11e7795b09b Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Sat, 4 Jun 2016 16:45:52 -0400 Subject: [PATCH] Cleaned up G-code parser. Fixed dwell command. Fixes #184. --- FlatCAMObj.py | 10 +++++- camlib.py | 84 ++++++++++++++------------------------------------- 2 files changed, 32 insertions(+), 62 deletions(-) diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 199c63f3..a3d7610f 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -1038,6 +1038,11 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): self.export_gcode(filename, preamble=preamble, postamble=postamble) def dwell_generator(self, lines): + """ + Inserts "G4 P..." instructions after spindle-start + instructions (M03 or M04). + + """ log.debug("dwell_generator()...") @@ -1060,7 +1065,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): # If start spindle, buffer a G4. if m3m4re.search(line): log.debug("Found M03/4") - bufline = "G4 {}\n".format(self.options['dwelltime']) + bufline = "G4 P{}\n".format(self.options['dwelltime']) yield line @@ -1070,10 +1075,13 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): lines = StringIO(self.gcode) + ## Post processing + # Dwell? if self.options['dwell']: log.debug("Will add G04!") lines = self.dwell_generator(lines) + ## Write with open(filename, 'w') as f: f.write(preamble + "\n") diff --git a/camlib.py b/camlib.py index 9b4f02f3..d29367d1 100644 --- a/camlib.py +++ b/camlib.py @@ -9,6 +9,7 @@ #from scipy import optimize #import traceback +from cStringIO import StringIO from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos, dot, float32, \ transpose from numpy.linalg import solve, norm @@ -3024,67 +3025,25 @@ class CNCjob(Geometry): self.gcode += "G00 X0Y0\n" self.gcode += "M05\n" # Spindle stop - def pre_parse(self, gtext): + @staticmethod + def codes_split(gline): """ - Separates parts of the G-Code text into a list of dictionaries. - Used by ``self.gcode_parse()``. + Parses a line of G-Code such as "G01 X1234 Y987" into + a dictionary: {'G': 1.0, 'X': 1234.0, 'Y': 987.0} - :param gtext: A single string with g-code + :param gline: G-Code line string + :return: Dictionary with parsed line. """ - log.debug("pre_parse()") + command = {} - # Units: G20-inches, G21-mm - units_re = re.compile(r'^G2([01])') + match = re.search(r'^\s*([A-Z])\s*([\+\-\.\d\s]+)', gline) + while match: + command[match.group(1)] = float(match.group(2).replace(" ", "")) + gline = gline[match.end():] + match = re.search(r'^\s*([A-Z])\s*([\+\-\.\d\s]+)', gline) - # TODO: This has to be re-done - gcmds = [] - lines = gtext.split("\n") # TODO: This is probably a lot of work! - for line in lines: - # Clean up - line = line.strip() - - # Remove comments - # NOTE: Limited to 1 bracket pair - op = line.find("(") - cl = line.find(")") - #if op > -1 and cl > op: - if cl > op > -1: - #comment = line[op+1:cl] - line = line[:op] + line[(cl+1):] - - # Units - match = units_re.match(line) - if match: - self.units = {'0': "IN", '1': "MM"}[match.group(1)] - - # Parse GCode - # 0 4 12 - # G01 X-0.007 Y-0.057 - # --> codes_idx = [0, 4, 12] - codes = "NMGXYZIJFPST" - codes_idx = [] - i = 0 - for ch in line: - if ch in codes: - codes_idx.append(i) - i += 1 - n_codes = len(codes_idx) - if n_codes == 0: - continue - - # Separate codes in line - parts = [] - for p in range(n_codes - 1): - parts.append(line[codes_idx[p]:codes_idx[p+1]].strip()) - parts.append(line[codes_idx[-1]:].strip()) - - # Separate codes from values - cmds = {} - for part in parts: - cmds[part[0]] = float(part[1:]) - gcmds.append(cmds) - return gcmds + return command def gcode_parse(self): """ @@ -3097,11 +3056,7 @@ class CNCjob(Geometry): # Results go here geometry = [] - - # TODO: Merge into single parser? - gobjs = self.pre_parse(self.gcode) - log.debug("gcode_parse(): pre_parse() done.") # Last known instruction current = {'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'G': 0} @@ -3110,7 +3065,14 @@ class CNCjob(Geometry): path = [(0, 0)] # Process every instruction - for gobj in gobjs: + for line in StringIO(self.gcode): + + gobj = self.codes_split(line) + + ## Units + if 'G' in gobj and (gobj['G'] == 20.0 or gobj['G'] == 21.0): + self.units = {20.0: "IN", 21.0: "MM"}[gobj['G']] + continue ## Changing height if 'Z' in gobj: @@ -3154,7 +3116,7 @@ class CNCjob(Geometry): center = [gobj['I'] + current['X'], gobj['J'] + current['Y']] radius = sqrt(gobj['I']**2 + gobj['J']**2) start = arctan2(-gobj['J'], -gobj['I']) - stop = arctan2(-center[1]+y, -center[0]+x) + stop = arctan2(-center[1] + y, -center[0] + x) path += arc(center, radius, start, stop, arcdir[current['G']], self.steps_per_circ)