def optimize_tower_skip(skipmax, layersize): skipped = 0.0 skipped_num = 0 if v.side_wipe or v.bigbrain3d_purge_enabled: base = -1 else: base = 0 for idx in range(len(v.skippable_layer) - 1, base, -1): if skipped + 0.005 >= skipmax: v.skippable_layer[idx] = False elif v.skippable_layer[idx]: skipped = skipped + layersize skipped_num += 1 if v.tower_delta: if skipped > 0: gui.log_warning( "Warning: Purge Tower delta in effect: {} Layers or {:-6.2f}mm".format(skipped_num, skipped)) else: gui.create_logitem("Tower Purge Delta could not be applied to this print") for idx in range(len(v.skippable_layer)): v.skippable_layer[idx] = False v.tower_delta = False if not v.side_wipe and not v.bigbrain3d_purge_enabled: v.skippable_layer[0] = False
def callback(monitor): pct = min(int(50 * monitor.bytes_read / (total_bytes + 1)) + 1, 50) newline = "|" + '█' * pct + '-' * (50 - pct) + "| {}/{}Kb [{:3}%]".format( int(monitor.bytes_read / 1024), int(total_bytes / 1024), pct * 2) cur = gui.form.textBrowser.textCursor() gui.form.textBrowser.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor) gui.form.textBrowser.moveCursor(QTextCursor.StartOfLine, QTextCursor.MoveAnchor) gui.form.textBrowser.moveCursor(QTextCursor.End, QTextCursor.KeepAnchor) gui.form.textBrowser.textCursor().removeSelectedText() gui.form.textBrowser.textCursor().deletePreviousChar() gui.form.textBrowser.setTextCursor(cur) gui.create_logitem(newline, "blue", True)
def create_tower_gcode(): # generate a purge tower alternative _x = v.wipe_tower_info['minx'] _y = v.wipe_tower_info['miny'] _w = v.wipe_tower_info['maxx'] - v.wipe_tower_info['minx'] _h = v.wipe_tower_info['maxy'] - v.wipe_tower_info['miny'] 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))
def generatewarnings(): warnings = [ "\n", ";------------------------:\n", "; - Process Info/Warnings:\n", ";------------------------:\n", ";Generated with P2PP version {}\n".format(v.version), ";Processed file:. {}\n".format(v.filename), ";P2PP Processing time {:-5.2f}s\n".format(v.processtime) ] gui.create_logitem(("Processing time {:-5.2f}s".format(v.processtime))) if len(v.process_warnings) == 0: warnings.append(";No warnings\n") else: for i in range(len(v.process_warnings)): warnings.append("{}\n".format(v.process_warnings[i])) return warnings
def optimize_tower_skip(skipmax, layersize): skipped = 0.0 skipped_num = 0 for idx in range(len(v.skippable_layer) - 1, 0, -1): if skipped >= skipmax: v.skippable_layer[idx] = False elif v.skippable_layer[idx]: skipped = skipped + layersize skipped_num += 1 if v.tower_delta and skipped > 0: gui.log_warning( "Warning: Purge Tower delta in effect: {} Layers or {:-6.2f}mm". format(skipped_num, skipped)) else: gui.create_logitem( "Tower Purge Delta could not be applied to this print")
def find_alternative_tower(): v.wipe_tower_info_maxx = v.wipe_tower_posx v.wipe_tower_info_minx = v.wipe_tower_posx v.wipe_tower_info_maxy = v.wipe_tower_posy v.wipe_tower_info_miny = v.wipe_tower_posy if v.wipe_tower_posx and v.wipe_tower_posy: state = 0 for i in range(len(v.input_gcode)): line = v.input_gcode[i] if line.startswith(";"): if line.startswith(";TYPE:Wipe tower"): state = 1 continue if state == 1 and line.startswith(";TYPE"): check_tower_update(False) purgetower.purge_create_layers(v.wipe_tower_info_minx, v.wipe_tower_info_miny, v.wipe_tower_xsize, v.wipe_tower_ysize) gui.create_logitem( "Tower detected from ({}, {}) to ({}, {})".format( v.wipe_tower_info_minx, v.wipe_tower_info_miny, v.wipe_tower_info_maxx, v.wipe_tower_info_maxy)) break if state == 1: gc = gcode.create_command(line) if gc[gcode.EXTRUDE]: if gc[gcode.X] is not None: v.wipe_tower_info_maxx = max( v.wipe_tower_info_maxx, gc[gcode.X] + 6 * v.extrusion_width) v.wipe_tower_info_minx = min( v.wipe_tower_info_minx, gc[gcode.X] - 6 * v.extrusion_width) if gc[gcode.Y] is not None: v.wipe_tower_info_maxy = max( v.wipe_tower_info_maxy, gc[gcode.Y] + 6 * v.extrusion_width) v.wipe_tower_info_miny = min( v.wipe_tower_info_miny, gc[gcode.Y] - 6 * v.extrusion_width)
def parse_config_parameters(): # TODO - get this information from the environment parameters # TODO - need to find out as from what version of PS this is working for idx in range(len(v.input_gcode) - 1, -1, -1): gcode_line = v.input_gcode[idx] if gcode_line.startswith("; estimated printing time"): try: fields = gcode_line.split("=") fields = fields[-1].split(" ") for i in range(len(fields)): fields[i] = "0" + fields[i].strip('hms') if len(fields) > 2: h = int(fields[-3]) else: h = 0 if len(fields) > 1: m = int(fields[-2]) else: m = 0 if len(fields) > 0: s = int(fields[-1]) else: s = 0 v.printing_time = h * 3600 + m * 60 + s except (ValueError, IndexError): pass return if gcode_line.startswith("; filament_settings_id"): v.filament_ids = split_csv_strings(gcode_line) if "generated by PrusaSlicer" in gcode_line: try: s1 = gcode_line.split("+") s2 = s1[0].split(" ") v.ps_version = s2[-1] gui.create_logitem( "File was created with PS version:{}".format(v.ps_version)) if v.ps_version < "2.2": gui.create_logitem( "<b>This version of P2PP is optimized to work with PS2.2 and higher!<b>" ) except (ValueError, IndexError): pass continue if gcode_line.startswith("; single_extruder_multi_material_priming"): parameter_start = gcode_line.find("=") if parameter_start != -1: try: if int(gcode_line[parameter_start + 1:].strip()) == 1: gui.log_warning( "[Print Settings][Multiple Extruders][Wipe Tower]Prime all printing extruders MUST be turned off" ) gui.log_warning("THIS FILE WILL NOT PRINT CORRECTLY") except (ValueError, IndexError): pass continue if gcode_line.startswith("; wipe_tower_no_sparse_layers"): parameter_start = gcode_line.find("=") if parameter_start != -1: try: v.wipe_remove_sparse_layers = (int( gcode_line[parameter_start + 1:].strip()) == 1) except (ValueError, IndexError): pass continue if gcode_line.startswith("; variable_layer_height"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.variable_layer = int(gcode_line[parameter_start + 1:].strip()) == 1 continue if gcode_line.startswith("; bed_shape") and not v.bed_shape_warning: get_bedshape(gcode_line) if gcode_line.startswith("; first_layer_temperature"): parameter_start = gcode_line.find("=") if parameter_start != -1: try: temps = gcode_line[parameter_start + 1:].strip().split(",") v.p3_printtemp = [] for i in range(len(temps)): v.p3_printtemp.append(int(temps[i])) except (IndexError, ValueError): v.p3_printtemp = [0, 0, 0, 0, 0, 0, 0, 0] if gcode_line.startswith("; first_layer_bed_temperature"): parameter_start = gcode_line.find("=") if parameter_start != -1: try: temps = gcode_line[parameter_start + 1:].strip().split(",") v.p3_bedtemp = [] for i in range(len(temps)): v.p3_bedtemp.append(int(temps[i])) except (IndexError, ValueError): v.p3_bedtemp = [0, 0, 0, 0, 0, 0, 0, 0] if gcode_line.startswith("; max_print_height"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.z_maxheight = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; wipe_tower_x"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipe_tower_posx = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; min_skirt_length"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.skirtsize = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; skirts"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.skirts = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; wipe_tower_width"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipe_tower_width = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; wipe_tower_y"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipe_tower_posy = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; extrusion_width"): parameter_start = gcode_line.find("=") if parameter_start != -1: parm = gcode_line[parameter_start + 1:].strip() if len(parm) == 0: gui.log_warning( "extrusion width parameter does not contain any values FULL PURGE REDUCTION will not work" ) gui.log_warning( "Please manually set the values for default extrusion (Print Settings/Advanced/Extrusion Width to resolve" ) continue if parm[-1] == "%": parm = parm.replace("%", "").strip() tmpval = float(parm) v.extrusion_width = v.nozzle_diameter * tmpval / 100.0 else: v.extrusion_width = float(gcode_line[parameter_start + 1:].strip()) v.tx_offset = 2 + 4 * v.extrusion_width v.yy_offset = 2 + 8 * v.extrusion_width continue if gcode_line.startswith("; infill_speed"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.infill_speed = float( gcode_line[parameter_start + 1:].strip()) * 60 continue if gcode_line.startswith("; layer_height"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.layer_height = float(gcode_line[parameter_start + 1:].strip()) continue # this next function assumes that the parameters are stored in alphabetical order # so layer_height is defined BEFORE first layer height when parsing back to front if gcode_line.startswith("; first_layer_height"): parameter_start = gcode_line.find("=") if parameter_start != -1: value = gcode_line[parameter_start + 1:].strip() if value[-1] == "%": v.first_layer_height = float( value[:-1]) / 100.0 * v.layer_height else: v.first_layer_height = float(value) continue if gcode_line.startswith("; support_material_synchronize_layers"): parameter_start = gcode_line.find("=") if parameter_start != -1: tmp = float(gcode_line[parameter_start + 1:].strip()) v.synced_support = tmp == 1 continue if gcode_line.startswith("; support_material "): parameter_start = gcode_line.find("=") if parameter_start != -1: tmp = float(gcode_line[parameter_start + 1:].strip()) v.support_material = tmp == 1 continue if gcode_line.startswith("; nozzle_diameter "): parameter_start = gcode_line.find("=") if parameter_start != -1: tmp = gcode_line[parameter_start + 1:].strip().split(",") tmp = float(tmp[0]) v.nozzle_diameter = tmp continue if gcode_line.startswith("; start_filament_gcode "): parameter_start = gcode_line.find("=") gcode_value = gcode_line[parameter_start + 2:].strip() fields = split_csv_strings(gcode_value) for i in range(len(fields)): lines = fields[0].split("\\n") for line in lines: if line.startswith(";P2PP PROFILETYPEOVERRIDE="): value = line[26:] v.filament_type[i] = value v.used_filament_types.append(v.filament_type[i]) v.used_filament_types = list( dict.fromkeys(v.used_filament_types)) continue if gcode_line.startswith("; start_gcode "): parameter_start = gcode_line.find("=") gcode_value = gcode_line[parameter_start + 2:].strip() lines = gcode_value.split("\\n") for line in lines: m = v.regex_p2pp.match(line) if m: if m.group(1).startswith("MATERIAL"): algorithm_process_material_configuration( m.group(1)[9:]) else: parameters.check_config_parameters( m.group(1), m.group(2)) if v.blobster_advanced: if len(v.blobster_advanced_speed) == 0: gui.log_warning( "BLOBSTER - Advanced mode required BLOBSTER_ADVANCED_SPEED parameter" ) if len(v.blobster_advanced_fan) == 0: gui.log_warning( "BLOBSTER - Advanced mode required BLOBSTER_ADVANCED_FAN parameter" ) if len(v.blobster_advanced_length) == 0: gui.log_warning( "BLOBSTER - Advanced mode required BLOBSTER_ADVANCED_LENGTH parameter" ) if len(v.blobster_advanced_speed) != len( v.blobster_advanced_fan) or len( v.blobster_advanced_speed) != len( v.blobster_advanced_length): gui.log_warning( "BLOBSTER - Advanced mode - BLOBSTER_ADVANCED_LENGTH/FAN/SPEED parameter must have same number of parameters" ) if gcode_line.startswith("; extruder_colour") or gcode_line.startswith( "; filament_colour"): filament_colour = '' parameter_start = gcode_line.find("=") gcode_line = gcode_line[parameter_start + 1:].strip() parameter_start = gcode_line.find("#") if parameter_start != -1: filament_colour = gcode_line.split(";") v.filament_count = len(filament_colour) for i in range(v.filament_count): v.filament_color_code[i] = filament_colour[i][1:] if gcode_line.startswith("; filament_diameter"): parameter_start = gcode_line.find("=") if parameter_start != -1: filament_diameters = gcode_line[parameter_start + 1:].strip(" ").split(",") v.filament_diameter = [1.75] * max(len(filament_diameters), 4) for i in range(len(filament_diameters)): v.filament_diameter[i] = float(filament_diameters[i]) continue if gcode_line.startswith("; filament_type"): parameter_start = gcode_line.find("=") if parameter_start != -1: filament_string = gcode_line[parameter_start + 1:].strip(" ").split(";") for i in range(len(filament_string)): if v.filament_type[i] != "": filament_string[i] = v.filament_type[i] v.filament_type = filament_string v.used_filament_types = list(set(filament_string)) continue if gcode_line.startswith("; retract_length = "): retract_error = False parameter_start = gcode_line.find("=") if parameter_start != -1: retracts = gcode_line[parameter_start + 1:].strip(" ").split(",") v.retract_length = [0.8] * max(len(retracts), v.colors) for i in range(len(retracts)): v.retract_length[i] = float(retracts[i]) - 0.02 if v.retract_length[i] < 0.0: retract_error = True gui.log_warning( "[Printer Settings]->[Extruders 1 -> {}]->[Retraction Length] should not be set to zero." .format(i)) if retract_error: gui.log_warning( "Generated file might not print correctly") continue if gcode_line.startswith("; gcode_flavor"): if "reprap" in gcode_line: v.isReprap_Mode = True continue if "use_firmware_retraction" in gcode_line: parameter_start = gcode_line.find("=") if parameter_start != -1: gcode_line = gcode_line[parameter_start + 1:].replace(";", "") if "1" in gcode_line: gui.log_warning("Hardware retraction no longer supported") continue if "use_relative_e_distances" in gcode_line: parameter_start = gcode_line.find("=") if parameter_start != -1: gcode_line = gcode_line[parameter_start + 1:].replace(";", "") if "1" not in gcode_line: gui.log_warning( "P2PP requires input file with RELATIVE extrusion") continue if gcode_line.startswith("; wiping_volumes_matrix"): wiping_info = [] _warning = False parameter_start = gcode_line.find("=") if parameter_start != -1: wiping_info = gcode_line[parameter_start + 1:].strip(" ").split(",") _warning = True for i in range(len(wiping_info)): if int(wiping_info[i]) != 140 and int(wiping_info[i]) != 0: _warning = False wiping_info[i] = float(wiping_info[i]) v.max_wipe = max(wiping_info) v.bigbrain3d_matrix_blobs = v.max_wipe < 20 if not v.bigbrain3d_matrix_blobs: map(filament_volume_to_length, wiping_info) else: gui.create_emptyline() gui.create_logitem("BigBrain3D BLOB transitions detected") color_table_size = int(math.sqrt(len(wiping_info))) header = "<table><tr><th>From\\To</th>" data = "" for i in range(color_table_size): header = header + "<th> Input {} </th>".format(i) data = data + "<tr><th>Input {} </th>".format(i) for j in range(color_table_size): data = data + ("<td align=center>{}</td>".format( int(wiping_info[j * color_table_size + i]))) data = data + "</tr>" header = header + "</tr>" data = data + "</table>" gui.create_logitem(header + data) gui.create_emptyline() v.wiping_info = wiping_info if _warning: gui.create_logitem( "<b>All purge lenghths 70/70 OR 140. Purge lengths may not have been set correctly.</b>" ) continue
def check_config_parameters(keyword, value): keyword = keyword.upper() if value is None: value = "" if keyword == "TEMPERATURECONTROL": v.process_temp = True if keyword == "PRINTERPROFILE": if len(value) != 16: gui.log_warning("Invalid Printer profile! - Has invalid length (expect 16) - [{}]" .format(value)) value = "" if not all(char in set("0123456789ABCDEFabcdef") for char in value): gui.log_warning("Invalid Printer profile! - Invalid characters (expect 0123456789abcdef) - [{}]" .format(value)) value = "" if len(value) == 16: v.printer_profile_string = value gui.set_printer_id(v.printer_profile_string) return if keyword == "ACCESSORYMODE_MAF": v.accessory_mode = True gui.create_logitem("Config: Palette2 Accessory Mode Selected") return if keyword == "ACCESSORYMODE_MSF": v.accessory_mode = True v.palette_plus = True gui.create_logitem("Config: Palette+ Accessory Mode Selected") return if keyword == "P+LOADINGOFFSET": v.palette_plus_loading_offset = int(value) return if keyword == "P+PPM": v.palette_plus_ppm = intparameter(value) return if keyword == "SPLICEOFFSET": v.splice_offset = floatparameter(value) gui.create_logitem("Splice Offset set tofiloverride {:-5.2f}mm".format(v.splice_offset)) return if keyword == "PROFILETYPEOVERRIDE": v.filament_type[v.set_tool] = value v.used_filament_types.append(v.filament_type[v.set_tool]) v.used_filament_types = list(dict.fromkeys(v.used_filament_types)) return if keyword == "EXTRUSIONMULTIPLIERCORRECTION": v.filament_type[v.current_tool] = floatparameter(value) return if keyword == "EXTRAENDFILAMENT": v.extra_runout_filament = floatparameter(value) gui.create_logitem("Extra filament at end of print {:-8.2f}mm".format(v.extra_runout_filament)) return if keyword == "BEFORESIDEWIPEGCODE": v.before_sidewipe_gcode.append(value) return if keyword == "AFTERSIDEWIPEGCODE": v.after_sidewipe_gcode.append(value) return if keyword == "AUTOLOADINGOFFSET": v.autoloadingoffset = floatparameter(value) return if keyword == "AUTOADDPURGE": v.autoaddsplice = True return if keyword == "MINSTARTSPLICE": v.min_start_splice_length = floatparameter(value) if v.min_start_splice_length < 100: v.min_start_splice_length = 100 gui.log_warning("Minimal first slice length adjusted to 100mm") return if keyword == "BEDSIZEX": v.bed_size_x = floatparameter(value) return if keyword == "BEDSIZEY": v.bed_size_y = floatparameter(value) return if keyword == "BEDORIGINX": v.bed_origin_x = floatparameter(value) return if keyword == "BEDORIGINY": v.bed_origin_y = floatparameter(value) return if keyword == "BIGBRAIN3D_BLOBSIZE": v.bigbrain3d_blob_size = intparameter(value) return if keyword == "BIGBRAIN3D_BLOBSPEED": v.bigbrain3d_blob_speed = intparameter(value) return if keyword == "BIGBRAIN3D_COOLINGTIME": v.bigbrain3d_blob_cooling_time = intparameter(value) return if keyword == "BIGBRAIN3D_PURGEPOSITION": v.bigbrain3d_x_position = floatparameter(value) return if keyword == "BIGBRAIN3D_PURGEYPOSITION": v.bigbrain3d_y_position = floatparameter(value) return if keyword == "BIGBRAIN3D_MOTORPOWER_HIGH": v.bigbrain3d_motorpower_high = intparameter(value) return if keyword == "BIGBRAIN3D_MOTORPOWER_NORMAL": v.bigbrain3d_motorpower_normal = intparameter(value) return if keyword == "BIGBRAIN3D_NUMBER_OF_WHACKS": v.bigbrain3d_whacks = intparameter(value) return if keyword == "BIGBRAIN3D_PRIME_BLOBS": v.bigbrain3d_prime = intparameter(value) return if keyword == "BIGBRAIN3D_FAN_OFF_PAUSE": v.bigbrain3d_fanoffdelay = intparameter(value) return if keyword == "BIGBRAIN3D_LEFT_SIDE": v.bigbrain3d_left = -1 return if keyword == "BIGBRAIN3D_ENABLE": if not v.wipe_remove_sparse_layers: v.bigbrain3d_purge_enabled = True gui.log_warning("BIGBRAIN3D Will only work with installed hardware on a Prusa Printer") else: gui.log_warning("BIGBRAIN3D mode not compatible with sparse wipe tower in PS") return if keyword == "BIGBRAIN3D_SMARTFAN": v.bigbrain3d_smartfan = True return if keyword == "MINSPLICE": v.min_splice_length = floatparameter(value) if v.min_splice_length < 70: v.min_splice_length = 70 gui.log_warning("Minimal slice length adjusted to 70mm") return # LINEAR PING removed if keyword == "LINEARPINGLENGTH": v.ping_interval = floatparameter(value) v.ping_length_multiplier = 1.0 if v.ping_interval < 300: v.ping_interval = 300 gui.log_warning("Minimal Linear Ping distance is 300mm! Your config stated: {}".format(line)) gui.create_logitem("Linear Ping interval of {:-6.2f}mm".format(v.ping_interval)) return # SIDE TRANSITIONING if keyword == "SIDEWIPELOC": v.side_wipe_loc = value return if keyword == "PURGETOPSPEED": v.purgetopspeed = int(floatparameter(value)) gui.create_logitem("Purge Max speed set to {:.0f}mm/min ({}mm/s)".format(v.purgetopspeed, v.purgetopspeed / 60)) return if keyword == "WIPEFEEDRATE": v.wipe_feedrate = floatparameter(value) return if keyword == "SIDEWIPEMINY": v.sidewipe_miny = floatparameter(value) return if keyword == "SIDEWIPEMAXY": v.sidewipe_maxy = floatparameter(value) return if keyword == "SIDEWIPECORRECTION": v.sidewipe_correction = floatparameter(value) if v.sidewipe_correction < 0.9 or v.sidewipe_correction > 1.10: v.sidewipe_correction = 1.0 return if keyword == "PURGETOWERDELTA": parm = abs(floatparameter(value)) if parm > 0.001 and v.wipe_remove_sparse_layers: gui.log_warning("TOWER DELTA feature mode not compatible with sparse wipe tower in PS") v.max_tower_delta = 0.0 else: if parm != float(0): v.max_tower_z_delta = abs(floatparameter(value)) gui.create_logitem("Max Purge Tower Delta set to {:-2.2f}mm".format(v.max_tower_z_delta)) return if keyword == "FULLPURGEREDUCTION": if not v.wipe_remove_sparse_layers: gui.create_logitem("Full purge reduction configured") v.full_purge_reduction = True else: gui.log_warning("FULL PURGE TOWER REDUCTION feature mode not compatible with sparse wipe tower in PS") v.full_purge_reduction = False return if keyword == "CHECKVERSION": import p2pp.checkversion as cv import version latest = cv.get_version(cv.MASTER) if latest > version.Version: gui.create_logitem("New development version of P2PP available ({})".format(latest), "red", False, "2.0") else: if (latest < version.Version): latest = cv.get_version(cv.DEV) if (latest > version.Version): gui.create_logitem("New development version of P2PP available ({})".format(latest), "red", False, "2.0") # Program parameters if keyword == "NOGUI": v.gui = False return if keyword == "CONSOLEWAIT": v.consolewait = True return if keyword == "IGNOREWARNINGS": v.ignore_warnings = True return if keyword == "ABSOLUTEEXTRUDER": v.absolute_extruder = True gui.create_logitem("Convert to absolute extrusion parameters") return if keyword == "DEBUGTCOMMAND": v.debug_leaveToolCommands = True gui.log_warning("DEBUGTCOMMAND ACTIVE - File will not print correctly!!") return
def check_config_parameters(line): # BASIC SETUP (material setup handled in mcf.py # -p takes precedence over printer defined in file if "PRINTERPROFILE" in line: tmp_string = stringparameter(line) if len(tmp_string) != 16: gui.log_warning( "Invalid Printer profile! - Has invalid length (expect 16) - [{}]" .format(tmp_string)) tmp_string = "" if not all(char in set("0123456789ABCDEFabcdef") for char in tmp_string): gui.log_warning( "Invalid Printer profile! - Invalid characters (expect 0123456789abcdef) - [{}]" .format(tmp_string)) tmp_string = "" if len(tmp_string) == 16: v.printer_profile_string = tmp_string gui.set_printer_id(v.printer_profile_string) return if "ACCESSORYMODE_MAF" in line: v.accessory_mode = True gui.create_logitem("Config: Palette2 Accessory Mode Selected") if "ACCESSORYMODE_MSF" in line: v.accessory_mode = True v.palette_plus = True gui.create_logitem("Config: Palette+ Accessory Mode Selected") if "P+LOADINGOFFSET" in line: v.palette_plus_loading_offset = int(floatparameter(line)) if "P+PPM" in line: v.palette_plus_ppm = int(floatparameter(line)) if "SPLICEOFFSET" in line: v.splice_offset = floatparameter(line) gui.create_logitem("Splice Offset set to {:-5.2f}mm".format( v.splice_offset)) return if "PROFILETYPEOVERRIDE" in line: v.filament_type[v.current_tool] = stringparameter(line) v.used_filament_types.append(v.filament_type[v.current_tool]) v.used_filament_types = list(dict.fromkeys(v.used_filament_types)) return if "EXTRUSIONMULTIPLIERCORRECTION" in line: v.filament_type[v.current_tool] = floatparameter(line) return if "EXTRAENDFILAMENT" in line: v.extra_runout_filament = floatparameter(line) gui.create_logitem("Extra filament at end of print {:-8.2f}mm".format( v.extra_runout_filament)) return if "BEFORESIDEWIPEGCODE" in line: v.before_sidewipe_gcode.append(stringparameter(line)) return if "AFTERSIDEWIPEGCODE" in line: v.after_sidewipe_gcode.append(stringparameter(line)) return if "MINSTARTSPLICE" in line: v.min_start_splice_length = floatparameter(line) if v.min_start_splice_length < 100: v.min_start_splice_length = 100 gui.log_warning("Minimal first slice length adjusted to 100mm") return if "BEDSIZEX" in line: v.bed_size_x = floatparameter(line) return if "BEDSIZEY" in line: v.bed_size_y = floatparameter(line) return if "BEDORIGINX" in line: v.bed_origin_x = floatparameter(line) return if "BEDORIGINY" in line: v.bed_origin_y = floatparameter(line) return if "BIGBRAIN3D_BLOBSIZE" in line: v.bigbrain3d_blob_size = int(floatparameter(line)) if "BIGBRAIN3D_COOLINGTIME" in line: v.bigbrain3d_blob_cooling_time = int(floatparameter(line)) if "BIGBRAIN3D_PURGEPOSITION" in line: v.bigbrain3d_x_position = floatparameter(line) if "BIGBRAIN3D_MOTORPOWER_HIGH" in line: v.bigbrain3d_motorpower_high = int(floatparameter(line)) if "BIGBRAIN3D_MOTORPOWER_NORMAL" in line: v.bigbrain3d_motorpower_normal = int(floatparameter(line)) if "BIGBRAIN3D_ENABLE" in line: v.bigbrain3d_purge_enabled = True gui.log_warning( "BIGBRAIN3D Will only work with installed hardware on a Prusa Printer" ) if "BIGBRAIN3D_SMARTFAN" in line: v.bigbrain3d_smartfan = True if "MINSPLICE" in line: v.min_splice_length = floatparameter(line) if v.min_splice_length < 70: v.min_splice_length = 70 gui.log_warning("Minimal slice length adjusted to 70mm") return # LINEAR PING removed if "LINEARPINGLENGTH" in line: v.ping_interval = floatparameter(line) v.ping_length_multiplier = 1.0 if v.ping_interval < 300: v.ping_interval = 300 gui.log_warning( "Minimal Linear Ping distance is 300mm! Your config stated: {}" .format(line)) gui.create_logitem("Linear Ping interval of {:-6.2f}mm".format( v.ping_interval)) return if line.endswith("LINEARPING"): gui.log_warning( "LINEARPING deprecated, use LINEARPINGLENGTH parameter instead") return # SIDE TRANSITIONING if "SIDEWIPELOC" in line: v.side_wipe_loc = stringparameter(line) return if "WIPEFEEDRATE" in line: v.wipe_feedrate = floatparameter(line) return if "SIDEWIPEMINY" in line: v.sidewipe_miny = floatparameter(line) return if "SIDEWIPEMAXY" in line: v.sidewipe_maxy = floatparameter(line) return if "SIDEWIPECORRECTION" in line: v.sidewipe_correction = floatparameter(line) if v.sidewipe_correction < 0.9 or v.sidewipe_correction > 1.10: v.sidewipe_correction = 1.0 return if "PURGETOWERDELTA" in line: if abs(floatparameter(line)) != abs(float(0)): v.max_tower_z_delta = abs(floatparameter(line)) gui.create_logitem( "Max Purge Tower Delta set to {:-2.2f}mm".format( v.max_tower_z_delta)) return if "FULLPURGEREDUCTION" in line: gui.create_logitem("Full purge reduction configured") v.full_purge_reduction = True if line.endswith("CHECKVERSION"): import p2pp.checkversion as cv import version latest = cv.get_version(cv.MASTER) if latest > version.Version: gui.create_logitem( "New development version of P2PP available ({})".format( latest), "red", False, "2.0") else: if (latest < version.Version): latest = cv.get_version(cv.DEV) if (latest > version.Version): gui.create_logitem( "New development version of P2PP available ({})". format(latest), "red", False, "2.0") # REPRAP COMPATIBILITY if "REPRAPCOMPATIBLE" in line: v.reprap_compatible = True return # Program parameters if "NOGUI" in line: v.gui = False return if "CONSOLEWAIT" in line: v.consolewait = True if "IGNOREWARNINGS" in line: v.ignore_warnings = True if "ABSOLUTEEXTRUDER" in line: v.absolute_extruder = True gui.create_logitem("Convert to absolute extrusion parameters")
if v.version > MASTER_VERSION: if v.version < DEV_VERSION: v.version += " (New dev version {} available)".format(DEV_VERSION) color = "red" else: v.version += " (Dev version up to date)" color = "green" else: if v.version < MASTER_VERSION: v.version += " (New stable version {} available)".format(MASTER_VERSION) color = "red" else: v.version += " (Version up to date)" color = "green" gui.create_logitem(v.version, color , True) gui.configinfo() gui.create_emptyline() gui.create_logitem("Line to be used in PrusaSlicer [Print Settings][Output Options][Post Processing Script]", "blue") gui.create_emptyline() if platformD == 'Darwin': gui.create_logitem("{}/p2pp.command".format(os.path.dirname(sys.argv[0])), "red") elif platformD == 'Windows': pathname = os.path.dirname(sys.argv[0]) pathname = pathname.replace(" ","! ") gui.create_logitem("{}\\p2pp.bat".format(os.path.dirname(sys.argv[0])), "red") gui.create_emptyline()
def check_config_parameters(keyword, value): keyword = keyword.upper().strip() if value is None: value = "" # allows for delaying the change of temperature until after the putge block # not sure if this should stay in future releases. To be evaluated # low complexity, but it adds a waiting position calculation which moves off the regular path. if keyword == "TEMPERATURECONTROL": v.process_temp = True # saves the unprocessed file, so it can be sent for processing simulation in case of errors if keyword == "SAVEUNPROCESSED": v.save_unprocessed = True # enable the preheat function on the Palette 3 if keyword == "P3_PROCESSPREHEAT": v.p3_process_preheat = True # defines the printer profile for config storage on the Palette hardware if keyword == "PRINTERPROFILE": value = value.strip(" ") _idlen = 16 if v.palette3: _idlen = 32 if len(value) != _idlen: gui.log_warning( "Invalid Printer profile! - Has invalid length (expect {}) - [{}]" .format(_idlen, value)) value = "" if not all(char in set("0123456789ABCDEFabcdef") for char in value): gui.log_warning( "Invalid Printer profile! - Invalid characters (expect 0123456789abcdef) - [{}]" .format(value)) value = "" if len(value) <= _idlen: v.printer_profile_string = value return # toggles hardware to Palette 3 - sets the number of inputs, output format. if keyword == "PALETTE3": if len(v.printer_profile_string) == 16: gui.log_warning( "Invalid Printer profile! - P3 printer profile should be 32 characters ({})" .format(v.printer_profile_string)) v.palette3 = True v.colors = 4 # Min first splice length for P3 == 130 v.min_start_splice_length = max(v.min_start_splice_length, v.min_first_splice_p3) v.min_splice_length = max(v.min_splice_length, v.min_splice_p3) check_splice_table() return # toggles hardware to Palette 3 Pro - sets the number of inputs, output format. if keyword == "PALETTE3_PRO": if len(v.printer_profile_string) == 16: gui.log_warning( "Invalid Printer profile! - P3 printer profile should be 32 characters" ) v.palette3 = True v.colors = 8 # Min first splice length for P3 == 130 v.min_start_splice_length = max(v.min_start_splice_length, v.min_first_splice_p3) v.min_splice_length = max(v.min_splice_length, v.min_splice_p3) check_splice_table() return if keyword == "P3_HOSTNAME": v.p3_hostname = value if keyword == "P3_PROFILENAME": v.p3_printername = value if keyword == "P3_UPLOADFILE": v.uploadfile = True if keyword == "P3_SHOWPRINTERPAGE": v.showwebbrowser = True # toggles Palette 3 accessory mode = added 22/02/2022 if keyword == "ACCESSORYMODE_MAFX": v.accessory_mode = True gui.create_logitem("Config: Palette3 Accessory Mode Selected") return # toggles Palette 2 accessory mode if keyword == "ACCESSORYMODE_MAF": v.accessory_mode = True v.colors = 4 gui.create_logitem("Config: Palette2 Accessory Mode Selected") check_splice_table() return # toggles Palette + Accessory Mode if keyword == "ACCESSORYMODE_MSF": v.accessory_mode = True v.palette_plus = True v.colors = 4 gui.create_logitem("Config: Palette+ Accessory Mode Selected") check_splice_table() return # Loading Offset - Required for the P+ configuration, take from existing print after callibration with Chroma if keyword == "P+LOADINGOFFSET": v.palette_plus_loading_offset = int(float(value)) return # PPM - Required for the P+ configuration, take from existing print after callibration with Chroma if keyword == "P+PPM": v.palette_plus_ppm = floatparameter(value) return # Splice offset defines how much the start of the toolchange is located after the position of the toolchange. # in general, you want this value as small as possible BUT this value is the buffer you need when material is consumed # at a too high rate, so putting it very low may result in early transition if keyword == "SPLICEOFFSET": v.splice_offset = floatparameter(value) gui.create_logitem("SPLICE OFFSET: {:-5.2f}mm".format(v.splice_offset)) return # This parameter sets the amount of extra filament that is generated at the end of the print, to allow for the filament to still # engage with the motor gears. This should be at least the plength of the path from the nozzel to the gears of the extruder motor if keyword == "EXTRAENDFILAMENT": v.extra_runout_filament = floatparameter(value) gui.create_logitem("Extra filament at end of print {:-8.2f}mm".format( v.extra_runout_filament)) return # This parameter specified the minimal amount of total filament USE ??? if keyword == "P3_MINIMALTOTALFILAMENT": v.minimaltotal_filament = floatparameter(value) gui.create_logitem("Minimal ilament length {:-8.2f}mm".format( v.minimaltotal_filament)) return # Specially Added for Manmeet - Not documented if keyword == "MANUAL_SWAP": v.manual_filament_swap = True gui.create_logitem("Manual filament swap in place.") return # May be removed ?? if keyword == "BEFORESIDEWIPEGCODE": v.before_sidewipe_gcode.append(value) return # May be removed ?? if keyword == "AFTERSIDEWIPEGCODE": v.after_sidewipe_gcode.append(value) return # unused ??? if keyword == "AUTOLOADINGOFFSET": v.autoloadingoffset = floatparameter(value) return # autmoaticall adds purge in case of short splices when fullpruereduction is applied if keyword == "AUTOADDPURGE": v.autoaddsplice = True return # special reauest feature - not documents - allows for pings shorter than 300 mm if keyword == "POWERCHAOS": # Special feature request to allow sub 300 mm pings v.powerchaos = True return # sets the minimal first splice length (100 / 130 for P2/P3 resp) if keyword == "MINSTARTSPLICE": v.min_start_splice_length = floatparameter(value) if v.palette3: if v.min_start_splice_length < v.min_first_splice_p3: gui.log_warning( "Minimal first slice length adjusted to {}mm for palette 3" .format(v.min_first_splice_p3)) v.min_start_splice_length = v.min_first_splice_p3 if v.min_start_splice_length < 100: v.min_start_splice_length = 100 gui.log_warning("Minimal first slice length adjusted to 100mm") return # firmware purge support if keyword == "FIRMWARE_PURGE_LENGTH": v.firmwarepurge = intparameter(value) return # SECTION BLOBSTER and BB3D # BB3D/BLOBSTER config parm if keyword in ["BIGBRAIN3D_BLOBSIZE", "BLOBSTER_BLOBSIZE"]: v.mechpurge_blob_size = intparameter(value) return # BLOBSTER config parm if keyword in ["BLOBSTER_ENGAGETIME"]: v.blobster_engagetime = intparameter(value) return # BB3D config parm if keyword == "BIGBRAIN3D_SINGLEBLOB": v.single_blob = True return # BB3D/blobster config parm if keyword in ["BIGBRAIN3D_BLOBSPEED", "BLOBSTER_BLOBSPEED"]: v.mechpurge_blob_speed = intparameter(value) return # BB3D/blobster config parm if keyword in ["BIGBRAIN3D_COOLINGTIME", "BLOBSTER_COOLINGTIME"]: v.mechpurge_blob_cooling_time = intparameter(value) return # BB3D/blobster config parm if keyword in ["BIGBRAIN3D_PURGEPOSITION", "BLOBSTER_PURGEPOSITION"]: v.mechpurge_x_position = floatparameter(value) return # BB3D config parm if keyword == "BIGBRAIN3D_PURGEYPOSITION": v.bigbrain3d_y_position = floatparameter(value) return # BB3D config parm if keyword == "BIGBRAIN3D_MOTORPOWER_HIGH": v.bigbrain3d_motorpower_high = intparameter(value) return # BB3D config parm if keyword == "BIGBRAIN3D_MOTORPOWER_NORMAL": v.bigbrain3d_motorpower_normal = intparameter(value) return # BB3D config parm if keyword == "BIGBRAIN3D_NUMBER_OF_WHACKS": v.bigbrain3d_whacks = intparameter(value) return # BB3D config parm if keyword in ["BIGBRAIN3D_PRIME_BLOBS", "BLOBSTER_PRIME_BLOBS"]: v.mechpurge_prime_blobs = intparameter(value) return # BB3D config parm if keyword == "BIGBRAIN3D_FAN_OFF_PAUSE": v.bigbrain3d_fanoffdelay = intparameter(value) return # BB3D config parm if keyword == "BIGBRAIN3D_LEFT_SIDE": v.bigbrain3d_left = -1 return # BB3D/BLOBSTER config parm if keyword in ["BIGBRAIN3D_CLEARANCE_MM", "BLOBSTER_CLEARANCE_MM"]: v.mechpurge_minimalclearenceheight = floatparameter(value) return # BB3D/BLBSTER config parm if keyword in ["BIGBRAIN3D_RETRACT", "BLOBSTER_RETRACT"]: v.mechpurge_retract = floatparameter(value) return # BB3D/BLOBSTER config parm if keyword == "BIGBRAIN3D_ENABLE": if not v.wipe_remove_sparse_layers: v.bigbrain3d_purge_enabled = True gui.create_logitem( "<b>BIGBRAIN3D Will only work with installed hardware on a Prusa Printer</b>" ) else: gui.log_warning( "<b>BIGBRAIN3D mode not compatible with sparse wipe tower in PS</b>" ) return if keyword == "BLOBSTER_ADVANCED": v.blobster_advanced = True gui.create_logitem("<b>BLOBSTER ADVANCED MODE ENABLED</b>") return if keyword == "BLOBSTER_ADVANCED_LENGTH": v.blobster_advanced_length = [] fields = value.split(",") for i in fields: try: v.blobster_advanced_length.append(abs(int(i))) except ValueError: gui.log_warning( "BLOBSTER_ADVANCED_LENGTH parameter accepts a list of interger values (length in mm)" ) return if keyword == "BLOBSTER_ADVANCED_SPEED": v.blobster_advanced_speed = [] fields = value.split(",") for i in fields: try: v.blobster_advanced_speed.append(abs(int(i))) except ValueError: gui.log_warning( "BLOBSTER_ADVANCED_SPEEDH parameter accepts a list of interger values (length in mm)" ) return if keyword == "BLOBSTER_ADVANCED_FAN": v.blobster_advanced_fan = [] fields = value.split(",") for i in fields: try: v.blobster_advanced_fan.append( int(min(abs(int(i)), 100) * 2.55)) except ValueError: gui.log_warning( "BLOBSTER_ADVANCED_FAN parameter accepts a list of interger values (percentage 0-100)" ) return # BB3D/BLOBSTER config parm if keyword == "BLOBSTER_ENABLE": if not v.wipe_remove_sparse_layers: v.blobster_purge_enabled = True v.mechpurge_blob_size = 180 v.mechpurge_minimalclearenceheight = 30 v.mechpurge_blob_cooling_time = 60 gui.create_logitem( "<b>BLOBSTER Will only work with installed hardware on a Prusa Printer</b>" ) else: gui.log_warning( "<b>BLOBSTER mode not compatible with sparse wipe tower in PS</b>" ) return # BB3D/BLOBSTER config parm if keyword in ["BIGBRAIN3D_SMARTFAN", "BLOBSTER_SMARTFAN"]: v.mechpurge_smartfan = True return # defines the minimal splice length ( this is the safe length to make sure a splice is only heated once (70/90 for P2/P3 resp) if keyword == "MINSPLICE": v.min_splice_length = floatparameter(value) if v.palette3: if v.min_splice_length < v.min_splice_p3: gui.log_warning( "Minimal slice length adjusted to {}mm for palette 3". format(v.min_splice_p3)) v.min_splice_length = v.min_splice_p3 if v.min_splice_length < 70: v.min_splice_length = 70 gui.log_warning("Minimal slice length adjusted to 70mm") return # LINEAR PING removed # set the distance between pings (same length every time), instead of increasing ping lengths if keyword == "LINEARPINGLENGTH": v.ping_interval = floatparameter(value) v.ping_length_multiplier = 1.0 if not v.powerchaos: if v.ping_interval < 100: v.ping_interval = 100 gui.log_warning( "Minimal Linear Ping distance is 300mm! Your config stated: {}" .format(value)) gui.create_logitem("Linear Ping interval of {:-6.2f}mm".format( v.ping_interval)) return # Set the location for the side wipes if keyword == "SIDEWIPELOC": v.side_wipe_loc = value return # define a Z-Hop for jumps to the wipe location if keyword == "SIDEWIPEZHOP": v.addzop = floatparameter(value) gui.create_logitem("Side Wipe ZHOP of {:3.2f}mm".format(v.addzop)) # maybe removed?? if keyword == "SIDEWIPEZHOP_SKIPRETURN": v.sidewipe_delay_zreturn = True # set the highest top speed for purging if keyword == "PURGETOPSPEED": v.purgetopspeed = int(floatparameter(value)) # if parameter specified is below 200 then the value is assumed mm/sec and is converted to mm/min if v.purgetopspeed < 200: v.purgetopspeed = v.purgetopspeed * 60 gui.create_logitem( "Purge Max speed set to {:.0f}mm/min ({}mm/s)".format( v.purgetopspeed, v.purgetopspeed / 60)) return # set the wipe feedrate if keyword == "WIPEFEEDRATE": v.wipe_feedrate = floatparameter(value) return # define the sidewipe minimal and macimal Y position if keyword == "SIDEWIPEMINY": v.sidewipe_miny = floatparameter(value) return # define the sidewipe minimal and macimal Y position if keyword == "SIDEWIPEMAXY": v.sidewipe_maxy = floatparameter(value) return # define a extrusion multiplier for sidewipe. needed??? if keyword == "SIDEWIPECORRECTION": v.sidewipe_correction = floatparameter(value) if v.sidewipe_correction < 0.9 or v.sidewipe_correction > 1.10: v.sidewipe_correction = 1.0 return # apply delta (similar to sparse layer removal in PS2.4 if keyword == "PURGETOWERDELTA": parm = abs(floatparameter(value)) if parm > 0.001 and v.wipe_remove_sparse_layers: gui.log_warning( "TOWER DELTA feature mode not compatible with sparse wipe tower in PS" ) v.max_tower_delta = 0.0 else: if parm != float(0): v.max_tower_z_delta = abs(floatparameter(value)) gui.create_logitem( "Max Purge Tower Delta set to {:-2.2f}mm".format( v.max_tower_z_delta)) return # simlir to tower delta but rather reduces the base of the tower to make it growmore evenly with the print if keyword == "FULLPURGEREDUCTION": if not v.wipe_remove_sparse_layers: gui.create_logitem("Full purge reduction configured") v.full_purge_reduction = True v.needpurgetower = True else: gui.log_warning( "FULL PURGE TOWER REDUCTION feature mode not compatible with sparse wipe tower in PS" ) v.full_purge_reduction = False return # chech the version of P2PP on startup (requires an internet connection) if keyword == "CHECKVERSION": import p2pp.checkversion as cv import version latest = cv.get_version(cv.MASTER) if latest: if latest > version.Version: gui.create_logitem( "New version of P2PP available ({})".format(latest), "red", False, "2.0") # co be removed ? if keyword == "DO_NOT_GENERATE_M0": v.generate_M0 = False return # wait for user feedback at the ned of processing if keyword == "CONSOLEWAIT": v.consolewait = True return if keyword == "FINISH_MOVES_M400": v.finish_moves = "M400" v.replace_G4S0 = True # process toolchanges the KLIPPER way if keyword == "KLIPPER_TOOLCHANGE": v.klipper = True return # close and continue even if there are warnings if keyword == "IGNOREWARNINGS": v.ignore_warnings = True return # p2pp_process_file a gcode file with absolute extrusios instead of relative ones if keyword == "ABSOLUTEEXTRUDER": v.absolute_extruder = True gui.create_logitem("Convert to absolute extrusion parameters") return # unused !!! to be removed. if keyword == "DEBUGTCOMMAND": v.debug_leaveToolCommands = True gui.log_warning( "DEBUGTCOMMAND ACTIVE - File will not print correctly!!") return
def generate(input_file, output_file, printer_profile, splice_offset, silent): starttime = time.time() v.printer_profile_string = printer_profile basename = os.path.basename(input_file) _taskName = os.path.splitext(basename)[0].replace(" ", "_") _taskName = _taskName.replace(".mcf", "") v.splice_offset = splice_offset try: # python 3.x opf = open(input_file, encoding='utf-8') except TypeError: try: # python 2.x opf = open(input_file) except IOError: if v.gui: gui.user_error("P2PP - Error Occurred", "Could not read input file\n'{}'".format(input_file)) else: print ("Could not read input file\n'{}".format(input_file)) return except IOError: if v.gui: gui.user_error("P2PP - Error Occurred", "Could not read input file\n'{}'".format(input_file)) else: print ("Could not read input file\n'{}".format(input_file)) return gui.setfilename(input_file) gui.set_printer_id(v.printer_profile_string) gui.create_logitem("Reading File " + input_file) gui.progress_string(1) v.input_gcode = opf.readlines() opf.close() v.input_gcode = [item.strip() for item in v.input_gcode] gui.create_logitem("Analyzing slicer parameters") gui.progress_string(2) parse_slic3r_config() gui.create_logitem("Pre-parsing GCode") gui.progress_string(4) parse_gcode() if v.tower_delta or v.full_purge_reduction: if v.variable_layer: gui.log_warning("Variable layers are not compatible with fullpruge/tower delta") if v.process_temp and v.side_wipe: gui.log_warning("TEMPERATURECONTROL and Side Wipe / BigBrain3D are not compatible") if v.palette_plus: if v.palette_plus_ppm == -9: gui.log_warning("P+ parameter P+PPM not set correctly in startup GCODE") if v.palette_plus_loading_offset == -9: gui.log_warning("P+ parameter P+LOADINGOFFSET not set correctly in startup GCODE") v.side_wipe = not coordinate_on_bed(v.wipetower_posx, v.wipetower_posy) v.tower_delta = v.max_tower_z_delta > 0 gui.create_logitem("Creating tool usage information") m4c.calculate_loadscheme() if v.side_wipe: if v.skirts and v.ps_version > "2.2": gui.log_warning("SIDEWIPE and SKIRTS are NOT compatible in PS2.2 or later") gui.log_warning("THIS FILE WILL NOT PRINT CORRECTLY") if v.wipe_remove_sparse_layers: gui.log_warning("SIDE WIPE mode not compatible with sparse wipe tower in PS") gui.log_warning("THIS FILE WILL NOT PRINT CORRECTLY") gui.create_logitem("Side wipe activated", "blue") if v.full_purge_reduction: gui.log_warning("Full Purge Reduction is not compatible with Side Wipe, performing Side Wipe") v.full_purge_reduction = False if v.full_purge_reduction: v.side_wipe = False gui.create_logitem("Full Tower Reduction activated", "blue") if v.tower_delta: gui.log_warning("Full Purge Reduction is not compatible with Tower Delta, performing Full Purge Reduction") v.tower_delta = False v.pathprocessing = (v.tower_delta or v.full_purge_reduction or v.side_wipe) if v.autoaddsplice and not v.full_purge_reduction and not v.side_wipe: gui.log_warning("AUTOEDDPURGE only works with side wipe and fullpurgereduction at this moment") if (len(v.skippable_layer) == 0) and v.pathprocessing: gui.log_warning("LAYER configuration is missing. NO OUTPUT FILE GENERATED.") gui.log_warning("Check the P2PP documentation for furhter info.") else: if v.tower_delta: optimize_tower_skip(v.max_tower_z_delta, v.layer_height) if v.side_wipe: optimize_tower_skip(999, v.layer_height) gui.create_logitem("Generate processed GCode") total_line_count = len(v.input_gcode) v.retraction = 0 for process_line_count in range(total_line_count): gcode_parseline(process_line_count) gui.progress_string(50 + 50 * process_line_count // total_line_count) v.processtime = time.time() - starttime gcode_process_toolchange(-1, v.total_material_extruded, 0) omega_result = header_generate_omega(_taskName) header = omega_result['header'] + omega_result['summary'] + omega_result['warnings'] if v.absolute_extruder and v.gcode_has_relative_e: gui.create_logitem("Converting to absolute extrusion") convert_to_absolute() # write the output file ###################### if not output_file: output_file = input_file gui.create_logitem("Generating GCODE file: " + output_file) opf = open(output_file, "w") if not v.accessory_mode: opf.writelines(header) opf.write("\n\n;--------- START PROCESSED GCODE ----------\n\n") if v.accessory_mode: opf.write("M0\n") opf.write("T0\n") if v.splice_offset == 0: gui.log_warning("SPLICE_OFFSET not defined") opf.writelines(v.processed_gcode) opf.close() if v.accessory_mode: pre, ext = os.path.splitext(output_file) if v.palette_plus: maffile = pre + ".msf" else: maffile = pre + ".maf" gui.create_logitem("Generating PALETTE MAF/MSF file: " + maffile) maf = open(maffile, 'w') for h in header: h = h.strip('\r\n') maf.write(unicode(h)) maf.write('\r\n') maf.close() # # with io.open(maffile, 'w', newline='\r\n') as maf: # # for i in range(len(header)): # h = header[i].strip('\n\r') + "\n" # if not h.startswith(";"): # try: # maf.write(unicode(h)) # except: # maf.write(h) gui.print_summary(omega_result['summary']) gui.progress_string(100) if (len(v.process_warnings) > 0 and not v.ignore_warnings) or v.consolewait: gui.close_button_enable()
def p2pp_process_file(input_file, output_file): starttime = time.time() if output_file is None: output_file = input_file # get the base name from the environment variable if available.... # check for P3 that output is written to file at this point. # check for P3 that the output file is named mcfx try: basename = os.environ["SLIC3R_PP_OUTPUT_NAME"] pathname = os.path.dirname(os.environ["SLIC3R_PP_OUTPUT_NAME"]) maffile = basename mybasename = os.path.basename(basename) if v.palette3 and not os.environ["SLIC3R_PP_HOST"].startswith("File"): gui.log_warning("Palette 3 File uploading currently not supported") if v.palette3 and not os.environ["SLIC3R_PP_HOST"].endswith(".mcfx"): gui.log_warning("Palette 3 files should have a .mcfx extension") # if any the retrieval of this information fails, the good old way is used except KeyError: maffile = output_file basename = os.path.basename(input_file) mybasename = basename pathname = os.path.dirname(input_file) gui.setfilename(basename) # Determine the task name for this print form the filename without any extensions. _task_name = os.path.splitext(mybasename)[0].replace(" ", "_") _task_name = _task_name.replace(".mcfx", "") _task_name = _task_name.replace(".mcf", "") _task_name = _task_name.replace(".gcode", "") gui.app.sync() # Read the input file try: opf = open(input_file, encoding='utf-8') gui.create_logitem("Reading File " + input_file) gui.progress_string(1) v.input_gcode = opf.readlines() opf.close() v.input_gcode = [item.strip() for item in v.input_gcode] except (IOError, MemoryError): gui.log_warning("Error Reading: '{}'".format(input_file)) return gui.create_logitem("Analyzing Prusa Slicer Configuration") gui.progress_string(2) parse_config_parameters( ) # Parse the Prusa Slicer and P2PP Config Parameters # Write the unprocessed file if v.save_unprocessed: pre, ext = os.path.splitext(input_file) of = pre + "_unprocessed" + ext gui.create_logitem("Saving unpocessed code to: " + of) opf = open(of, "wb") for line in v.input_gcode: opf.write(line.encode('utf8')) opf.write("\n" "".encode('utf8')) opf.close() gui.progress_string(4) gui.create_logitem("GCode Analysis ... Pass 1") parse_gcode_first_pass() if config_checks() == -1: return gui.create_logitem("Gcode Analysis ... Pass 2") parse_gcode_second_pass() v.processtime = time.time() - starttime omega_result = header_generate_omega(_task_name) header = omega_result['header'] + omega_result['summary'] + omega_result[ 'warnings'] # write the output file ###################### path, _ = os.path.split(output_file) if v.palette3 and not v.accessory_mode: opf = open(os.path.join(path, "print.gcode"), "wb") gui.create_logitem("Generating MCFX file: " + output_file) else: opf = open(output_file, "wb") gui.create_logitem( "Generating GCODE file: (temp location, PS will move) " + output_file) if not v.accessory_mode and not v.palette3: for line in header: opf.write(line.encode('utf8')) opf.write( ("\n\n;--------- THIS CODE HAS BEEN PROCESSED BY P2PP v{} --- \n\n" .format(version.Version)).encode('utf8')) if v.generate_M0: header.append("M0\n") opf.write("T0\n".encode('utf8')) else: opf.write( ("\n\n;--------- THIS CODE HAS BEEN PROCESSED BY P2PP v{} --- \n\n" .format(version.Version)).encode('utf8')) if v.splice_offset == 0: gui.log_warning("SPLICE_OFFSET not defined") for line in v.processed_gcode: try: opf.write(line.encode('utf8')) except IOError: gui.log_warning( "Line : {} could not be written to output".format(line)) opf.write("\n".encode('utf8')) opf.close() if v.palette3: meta, palette = header_generate_omega_palette3(None) meta_file = os.path.join(path, "meta.json") palette_file = os.path.join(path, "palette.json") im_file = os.path.join(path, "thumbnail.png") # 22/02/2022 added accessory mode for palette 3 if v.accessory_mode: gcode_file = os.path.join(path, output_file) else: gcode_file = os.path.join(path, "print.gcode") gui.create_logitem("Generating Palette 3 output files") mf = open(meta_file, 'wb') mf.write(meta.__str__().encode('ascii')) mf.close() pa = open(palette_file, 'wb') pa.write(palette.__str__().encode('ascii')) pa.close() im = open(im_file, "wb") if len(v.p3_thumbnail_data) == 0: gui.log_warning( "Thumbnail Info missing (Printer Settings/General/Firmware/G-Code Thumbnail" ) im.write(base64.b64decode(v.p3_thumbnail_data)) im.close() # 22/02/2022 added accessory mode for palette 3 if v.accessory_mode: maffile = maffile + ".mafx" maffile = maffile.replace(".gcode", "") gui.create_logitem("Generating PALETTE MAFX file: " + maffile) zipf = zipfile.ZipFile(maffile, 'w', zipfile.ZIP_DEFLATED) zipf.write(meta_file, "meta.json") zipf.write(palette_file, "palette.json") zipf.write(im_file, "thumbnail.png") zipf.close() else: zipf = zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) zipf.write(meta_file, "meta.json") zipf.write(palette_file, "palette.json") zipf.write(gcode_file, "print.gcode") zipf.write(im_file, "thumbnail.png") zipf.close() os.remove(os.path.join(path, "print.gcode")) os.remove(meta_file) os.remove(palette_file) os.remove(im_file) # 22/02/2022 added accessory mode for palette 3 if v.accessory_mode and not v.palette3: pre, ext = os.path.splitext(maffile) if v.palette_plus: maffile = pre + ".msf" else: maffile = pre + ".maf" maffile = os.path.basename(maffile) maffile = os.path.join(pathname, maffile) gui.create_logitem("Generating PALETTE MAF/MSF file: " + maffile) maf = open(maffile, 'wb') for h in header: h = str(h).strip("\r\n") maf.write(h.encode('ascii')) maf.write("\r\n".encode('ascii')) maf.close() gui.print_summary(omega_result['summary']) gui.progress_string(101) if v.palette3: gui.create_logitem( "===========================================================================================", "green") gui.create_logitem( "Go to https://github.com/tomvandeneede/p2pp/wiki for more information on P2PP Configuration", "green") gui.create_logitem( "===========================================================================================", "green") if v.uploadfile: try: # get the correct output filename from the PS environment variable filename = os.path.basename(os.environ["SLIC3R_PP_OUTPUT_NAME"]) if filename.endswith(".gcode"): filename = filename.replace(".gcode", ".mcfx") filename = filename.replace(" ", "_") except (TypeError, KeyError): # regardsless of the error, use this filename filename = "output.mcfx" upload.uploadfile(output_file, filename) if (len(v.process_warnings) > 0 and not v.ignore_warnings) or v.consolewait: gui.close_button_enable()
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
def parse_slic3r_config(): for idx in range(len(v.input_gcode) - 1, -1, -1): gcode_line = v.input_gcode[idx] if gcode_line.startswith("; filament_settings_id"): v.filament_ids = split_csv_strings(gcode_line) if ("generated by PrusaSlicer") in gcode_line: try: s1 = gcode_line.split("+") s2 = s1[0].split(" ") v.ps_version = s2[-1] gui.create_logitem( "File was created with PS version:{}".format(v.ps_version)) if v.ps_version < "2.2": gui.log_warning( "This version of P2PP is optimized to work with PS2.2!" ) except: pass continue if gcode_line.startswith("; wipe_tower_no_sparse_layers"): parameter_start = gcode_line.find("=") if parameter_start != -1: try: v.wipe_remove_sparse_layers = (int( gcode_line[parameter_start + 1:].strip()) == 1) except: pass continue if gcode_line.startswith("; wipe_tower_x"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipetower_posx = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; min_skirt_length"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.skirtsize = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; skirts"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.skirts = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; wipe_tower_width"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipetower_width = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; wipe_tower_y"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipetower_posy = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; extrusion_width"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.extrusion_width = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; infill_speed"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.infill_speed = float( gcode_line[parameter_start + 1:].strip()) * 60 continue if gcode_line.startswith("; layer_height"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.layer_height = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; first_layer_height"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.first_layer_height = float(gcode_line[parameter_start + 1:].strip()) continue if gcode_line.startswith("; support_material_synchronize_layers"): parameter_start = gcode_line.find("=") if parameter_start != -1: tmp = float(gcode_line[parameter_start + 1:].strip()) if tmp == 0: v.synced_support = False else: v.synced_support = True continue if gcode_line.startswith("; support_material "): parameter_start = gcode_line.find("=") if parameter_start != -1: tmp = float(gcode_line[parameter_start + 1:].strip()) if tmp == 0: v.support_material = False else: v.support_material = True continue # TVDE: needs to be expanded to be able to support more than 4 colors if gcode_line.startswith("; extruder_colour") or gcode_line.startswith( "; filament_colour"): filament_colour = '' parameter_start = gcode_line.find("=") gcode_line = gcode_line[parameter_start + 1:].strip() parameter_start = gcode_line.find("#") if parameter_start != -1: filament_colour = gcode_line.split(";") if len(filament_colour) >= 4: for i in range(len(filament_colour)): if filament_colour[i] == "": filament_colour[i] = v.filament_color_code[i] else: v.filament_color_code[i] = filament_colour[i][1:] continue if gcode_line.startswith("; filament_diameter"): parameter_start = gcode_line.find("=") if parameter_start != -1: filament_diameters = gcode_line[parameter_start + 1:].strip(" ").split(",") if len(filament_diameters) >= 4: for i in range(4): v.filament_diameter[i] = float(filament_diameters[i]) continue # TVDE: needs to be expanded to be able to support more than 4 colors # only check that is needed is that if nore than 4 colors exist, all must be of same type if gcode_line.startswith("; filament_type"): parameter_start = gcode_line.find("=") if parameter_start != -1: filament_string = gcode_line[parameter_start + 1:].strip(" ").split(";") v.m4c_numberoffilaments = len(filament_string) if v.m4c_numberoffilaments == 4: v.filament_type = filament_string v.used_filament_types = list(set(filament_string)) elif v.m4c_numberoffilaments >= 4: v.used_filament_types = list(set(filament_string)) if len(v.used_filament_types) > 1: gui.log_warning( "Prints with more than 4 colors should be of one filament type only!" ) gui.log_warning("This file will not print correctly") v.filament_type = filament_string[:4] if v.m4c_numberoffilaments > 4: gui.log_warning( "Number of inputs defined in print: {}. Swaps may be required!!!" .format(v.m4c_numberoffilaments)) continue # TVDE: needs to be expanded to be able to support more than 4 colors # if more than 4, just retain the first four (check is done at other level, but for not all settings should be the same) if gcode_line.startswith("; retract_lift = "): if v.filament_list: continue lift_error = False parameter_start = gcode_line.find("=") if parameter_start != -1: retracts = gcode_line[parameter_start + 1:].strip(" ").split(",") if len(retracts) >= 4: for i in range(4): v.retract_lift[i] = float(retracts[i]) if v.retract_lift[i] == 0: lift_error = True if lift_error: gui.log_warning( "[Printer Settings]->[Extruders 1 -> {}]->[Retraction]->[Lift Z] should not be set to zero." .format(len(retracts))) gui.log_warning("Generated file might not print correctly") continue # TVDE: needs to be expanded to be able to support more than 4 colors # if more than 4, just retain the first four (check is done at other level, but for not all settings should be the same) if gcode_line.startswith("; retract_length = "): retract_error = False parameter_start = gcode_line.find("=") if parameter_start != -1: retracts = gcode_line[parameter_start + 1:].strip(" ").split(",") if len(retracts) >= 4: for i in range(4): v.retract_length[i] = float(retracts[i]) if v.retract_length[i] == 0.0: retract_error = True if retract_error: gui.log_warning( "[Printer Settings]->[Extruders 1 -> {} 4]->[Retraction Length] should not be set to zero." .format(len(retracts))) continue if gcode_line.startswith("; gcode_flavor"): if "reprap" in gcode_line: v.isReprap_Mode = True continue if "use_firmware_retraction" in gcode_line: parameter_start = gcode_line.find("=") if parameter_start != -1: gcode_line = gcode_line[parameter_start + 1:].replace(";", "") if "1" in gcode_line: v.use_firmware_retraction = True else: v.use_firmware_retraction = False continue if "use_relative_e_distances" in gcode_line: parameter_start = gcode_line.find("=") if parameter_start != -1: gcode_line = gcode_line[parameter_start + 1:].replace(";", "") if "1" in gcode_line: v.gcode_has_relative_e = True else: v.gcode_has_relative_e = False continue # TVDE: needs to be expanded to be able to support more than 4 colors # this should be expanded to nxn filaments where n = the number of filaments used. # needs to be a perfect square, calculate from there. if gcode_line.startswith("; wiping_volumes_matrix"): wiping_info = [] parameter_start = gcode_line.find("=") if parameter_start != -1: wiping_info = gcode_line[parameter_start + 1:].strip(" ").split(",") _warning = True for i in range(len(wiping_info)): if int(wiping_info[i]) != 140 and int(wiping_info[i]) != 0: _warning = False wiping_info[i] = filament_volume_to_length( float(wiping_info[i])) v.max_wipe = max(wiping_info) v.wiping_info = wiping_info if _warning: gui.log_warning( "All purge lenghts 70/70 OR 140. Purge lenghts may not have been set correctly." ) continue
def uploadfile(localfile, p3file): global total_bytes _error = None v.retry_state = True # read file # check the size of the file to be sent: try: file_size = os.path.getsize(localfile) if file_size > 1 << 30: #1GB gui.log_warning( "Filesize ({}) exceeds 1GB. This file cannot be uploaded.". format(file_size)) gui.log_warning( "Please use a memory stick to transfer files >1GB.") return except OSError: gui.log_warning( "Upload file does not seems to be ready for uploading (does not exists of is inaccessible)" ) gui.log_warning("Please try uploading using a memory stick.") return while v.p3_hostname == "": form.label_5.setText( "Please specify hostname or IP.\nP3_HOSTNAME config parameter missing." ) form.RetryButton.setText("Upload") window.show() gui.app.exec() v.p3_hostname = form.hostname.text() else: form.hostname.setText(v.p3_hostname) form.RetryButton.setText("Retry") gui.create_logitem( "Sending file {} to P3 ({})".format(p3file, v.p3_hostname), "blue", True) gui.app.sync() while v.retry_state: try: with open(localfile, "rb") as mcfx_file: gui.create_logitem("Uploading {}".format(p3file), "blue", True) encoder = MultipartEncoder({ 'printFile': (p3file, mcfx_file, "application/octet-stream"), }) data = MultipartEncoderMonitor(encoder, callback) gui.create_logitem("|" + '.' * 50 + "|", "blue", True) total_bytes = encoder.len # data = {'printFile': (p3file, mcfx_file, "application/octet-stream")} url = "http://{}:5000/print-file".format(v.p3_hostname) response = requests.post( url, data=data, headers={'Content-Type': data.content_type}) if response.ok: _error = None v.retry_state = False gui.create_logitem("Upload completed".format(p3file), "blue", True) else: _error = "Error [{}] {} ".format(response.status_code, response.reason) except Exception as e: print(e) gui.log_warning("Could not send file ({}) to P3 ({})".format( p3file, v.p3_hostname)) gui.app.sync() _error = "Connection Error occurred!" if v.showwebbrowser and _error is None: try: tgtName = "http://{}:5000".format(v.p3_hostname) webform.webBrowser.load(QtCore.QUrl(tgtName)) webwindow.show() gui.app.exec() except Exception as e: gui.logexception(e) if v.retry_state and _error is not None: form.label_5.setText(_error) window.show() gui.app.exec() v.p3_hostname = form.hostname.text() gui.close_button_enable()
def config_checks(): # CHECK BED SIZE PARAMETERS if v.bed_size_x == -9999 or v.bed_size_y == -9999 or v.bed_origin_x == -9999 or v.bed_origin_y == -9999: gui.log_warning("Bedsize nor or incorrectly defined.") v.bed_max_x = v.bed_origin_x + v.bed_size_x v.bed_max_y = v.bed_origin_y + v.bed_size_y # CHECK EXTRUSION WIDTH if v.extrusion_width == 0: gui.create_logitem("Extrusionwidth set to 0, defaulted back to 0.45") v.extrusion_width = 0.45 if v.process_temp and v.side_wipe: gui.log_warning( "TEMPERATURECONTROL and SIDEWIPE / BigBrain3D are incompatible (TEMPERATURECONTROL disabled" ) v.process_temp = False if v.palette_plus: if v.palette_plus_ppm == -9: gui.log_warning( "P+ parameter P+PPM incorrectly set up in startup GCODE - Processing Halted" ) return -1 if v.palette_plus_loading_offset == -9: gui.log_warning( "P+ parameter P+LOADINGOFFSET incorrectly set up in startup GCODE - Processing Halted" ) return -1 v.side_wipe = not ((v.bed_origin_x <= v.wipe_tower_posx <= v.bed_max_x) and (v.bed_origin_y <= v.wipe_tower_posy <= v.bed_max_y)) v.tower_delta = v.max_tower_z_delta > 0 if (v.tower_delta or v.full_purge_reduction) and v.variable_layer: gui.log_warning( "Variable layers may cause issues with FULLPURGE / TOWER DELTA") gui.log_warning( "This warning could be caused by support that will print on variable layer offsets" ) # sidewipe option compatibility test if v.side_wipe: if v.full_purge_reduction: gui.log_warning( "FULLURGEREDUCTION is incompatible with SIDEWIPE, parameter ignored" ) v.full_purge_reduction = False if v.skirts: if v.ps_version >= "2.2": gui.log_warning( "SIDEWIPE and SKIRTS are NOT compatible in PS2.2 or later") if v.wipe_remove_sparse_layers: gui.log_warning( "SIDE WIPE mode not compatible with sparse wipe tower in PS - Processing Halted" ) return -1 gui.create_logitem("Side wipe activated", "blue") # fullpurge option compatibility test if v.full_purge_reduction: if v.skirts: gui.log_warning( "FULLPURGE and SKIRTS are NOT compatible. Overlaps may occur") if v.tower_delta: gui.log_warning( "FULLPURGEREDUCTION is incompatible with TOWERDELTA") v.tower_delta = False gui.create_logitem("FULLPURGEREDUCTION activated", "blue") # auto add splice length only works with full purge reeduction / sidewipe if v.autoaddsplice: if not v.full_purge_reduction and not v.side_wipe: gui.log_warning( "AUTOADDPURGE only works with SIDEWIPE and FULLPURGEREDUCTION") else: gui.create_logitem("Automatic Splice length increase activated", "blue") if len(v.skippable_layer) == 0: gui.log_warning("P2PP Layer Configuration is missing!!") return -1 skippable = optimize_tower_skip(int(v.max_tower_z_delta / v.layer_height)) if v.tower_delta: v.skippable_layer[0] = False if skippable > 0: gui.log_warning( "TOWERDELTA in effect for {} Layers or {:.2f}mm".format( skippable, skippable * v.layer_height)) else: gui.create_logitem("TOWERDELTA could not be applied to this print") v.tower_delta = False return 0
def on_clickabort(): v.retry_state = False gui.create_logitem("Upload aborted by user") window.hide() webwindow.hide() gui.app.quit()
def generate(input_file, output_file, printer_profile, splice_offset, silent): starttime = time.time() v.printer_profile_string = printer_profile basename = os.path.basename(input_file) _taskName = os.path.splitext(basename)[0].replace(" ", "_") _taskName = _taskName.replace(".mcf", "") v.splice_offset = splice_offset try: # python 3.x opf = open(input_file, encoding='utf-8') except TypeError: try: # python 2.x opf = open(input_file) except IOError: if v.gui: gui.user_error( "P2PP - Error Occurred", "Could not read input file\n'{}'".format(input_file)) else: print("Could not read input file\n'{}".format(input_file)) return except IOError: if v.gui: gui.user_error( "P2PP - Error Occurred", "Could not read input file\n'{}'".format(input_file)) else: print("Could not read input file\n'{}".format(input_file)) return gui.setfilename(input_file) gui.set_printer_id(v.printer_profile_string) gui.create_logitem("Reading File " + input_file) gui.progress_string(1) v.input_gcode = opf.readlines() opf.close() v.input_gcode = [item.strip() for item in v.input_gcode] gui.create_logitem("Analyzing slicer parameters") gui.progress_string(2) parse_slic3r_config() gui.create_logitem("Pre-parsing GCode") gui.progress_string(4) parse_gcode() if v.palette_plus: if v.palette_plus_ppm == -9: gui.log_warning( "P+ parameter P+PPM not set correctly in startup GCODE") if v.palette_plus_loading_offset == -9: gui.log_warning( "P+ parameter P+LOADINGOFFSET not set correctly in startup GCODE" ) v.side_wipe = not coordinate_on_bed(v.wipetower_posx, v.wipetower_posy) v.tower_delta = v.max_tower_z_delta > 0 if v.side_wipe: gui.create_logitem("Side wipe activated", "blue") if v.full_purge_reduction: gui.log_warning( "Full Purge Reduction is not compatible with Side Wipe, performing Side Wipe" ) v.full_purge_reduction = False if v.full_purge_reduction: v.side_wipe = False gui.create_logitem("Full Tower Reduction activated", "blue") if v.tower_delta: gui.log_warning( "Full Purge Reduction is not compatible with Tower Delta, performing Full Purge Reduction" ) v.tower_delta = False v.pathprocessing = (v.tower_delta or v.full_purge_reduction or v.side_wipe) if v.tower_delta: optimize_tower_skip(v.max_tower_z_delta, v.layer_height) if v.side_wipe: optimize_tower_skip(999, v.layer_height) gui.create_logitem("Generate processed GCode") total_line_count = len(v.input_gcode) v.retraction = 0 for process_line_count in range(total_line_count): gcode_parseline(process_line_count) gui.progress_string(50 + 50 * process_line_count // total_line_count) v.processtime = time.time() - starttime gcode_process_toolchange(-1, v.total_material_extruded, 0) omega_result = header_generate_omega(_taskName) header = omega_result['header'] + omega_result['summary'] + omega_result[ 'warnings'] if v.absolute_extruder and v.gcode_has_relative_e: gui.create_logitem("Converting to absolute extrusion") convert_to_absolute() # write the output file ###################### if not output_file: output_file = input_file gui.create_logitem("Generating GCODE file: " + output_file) opf = open(output_file, "w") if not v.accessory_mode: opf.writelines(header) opf.write("\n\n;--------- START PROCESSED GCODE ----------\n\n") if v.accessory_mode: opf.write("M0\n") opf.write("T0\n") if v.splice_offset == 0: gui.log_warning("SPLICE_OFFSET not defined") opf.writelines(v.processed_gcode) opf.close() if v.accessory_mode: pre, ext = os.path.splitext(output_file) if v.palette_plus: maffile = pre + ".msf" else: maffile = pre + ".maf" gui.create_logitem("Generating PALETTE MAF/MSF file: " + maffile) opf = open(maffile, "w") for i in range(len(header)): if not header[i].startswith(";"): opf.write(header[i]) gui.print_summary(omega_result['summary']) gui.progress_string(100) if (len(v.process_warnings) > 0 and not v.ignore_warnings) or v.consolewait: gui.close_button_enable()
def parse_slic3r_config(): for idx in range(len(v.input_gcode) - 1, -1, -1): gcode_line = v.input_gcode[idx] if ("generated by PrusaSlicer") in gcode_line: try: s1 = gcode_line.split("+") s2 = s1[0].split(" ") v.ps_version = s2[-1] gui.create_logitem("File was created with PS version:{}".format(v.ps_version)) if v.ps_version < "2.2": gui.log_warning("This version of P2PP is optimized to work with PS2.2!") except: pass if gcode_line.startswith("; wipe_tower_no_sparse_layers"): parameter_start = gcode_line.find("=") if parameter_start != -1: try: v.wipe_remove_sparse_layers = (int(gcode_line[parameter_start + 1:].strip()) == 1) except: pass if gcode_line.startswith("; wipe_tower_x"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipetower_posx = float(gcode_line[parameter_start + 1:].strip()) if gcode_line.startswith("; min_skirt_length"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.skirtsize = float(gcode_line[parameter_start + 1:].strip()) if gcode_line.startswith("; skirts"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.skirts = float(gcode_line[parameter_start + 1:].strip()) if gcode_line.startswith("; wipe_tower_width"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipetower_width = float(gcode_line[parameter_start + 1:].strip()) if gcode_line.startswith("; wipe_tower_y"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.wipetower_posy = float(gcode_line[parameter_start + 1:].strip()) if gcode_line.startswith("; extrusion_width"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.extrusion_width = float(gcode_line[parameter_start + 1:].strip()) if gcode_line.startswith("; infill_speed"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.infill_speed = float(gcode_line[parameter_start + 1:].strip()) * 60 if gcode_line.startswith("; layer_height"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.layer_height = float(gcode_line[parameter_start + 1:].strip()) if gcode_line.startswith("; first_layer_height"): parameter_start = gcode_line.find("=") if parameter_start != -1: v.first_layer_height = float(gcode_line[parameter_start + 1:].strip()) if gcode_line.startswith("; support_material_synchronize_layers"): parameter_start = gcode_line.find("=") if parameter_start != -1: tmp = float(gcode_line[parameter_start + 1:].strip()) if tmp == 0: v.synced_support = False else: v.synced_support = True if gcode_line.startswith("; support_material "): parameter_start = gcode_line.find("=") if parameter_start != -1: tmp = float(gcode_line[parameter_start + 1:].strip()) if tmp == 0: v.support_material = False else: v.support_material = True if gcode_line.startswith("; extruder_colour") or gcode_line.startswith("; filament_colour"): filament_colour = '' parameter_start = gcode_line.find("=") gcode_line = gcode_line[parameter_start + 1:].strip() parameter_start = gcode_line.find("#") if parameter_start != -1: filament_colour = gcode_line.split(";") if len(filament_colour) == 4: for i in range(4): if filament_colour[i] == "": filament_colour[i] = v.filament_color_code[i] else: v.filament_color_code[i] = filament_colour[i][1:] if gcode_line.startswith("; filament_diameter"): parameter_start = gcode_line.find("=") if parameter_start != -1: filament_diameters = gcode_line[parameter_start + 1:].strip(" ").split(",") if len(filament_diameters) == 4: for i in range(4): v.filament_diameter[i] = float(filament_diameters[i]) if gcode_line.startswith("; filament_type"): parameter_start = gcode_line.find("=") if parameter_start != -1: filament_string = gcode_line[parameter_start + 1:].strip(" ").split(";") if len(filament_string) == 4: v.filament_type = filament_string v.used_filament_types = list(set(filament_string)) continue if gcode_line.startswith("; retract_lift = "): if v.filament_list: continue lift_error = False parameter_start = gcode_line.find("=") if parameter_start != -1: retracts = gcode_line[parameter_start + 1:].strip(" ").split(",") if len(retracts) == 4: for i in range(4): v.retract_lift[i] = float(retracts[i]) if v.retract_lift[i] == 0: lift_error = True if lift_error: gui.log_warning( "[Printer Settings]->[Extruders 1/2/3/4]->[Retraction]->[Lift Z] should not be set to zero.") gui.log_warning( "Generated file might not print correctly") continue if gcode_line.startswith("; retract_length = "): retract_error = False parameter_start = gcode_line.find("=") if parameter_start != -1: retracts = gcode_line[parameter_start + 1:].strip(" ").split(",") if len(retracts) == 4: for i in range(4): v.retract_length[i] = float(retracts[i]) if v.retract_length[i] == 0.0: retract_error = True if retract_error: gui.log_warning( "[Printer Settings]->[Extruders 1/2/3/4]->[Retraction Length] should not be set to zero.") continue if gcode_line.startswith("; gcode_flavor"): if "reprap" in gcode_line: v.isReprap_Mode = True if "use_firmware_retraction" in gcode_line: parameter_start = gcode_line.find("=") if parameter_start != -1: gcode_line = gcode_line[parameter_start + 1:].replace(";", "") if "1" in gcode_line: v.use_firmware_retraction = True else: v.use_firmware_retraction = False if "use_relative_e_distances" in gcode_line: parameter_start = gcode_line.find("=") if parameter_start != -1: gcode_line = gcode_line[parameter_start + 1:].replace(";", "") if "1" in gcode_line: v.gcode_has_relative_e = True else: v.gcode_has_relative_e = False if gcode_line.startswith("; wiping_volumes_matrix"): wiping_info = [] parameter_start = gcode_line.find("=") if parameter_start != -1: wiping_info = gcode_line[parameter_start + 1:].strip(" ").split(",") _warning = True for i in range(len(wiping_info)): if int(wiping_info[i]) != 140 and int(wiping_info[i]) != 0: _warning = False wiping_info[i] = filament_volume_to_length(float(wiping_info[i])) if _warning: gui.log_warning("All purge lenghts 70/70 OR 140. Purge lenghts may not have been set correctly.") v.max_wipe = max(wiping_info) if len(wiping_info) == 16: v.wiping_info = wiping_info continue