2

I want to read a g code file line by line, and based off of a comment at the end, do an action to it. The g code is coming out of Slic3r. Some example lines in no particular order look like this:

G1 Z0.242 F7800.000 ; move to next layer (0)
G1 E-2.00000 F2400.00000 ; retract
G1 X0.000 Y30.140 F7800.000 ; move to first skirt point
G1 E0.00000 F2400.00000 ; unretract
G1 X-53.493 Y30.140 E2.14998 F1800.000 ; skirt
G1 X57.279 Y-37.776 E22.65617 ; perimeter
G1 X-52.771 Y-38.586 E56.83128 ; infill

The comment always starts with a semicolon and also uses consistent terms, such as perimeter or infill. Ideally the script would read the line, search for the particular comment case, perform an action based on that, then update the file, and then go to the next line. I am somewhat new to python, so I know that this would be done with a for-loop with nested if statements, however I am not sure how to set up the architecture to be based of those key terms.

paperstsoap
  • 335
  • 4
  • 13

1 Answers1

2

I am not sure what you want to modify exactly, so I chose to add the word '(up)' in front of a comment that indicates retraction, and '(down)' in front of a comment that indicates unretraction.

file_name = "file.gcode"  # put your filename here

with open(file_name, 'r+') as f:

    new_code = ""            # where the new modified code will be put
    content = f.readlines()  # gcode as a list where each element is a line 

    for line in content: 
        gcode, comment = line.strip('\n').split(";")  # seperates the gcode from the comments 
        if 'unretract' in comment:  
            comment = ' (down) ' + comment
        elif 'retract' in comment:
            comment = ' (up)' + comment

        new_code += gcode + ';' + comment + '\n'  # rebuild the code from the modified pieces

    f.seek(0)           # set the cursor to the beginning of the file
    f.write(new_code)   # write the new code over the old one

The file contents would now be:

G1 Z0.242 F7800.000 ; move to next layer (0)
G1 E-2.00000 F2400.00000 ; (up) retract
G1 X0.000 Y30.140 F7800.000 ; move to first skirt point
G1 E0.00000 F2400.00000 ; (down) unretract
G1 X-53.493 Y30.140 E2.14998 F1800.000 ; skirt
G1 X57.279 Y-37.776 E22.65617 ; perimeter
G1 X-52.771 Y-38.586 E56.83128 ; infill

If you want to modify the gcode instead, lets say replace the first letter by a 'U' if the comment indicates retraction, and 'D' if the comment indicates unretraction, you just have to replace this:

if 'unretract' in comment:  
    comment = ' (down) ' + comment
elif 'retract' in comment:
    comment = ' (up)' + comment

By this:

if 'unretract' in comment:  
    gcode = 'D' + gcode[1:]
elif 'retract' in comment:
    gcode = 'U' + gcode[1:]

New file contents:

G1 Z0.242 F7800.000 ; move to next layer (0)
U1 E-2.00000 F2400.00000 ; retract
G1 X0.000 Y30.140 F7800.000 ; move to first skirt point
D1 E0.00000 F2400.00000 ; unretract
G1 X-53.493 Y30.140 E2.14998 F1800.000 ; skirt
G1 X57.279 Y-37.776 E22.65617 ; perimeter
G1 X-52.771 Y-38.586 E56.83128 ; infill

I hope this helps !


EDIT

To answer your request to get the X, Y, and F values, here is the updated script to store these values:

file_name = "file.gcode"  # put your filename here

    with open(file_name, 'r+') as f:

        coordinates = []
        content = f.readlines()  # gcode as a list where each element is a line 

        for line in content: 
            gcode, comment = line.strip('\n').split(";")  
            coordinate_set = {}
            if 'retract' not in comment and 'layer' not in comment:
                for num in gcode.split()[1:]:
                    coordinate_set[num[:1]] = float(num[1:])
                coordinates.append(coordinate_set)

If you print(coordinates) you get:

[{'X': 0.0, 'F': 7800.0, 'Y': 30.14}, {'E': 2.14998, 'X': -53.493, 'F': 1800.0, 'Y': 30.14}, {'E': 22.65617, 'X': 57.279, 'Y': -37.776}, {'E': 56.83128, 'X': -52.771, 'Y': -38.586}]

EDIT 2

Script 1:

file_name = "file.gcode"

