Ejemplo n.º 1
0
    def gcode_pillar_idle_tool_bands(self, tool_id):
        # Generate vertices
        tokens = doublelinkedlist.DLList()

        for idle_tool_id in self.tools_idle:
            gcode_band = doublelinkedlist.DLList()
            for radius in self.prime_tower.get_pillar_bands(
                    self.layer_num, idle_tool_id):
                vertices = circle_generate_vertices(
                    conf.prime_tower_x, conf.prime_tower_y, radius,
                    conf.prime_tower_band_num_faces)
                gcode_band.append_nodes(
                    self.gcode_print_shape(vertices, tool_id))

            gcode_band.head.append_node(gcode_analyzer.GCode('G11'))
            gcode_band.head.append_node_left(gcode_analyzer.GCode('G10'))

            tokens.append_nodes(gcode_band)

        if conf.GCODE_VERBOSE:
            tokens.head.comment = "TC-PSPP - Prime tower idle tool infill for layer #{layer} - start".format(
                layer=self.layer_num)
            tokens.tail.comment = "TC-PSPP - Prime tower idle tool infill for layer #{layer} - end".format(
                layer=self.layer_num)

        return tokens
Ejemplo n.º 2
0
 def gcode_prep_bed_temp(self):
     self.temp_layer1.append_node(
         gcode_analyzer.GCode('M140', {
             'S':
             conf.bed_temperature(1, self.tool_activation_seq.keys())
         }))
     self.temp_layer1.append_node(gcode_analyzer.GCode('M190'))
Ejemplo n.º 3
0
    def gcode_wipe_path(self, start_point, length, retract_length):
        path = []
        x0, y0, z0 = start_point.state_post.x, start_point.state_post.y, start_point.state_post.z
        previous_move = start_point

        accumulated_dist = 0.0
        while accumulated_dist < length:
            if previous_move.state_pre == None:
                break
            x1, y1, z1 = previous_move.state_pre.x, previous_move.state_pre.y, previous_move.state_pre.z

            if z1 != z0:
                break

            dist = math.sqrt((x1 - x0)**2 + (y1 - y0)**2)

            # If end
            if dist + accumulated_dist > length:
                # Check cutoff
                dist_corrected = length - accumulated_dist
                x1 = x0 + (x1 - x0) * (dist_corrected / dist)
                y1 = y0 + (y1 - y0) * (dist_corrected / dist)
                dist = dist_corrected

            if dist != 0.0:
                accumulated_dist += dist
                x0, y0 = x1, y1
                path.append((x1, y1, dist))

            if previous_move.prev is not None:
                previous_move = previous_move.prev

        if accumulated_dist < length:
            logger.warn(
                "Calculated wipe path {wipe_length:0.2f}[mm] less then configured {length:0.2f}[mm]"
                .format(wipe_length=accumulated_dist, length=length))

        # Generate GCodes
        gcode = doublelinkedlist.DLList()

        if len(path) > 2:
            for vertex in path:
                to_retract = -retract_length * (vertex[2]) / accumulated_dist
                gcode.append_node(
                    gcode_analyzer.GCode('G1', {
                        'X': vertex[0],
                        'Y': vertex[1],
                        'E': to_retract
                    }))
            logger.info("Calculated wipe path: {length}".format(
                length=accumulated_dist))
        else:
            # Just add retraction
            gcode.append_node(
                gcode_analyzer.GCode('G1', {'E', -retract_length}))

        gcode.head.comment = "wipe start"

        return gcode
Ejemplo n.º 4
0
    def gcode_prep_deactivation(self):
        # For each tool add disable block
        for tool_id, activation_seq in self.tool_activation_seq.items():
            tool_info = activation_seq[-1]

            if tool_info.block_end is not None:
                print("TempController: Disabling T{tool} at layer {layer}".format(
                    tool = tool_id, layer = tool_info.block_end.state_post.layer_num))

                tool_info.block_end.append_node(gcode_analyzer.GCode('M104', {'S' : 0, 'T' : tool_id}))

        # Insert deactivation at the end
        self.temp_footer.append_node(gcode_analyzer.GCode('M104', {'S' : 0}))
        self.temp_footer.append_node(gcode_analyzer.GCode('M140', {'S' : 0}))
