pythonif-statementfor-loopg-code

Edit G code Line by Line


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.


Solution

  • 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)