Exemplo n.º 1
0
def gcode_parseline(index):
    g = v.parsed_gcode[index]

    if g.Command == 'T':
        gcode_process_toolchange(int(g.Command_value), v.total_material_extruded, g.Layer)
        if not v.debug_leaveToolCommands:
            g.move_to_comment("Color Change")
        g.issue_command()
        v.toolchange_processed = True
        return

    if g.fullcommand in [ "M140", "M190", "M73", "M84", "M201", "M204"]:
        g.issue_command()
        return

    if g.fullcommand in [ "M104" , "M109" ]:
        if not v.process_temp or not g.Class in [CLS_TOOL_PURGE, CLS_TOOL_START, CLS_TOOL_UNLOAD]:
            g.add_comment(" Unprocessed temp ")
            g.issue_command()
            v.new_temp = g.get_parameter("S", v.current_temp)
            v.current_temp = v.new_temp
        else:
            v.new_temp = g.get_parameter("S", v.current_temp)
            if v.new_temp >= v.current_temp:
                g.fullcommand = "M109"
                v.temp2_stored_command = g.__str__();
                g.move_to_comment("delayed temp rise until after purge {}-->{}".format(v.current_temp, v.new_temp))
                v.current_temp = v.new_temp
            else:
                v.temp1_stored_command = g.__str__();
                g.move_to_comment("delayed temp drop until after purge {}-->{}".format(v.current_temp,v.new_temp))
                g.issue_command()

        return



    if not g.Class in [CLS_TOOL_PURGE, CLS_TOOL_START, CLS_TOOL_UNLOAD] and v.current_temp != v.new_temp:
        print(v.temp1_stored_command)
        gcode.issue_code(v.temp1_stored_command)
        v.temp1_stored_command = ""

    # fan speed command

    if g.fullcommand == "M107":
        g.issue_command()
        v.saved_fanspeed = 0
        return

    if g.fullcommand == "M106":
        g.issue_command()
        v.saved_fanspeed = g.get_parameter("S", v.saved_fanspeed)
        return

    # flow rate changes have an effect on the filament consumption.  The effect is taken into account for ping generation
    if g.fullcommand == "M221":
        v.extrusion_multiplier = float(g.get_parameter("S", v.extrusion_multiplier * 100)) / 100
        g.issue_command()
        return

    # feed rate changes in the code are removed as they may interfere with the Palette P2 settings
    if g.fullcommand in ["M220"]:
        g.move_to_comment("Feed Rate Adjustments are removed")
        g.issue_command()
        return

    if g.is_movement_command():
        if g.has_X():
            v.previous_purge_keep_x = v.purge_keep_x
            v.purge_keep_x = g.X

        if g.has_Y():
            v.previous_purge_keep_y = v.purge_keep_y
            v.purge_keep_y = g.Y

        v.keep_speed = g.get_parameter("F", v.keep_speed)


    previous_block_class = v.parsed_gcode[max(0, index - 1)].Class
    classupdate = g.Class != previous_block_class

    if classupdate and previous_block_class in [CLS_TOOL_PURGE, CLS_EMPTY]:
        if v.purge_count > 0:
            gcode.issue_code(
                ";>>> Total purge {:4.0f}mm3 - {:4.0f}mm <<<\n".format(purgetower.volfromlength(v.purge_count),
                                                                       v.purge_count))

    if classupdate and g.Class in [CLS_TOOL_PURGE, CLS_EMPTY]:
        v.purge_count = 0

    if classupdate and g.Class == CLS_BRIM and v.side_wipe and v.bigbrain3d_purge_enabled:
        v.side_wipe_length = v.bigbrain3d_prime * v.bigbrain3d_blob_size
        create_sidewipe_BigBrain3D()

    if not v.side_wipe:
        if x_coordinate_in_tower(g.X):
            v.keep_x = g.X
        if y_coordinate_in_tower(g.Y):
            v.keep_y = g.Y

    # remove M900 K0 commands during unload
    if g.Class == CLS_TOOL_UNLOAD:
        if (g.fullcommand == "G4" or (g.fullcommand in ["M900"] and g.get_parameter("K", 0) == 0)):
            g.move_to_comment("tool unload")


    ## ALL SITUATIONS
    ##############################################


    if g.Class in [CLS_TOOL_START, CLS_TOOL_UNLOAD]:

        if g.is_movement_command():
            if v.side_wipe or v.tower_delta or v.full_purge_reduction:
                g.move_to_comment("tool unload")
            else:
                if g.has_Z():
                    g.remove_parameter("X")
                    g.remove_parameter("Y")
                    g.remove_parameter("F")
                    g.remove_parameter("E")
                else:
                    g.move_to_comment("tool unload")

            g.issue_command()
            return

    if g.Class == CLS_TOOL_PURGE and not (v.side_wipe or v.full_purge_reduction):



        if g.is_movement_command() and g.has_E():
            _x = g.get_parameter("X", v.current_position_x)
            _y = g.get_parameter("Y", v.current_position_y)
            # removepositive extrusions while moving into the tower
            if not (coordinate_in_tower(_x, _y) and coordinate_in_tower(v.purge_keep_x, v.purge_keep_y)) and g.E > 0:
                g.remove_parameter("E")

    if v.side_wipe:

        _x = g.get_parameter("X", v.current_position_x)
        _y = g.get_parameter("Y", v.current_position_y)
        if not coordinate_on_bed(_x, _y):
            g.remove_parameter("X")
            g.remove_parameter("Y")

    # top off the purge speed in the tower during tower delta or during no tower processing
    if not v.full_purge_reduction and not v.side_wipe and g.is_movement_command() and g.has_E() and g.has_parameter(
            "F"):
        f = int(g.get_parameter("F", 0))
        if f > v.purgetopspeed:
            g.update_parameter("F", v.purgetopspeed)
            g.add_comment(" prugespeed topped")

    ## SIDEWIPE / FULLPURGEREDUCTION / TOWER DELTA
    ###############################################
    if v.pathprocessing:

        if g.Class == CLS_TONORMAL:
            if not g.is_comment():
                g.move_to_comment("post block processing")
            g.issue_command()
            return

        # remove any commands that are part of the purge tower and still perofrm actions WITHIN the tower

        if g.is_movement_command() and g.Class in [CLS_ENDPURGE, CLS_ENDGRID] and g.has_X() and g.has_Y():
            if coordinate_in_tower(g.X, g.Y):
                g.remove_parameter("X")
                g.remove_parameter("Y")

        ###################################
        # sepcific for FULL_PURGE_REDUCTION
        ###################################

        if v.full_purge_reduction:

            if g.Class == CLS_BRIM_END:
                create_tower_gcode()
                purgetower.purge_generate_brim()

        ###################################
        # sepcific for SIDEWIPE
        ###################################

        if v.side_wipe:

            # side wipe does not need a brim
            if g.Class == CLS_BRIM:
                g.move_to_comment("side wipe - removed")
                g.issue_command()
                return

        #######################################
        # specific for TOWER DELTA
        #######################################

        # changed for version 4.09.0 moves in tower were going all wrong.
        if not v.side_wipe and classupdate and g.Class == CLS_TOOL_PURGE:
            g.issue_command()
            gcode.issue_code("G1 X{} Y{} F8640;\n".format(v.keep_x, v.keep_y))
            v.current_position_x = v.keep_x
            v.current_position_x = v.keep_y

        if v.tower_delta:
            if classupdate and g.Class == CLS_TOOL_PURGE:
                entertower(g.Layer * v.layer_height + v.first_layer_height)
                return

            if classupdate and previous_block_class == CLS_TOOL_PURGE:
                leavetower()

        ################################################################
        # EMPTY GRID SKIPPING CHECK FOR SIDE WIPE/TOWER DELTA/FULLPURGE
        ################################################################
        if g.Class == CLS_EMPTY and "EMPTY GRID START" in g.get_comment():
            if g.Layer < len(v.skippable_layer) and v.skippable_layer[g.Layer]:
                v.towerskipped = True
                remove_previous_move_in_tower()
                if v.tower_delta:
                    v. cur_tower_z_delta += v.layer_height
                    gcode.issue_code(";-------------------------------------\n")
                    gcode.issue_code(";  GRID SKIP --TOWER DELTA {:6.2f}mm\n".format(v.cur_tower_z_delta))
                    gcode.issue_code(";-------------------------------------\n")
            else:
                if "EMPTY GRID START" in g.get_comment() and not v.side_wipe:
                    entertower(g.Layer * v.layer_height + v.first_layer_height)


        # changing from EMPTY to NORMAL
        ###############################
        if (previous_block_class == CLS_ENDGRID) and (g.Class == CLS_NORMAL):
            v.towerskipped = False

        if v.towerskipped:
            if not g.is_comment():
                g.move_to_comment("tower skipped")
            g.issue_command()
            return
    else:
        if classupdate and g.Class in [CLS_TOOL_PURGE, CLS_EMPTY]:

            if v.acc_ping_left <= 0:
                pings.check_accessorymode_first()
            v.enterpurge = True

        if v.enterpurge and g.is_movement_command():

            v.enterpurge = False

            if g.has_X():
                _x = v.previous_purge_keep_x
            else:
                _x = v.purge_keep_x

            if g.has_Y():
                _y = v.previous_purge_keep_y
            else:
                _y = v.purge_keep_y

            if not coordinate_in_tower(_x, _y):
                _x = v.purge_keep_x
                _y = v.purge_keep_y

            if v.retraction == 0:
                purgetower.retract(v.current_tool, 3000)

            gcode.issue_code(
                "G1 X{:.3f} Y{:.3f} F8640; P2PP Inserted to realign\n".format(v.purge_keep_x, v.purge_keep_y))
            v.current_position_x = _x
            v.current_position_x = _y

            if v.temp2_stored_command != "":
                gcode.issue_code(v.temp2_stored_command)
                v.temp2_stored_command = ""

            g.remove_parameter("E")
            if g.get_parameter("X") == _x:
                g.remove_parameter("X")
            if len(g.Parameters) == 0:
                g.move_to_comment("-useless command-")

    if v.tower_delta:
        if g.has_E() and g.Class in [CLS_TOOL_UNLOAD, CLS_TOOL_PURGE]:
            if not inrange(g.X, v.wipe_tower_info['minx'], v.wipe_tower_info['maxx']):
                g.remove_parameter("E")
            if not inrange(g.Y, v.wipe_tower_info['miny'], v.wipe_tower_info['maxy']):
                g.remove_parameter("E")

    # process movement commands
    ###########################

    if not g.has_E():
        g.E = 0

    if v.full_purge_reduction and g.Class == CLS_NORMAL and classupdate:
        purgetower.purge_generate_sequence()

    if g.is_movement_command():

        if v.expect_retract and g.has_X() or g.has_Y():
            if not v.retraction < 0:
                if not g.has_E and g.E < 0:
                    purgetower.retract(v.current_tool)
            v.expect_retract = False


        if v.retract_move and g.is_retract_command():
            # This is going to break stuff, G10 cannot take X and Y, what to do?
            if v.retract_x:
                g.update_parameter("X", v.retract_x)
            else:
                g.remove_parameter("X")
            if v.retract_y:
                g.update_parameter("Y", v.retract_y)
            else:
                g.remove_parameter("Y")
            v.retract_move = False

        v.current_position_x = g.get_parameter("X", v.current_position_x)
        v.current_position_y = g.get_parameter("Y", v.current_position_y)
        v.current_position_z = g.get_parameter("Z", v.current_position_z)

        if g.Class == CLS_BRIM and v.full_purge_reduction:
            g.move_to_comment("replaced by P2PP brim code")
            g.remove_parameter("E")


    if v.side_wipe or v.full_purge_reduction:
        if g.Class in [CLS_TOOL_PURGE, CLS_ENDPURGE, CLS_EMPTY]:
            if g.Layer < len(v.skippable_layer) and v.skippable_layer[g.Layer]:
                g.move_to_comment("skipped purge")
            else:
                v.side_wipe_length += g.E
                g.move_to_comment("side wipe/full purge")

    if v.toolchange_processed:
        if v.side_wipe and g.Class == CLS_NORMAL and classupdate:
            if v.bigbrain3d_purge_enabled:
                create_sidewipe_BigBrain3D()
            else:
                create_side_wipe()
            v.toolchange_processed = False

        if g.Class == CLS_NORMAL:
            gcode.GCodeCommand(";TOOLCHANGE PROCESSED").issue_command()
            v.toolchange_processed = False

    # check here issue with unretract
    #################################

    # g.Comment = " ; - {}".format(v.total_material_extruded)



    if g.is_retract_command():
        if v.retraction <= - (v.retract_length[v.current_tool] - 0.02):
            g.move_to_comment("Double Retract")
        else:
            if g.has_E():
                v.retraction += g.E
            else:
                v.retraction -= 1

    if g.is_unretract_command():
        if g.has_E():
            g.update_parameter("E", min(-v.retraction, g.E))
            v.retraction += g.E
        else:
            v.retraction = 0

    if (g.has_X() or g.has_Y()) and (g.has_E() and g.E > 0) and v.retraction < 0 and abs(v.retraction) > 0.01:
        gcode.issue_code(";fixup retracts\n")
        purgetower.unretract(v.current_tool)
        # v.retracted = False

    g.issue_command()

    ### PING PROCESSING
    ###################

    if v.accessory_mode:
        pings.check_accessorymode_second(g.E)

    if (g.has_E() and g.E > 0) and v.side_wipe_length == 0:
        pings.check_connected_ping()

    v.previous_position_x = v.current_position_x
    v.previous_position_y = v.current_position_y