Ejemplo n.º 5
0
    def inject_gcode(self):
        # Go over all the tool changes
        for tool_change in self.tool_change_seq:
            # Disable the old tool
            tool_change.append_node_left(gcode_analyzer.GCode(
                'M106', {'S': 0}))

            layer_num = tool_change.state_post.layer_num
            if layer_num is not None and layer_num > conf.tool_pcfan_disable_first_layers[
                    tool_change.next_tool]:
                tool_change.append_node(
                    gcode_analyzer.GCode(
                        'M106',
                        {'S': conf.tool_pcfan_speed[tool_change.next_tool]}))
Ejemplo n.º 6
0
    def gcode_print_shape(self,
                          vertices,
                          tool_id,
                          retract_on_move=True,
                          closed=True):
        tokens = doublelinkedlist.DLList()

        if tool_id not in self.tools_active:
            raise gcode_analyzer.GCodeSerializeException(
                "Tool {tool_id} not in active set of prime tower layer #{layer_num}"
                .format(tool_id=tool_id, layer_num=self.layer_num))

        tokens.append_node(
            gcode_analyzer.GCode('G1', {
                'X': vertices[0][0],
                'Y': vertices[0][1]
            }))
        tokens.append_node(
            gcode_analyzer.GCode('G1', {'F': conf.prime_tower_print_speed}))
        for v in range(1, len(vertices)):
            distance = math.sqrt((vertices[v][0] - vertices[v - 1][0])**2 +
                                 (vertices[v][1] - vertices[v - 1][1])**2)
            E = conf.calculate_E(tool_id, self.layer_height, distance)
            tokens.append_node(
                gcode_analyzer.GCode('G1', {
                    'X': vertices[v][0],
                    'Y': vertices[v][1],
                    'E': E
                }))

        if closed:
            distance = math.sqrt((vertices[-1][0] - vertices[0][0])**2 +
                                 (vertices[-1][1] - vertices[0][1])**2)
            E = conf.calculate_E(tool_id, self.layer_height, distance)
            tokens.append_node(
                gcode_analyzer.GCode('G1', {
                    'X': vertices[0][0],
                    'Y': vertices[0][1],
                    'E': E
                }))

        return tokens
Ejemplo n.º 7
0
    def inject_prime_tower_move_in(self, inject_point, gcode):
        # - if prime tower Z is higher then current Z - inject Z move before moving to brim XY
        # - if prime tower Z is lower then current Z - inject Z move after brim XY
        if inject_point.state_post.z == None or inject_point.state_post.z < self.layer_z:
            gcode.head.append_node_left(
                gcode_analyzer.GCode('G1', {'Z': self.layer_z}))
        elif inject_point.state_post.z > self.layer_z:
            gcode.head.append_node(
                gcode_analyzer.GCode('G1', {'Z': self.layer_z}))

        # - if was unretracted - add retraction/unretraction around the first move from gcode
        if inject_point.state_post.retraction == gcode_analyzer.GCodeAnalyzer.State.UNRETRACTED:
            gcode.head.append_node(
                gcode_analyzer.GCode('G11', comment='move-in detract'))
            gcode.head.append_node_left(
                gcode_analyzer.GCode('G10', comment='move-in retract'))

        # - if was retracted, just add unretraction after first move from gcode
        if inject_point.state_post.retraction == gcode_analyzer.GCodeAnalyzer.State.RETRACTED:
            gcode.head.append_node(
                gcode_analyzer.GCode('G11', comment='move-in detract'))
        gcode.head.append_node_left(
            gcode_analyzer.GCode('G1', {'F': conf.prime_tower_move_speed}))

        return gcode
