Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
maculata opened this issue Apr 11, 2018 · 6 comments

Comments

@maculata
Copy link

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

@fragmuffin
Copy link
Owner

@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
Copy link
Owner

linuxcnc specs:

@maculata
Copy link
Author

(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
Copy link
Owner

@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
Copy link
Owner

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

  • Each dialect may have a different set of pre-defined parameters (such as #<_rpm>, #<_x>)
  • Parameter values belong to the virtual machine.

Expressions in different Dialects

  • Different sytnax : could other dialects use {} brackets as opposed to [], (TODO: research)
  • Support switchable : do some dialects not support praameters, syntax may conflict with another dialect feature.

Line Processor Idea

  • Deciphering parameters & expressions may be the role of a "line processing" class instance
    • each dialect may employ said processor, an altered processor, or none at all.
    • multiple processors per interpreter are in an ordered list (allowing different dialects to prioritise differently)
    • existing line decoding may be moved to this design, expression processing pre-processed

Ideas welcome

@BillyBumbler
Copy link

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!

@fragmuffin fragmuffin mentioned this issue May 30, 2019
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants