fragmuffin / pygcode

GCODE Parser for Python
GNU General Public License v3.0
123 stars 38 forks source link

Support for variables (#3), math (#3*5+#<gcb>) and Brackets ( X[56*#<scale>] ) #12

Open maculata opened 6 years ago

maculata commented 6 years ago

This appears to be missing.... did I miss something? Thanks

fragmuffin commented 6 years ago

@maculata No, you're not missing anything, the current version of pygcode doesn't support this.

Do you have some sample gcode you could post here? Also: is there an online specification you can link.

I'd imagine this would be done by pre-processing the gcode, which may be out of scope of pygcode, but I'll look into it.

fragmuffin commented 6 years ago

linuxcnc specs:

maculata commented 6 years ago

(Mill - generated: Sat Apr 7 22:26:21 2018 ) (Version: 2.0.2) (Description = engrave02) (Material = Aluminum : -any-)

(Units = G20 inches) (Work Offset = G54) (Tool Number = 101) (Tool Description = Drag Knife) (Tool Diameter = 0.0010 inches) (Spindle RPM = 0) (Text to engrave = 0123) (Font = FONT.TTF) (Font height = 0.5000) (Scale = 0.000117564072419) (X Base Location = 0.0000) (Justification Setting = left) (Y Base Location = 1.3500) (Feed Rate = 20.0 inches/minute)

(Z Clear Location = 0.0800) (Z Start Location = 0.0250)

(Z Depth of Cut = 0.0300) (Ammend Serial Number = no)

(----- Start of G-code -----) ()

G17 G90 (XY Plane, Absolute Distance Mode)

G64 P 0.0050 Q 0.0000 (Path Blending)

(font: FONT.TTF) (text: 0123)

1=0.055000 (SafeHeight)

2=0.030000 (Depth of Cut)

3=0.000118 (XY Scale)

4=20.000000 (Feed)

5=0.000000 (X offset)

6=1.350000 (Y offset)

7=0.025000 (Z offset)

(Fill Line Scale = 24) (Fill = 0)

G17 (set current plane to XY) G90 (set Absolute Distance Mode) G20 (set units to inches) G54 (set current work offset)

G30 (move to preset G30) T101 M6 G43 H101 M5 (0 RPM. turn off spindle) M8 (set coolant to ON)

G00 X [#5] Y [#6] G00 Z [#1+#7] (start of symbol 0) (starting X offset: 0) G00 Z [#1+#7] G00 X [1393#3+#5] Y [2077#3+#6] (moveto) G01 Z [0-#2+#7] F#4 G5.1 X[1577#3+#5] Y[1716#3+#6] I[139#3] J[-156#3] G5.1 X[1597#3+#5] Y[1159#3+#6] I[20#3] J[-90#3] G5.1 X[1602#3+#5] Y[991#3+#6] I[0#3] J[-69#3] G5.1 X[1602#3+#5] Y[815#3+#6] I[4#3] J[-90#3] G5.1 X[1585#3+#5] Y[655#3+#6] I[-5#3] J[-106#3] G5.1 X[1307#3+#5] Y[180#3+#6] I[-69#3] J[-299#3] G5.1 X[795#3+#5] Y[4#3+#6] I[-209#3] J[-176#3] G5.1 X[313#3+#5] Y[176#3+#6] I[-271#3] J[0#3] G5.1 X[37#3+#5] Y[614#3+#6] I[-211#3] J[172#3] G5.1 X[12#3+#5] Y[807#3+#6] I[-21#3] J[82#3] G5.1 X[12#3+#5] Y[1016#3+#6] I[-4#3] J[86#3] G5.1 X[16#3+#5] Y[1221#3+#6] I[4#3] J[139#3] G5.1 X[29#3+#5] Y[1647#3+#6] I[0#3] J[335#3] G5.1 X[164#3+#5] Y[2007#3+#6] I[28#3] J[213#3] G5.1 X[721#3+#5] Y[2331#3+#6] I[217#3] J[299#3] G5.1 X[1393#3+#5] Y[2077#3+#6] I[418#3] J[28#3] G00 Z [#1+#7] G00 X [365#3+#5] Y [1520#3+#6] (moveto) G01 Z [0-#2+#7] F#4 G5.1 X[360#3+#5] Y[1212#3+#6] I[-5#3] J[-54#3] G5.1 X[373#3+#5] Y[721#3+#6] I[0#3] J[-417#3] G5.1 X[530#3+#5] Y[436#3+#6] I[28#3] J[-172#3] G5.1 X[831#3+#5] Y[328#3+#6] I[129#3] J[-112#3] G5.1 X[975#3+#5] Y[365#3+#6] I[54#3] J[0#3] G5.1 X[1253#3+#5] Y[811#3+#6] I[246#3] J[98#3] G01 X [1253#3+#5] Y [1126#3+#6] (lineto) G5.1 X[1225#3+#5] Y[1647#3+#6] I[0#3] J[398#3] G5.1 X[897#3+#5] Y[1995#3+#6] I[-70#3] J[295#3] G5.1 X[717#3+#5] Y[2003#3+#6] I[-123#3] J[24#3] G5.1 X[365#3+#5] Y[1520#3+#6] I[-324#3] J[-98#3] (symbol extents: X = 365 to 1393, Y = 1126 to 2077) (symbol advance: X = 1815, Y = 0) (start of symbol 1) (starting X offset: 1815) G00 Z [#1+#7] G00 X [2761#3+#5] Y [2236#3+#6] (moveto) G01 Z [0-#2+#7] F#4 G5.1 X[2802#3+#5] Y[1884#3+#6] I[41#3] J[-65#3] G01 X [2802#3+#5] Y [918#3+#6] (lineto) G5.1 X[2798#3+#5] Y[766#3+#6] I[0#3] J[-41#3] G5.1 X[2798#3+#5] Y[606#3+#6] I[-4#3] J[-98#3] G5.1 X[2962#3+#5] Y[332#3+#6] I[12#3] J[-217#3] G5.1 X[3105#3+#5] Y[315#3+#6] I[20#3] J[-4#3] G01 X [3212#3+#5] Y [307#3+#6] (lineto) G5.1 X[3404#3+#5] Y[193#3+#6] I[164#3] J[-16#3] G5.1 X[3392#3+#5] Y[94#3+#6] I[17#3] J[-58#3] G5.1 X[3265#3+#5] Y[12#3+#6] I[-33#3] J[-61#3] G5.1 X[2933#3+#5] Y[0#3+#6] I[-57#3] J[-12#3] G01 X [2352#3+#5] Y [0#3+#6] (lineto) G5.1 X[1938#3+#5] Y[8#3+#6] I[-295#3] J[0#3] G5.1 X[1852#3+#5] Y[53#3+#6] I[-49#3] J[4#3] G5.1 X[1819#3+#5] Y[143#3+#6] I[-37#3] J[41#3] G5.1 X[1962#3+#5] Y[295#3+#6] I[8#3] J[123#3] G5.1 X[2192#3+#5] Y[307#3+#6] I[58#3] J[12#3] G5.1 X[2261#3+#5] Y[319#3+#6] I[20#3] J[0#3] G5.1 X[2417#3+#5] Y[492#3+#6] I[119#3] J[46#3] G5.1 X[2438#3+#5] Y[733#3+#6] I[21#3] J[69#3] G01 X [2438#3+#5] Y [1094#3+#6] (lineto) G5.1 X[2425#3+#5] Y[1507#3+#6] I[0#3] J[364#3] G5.1 X[2327#3+#5] Y[1651#3+#6] I[-20#3] J[82#3] G5.1 X[2192#3+#5] Y[1831#3+#6] I[-135#3] J[110#3] G5.1 X[2433#3+#5] Y[2253#3+#6] I[0#3] J[192#3] G5.1 X[2597#3+#5] Y[2331#3+#6] I[82#3] J[78#3] G5.1 X[2761#3+#5] Y[2236#3+#6] I[107#3] J[0#3] (symbol extents: X = 2352 to 3212, Y = 0 to 2236) (symbol advance: X = 1741, Y = 0) (start of symbol 2) (starting X offset: 3556) G00 Z [#1+#7] G00 X [4994#3+#5] Y [1982#3+#6] (moveto) G01 Z [0-#2+#7] F#4 G5.1 X[5088#3+#5] Y[1573#3+#6] I[114#3] J[-180#3] G5.1 X[4949#3+#5] Y[1237#3+#6] I[-16#3] J[-201#3] G5.1 X[4650#3+#5] Y[1016#3+#6] I[-82#3] J[-90#3] G5.1 X[4375#3+#5] Y[852#3+#6] I[-140#3] J[-82#3] G5.1 X[4121#3+#5] Y[655#3+#6] I[-155#3] J[-98#3] G5.1 X[3998#3+#5] Y[446#3+#6] I[-123#3] J[-123#3] G5.1 X[4371#3+#5] Y[299#3+#6] I[0#3] J[-147#3] G01 X [4551#3+#5] Y [299#3+#6] (lineto) G5.1 X[4658#3+#5] Y[303#3+#6] I[-20#3] J[0#3] G5.1 X[4736#3+#5] Y[303#3+#6] I[0#3] J[0#3] G5.1 X[5104#3+#5] Y[205#3+#6] I[315#3] J[0#3] G5.1 X[5104#3+#5] Y[94#3+#6] I[29#3] J[-53#3] G5.1 X[4871#3+#5] Y[-4#3+#6] I[-45#3] J[-86#3] G5.1 X[4719#3+#5] Y[-4#3+#6] I[-58#3] J[-4#3] G5.1 X[4572#3+#5] Y[0#3+#6] I[-102#3] J[4#3] G01 X [3998#3+#5] Y [0#3+#6] (lineto) G5.1 X[3646#3+#5] Y[33#3+#6] I[-282#3] J[0#3] G5.1 X[3544#3+#5] Y[250#3+#6] I[-115#3] J[53#3] G5.1 X[4007#3+#5] Y[999#3+#6] I[37#3] J[450#3] G5.1 X[4285#3+#5] Y[1188#3+#6] I[127#3] J[91#3] G5.1 X[4568#3+#5] Y[1356#3+#6] I[57#3] J[37#3] G5.1 X[4740#3+#5] Y[1622#3+#6] I[164#3] J[123#3] G5.1 X[4649#3+#5] Y[1867#3+#6] I[8#3] J[139#3] G5.1 X[4408#3+#5] Y[1999#3+#6] I[-98#3] J[107#3] G5.1 X[4129#3+#5] Y[1946#3+#6] I[-156#3] J[24#3] G5.1 X[3978#3+#5] Y[1815#3+#6] I[-8#3] J[-4#3] G5.1 X[3810#3+#5] Y[1729#3+#6] I[-98#3] J[-86#3] G5.1 X[3728#3+#5] Y[1757#3+#6] I[-41#3] J[0#3] G5.1 X[3671#3+#5] Y[1905#3+#6] I[-74#3] J[53#3] G5.1 X[4064#3+#5] Y[2277#3+#6] I[37#3] J[229#3] G5.1 X[4244#3+#5] Y[2327#3+#6] I[110#3] J[45#3] G5.1 X[4994#3+#5] Y[1982#3+#6] I[512#3] J[32#3] (symbol extents: X = 3998 to 4994, Y = 0 to 1982) (symbol advance: X = 1753, Y = 0) (start of symbol 3) (starting X offset: 5309) G00 Z [#1+#7] G00 X [6206#3+#5] Y [2277#3+#6] (moveto) G01 Z [0-#2+#7] F#4 G5.1 X[6607#3+#5] Y[2003#3+#6] I[250#3] J[-69#3] G5.1 X[6759#3+#5] Y[1536#3+#6] I[152#3] J[-205#3] G5.1 X[6685#3+#5] Y[1217#3+#6] I[0#3] J[-197#3] G5.1 X[6587#3+#5] Y[1061#3+#6] I[-49#3] J[-78#3] (this line fails) G5.1 X[6554#3+#5] Y[885#3+#6] I[-45#3] J[-90#3] G5.1 X[6648#3+#5] Y[705#3+#6] I[4#3] J[-33#3] G5.1 X[6722#3+#5] Y[541#3+#6] I[45#3] J[-74#3] G5.1 X[6767#3+#5] Y[70#3+#6] I[45#3] J[-144#3] G5.1 X[6743#3+#5] Y[-344#3+#6] I[0#3] J[-275#3] G5.1 X[6587#3+#5] Y[-692#3+#6] I[-37#3] J[-209#3] G5.1 X[5964#3+#5] Y[-971#3+#6] I[-238#3] J[-279#3] G5.1 X[5399#3+#5] Y[-725#3+#6] I[-348#3] J[0#3] G5.1 X[5305#3+#5] Y[-537#3+#6] I[-94#3] J[107#3] G5.1 X[5346#3+#5] Y[-430#3+#6] I[0#3] J[62#3] G5.1 X[5448#3+#5] Y[-385#3+#6] I[41#3] J[45#3] G5.1 X[5612#3+#5] Y[-463#3+#6] I[62#3] J[0#3] G5.1 X[5760#3+#5] Y[-586#3+#6] I[74#3] J[-61#3] G5.1 X[6022#3+#5] Y[-647#3+#6] I[114#3] J[-65#3] G5.1 X[6165#3+#5] Y[-602#3+#6] I[53#3] J[0#3] G5.1 X[6390#3+#5] Y[-303#3+#6] I[172#3] J[82#3] G5.1 X[6423#3+#5] Y[86#3+#6] I[33#3] J[131#3] G5.1 X[6407#3+#5] Y[356#3+#6] I[0#3] J[193#3] G5.1 X[6288#3+#5] Y[594#3+#6] I[-25#3] J[123#3] G5.1 X[6149#3+#5] Y[692#3+#6] I[-45#3] J[53#3] G5.1 X[5973#3+#5] Y[762#3+#6] I[-135#3] J[45#3] G5.1 X[5854#3+#5] Y[893#3+#6] I[-107#3] J[53#3] G5.1 X[5973#3+#5] Y[1081#3+#6] I[-21#3] J[123#3] G5.1 X[6206#3+#5] Y[1167#3+#6] I[118#3] J[41#3] G5.1 X[6411#3+#5] Y[1540#3+#6] I[205#3] J[115#3] G5.1 X[6218#3+#5] Y[1917#3+#6] I[0#3] J[238#3] G5.1 X[5985#3+#5] Y[1991#3+#6] I[-102#3] J[74#3] G5.1 X[5706#3+#5] Y[1892#3+#6] I[-160#3] J[0#3] G5.1 X[5518#3+#5] Y[1737#3+#6] I[-61#3] J[-61#3] G5.1 X[5456#3+#5] Y[1724#3+#6] I[-33#3] J[-13#3] G5.1 X[5356#3+#5] Y[1763#3+#6] I[-57#3] J[0#3] G5.1 X[5313#3+#5] Y[1860#3+#6] I[-43#3] J[39#3] G5.1 X[5420#3+#5] Y[2064#3+#6] I[0#3] J[86#3] G5.1 X[5604#3+#5] Y[2212#3+#6] I[90#3] J[103#3] G5.1 X[5870#3+#5] Y[2306#3+#6] I[164#3] J[82#3] G5.1 X[6206#3+#5] Y[2277#3+#6] I[156#3] J[21#3]

G0 Z [#1+#7]

M9 (stop coolant) M5 (stop spindle)

G30 Z [#1+#7] (move in Z only to preset G30) G30 (move to preset G30) M30 (end program)

fragmuffin commented 6 years ago

@maculata That looks quite simple, you're not using any dynamic variables like #<_rpm>, so you can pre-process the file to remove parameters.

I think this does the job:

#!/usr/bin/env python

import sys
import re
filename = sys.argv[1]

regex_paramset = re.compile(r'^#(?P<name>\w+)=(?P<value>[^\s\(]+)')
regex_expression = re.compile(r'\[(?P<exp>[^\]]+)\]')
regex_param = re.compile(r'#(?P<name>\w+)')

params = {}

with open(filename, 'r') as fh:
    for line in fh:
        # parameter set
        match = regex_paramset.search(line)
        if match:
            params[match.group('name')] = float(match.group('value'))
            sys.stdout.write("(%s)\n" % line.rstrip('\n'))
            continue # don't print line

        # evaluate expressions (with parameter substitution)
        for match in reversed(list(regex_expression.finditer(line))):
            exp_str = match.group('exp')
            for m in reversed(list(regex_param.finditer(exp_str))):
                exp_str = exp_str[:m.start()] + ("%g" % params[m.group('name')]) + exp_str[m.end():]
            exp_result = eval(exp_str)
            line = line[:match.start()] + ("%g" % exp_result) + line[match.end():]

        sys.stdout.write(line)

so if the content you've pasted above is in a file called input.g, we can create a pre-processed file called output.g with:

./pre-process.py input.g > output.g

I haven't tested it, and it has limits, but it should do the trick for now as a workaround.

fragmuffin commented 6 years ago

To clarify: I agree with @maculata ; parameters should be deciphered by pygcode interpreter where possible, and shouldn't need pre-processing.

Furthermore, the pre-processing done by the above script is something pygcode should do, namely a new feature for pygcode-norm

I'm currently planning dialects for pygcode, variables and expressions will be a part of that.

Design

stuff to think about, mostly brain-storming

Variables in different Dialects

Expressions in different Dialects

Line Processor Idea

Ideas welcome

BillyBumbler commented 6 years ago

Good day folks!

This is quite the conundrum.... And is why G-code files are not universal and cannot be shared across most machines. I program CNC machines in an industrial manufacturing environment, and this was very disappointing when I first learned it. Every machine controller can have different capabilities and can choose to interpret g-code files in it's own way. This is why your CAM software of choice requires a "Post" file to help it generate a G-code file that is compatible with your machine. Many times, the only way to utilize some features of the controller is to hand edit the G-code file yourself (or write your own plug-in for your CAM software). The machine I use at work uses an Osai 10 Series controller which interprets code differently than a Haas machine would, for example. It's easy for a hobbyist to assume that G-Code files are all interchangeable because most of them use the same software (Linux CNC or Mach 3) in their controllers, but this is not the case in the rest of the CNC world.

Basically, the "dialects" you speak of will be a configuration file of sorts to help pygcode emulate certain controllers, is that right? Or perhaps you could choose to emulate a LINUX CNC or MACH 3 based machine with the dialects being different post file configurations? I look forward to see how this goes!