Ejemplo n.º 8
0
    def gcode_prep_header(self):
        # Prepare gcode in the header
        gcode_init = doublelinkedlist.DLList()
        gcode_wait = doublelinkedlist.DLList()

        # Check the runtime estimate between TC_TEMP_INITIALIZE and first tool activation
        for tool_id, activation_seq in self.tool_activation_seq.items():
            tool_info = activation_seq[0]

            time_delta = 0.0
            token = self.temp_header.next
            while token != tool_info.tool_change:
                time_delta += token.runtime
                token = token.next

            if conf.DEBUG:
                print("(DEBUG) TempController: INIT -> T{tool} - runtime estimate: {delta:0.2f}".format(tool = tool_id, delta = time_delta))

            tool_temp = conf.tool_temperature(tool_info.tool_change.state_pre.layer_num, tool_id)
            # Check if should set idle temp or tool temp at INIT point
            # temp_idle = tool_temp - temp_idle_delta
            time_temp_idle2tool = float(conf.temp_idle_delta) / float(conf.temp_heating_rate)

            if time_temp_idle2tool < time_delta:
                # Find the inject point 
                acc_time = 0.0
                inject_point = tool_info.tool_change.prev
                while inject_point is not None:
                    acc_time += inject_point.runtime
                    if acc_time >= time_temp_idle2tool:
                        break
                    inject_point = inject_point.prev

                if conf.DEBUG:
                    print("(DEBUG) TempController: Inject point for T{tool} is before \"{token}\" - time diff: {delta:0.2f}s".format(tool = tool_id, token = str(inject_point), delta = acc_time))

                # Insert idle temp in TC_INIT
                # Insert ramp up at inject point
                # Insert temp wait before tool change
                gcode_init.append_node(gcode_analyzer.GCode('M104', {'S' : tool_temp - conf.temp_idle_delta, 'T' : tool_id}))
                gcode_wait.append_node(gcode_analyzer.GCode('M116', {'P' : tool_id, 'S' : 5}))

                inject_point.append_node(gcode_analyzer.GCode('M104', {'S' : tool_temp, 'T' : tool_id}))
                tool_info.tool_change.append_node_left(gcode_analyzer.GCode('M116', {'P' : tool_id, 'S' : 5}))
            else:
                if conf.DEBUG:
                    print("(DEBUG) TempController: Inject point for T{tool} at TC_INIT".format(tool = tool_id))

                # Insert target temp at TC_INIT
                # Insert temp wait at TC_INIT
                gcode_init.append_node(gcode_analyzer.GCode('M104', {'S' : tool_temp, 'T' : tool_id}))
                gcode_wait.append_node(gcode_analyzer.GCode('M116', {'P' : tool_id, 'S' : 5}))

        # Inject the gcode at TC_INIT
        self.temp_header.append_nodes_right(gcode_wait)
        self.temp_header.append_nodes_right(gcode_init)
Ejemplo n.º 9
0
    def inject_prime_tower_move_out(self, inject_point, gcode):
        inject_state = inject_point.state_post

        # - if prime tower is higher then inject point Z, move XY first and then Z
        # - if prime tower is lower then inject point Z, move Z first and then XY
        if inject_state.z is None:
            raise PrimeTowerException(
                "Malformed GCode - injecting prime tower move-out code where Z is not set"
            )

        # If firmware retracts - it is quite simple
        if conf.retraction_firmware:
            # No need to move out in
            # -To handle the situation where state is not set initially
            # - Inject point is BEFORE_LAYER_END
            #
            if inject_point.type != Token.PARAMS or inject_point.label != 'BEFORE_LAYER_CHANGE':
                gcode.append_node(
                    gcode_analyzer.GCode('G10', comment='move-out retract'))
                if inject_state.x != None and inject_state.y != None:
                    gcode.append_node(
                        gcode_analyzer.GCode(
                            'G1', {'F': conf.prime_tower_move_speed}))
                    if inject_state.z < self.layer_z:
                        gcode.append_node(
                            gcode_analyzer.GCode('G1', {
                                'X': inject_state.x,
                                'Y': inject_state.y
                            }))
                        gcode.append_node(
                            gcode_analyzer.GCode('G1', {'Z': inject_state.z}))
                    elif inject_state.z > self.layer_z:
                        gcode.append_node(
                            gcode_analyzer.GCode('G1', {'Z': inject_state.z}))
                        gcode.append_node(
                            gcode_analyzer.GCode('G1', {
                                'X': inject_state.x,
                                'Y': inject_state.y
                            }))
                    else:
                        gcode.append_node(
                            gcode_analyzer.GCode('G1', {
                                'X': inject_state.x,
                                'Y': inject_state.y
                            }))
                    gcode.append_node(
                        gcode_analyzer.GCode('G1',
                                             {'F': inject_state.feed_rate}))
                else:
                    logger.warn(
                        "X/Y position state not present, if this error apears more then once the GCode is malformed"
                    )

                # Retract before
                # - if  tool was unretracted before entry point - unretract after the move
                if not inject_state.is_retracted:
                    gcode.append_node(
                        gcode_analyzer.GCode('G11',
                                             comment='move-out detract'))
            else:
                if inject_state.is_retracted:
                    gcode.append_node(
                        gcode_analyzer.GCode('G10',
                                             comment='move-out retract'))

        # If slicer retracts - more work
        if not conf.retraction_firmware:
            # No need to move out in:
            # - to handle the situation where state is not set initially
            # - Inject point is BEFORE_LAYER_END
            if inject_point.type != Token.PARAMS or inject_point.label != 'BEFORE_LAYER_CHANGE':
                move_z = max(inject_state.z,
                             self.layer_z) + conf.retraction_zhop[
                                 inject_state.tool_selected]

                gcode.append_node(
                    gcode_analyzer.GCode('G1', {
                        'F':
                        conf.retraction_speed[inject_state.tool_selected]
                    },
                                         comment='move-out retract'))
                gcode.append_node(
                    gcode_analyzer.GCode('G1', {
                        'E':
                        -conf.retraction_length[inject_state.tool_selected]
                    }))
                gcode.append_node(
                    gcode_analyzer.GCode('G1',
                                         {'F': conf.prime_tower_move_speed}))
                gcode.append_node(gcode_analyzer.GCode('G1', {'Z': move_z}))
                gcode.append_node(
                    gcode_analyzer.GCode('G1', {
                        'X': inject_state.x,
                        'Y': inject_state.y
                    }))
                gcode.append_node(
                    gcode_analyzer.GCode('G1', {'Z': inject_state.z}))
                if not inject_state.is_retracted:
                    gcode.append_node(
                        gcode_analyzer.GCode('G1', {
                            'F':
                            conf.retraction_speed[inject_state.tool_selected]
                        }))
                    gcode.append_node(
                        gcode_analyzer.GCode(
                            'G1', {
                                'E':
                                conf.retraction_length[
                                    inject_state.tool_selected]
                            }))
            else:
                if inject_state.is_retracted:
                    gcode.append_node(
                        gcode_analyzer.GCode('G1', {
                            'F':
                            conf.retraction_speed[inject_state.tool_selected]
                        }))
                    gcode.append_node(
                        gcode_analyzer.GCode(
                            'G1', {
                                'E':
                                -conf.retraction_length[
                                    inject_state.tool_selected]
                            }))

        return gcode