with open(file_name, 'r+') as f:

    new_code = ""            
    content = f.readlines() 

    for line in content: 
        if ';' in line:
            try:
                gcode, comment = line.strip('\n').split(";")
            except:
                print('ERROR\n', line)
        else:  # when there are no comments
            gcode = line.strip('\n')
            comment = ""

        if 'unretract' in comment:  
            comment = ' (down) ' + comment
        elif 'retract' in comment:
            comment = ' (up)' + comment

        if comment != "":
            new_code += gcode + ';' + comment + '\n' 
        else:  # when there are no comments
            new_code += gcode + '\n'

    f.seek(0)           
    f.write(new_code) 

Script 2:

file_name = "file.gcode" 

with open(file_name, 'r+') as f:

    coordinates = []
    content = f.readlines() 

    for line in content:
        if ';' in line:
            try:
                gcode, comment = line.strip('\n').split(";")
            except:
                print(' ERROR \n', line, '\n')
        else:
            gcode = line.strip('\n')
            comment = ""

        coordinate_set = {}
        if 'retract' not in comment and 'layer' not in comment and gcode:
            for num in gcode.split()[1:]:
                coordinate_set[num[:1]] = float(num[1:])
            coordinates.append(coordinate_set)

Some lines where Slic3r does some weird text do not work, this is what the ERROR printed thing is. Also, I recommend you don't try and print all of the coordinates, as this made Python crash. If you want to see the totality of the coordinates, you can paste them on a seperate .txt file, as such:

with ('coordinates.txt', 'w') as f2:
    f2.write(coordinates)

EDIT 3

Updated scripts to work with comments in parentheses.

Script 1:

file_name = "file.gcode"

with open(file_name, 'r+') as f:

    new_code = ""
    content = f.readlines()

    for line in content:
        if ';' in line:
            try:
                gcode, comment = line.strip('\n').split(";")
            except:
                print('ERROR\n', line)
        else:  # when there are no comments
            gcode = line.strip('\n')
            comment = ""

        if 'unretract' in comment:
            comment = ' (down) ' + comment
        elif 'retract' in comment:
            comment = ' (up)' + comment

        if comment != "":
            new_code += gcode + ';' + comment + '\n'
        else:  # when there are no comments
            new_code += gcode + '\n'

    f.seek(0)
    f.write(new_code)

Script 2:

file_name = "3Samples_0skin_3per.gcode"  # put your filename here

with open(file_name, 'r+') as f:

    coordinates = []
    content = f.readlines()

    for line in content:
        if ';' in line:
            try:
                gcode, comment = line.strip('\n').split(";")
            except:
                print(' ERROR 1: \n', line, '\n') 
        elif '(' in line:
            try:
                gcode, comment = line.strip('\n').strip(')').split("(")
            except:
                print('ERROR 2: \n', line, '\n')
        else:
            gcode = line.strip('\n')
            comment = ""

        coordinate_set = {}
        if 'retract' not in comment and 'layer' not in comment and gcode:
            for num in gcode.split()[1:]:
                if len(num) > 1:
                    try:
                        coordinate_set[num[:1]] = float(num[1:])
                    except:
                        print('ERROR 3: \n', gcode, '\n')
            coordinates.append(coordinate_set)
