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
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'))
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
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}))
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]}))
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
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
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)
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
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
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}))
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