Ejemplo n.º 10
0
    def inject_prime_tower_move_in(self, inject_point, gcode):
        inject_state = inject_point.state_post

        gcode_pre = doublelinkedlist.DLList()
        gcode_post = doublelinkedlist.DLList()

        # If firmware retracts - it is quite simple
        if conf.retraction_firmware:
            # - if prime tower Z is higher then current Z - inject Z move before moving to brim XY
            # - if prime tower Z is lower then current Z - inject Z move after brim XY
            if inject_state.z == None or inject_state.z < self.layer_z:
                gcode.head.append_node_left(
                    gcode_analyzer.GCode('G1', {'Z': self.layer_z}))
            elif inject_state.z > self.layer_z:
                gcode.head.append_node(
                    gcode_analyzer.GCode('G1', {'Z': self.layer_z}))

            # - if was unretracted - add retraction/unretraction around the first move from gcode
            if not inject_state.is_retracted:
                gcode.head.append_node(
                    gcode_analyzer.GCode('G11', comment='move-in detract'))
                gcode.head.append_node_left(
                    gcode_analyzer.GCode('G10', comment='move-in retract'))

            # - if was retracted, just add unretraction after first move from gcode
            if inject_state.is_retracted:
                gcode.head.append_node(
                    gcode_analyzer.GCode('G11', comment='move-in detract'))
            gcode.head.append_node_left(
                gcode_analyzer.GCode('G1', {'F': conf.prime_tower_move_speed}))

        # If not firmware retract - do a move manually
        # If not retracted also do a wipe move (not longer then 1mm)
        if not conf.retraction_firmware:
            # If not retracted do a wipe move
            to_detract = 0.0
            if inject_state.is_retracted:
                to_detract = abs(inject_state.retraction)
            else:
                # We will need to detract same what retract in next step
                to_detract = conf.retraction_length[inject_state.tool_selected]

            # If retracted but we need to ajust the Z
            gcode_pre = doublelinkedlist.DLList()
            if inject_state.is_retracted:
                move_z = self.layer_z + conf.retraction_zhop[
                    inject_state.tool_selected]
                if move_z > inject_state.z:
                    gcode_pre.append_node(
                        gcode_analyzer.GCode(
                            'G1', {'F', conf.prime_tower_move_speed}))
                    gcode_pre.append_node(
                        gcode_analyzer.GCode('G1', {'Z', move_z}))
            else:
                # Need to retract and Z-hop
                move_z = max(inject_state.z,
                             self.layer_z) + conf.retraction_zhop[
                                 inject_state.tool_selected]

                gcode_pre.append_node(
                    gcode_analyzer.GCode('G1', {
                        'F':
                        conf.retraction_speed[inject_state.tool_selected]
                    }))
                # Add the wipe
                if conf.wipe_distance > 0.0:
                    wipe_gcode = self.gcode_wipe_path(
                        inject_point, conf.wipe_distance,
                        conf.retraction_length[inject_state.tool_selected] /
                        2.0)
                    gcode_pre.append_nodes(wipe_gcode)
                    gcode_pre.append_node(
                        gcode_analyzer.GCode(
                            'G1', {
                                'E':
                                -conf.retraction_length[
                                    inject_state.tool_selected] / 2.0
                            }))
                else:
                    gcode_pre.append_node(
                        gcode_analyzer.GCode(
                            'G1', {
                                'E':
                                -conf.retraction_length[
                                    inject_state.tool_selected]
                            }))

                gcode_pre.append_node(
                    gcode_analyzer.GCode('G1',
                                         {'F', conf.prime_tower_move_speed}))
                gcode_pre.append_node(gcode_analyzer.GCode(
                    'G1', {'Z', move_z}))

            # Add the speed
            gcode_pre.append_node(
                gcode_analyzer.GCode('G1', {'F', conf.prime_tower_move_speed}))

            gcode_post = doublelinkedlist.DLList()
            gcode_post.append_node(
                gcode_analyzer.GCode(
                    'G1', {
                        'Z': self.layer_z,
                        'E': conf.retraction_length[inject_state.tool_selected]
                    }))

            # Add to gcode
            gcode.head.append_nodes_right(gcode_post)
            gcode.head.append_nodes_left(gcode_pre)

        return gcode