TrakJohnson
  • 1,755
  • 2
  • 18
  • 31
  • This is very helpful thank you. I'm am going to have to mess around with this to get it all figured out, but this is exactly the framework I needed to get started. One question I have is, how could I grab the X,Y, and extruder feed values to do some math with? – paperstsoap Aug 03 '16 at 11:30
  • You can split the gcode between spaces, and then remove the letters in front and convert it into float. I'll edit the answer so that its clearer. – TrakJohnson Aug 03 '16 at 12:32
  • This is making sense to me. My last question is, when you pass the gcode file in to the for loop, is it treating it as a string? – paperstsoap Aug 03 '16 at 13:24
  • Not really, `f.readlines()` creates a list, where each element is a line of the text file (and this element is a string). – TrakJohnson Aug 03 '16 at 13:39
  • So I can use all of the list built ins to manipulate the lines correct? – paperstsoap Aug 03 '16 at 15:03
  • Yes, and if you want to manipulate the words in the lines you can use the string built ins. Don't forget to put it as an accepted answer if you're satisfied :) – TrakJohnson Aug 03 '16 at 15:07
  • Awesome. Thank you for the help! – paperstsoap Aug 03 '16 at 15:10
  • I was just trying to run both of these scripts on a full length g code script and I get the same error message: ValueError: need more than 1 value to unpack on the line : gcode, comment = line.strip('\n').split(";"). Do you know what would cause this/ I checked the particular file and it is showing up in full length (approx 200K commands). – paperstsoap Aug 03 '16 at 17:42
  • This would mean that the file is empty / has no comments (no `;` to split ?) I'll try getting some full gcode myself to test (would be even better if you had a link to download yours ?) – TrakJohnson Aug 03 '16 at 18:48
  • That would make sense. In the beginning there are commands that are only 1 thing, such as G21 that sets the units to metric. I am working off the app right now, but I will get a link to you as soon as I can. – paperstsoap Aug 03 '16 at 18:55
  • [link](https://drive.google.com/open?id=0B4nZIkMtM-VmeC1zd2hLd2FfUDQ) Here is a g code file link for google drive. – paperstsoap Aug 03 '16 at 19:10
  • Hmm, seems like I don't have acess – TrakJohnson Aug 03 '16 at 19:20
  • The sharing was turned off for some reason. Try this one, t should work. [link](https://drive.google.com/a/terpmail.umd.edu/file/d/0B4nZIkMtM-VmeC1zd2hLd2FfUDQ/view?usp=sharing) Just as a back up here is a back up link [link](https://www.justbeamit.com/j69ph) – paperstsoap Aug 03 '16 at 19:26
  • Very weird, none of these work. However, I think I have identified the issues (used some Benchy gcode). I had forgotten about: 1. When there is no comment in a line 2. When there only is a comment. I am currently updating the answer to include the corrections, but hopefully that should be good. – TrakJohnson Aug 03 '16 at 20:21
  • I've uploaded the latest fixes, both scripts work for me on a freshly created gcode file from the 3DBenchy model. I hope it works for you ! – TrakJohnson Aug 03 '16 at 20:48
  • I just tried running it on my g code and I am getting an error on both scripts. Script 1 says: ValueError: too many values to unpack on line gcode, comment = line.strip('\n').split(";") after running for about 10 seconds. On script 2 i get the error: ValueError: could not convert string to float :on line coordinate_set[num[:1]] = float(num[1:]). Not sure if it is it is just too big, although it shouldnt be for the first script. The second, I think is getting hung on some of the start up script which looks like this : G162 X Y F6000; home xy axis maximum M132 X Y Z A B; recall stored offsets – paperstsoap Aug 04 '16 at 12:39
  • Im also wondering if the g code flavor you choose makes a difference for this. The way it is formatted changes for say RepG vs Marlin. What flavor did you choose? Where you able to get the file from google drive? – paperstsoap Aug 04 '16 at 12:43
  • For the first script, my bad, I forgot something when copying. Mine now works without errors (on your code). For the second one, there are problems because your code (maybe because its another flavor, I used marlin) has some comments with parentheses, is this something custom ? To fix this we can add a `elif` case where we use `(` instead of `;` to seperate comments from gcode. – TrakJohnson Aug 04 '16 at 13:49
  • Also, what should be done in case there is only an X or a Y and no numbers next to them ? – TrakJohnson Aug 04 '16 at 13:59
  • I see what you are saying. I got the start script from a sailfish tutorial online, and it appears that it recognizes anything inside () or after ; as a comment. I can make that change for consistency. The cases with X Y etc with no numbers is just how in cases where it uses a built in command and needs to be pointed to a variable. Those cases it could just skip them and move on. It's part of the homing sequence which will never be modified. – paperstsoap Aug 04 '16 at 14:04
  • I just found a case that will need to use () and that is when the comment indicates a new layer with ; move to next layer (1) – paperstsoap Aug 04 '16 at 14:07
  • Right, but that normally wont create confusion because its already in a comment seperated with a `;`. I did another edit with the latest scripts which now support parenthese comments. I left the error detection (`try` and `except`), like this if you encounter problems with other gcodes you can easily locate the source. – TrakJohnson Aug 04 '16 at 14:24
  • I just ran both scripts. script 1 returned a print out of ERROR\n as it should, and script 2 ran with no issues. Thank you for all your help! Because this uses a list, if i make different edits via various if statements (such as adding or deleting a line), the order is always preserved correct? – paperstsoap Aug 04 '16 at 14:42
  • Indeed yes, orders in lists are always preserved. You're welcome and good luck with your printing ! – TrakJohnson Aug 04 '16 at 14:50