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_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 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 __init__(self, gcode_file=None): if gcode_file is None: self.tokens = doublelinkedlist.DLList() else: self.parse(gcode_file) self.total_runtime = 0 # total filament usage self.total_filament_usage = {} # cached list self.cached_tokens = []
def gcode_pillar_band(self, tool_id): band_gcode = doublelinkedlist.DLList() for radius in self.prime_tower.get_pillar_bands(self.layer_num, tool_id): # Start each circle at a different point to avoid weakening the tower circle_vertices = deque(circle_generate_vertices(conf.prime_tower_x, conf.prime_tower_y, radius, conf.prime_tower_band_num_faces)) circle_vertices.rotate(self.layer_num) band_gcode.append_nodes(self.gcode_print_shape(circle_vertices, tool_id)) if conf.GCODE_VERBOSE: band_gcode.head.comment = "TC-PSPP - T{tool} - Pillar - Start".format(tool = tool_id) band_gcode.tail.comment = "TC-PSPP - T{tool} - Pillar - End".format(tool = tool_id) return band_gcode
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 parse(self, gcode_file): self.tokens = doublelinkedlist.DLList() # Read all the lines with open(gcode_file, mode='r', encoding='utf8') as gcode_in: # Track the tool current_tool_head = -1 for line in gcode_in.readlines(): line = line.strip() if len(line) == 0: continue # Check if comment if line[0] == ';': # Check if comment params - starts with ;; if len(line) > 1 and line[1] == ';': contents = line[2:] # Check if has extra comment - strip comment_pos = contents.find(';') if comment_pos != -1: contents = contents[0:comment_pos].strip() # Check if has params label = None params = [] params_sep = contents.find(':') if params_sep != -1: label = contents[0:params_sep].strip() params = contents[params_sep + 1:].split(',') else: label = contents.strip() # Check if the label in params if label not in valid_params_format.keys(): raise GCodeParseException( "Param {label} not valid".format(label=label), line) if len(params) != len(valid_params_format[label]): raise GCodeParseException( "Param {label} has invalid number of arguments" .format(label=label), line) self.tokens.append_node( Params(label=label, param=[ valid_params_format[label][indx]( params[indx]) for indx in range(0, len(params)) ])) continue # Check if normal comment - single ; if len(line) > 1 and line[1] != ';': text = line[1:] self.tokens.append_node(Comment(text=text)) continue # Empty comment - skip if len(line) == 1: continue # Check if GCODE if line[0] in ['G', 'M']: contents = line comment = "" # Check if has extra comment - strip comment_pos = line.find(';') if comment_pos != -1: contents = line[0:comment_pos].strip() comment = line[comment_pos + 1:].strip() # Split into params args = contents.split() gcode = args[0] # # Check if omit the code if len(args) == 1: self.tokens.append_node( GCode(gcode=gcode, comment=comment)) else: self.tokens.append_node( GCode(gcode=gcode, param=dict([(p[0], p[1:]) for p in args[1:]]), comment=comment)) continue # Check if Toolchange if line[0] == 'T': # Check if has extra comment - strip contents = line comment_pos = line.find(';') if comment_pos != -1: contents = line[0:comment_pos].strip() previous_tool_head = current_tool_head current_tool_head = int(contents[1:]) self.tokens.append_node( ToolChange(prev_tool=previous_tool_head, next_tool=current_tool_head)) continue
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