Ejemplo n.º 11
0
    def gcode_prep_toolchange(self):
        # Check the runtime estimate between subsequent tool changes 
        for tool_id, activation_seq in self.tool_activation_seq.items():

            # Check each activation sequence
            for activation_indx in range(1, len(activation_seq)):
                tool_prev_info = activation_seq[activation_indx-1]
                tool_next_info = activation_seq[activation_indx]

                # Calculate the time delta between the deactivation and the activation
                time_delta = 0.0
                token = tool_prev_info.block_end.next
                while token != tool_next_info.tool_change:
                    time_delta += token.runtime
                    token = token.next

                if conf.DEBUG:
                    print("(DEBUG) TempController: T{tool} block_end -> T{tool} activation - runtime estimate: {delta:0.2f}s".format(tool = tool_id, delta = time_delta))

                # Get the temps
                prev_temp = conf.tool_temperature(tool_prev_info.block_end.state_post.layer_num, tool_id)
                next_temp = conf.tool_temperature(tool_next_info.tool_change.state_pre.layer_num, tool_id)

                # Idle temp - avg of the two minus the delta
                idle_temp = (prev_temp + next_temp) / 2.0 - conf.temp_idle_delta
                
                # Cooldown time
                time_cooling = (prev_temp - idle_temp) / conf.temp_cooling_rate
                time_heating = (next_temp - idle_temp) / conf.temp_heating_rate

                time_idling = time_delta - (time_cooling + time_heating)
                if time_idling <= 0.0:
                    # No idle time - check if there is temp difference between the two
                    if prev_temp < next_temp:
                        time_cooling = 0
                        time_heating = (next_temp - prev_temp) / conf.temp_heating_rate
                        if time_heating >= time_delta:
                            # Heating will take longer the difference - ramp up immedietly
                            idle_temp = next_temp
                        else:
                            # Heating will take less, keep current idle temp
                            idle_temp = prev_temp
                    elif prev_temp > next_temp:
                        idle_temp = next_temp
                        time_cooling = (prev_temp - next_temp) / conf.temp_cooling_rate
                        time_heating = 0
                        # Temp lower, immedietly try to ramp down temp
                        idle_temp = next_temp
                    else:
                        # Equal - nothing to do
                        idle_temp = next_temp
                        time_cooling = 0.0
                        time_heating = 0.0
                    time_idling = time_delta - (time_cooling + time_heating)

                # Statistics
                if conf.DEBUG:
                    print("(DEBUG) TempController: T{tool} {T_prev}C->{T_idle}C cooling time: {t_cooling:0.2f}s, idle time: {t_idling:0.2f}, {T_idle}C->{T_next}C heating time: {t_heating:0.2f}".format(
                        tool = tool_id, T_prev = prev_temp, T_next = next_temp, T_idle = idle_temp, t_cooling = time_cooling, t_idling = time_idling, t_heating = time_heating))

                # Use the new heating time
                if time_heating > 0.0:
                    # Find the injection point for next temp
                    acc_time = 0.0
                    inject_point = tool_next_info.tool_change.prev
                    while inject_point is not None:
                        acc_time += inject_point.runtime
                        if acc_time >= time_heating:
                            break
                        inject_point = inject_point.prev

                    if conf.DEBUG:
                        print("(DEBUG) TempController: Inject point for T{tool} temp ramp-up is before \"{token}\" - time diff: {delta:0.2f}s".format(
                            tool = tool_id, token = str(inject_point), delta = acc_time))
                    inject_point.append_node(gcode_analyzer.GCode('M104', {'S' : next_temp, 'T' : tool_id}))

                # Inject the idle temp
                tool_prev_info.block_end.append_node(gcode_analyzer.GCode('M104', {'S' : idle_temp, 'T' : tool_id}))
                tool_next_info.tool_change.append_node_left(gcode_analyzer.GCode('M116', {'P' : tool_id, 'S' : 5}))