Exemplo n.º 2
0
def gcode_parseline(index):
    g = v.parsedgcode[index]
    block_class = v.gcodeclass[index]
    previous_block_class = v.gcodeclass[max(0, index - 1)]
    classupdate = block_class != previous_block_class

    if g.Command == 'T':
        gcode_process_toolchange(int(g.Command_value),
                                 v.total_material_extruded, g.Layer)
        g.move_to_comment("Color Change")
        g.issue_command()
        return

    if g.fullcommand in [
            "M104", "M109", "M140", "M190", "M73", "M84", "M201", "M204"
    ]:
        g.issue_command()
        return

    # fan speed command

    if g.fullcommand == "M107":
        g.issue_command()
        v.saved_fanspeed = 0
        return

    if g.fullcommand == "M106":
        g.issue_command()
        v.saved_fanspeed = g.get_parameter("S", v.saved_fanspeed)
        return

    if block_class == CLS_TOOL_UNLOAD and g.fullcommand in [
            "M900"
    ] and g.get_parameter("K", 0) == 0:
        g.move_to_comment("tool unload")

    if g.fullcommand in ["M220"]:
        g.move_to_comment("Flow Rate Adjustments are removed")
        g.issue_command()
        return

    if g.fullcommand == "M221":
        v.extrusion_multiplier = float(
            g.get_parameter("S", v.extrusion_multiplier * 100)) / 100
        g.issue_command()
        return

    if g.is_movement_command():
        if g.has_X():
            v.previous_purge_keep_x = v.purge_keep_x
            v.purge_keep_x = g.X
        if g.has_Y():
            v.previous_purge_keep_y = v.purge_keep_y
            v.purge_keep_y = g.Y

    ## ALL SITUATIONS
    ##############################################
    if block_class in [CLS_TOOL_START, CLS_TOOL_UNLOAD]:

        if g.fullcommand == "G4":
            g.move_to_comment("tool unload")
        if g.is_movement_command():
            if g.has_Z():
                g.remove_parameter("X")
                g.remove_parameter("Y")
                g.remove_parameter("F")
                g.remove_parameter("E")
            else:
                g.move_to_comment("tool unload")

        g.issue_command()
        return

    if block_class == CLS_TOOL_PURGE and not (v.side_wipe
                                              or v.full_purge_reduction):
        if g.is_movement_command():
            _x = g.get_parameter("X", v.current_position_x)
            _y = g.get_parameter("Y", v.current_position_y)
            if not (coordinate_in_tower(_x, _y)
                    and coordinate_in_tower(v.purge_keep_x, v.purge_keep_y)):
                g.remove_parameter("E")

    if not v.side_wipe:
        if g.has_X():
            if v.wipe_tower_info['minx'] <= g.X <= v.wipe_tower_info['maxx']:
                v.keep_x = g.X
        if g.has_Y():
            if v.wipe_tower_info['miny'] <= g.Y <= v.wipe_tower_info['maxy']:
                v.keep_y = g.Y
    elif not x_on_bed(g.X):
        g.remove_parameter("X")

    ## SIDEWIPE / FULLPURGEREDUCTION / TOWER DELTA
    ###############################################
    if v.pathprocessing:

        if block_class == CLS_TONORMAL:

            if not g.is_comment():
                g.move_to_comment("post block processing")
            g.issue_command()
            return

        if flagset(v.parsecomment[index], SPEC_INTOWER):
            if coordinate_in_tower(g.X, g.Y):
                g.remove_parameter("X")
                g.remove_parameter("Y")

        # sepcific for FULL_PURGE_REDUCTION
        if v.full_purge_reduction:

            # get information about the purge tower dimensions
            if block_class == CLS_BRIM and not (g.has_X() and g.has_Y()):
                if g.has_X():
                    purgetower.purge_width = min(
                        purgetower.purge_width,
                        abs(g.X - v.previous_position_x))
                if g.has_Y():
                    purgetower.purge_height = min(
                        purgetower.purge_height,
                        abs(g.Y - v.previous_position_y))

            if block_class == CLS_BRIM_END:
                # generate a purge tower alternative

                _x = v.wipe_tower_info['minx'] + 4 * v.extrusion_width
                _y = v.wipe_tower_info['miny'] + 4 * v.extrusion_width
                _w = v.wipe_tower_info['maxx'] - v.wipe_tower_info[
                    'minx'] - 8 * v.extrusion_width
                _h = v.wipe_tower_info['maxy'] - v.wipe_tower_info[
                    'miny'] - 8 * v.extrusion_width

                purgetower.purge_create_layers(_x, _y, _w, _h)
                # generate og items for the new purge tower
                gui.create_logitem(
                    " Purge Tower :Loc X{:.2f} Y{:.2f}  W{:.2f} H{:.2f}".
                    format(_x, _y, _w, _h))
                gui.create_logitem(
                    " Layer Length Solid={:.2f}mm   Sparse={:.2f}mm".format(
                        purgetower.sequence_length_solid,
                        purgetower.sequence_length_empty))
                # issue the new purge tower
                for i in range(len(purgetower.brimlayer)):
                    purgetower.brimlayer[i].issue_command()
                    if i == 1 and v.retraction:
                        purgetower.unretract(v.current_tool)
                # set the flag to update the post-session retraction move section
                v.retract_move = True
                v.retract_x = purgetower.last_brim_x
                v.retract_y = purgetower.last_brim_y
                # correct the amount of extrusion for the brim

        # sepcific for SIDEWIPE
        if v.side_wipe:

            # side wipe does not need a brim
            if block_class == CLS_BRIM:
                if not g.is_comment():
                    g.move_to_comment("side wipe - removed")
                g.issue_command()
                return

        # entering the purge tower with a delta
        ########################################
        if v.tower_delta:

            if classupdate:

                if block_class == CLS_TOOL_PURGE:
                    g.issue_command()
                    gcode.issue_code("G1 X{} Y{} ;\n".format(
                        v.keep_x, v.keep_y))
                    v.current_position_x = v.keep_x
                    v.current_position_x = v.keep_y
                    entertower(g.Layer * v.layer_height)
                    return

        # going into an empty grid -- check if it should be consolidated
        ################################################################
        if classupdate and block_class in [CLS_FIRST_EMPTY, CLS_EMPTY]:
            if v.skippable_layer[v.layernumber[index]]:
                v.towerskipped = True
                # print("Skipped: {:.3f} now at delta {:.3f}".format(v.current_position_z- v.retract_lift[v.current_tool]+v.layer_height,v.cur_tower_z_delta+v.layer_height))
                remove_previous_move_in_tower()
                if v.tower_delta and "CP EMPTY GRID START" in g.Comment:
                    v.cur_tower_z_delta += v.layer_height
                    gcode.issue_code(
                        ";-------------------------------------\n")
                    gcode.issue_code(
                        ";  GRID SKIP --TOWER DELTA {:6.2f}mm\n".format(
                            v.cur_tower_z_delta))
                    gcode.issue_code(
                        ";-------------------------------------\n")

        # changing from EMPTY to NORMAL
        ###############################
        if (previous_block_class == CLS_ENDGRID) and (block_class
                                                      == CLS_NORMAL):
            v.towerskipped = False

        if v.towerskipped:
            if not g.is_comment():
                g.move_to_comment("tower skipped")
            g.issue_command()
            return
    else:
        if classupdate and block_class in [CLS_TOOL_PURGE, CLS_EMPTY]:
            if v.acc_ping_left <= 0:
                pings.check_accessorymode_first()
            v.enterpurge = True

        if v.enterpurge and g.is_movement_command():
            v.enterpurge = False
            if g.has_X():
                _x = v.previous_purge_keep_x
            else:
                _x = v.purge_keep_x
            if g.has_Y():
                _y = v.previous_purge_keep_y
            else:
                _y = v.purge_keep_y

            if not coordinate_in_tower(_x, _y):
                _x = v.purge_keep_x
                _y = v.purge_keep_y

            gcode.issue_code(
                "G1 X{:.3f} Y{:.3f}; P2PP Inserted to realign\n".format(
                    v.purge_keep_x, v.purge_keep_y))
            v.current_position_x = _x
            v.current_position_x = _y

            g.remove_parameter("E")
            if g.get_parameter("X") == _x:
                g.remove_parameter("X")
            if len(g.Parameters) == 0:
                g.move_to_comment("-useless command-")

    if v.tower_delta:
        if g.has_E() and block_class in [CLS_TOOL_UNLOAD, CLS_TOOL_PURGE]:
            if not inrange(g.X, v.wipe_tower_info['minx'],
                           v.wipe_tower_info['maxx']):
                g.remove_parameter("E")
            if not inrange(g.Y, v.wipe_tower_info['miny'],
                           v.wipe_tower_info['maxy']):
                g.remove_parameter("E")

    # process movement commands
    ###########################

    if not g.has_E():
        g.E = 0

    if v.full_purge_reduction and block_class == CLS_NORMAL and classupdate:
        purgetower.purge_generate_sequence()

    if g.is_movement_command():

        if v.expect_retract and g.has_X() or g.has_Y():
            if not v.retraction < 0:
                if not g.has_E and g.E < 0:
                    purgetower.retract(v.current_tool)
            v.expect_retract = False

        if v.retract_move and g.is_retract_command():
            # This is going to break stuff, G10 cannot take X and Y, what to do?
            if v.retract_x:
                g.update_parameter("X", v.retract_x)
            else:
                g.remove_parameter("X")
            if v.retract_y:
                g.update_parameter("Y", v.retract_y)
            else:
                g.remove_parameter("Y")
            v.retract_move = False

        v.current_position_x = g.get_parameter("X", v.current_position_x)
        v.current_position_y = g.get_parameter("Y", v.current_position_y)
        v.current_position_z = g.get_parameter("Z", v.current_position_z)

        if block_class == CLS_BRIM and v.full_purge_reduction:
            g.move_to_comment("replaced by P2PP brim code")
            g.remove_parameter("E")

    if v.side_wipe or v.full_purge_reduction:
        if block_class in [
                CLS_TOOL_PURGE, CLS_ENDPURGE, CLS_EMPTY, CLS_FIRST_EMPTY
        ]:
            if v.skippable_layer[g.Layer]:
                g.move_to_comment("skipped purge")
            else:
                v.side_wipe_length += g.E
                g.move_to_comment("side wipe/full purge")

    if v.side_wipe and block_class == CLS_NORMAL and classupdate:
        if v.bigbrain3d_purge_enabled:
            create_sidewipe_BigBrain3D()
        else:
            create_side_wipe()

    # check here issue with unretract
    #################################

    # g.Comment = " ; - {}".format(v.total_material_extruded)

    if g.is_retract_command():
        if g.has_E():
            v.retraction += g.E
        else:
            v.retraction -= 1

    if g.is_unretract_command():
        if g.has_E():
            v.retraction = min(0, v.retraction + g.E)
        else:
            v.retraction = 0

    if (g.has_X() or g.has_Y()) and (g.has_E()
                                     and g.E > 0) and v.retraction < 0 and abs(
                                         v.retraction) > 0.01:
        gcode.issue_code(";fixup retracts\n")
        purgetower.unretract(v.current_tool)
        # v.retracted = False

    g.issue_command()

    ### PING PROCESSING
    ###################

    if v.accessory_mode:
        pings.check_accessorymode_second(g.E)

    if (g.has_E() and g.E > 0) and v.side_wipe_length == 0:
        pings.check_connected_ping()

    v.previous_position_x = v.current_position_x
    v.previous_position_y = v.current_position_y