Ejemplo n.º 12
0
    def inject_prime_tower_move_out(self, inject_point, gcode):
        # - if prime tower is higher then inject point Z, move XY first and then Z
        # - if prime tower is lower then inject point Z, move Z first and then XY
        if inject_point.state_post.z is None:
            raise PrimeTowerException(
                "Malformed GCode - injecting prime tower move-out code where Z is not set"
            )

        # No need to move out in
        # -To handle the situation where state is not set initially
        # - Inject point is BEFORE_LAYER_END
        #
        if inject_point.type != Token.PARAMS or inject_point.label != 'BEFORE_LAYER_CHANGE':
            gcode.append_node(
                gcode_analyzer.GCode('G10', comment='move-out retract'))
            if inject_point.state_post.x != None and inject_point.state_post.y != None:
                gcode.append_node(
                    gcode_analyzer.GCode('G1',
                                         {'F': conf.prime_tower_move_speed}))
                if inject_point.state_post.z < self.layer_z:
                    gcode.append_node(
                        gcode_analyzer.GCode(
                            'G1', {
                                'X': inject_point.state_post.x,
                                'Y': inject_point.state_post.y
                            }))
                    gcode.append_node(
                        gcode_analyzer.GCode('G1',
                                             {'Z': inject_point.state_post.z}))
                elif inject_point.state_post.z > self.layer_z:
                    gcode.append_node(
                        gcode_analyzer.GCode('G1',
                                             {'Z': inject_point.state_post.z}))
                    gcode.append_node(
                        gcode_analyzer.GCode(
                            'G1', {
                                'X': inject_point.state_post.x,
                                'Y': inject_point.state_post.y
                            }))
                else:
                    gcode.append_node(
                        gcode_analyzer.GCode(
                            'G1', {
                                'X': inject_point.state_post.x,
                                'Y': inject_point.state_post.y
                            }))
                gcode.append_node(
                    gcode_analyzer.GCode(
                        'G1', {'F': inject_point.state_post.feed_rate}))
            else:
                print(
                    "Warning : X/Y position state not present, if this error apears more then once the GCode is malformed"
                )

            # Retract before
            # - if  tool was unretracted before entry point - unretract after the move
            if inject_point.state_post.retraction == gcode_analyzer.GCodeAnalyzer.State.UNRETRACTED:
                gcode.append_node(
                    gcode_analyzer.GCode('G11', comment='move-out detract'))
        else:
            if inject_point.state_post.retraction == gcode_analyzer.GCodeAnalyzer.State.RETRACTED:
                gcode.append_node(
                    gcode_analyzer.GCode('G10', comment='move-out retract'))

        return gcode