Exemplo n.º 3
0
def parse_gcode_second_pass():
    idx = 0
    intower = False
    purge = False
    total_line_count = len(v.parsed_gcode)
    v.retraction = 0
    v.last_parsed_layer = -1
    v.previous_block_classification = v.parsed_gcode[0][gcode.CLASS]

    # include firmware purge length accounting
    v.total_material_extruded = v.firmwarepurge
    v.material_extruded_per_color[v.current_tool] = v.firmwarepurge

    for process_line_count in range(total_line_count):

        try:
            if process_line_count >= v.layer_end[0]:
                v.last_parsed_layer += 1
                v.layer_end.pop(0)
                v.current_layer_is_skippable = v.skippable_layer[
                    v.last_parsed_layer] and not v.last_parsed_layer == 0
                if v.current_layer_is_skippable:
                    if v.last_parsed_layer == 0:
                        v.cur_tower_z_delta += v.first_layer_height
                    else:
                        v.cur_tower_z_delta += v.layer_height
        except IndexError:
            pass

        g = v.parsed_gcode[idx]

        idx = idx + 1

        # ----- MEMORY MANAGEMENT - when 100K lines are processed, remove the top of the list

        if idx > 100000:
            v.parsed_gcode = v.parsed_gcode[idx:]
            idx = 0

        if process_line_count % 10000 == 0:
            gui.progress_string(50 +
                                50 * process_line_count // total_line_count)

        current_block_class = g[gcode.CLASS]

        # ---- FIRST SECTION HANDLES DELAYED TEMPERATURE COMMANDS ----

        if current_block_class not in [
                CLS_TOOL_PURGE, CLS_TOOL_START, CLS_TOOL_UNLOAD
        ] and v.current_temp != v.new_temp:
            gcode.issue_code(v.temp1_stored_command)
            v.temp1_stored_command = ""

        # BLOCK Added 27/11/2021 - PS2.4 - P3 - showinf lines between print and tower
        if current_block_class != v.previous_block_classification and not v.side_wipe and not v.full_purge_reduction:
            if v.previous_block_classification == CLS_TOOL_UNLOAD:
                if v.restore_move_point:
                    v.restore_move_point = False
                    gcode.issue_code(
                        "G1 X{:0.3f} Y{:0.3f} F8640 ; P2PP positional alignment"
                        .format(v.current_position_x, v.current_position_y))
        # BLOCK END

        # ---- SECOND SECTION HANDLES COMMENTS AND NONE-MOVEMENT COMMANDS ----

        if g[gcode.COMMAND] is None:
            if v.disable_z and g[gcode.COMMENT].endswith("END"):
                v.disable_z = False

            if v.needpurgetower and g[gcode.COMMENT].endswith("BRIM END"):
                v.needpurgetower = False
                purgetower.purge_create_layers(v.wipe_tower_info_minx,
                                               v.wipe_tower_info_miny,
                                               v.wipe_tower_xsize,
                                               v.wipe_tower_ysize)
                purgetower.purge_generate_brim()
                v.toolchange_processed = False
            gcode.issue_command(g)
            continue

        elif g[gcode.MOVEMENT] == 0:

            if g[gcode.COMMAND].startswith('T'):

                if v.manual_filament_swap and not (
                        v.side_wipe or v.full_purge_reduction
                        or v.tower_delta) and (v.current_tool != -1):
                    swap.swap_pause("M25")
                    swap.swap_unpause()

                gcode_process_toolchange(int(g[gcode.COMMAND][1:]))
                if not v.debug_leaveToolCommands:
                    gcode.move_to_comment(g, "--P2PP-- Color Change")

                v.toolchange_processed = (current_block_class != CLS_NORMAL)

            elif v.klipper and g[gcode.COMMAND] == "ACTIVATE_EXTRUDER":

                extruder = g[gcode.OTHER].strip()

                if extruder.startswith("EXTRUDER=extruder"):
                    if len(extruder) == 17:
                        extruder_num = 0
                    else:
                        try:
                            extruder_num = int(extruder[17:])
                        except (ValueError, IndexError):
                            extruder_num = None
                            gui.log_warning(
                                "KLIPPER - Named extruders are not supported ({})"
                                .format(extruder))

                    if extruder_num is not None:
                        gcode_process_toolchange(extruder_num)

                    if not v.debug_leaveToolCommands:
                        gcode.move_to_comment(g, "--P2PP-- Color Change")
                        v.toolchange_processed = True
                else:
                    gui.log_warning(
                        "KLIPPER - Named extruders are not supported ({})".
                        format(extruder))
            else:
                if current_block_class == CLS_TOOL_UNLOAD:
                    if g[gcode.COMMAND] in ["G4", "M900", "M400"]:
                        gcode.move_to_comment(g, "--P2PP-- tool unload")

                if g[gcode.COMMAND] is not None and g[
                        gcode.COMMAND].startswith('M'):
                    try:
                        command_num = int(g[gcode.COMMAND][1:])
                    except (ValueError, KeyError):
                        command_num = 0

                    if command_num in [104, 109]:
                        if v.process_temp:
                            if current_block_class not in [
                                    CLS_TOOL_PURGE, CLS_TOOL_START,
                                    CLS_TOOL_UNLOAD
                            ]:
                                g[gcode.COMMENT] += " Unprocessed temp "
                                v.new_temp = gcode.get_parameter(
                                    g, gcode.S, v.current_temp)
                                v.current_temp = v.new_temp
                            else:
                                v.new_temp = gcode.get_parameter(
                                    g, gcode.S, v.current_temp)
                                if v.new_temp >= v.current_temp:
                                    g[gcode.COMMAND] = "M109"
                                    v.temp2_stored_command = gcode.create_commandstring(
                                        g)
                                    gcode.move_to_comment(
                                        g,
                                        "--P2PP-- delayed temp rise until after purge {}-->{}"
                                        .format(v.current_temp, v.new_temp))
                                    v.current_temp = v.new_temp

                                else:
                                    v.temp1_stored_command = gcode.create_commandstring(
                                        g)
                                    gcode.move_to_comment(
                                        g,
                                        "--P2PP-- delayed temp drop until after purge {}-->{}"
                                        .format(v.current_temp, v.new_temp))
                    elif command_num == 107:
                        v.saved_fanspeed = 0

                    elif command_num == 106:
                        v.saved_fanspeed = gcode.get_parameter(
                            g, gcode.S, v.saved_fanspeed)

                    elif command_num == 221:
                        v.extrusion_multiplier = float(
                            gcode.get_parameter(
                                g, gcode.S,
                                v.extrusion_multiplier * 100.0)) / 100.0

                    elif command_num == 220:
                        gcode.move_to_comment(
                            g, "--P2PP-- Feed Rate Adjustments are removed")

                    elif command_num == 572:
                        for i in range(1, v.filament_count):
                            g[gcode.OTHER] = g[gcode.OTHER].replace(
                                "D{}".format(i), "D0")

                    elif not v.generate_M0 and g[gcode.COMMAND] == "M0":
                        gcode.move_to_comment(g, "--P2PP-- remove M0 command")

            gcode.issue_command(g)
            continue

        classupdate = not current_block_class == v.previous_block_classification
        v.previous_block_classification = current_block_class

        # ---- AS OF HERE ONLY MOVEMENT COMMANDS ----

        if g[gcode.MOVEMENT] & 1:
            v.previous_purge_keep_x = v.purge_keep_x
            v.purge_keep_x = g[gcode.X]
            v.current_position_x = g[gcode.X]

        if g[gcode.MOVEMENT] & 2:
            v.previous_purge_keep_y = v.purge_keep_y
            v.purge_keep_y = g[gcode.Y]
            v.current_position_y = g[gcode.Y]

        if g[gcode.MOVEMENT] & 4:
            if v.disable_z:
                gcode.move_to_comment(g,
                                      "-- P2PP - invalid move in delta tower")
                gcode.issue_command(g)
                continue
            else:
                v.current_position_z = g[gcode.Z]

        if g[gcode.MOVEMENT] & 16:
            v.keep_speed = g[gcode.F]

        # this goes for all situations: START and UNLOAD are not needed
        if current_block_class in [CLS_TOOL_START, CLS_TOOL_UNLOAD]:
            # BLOCK Added 27/11/2021 - PS2.4 - P3 - showinf lines between print and tower
            if not (v.side_wipe or v.full_purge_reduction):
                v.restore_move_point = True
            # BLOCK END
            gcode.move_to_comment(g, "--P2PP-- tool unload")
            gcode.issue_command(g)
            continue

        # --------------------- TOWER DELTA PROCESSING
        if v.tower_delta:

            if classupdate:

                if current_block_class == CLS_TOOL_PURGE:
                    gcode.issue_command(g)
                    entertower(v.last_parsed_layer * v.layer_height +
                               v.first_layer_height)
                    continue

                if current_block_class == CLS_EMPTY and not v.towerskipped:

                    v.towerskipped = (
                        g[gcode.MOVEMENT] & gcode.INTOWER
                    ) == gcode.INTOWER and v.current_layer_is_skippable

                    if not v.towerskipped:
                        gcode.issue_command(g)
                        entertower(v.last_parsed_layer * v.layer_height +
                                   v.first_layer_height)
                        continue

                if current_block_class == CLS_NORMAL:
                    if v.towerskipped:
                        gcode.issue_code("G1 Z{:.2f} F10810".format(
                            v.current_position_z))
                        v.towerskipped = False

            if current_block_class == CLS_TOOL_PURGE:
                speed_limiter(g)

            if current_block_class == CLS_TOOL_PURGE:
                if g[gcode.F] is not None and g[
                        gcode.F] > v.purgetopspeed and g[gcode.E]:
                    g[gcode.F] = v.purgetopspeed
                    g[gcode.COMMENT] += " prugespeed topped"

            if v.towerskipped:
                gcode.move_to_comment(g, "--P2PP-- tower skipped")
                gcode.issue_command(g)
                continue
        # --------------------- SIDE WIPE PROCESSING
        elif v.side_wipe:

            if classupdate:

                if current_block_class == CLS_BRIM:
                    v.towerskipped = True
                    v.side_wipe_state = 0

            if not v.towerskipped and (g[gcode.MOVEMENT] & 3) != 0:
                if (g[gcode.MOVEMENT] & gcode.INTOWER) == gcode.INTOWER:
                    v.towerskipped = True
                    v.side_wipe_state = 1 if (current_block_class
                                              == CLS_TOOL_PURGE) else 0

            if v.towerskipped and current_block_class == CLS_NORMAL and (
                    g[gcode.MOVEMENT] & 3) == 3:
                if (v.bed_origin_x <= g[gcode.X] <= v.bed_max_x) and (
                        v.bed_origin_y <= g[gcode.Y] <= v.bed_max_y):
                    v.towerskipped = False
                    v.side_wipe_state = 0
                    if v.toolchange_processed and v.side_wipe_length:
                        create_side_wipe()
                        v.toolchange_processed = False

            if v.towerskipped:
                inc = "NO_E"
                if current_block_class in [
                        CLS_TOOL_PURGE, CLS_ENDPURGE
                ] or (current_block_class == CLS_EMPTY
                      and v.side_wipe_state == 1):
                    if g[gcode.EXTRUDE]:
                        v.side_wipe_length += g[gcode.E]
                        inc = "INC_E"

                gcode.move_to_comment(
                    g, "--P2PP-- side wipe skipped ({})".format(inc))
                gcode.issue_command(g)
                continue

            # for PS2.4
            # before first extrusion prime the nozzle
            if not v.mechpurge_hasprimed and g[gcode.EXTRUDE]:
                if v.bigbrain3d_purge_enabled or v.blobster_purge_enabled:
                    create_side_wipe(v.mechpurge_prime_blobs *
                                     v.mechpurge_blob_size)
                v.mechpurge_hasprimed = True

        # --------------------- FULL PURGE PROCESSING
        elif v.full_purge_reduction:

            if (g[gcode.MOVEMENT] & 3) > 0:  # if there is a movement
                intower = (g[gcode.MOVEMENT] & gcode.INTOWER) == gcode.INTOWER

            if classupdate:

                if current_block_class == CLS_NORMAL:
                    v.towerskipped = False
                    purge = False

                if current_block_class == CLS_TOOL_PURGE:
                    purge = True

            if not v.towerskipped and current_block_class == CLS_EMPTY and v.current_layer_is_skippable:
                v.towerskipped = (g[gcode.MOVEMENT]
                                  & gcode.INTOWER) == gcode.INTOWER

            if v.towerskipped or current_block_class in [
                    CLS_BRIM, CLS_ENDGRID
            ]:
                gcode.move_to_comment(
                    g, "--P2PP-- full purge skipped [Excluded]")
                gcode.issue_command(g)
                continue

            if current_block_class in [
                    CLS_TOOL_PURGE, CLS_ENDPURGE, CLS_EMPTY
            ]:
                if purge and g[gcode.EXTRUDE]:
                    v.side_wipe_length += g[gcode.E]
                    gcode.move_to_comment(
                        g, "--P2PP-- full purge skipped [Included]")
                else:
                    gcode.move_to_comment(
                        g, "--P2PP-- full purge skipped [Excluded]")
                gcode.issue_command(g)
                continue

            if v.toolchange_processed and current_block_class == CLS_NORMAL:
                if v.side_wipe_length and (
                        g[gcode.MOVEMENT]
                        & 3) == 3 and not (g[gcode.MOVEMENT]
                                           & gcode.INTOWER) == gcode.INTOWER:
                    purgetower.purge_generate_sequence()
                    v.toolchange_processed = False
                    # do not issue code here as the next code might require further processing such as retractioncorrection
                else:
                    gcode.move_to_comment(g, "--P2PP-- full purge skipped")
                    gcode.issue_command(g)
                    continue

            if v.expect_retract and (g[gcode.MOVEMENT] & 3):
                v.expect_retract = False
                if v.retraction >= 0 and g[gcode.RETRACT]:
                    purgetower.retract(v.current_tool)

            if v.retract_move and g[gcode.RETRACT]:
                g[gcode.X] = v.retract_x
                g[gcode.Y] = v.retract_y
                g[gcode.MOVEMENT] |= 3
                v.retract_move = False

                if v.retraction <= -v.retract_length[v.current_tool]:
                    gcode.move_to_comment(g, "--P2PP-- Double Retract")
                else:
                    v.retraction += g[gcode.E]

            if intower:
                gcode.move_to_comment(
                    g, "--P2PP-- full purge skipped [Excluded]")
                gcode.issue_command(g)
                continue

        # --------------------- NO TOWER PROCESSING
        else:

            if current_block_class in [CLS_TOOL_PURGE, CLS_EMPTY
                                       ] and g[gcode.E]:
                if v.acc_ping_left <= 0:
                    pings.check_accessorymode_first()
                    v.enterpurge = True

            # TOEE - Added to limit the speed of the extrusions during purge to defined WIPEFEEDRATE
            if current_block_class == CLS_TOOL_PURGE:
                speed_limiter(g)

            if v.toolchange_processed:
                if v.temp2_stored_command != "":
                    wait_location = calculate_temp_wait_position()
                    gcode.issue_code(
                        "G1 X{:.3f} Y{:.3f} F8640; temp wait position\n".
                        format(wait_location[0], wait_location[0]))
                    gcode.issue_code(v.temp2_stored_command)
                    v.temp2_stored_command = ""

                gcode.issue_code("G1 F8640 ; correct speed")
                gcode.issue_command(g)
                if v.wipe_remove_sparse_layers:
                    gcode.issue_code(
                        "G1 X{}  Y{} F8640 ;P2PP Position XY to avoid tower crash"
                        .format(v.current_position_x, v.current_position_y))
                v.z_correction = "G1 Z{} F10800 ;P2PP correct z-moves".format(
                    v.current_position_z)

                v.toolchange_processed = False
                continue

            if current_block_class == CLS_TOOL_PURGE:
                if g[gcode.F] is not None and g[
                        gcode.F] > v.purgetopspeed and g[gcode.E]:
                    g[gcode.F] = v.purgetopspeed
                    g[gcode.COMMENT] += " prugespeed topped"

        # --------------------- GLOBAL PROCEDDING

        if g[gcode.UNRETRACT]:
            g[gcode.E] = min(-v.retraction, g[gcode.E])
            v.retraction += g[gcode.E]
        elif g[gcode.RETRACT]:
            v.retraction += g[gcode.E]
        elif (g[gcode.MOVEMENT] & 3) and g[gcode.EXTRUDE]:
            if v.z_correction is not None or v.retraction < -0.01:
                if current_block_class != CLS_TOOL_START:
                    gcode.issue_code(";P2PP START Z/E alignment processing")
                    if v.z_correction is not None:
                        gcode.issue_code(v.z_correction)
                        v.z_correction = None
                    if v.retraction < -0.01:
                        purgetower.unretract(v.retraction, -1,
                                             ";--- P2PP --- fixup retracts")
                    gcode.issue_code(";P2PP END Z/E alignment processing")
                else:
                    gcode.issue_command(g)
                    gcode.issue_code(";P2PP START Z/E alignment processing")
                    if v.z_correction is not None:
                        gcode.issue_code(v.z_correction)
                        v.z_correction = None
                    if v.retraction < -0.01:
                        purgetower.unretract(v.retraction, -1,
                                             ";--- P2PP --- fixup retracts")
                    g = gcode.create_command(
                        ";P2PP END Z/E alignment processing")

        # --------------------- PING PROCESSING

        if v.accessory_mode and g[gcode.EXTRUDE]:
            if not pings.check_accessorymode_second(g[gcode.E]):
                gcode.issue_command(g)
        else:
            gcode.issue_command(g)
            if g[gcode.EXTRUDE] and v.side_wipe_length == 0:
                pings.check_connected_ping()

        v.previous_position_x = v.current_position_x
        v.previous_position_y = v.current_position_y

    # LAST STEP IS ADDING AN EXTRA TOOL UNLOAD TO DETERMINE THE LENGTH OF THE LAST SPLICE
    gcode_process_toolchange(-1)