mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge branch 'main' into patch-1
This commit is contained in:
commit
4bd1c4c4e0
7956 changed files with 237740 additions and 241926 deletions
837
plugins/PostProcessingPlugin/scripts/AddCoolingProfile.py
Normal file
837
plugins/PostProcessingPlugin/scripts/AddCoolingProfile.py
Normal file
|
@ -0,0 +1,837 @@
|
|||
# Designed in January 2023 by GregValiant (Greg Foresi)
|
||||
## My design intent was to make this as full featured and "industrial strength" as I could. People printing exotic materials on large custom printers may want to turn the fans off for certain layers, and then back on again later in the print. This script allows that.
|
||||
# Functions:
|
||||
## Remove all fan speed lines from the file (optional). This should be enabled for the first instance of the script. It is disabled by default in any following instances.
|
||||
## "By Layer" allows the user to adjust the fan speed up, or down, or off, within the print. "By Feature" allows different fan speeds for different features (;TYPE:WALL-OUTER, etc.).
|
||||
## If 'By Feature' then a Start Layer and/or an End Layer can be defined.
|
||||
## Fan speeds are scaled PWM (0 - 255) or RepRap (0.0 - 1.0) depending on {machine_scale_fan_speed_zero_to_one}.
|
||||
## A minimum fan speed of 12% is enforced. It is the slowest speed that my cooling fan will turn on so that's what I used. 'M106 S14' (as Cura might insert) was pretty useless.
|
||||
## If multiple extruders have separate fan circuits the speeds are set at tool changes and conform to the layer or feature setting. There is support for up to 4 layer cooling fan circuits.
|
||||
## My thanks to @5axes(@CUQ), @fieldOfView(@AHoeben), @Ghostkeeper, and @Torgeir. A special thanks to @RBurema for his patience in reviewing my 'non-pythonic' script.
|
||||
## 9/14/23 (Greg Foresi) Added support for One-at-a-Time print sequence.
|
||||
## 12/15/23 (Greg Foresi) Split off 'Single Fan By Layer', 'Multi-fan By Layer', 'Single Fan By Feature', and 'Multi-fan By Feature' from the main 'execute' script.
|
||||
## 1/5/24 (Greg Foresi) Revised the regex replacements.
|
||||
|
||||
from ..Script import Script
|
||||
from UM.Application import Application
|
||||
import re
|
||||
|
||||
class AddCoolingProfile(Script):
|
||||
|
||||
def getSettingDataString(self):
|
||||
return """{
|
||||
"name": "Advanced Cooling Fan Control",
|
||||
"key": "AddCoolingProfile",
|
||||
"metadata": {},
|
||||
"version": 2,
|
||||
"settings":
|
||||
{
|
||||
"fan_layer_or_feature":
|
||||
{
|
||||
"label": "Cooling Control by:",
|
||||
"description": "A fan percentage of ''0'' turns the fan off. Minimum Fan is 12% (when on). All layer entries are the Cura Preview number. ''By Layer'': Enter as ''Layer#/Fan%'' (foreslash is the delimiter). Your final layer speed will continue to the end of the Gcode. ''By Feature'': If you enable an 'End Layer' then the ''Final %'' is available and is the speed that will finish the file. 'By Feature' is better for large slow prints than it is for short fast prints.",
|
||||
"type": "enum",
|
||||
"options": {
|
||||
"by_layer": "Layer Numbers",
|
||||
"by_feature": "Feature Types"},
|
||||
"default_value": "by_layer"
|
||||
},
|
||||
"delete_existing_m106":
|
||||
{
|
||||
"label": "Remove M106 lines prior to inserting new.",
|
||||
"description": "If you have 2 or more instances of 'Advanced Cooling Fan Control' running (to cool a portion of a print differently), then you must uncheck this box or the followup instances will remove all the lines inserted by the first instance. Pay attention to the Start and Stop layers. Regardless of this setting: The script always removes M106 lines starting with the lowest layer number (when 'By Layer') or the starting layer number (when 'By Feature'). If you want to keep the M106 lines that Cura inserted up to the point where this post-processor will start making insertions, then un-check the box.",
|
||||
"type": "bool",
|
||||
"enabled": true,
|
||||
"value": true,
|
||||
"default_value": true
|
||||
},
|
||||
"feature_fan_start_layer":
|
||||
{
|
||||
"label": "Starting Layer",
|
||||
"description": "Layer to start the insertion at. Use the Cura preview numbers. Changes will begin at the start of that layer.",
|
||||
"type": "int",
|
||||
"default_value": 5,
|
||||
"minimum_value": 1,
|
||||
"unit": "Lay# ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_end_layer":
|
||||
{
|
||||
"label": "Ending Layer",
|
||||
"description": "Layer to complete the insertion at. Enter '-1' for the entire file or enter a layer number. Insertions will stop at the END of this layer. If you set an End Layer then you should set the Final % that will finish the file",
|
||||
"type": "int",
|
||||
"default_value": -1,
|
||||
"minimum_value": -1,
|
||||
"unit": "Lay# ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"layer_fan_1":
|
||||
{
|
||||
"label": "Layer/Percent #1",
|
||||
"description": "Enter as: 'LAYER / Percent' Ex: 55/100 with the layer first, then a '/' to delimit, and then the fan percentage. There are up to 8 changes. If you need more then add a second instance of this script and remember to turn off 'Remove M106 lines' in the second instance. The layer numbers in the second instance must start with a layer number higher than the last layer number in a previous script. You can't end the first script with a setting for layer 80 and then start the second script with a setting for layer 40 because 'Remove M106 lines' always starts with the lowest layer number when 'By Layer' is selected.",
|
||||
"type": "str",
|
||||
"default_value": "5/30",
|
||||
"unit": "L#/% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_layer'"
|
||||
},
|
||||
"layer_fan_2":
|
||||
{
|
||||
"label": "Layer/Percent #2",
|
||||
"description": "Enter as: 'LAYER / Percent' Ex: 55/100 with the layer first, then a '/' to delimit, and then the fan percentage.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"unit": "L#/% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_layer'"
|
||||
},
|
||||
"layer_fan_3":
|
||||
{
|
||||
"label": "Layer/Percent #3",
|
||||
"description": "Enter as: 'LAYER / Percent' Ex: 55/100 with the layer first, then a '/' to delimit, and then the fan percentage.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"unit": "L#/% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_layer'"
|
||||
},
|
||||
"layer_fan_4":
|
||||
{
|
||||
"label": "Layer/Percent #4",
|
||||
"description": "Enter as: 'LAYER / Percent' Ex: 55/100 with the layer first, then a '/' to delimit, and then the fan percentage.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"unit": "L#/% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_layer'"
|
||||
},
|
||||
"layer_fan_5":
|
||||
{
|
||||
"label": "Layer/Percent #5",
|
||||
"description": "Enter as: 'LAYER / Percent' Ex: 55/100 with the layer first, then a '/' to delimit, and then the fan percentage.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"unit": "L#/% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_layer'"
|
||||
},
|
||||
"layer_fan_6":
|
||||
{
|
||||
"label": "Layer/Percent #6",
|
||||
"description": "Enter as: 'LAYER / Percent' Ex: 55/100 with the layer first, then a '/' to delimit, and then the fan percentage.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"unit": "L#/% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_layer'"
|
||||
},
|
||||
"layer_fan_7":
|
||||
{
|
||||
"label": "Layer/Percent #7",
|
||||
"description": "Enter as: 'LAYER / Percent' Ex: 55/100 with the layer first, then a '/' to delimit, and then the fan percentage.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"unit": "L#/% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_layer'"
|
||||
},
|
||||
"layer_fan_8":
|
||||
{
|
||||
"label": "Layer/Percent #8",
|
||||
"description": "Enter as: 'LAYER / Percent' Ex: 55/100 with the layer first, then a '/' to delimit, and then the fan percentage.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"unit": "L#/% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_layer'"
|
||||
},
|
||||
"feature_fan_skirt":
|
||||
{
|
||||
"label": "Skirt/Brim/Ooze Shield %",
|
||||
"description": "Enter the fan percentage for skirt/brim. If you are starting at a layer above 1 then this setting only affects Ooze Shields and from the Start Layer up.",
|
||||
"type": "int",
|
||||
"default_value": 0,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_wall_inner":
|
||||
{
|
||||
"label": "Inner Walls %",
|
||||
"description": "Enter the fan percentage for the Wall-Inner.",
|
||||
"type": "int",
|
||||
"default_value": 35,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_wall_outer":
|
||||
{
|
||||
"label": "Outer Walls %",
|
||||
"description": "Enter the fan percentage for the Wall-Outer.",
|
||||
"type": "int",
|
||||
"default_value": 75,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_fill":
|
||||
{
|
||||
"label": "Infill %",
|
||||
"description": "Enter the fan percentage for the Infill.",
|
||||
"type": "int",
|
||||
"default_value": 35,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_skin":
|
||||
{
|
||||
"label": "Top/Bottom (Skin) %",
|
||||
"description": "Enter the fan percentage for the Skins.",
|
||||
"type": "int",
|
||||
"default_value": 100,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_support":
|
||||
{
|
||||
"label": "Support %",
|
||||
"description": "Enter the fan percentage for the Supports.",
|
||||
"type": "int",
|
||||
"default_value": 35,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_support_interface":
|
||||
{
|
||||
"label": "Support Interface %",
|
||||
"description": "Enter the fan percentage for the Support Interface.",
|
||||
"type": "int",
|
||||
"default_value": 100,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_prime_tower":
|
||||
{
|
||||
"label": "Prime Tower %",
|
||||
"description": "Enter the fan percentage for the Prime Tower (whether it's used or not).",
|
||||
"type": "int",
|
||||
"default_value": 35,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_bridge":
|
||||
{
|
||||
"label": "Bridge %",
|
||||
"description": "Enter the fan percentage for any Bridging (whether it's used on not).",
|
||||
"type": "int",
|
||||
"default_value": 100,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'"
|
||||
},
|
||||
"feature_fan_combing":
|
||||
{
|
||||
"label": "Fan 'OFF' during Combing:",
|
||||
"description": "When checked will set the fan to 0% for combing moves over 5 lines long in the gcode. When un-checked the fan speed during combing is whatever the previous speed is set to.",
|
||||
"type": "bool",
|
||||
"enabled": "fan_layer_or_feature == 'by_feature'",
|
||||
"default_value": true
|
||||
},
|
||||
"feature_fan_feature_final":
|
||||
{
|
||||
"label": "Final %",
|
||||
"description": "If you choose an 'End Layer' then this is the fan speed that will carry through to the end of the gcode file. It will go into effect at the 'END' of your End layer.",
|
||||
"type": "int",
|
||||
"default_value": 35,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "(int(feature_fan_end_layer) != -1) and (fan_layer_or_feature == 'by_feature')"
|
||||
},
|
||||
"fan_enable_raft":
|
||||
{
|
||||
"label": "Enable Raft Cooling",
|
||||
"description": "Enable the fan for the raft layers. When enabled the Raft Fan Speed will continue until another Layer or Feature setting over-rides it.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": true
|
||||
},
|
||||
"fan_raft_percent":
|
||||
{
|
||||
"label": "Raft Fan %:",
|
||||
"description": "Enter the percentage for the Raft.",
|
||||
"type": "int",
|
||||
"default_value": 35,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 100,
|
||||
"unit": "% ",
|
||||
"enabled": "fan_enable_raft"
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
def initialize(self) -> None:
|
||||
super().initialize()
|
||||
scripts = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("post_processing_scripts")
|
||||
if scripts != None:
|
||||
script_count = scripts.count("AddCoolingProfile")
|
||||
if script_count > 0:
|
||||
## Set 'Remove M106 lines' to "false" if there is already an instance of this script running.
|
||||
self._instance.setProperty("delete_existing_m106", "value", False)
|
||||
|
||||
def execute(self, data):
|
||||
#Initialize variables that are buried in if statements.
|
||||
mycura = Application.getInstance().getGlobalContainerStack()
|
||||
t0_fan = " P0"; t1_fan = " P0"; t2_fan = " P0"; t3_fan = " P0"; is_multi_extr_print = True
|
||||
|
||||
#Get some information from Cura-----------------------------------
|
||||
extruder = mycura.extruderList
|
||||
|
||||
#This will be true when fan scale is 0-255pwm and false when it's RepRap 0-1 (Cura 5.x)
|
||||
fan_mode = True
|
||||
##For 4.x versions that don't have the 0-1 option
|
||||
try:
|
||||
fan_mode = not bool(extruder[0].getProperty("machine_scale_fan_speed_zero_to_one", "value"))
|
||||
except:
|
||||
pass
|
||||
bed_adhesion = (extruder[0].getProperty("adhesion_type", "value"))
|
||||
extruder_count = mycura.getProperty("machine_extruder_count", "value")
|
||||
print_sequence = str(mycura.getProperty("print_sequence", "value"))
|
||||
|
||||
#Assign the fan numbers to the tools------------------------------
|
||||
if extruder_count == 1:
|
||||
is_multi_fan = False
|
||||
is_multi_extr_print = False
|
||||
if int((extruder[0].getProperty("machine_extruder_cooling_fan_number", "value"))) > 0:
|
||||
t0_fan = " P" + str((extruder[0].getProperty("machine_extruder_cooling_fan_number", "value")))
|
||||
else:
|
||||
#No P parameter if there is a single fan circuit------------------
|
||||
t0_fan = ""
|
||||
|
||||
#Get the cooling fan numbers for each extruder if the printer has multiple extruders
|
||||
elif extruder_count > 1:
|
||||
is_multi_fan = True
|
||||
t0_fan = " P" + str((extruder[0].getProperty("machine_extruder_cooling_fan_number", "value")))
|
||||
if is_multi_fan:
|
||||
if extruder_count > 1: t1_fan = " P" + str((extruder[1].getProperty("machine_extruder_cooling_fan_number", "value")))
|
||||
if extruder_count > 2: t2_fan = " P" + str((extruder[2].getProperty("machine_extruder_cooling_fan_number", "value")))
|
||||
if extruder_count > 3: t3_fan = " P" + str((extruder[3].getProperty("machine_extruder_cooling_fan_number", "value")))
|
||||
|
||||
#Initialize the fan_list with defaults----------------------------
|
||||
fan_list = ["z"] * 16
|
||||
for num in range(0,15,2):
|
||||
fan_list[num] = len(data)
|
||||
fan_list[num + 1] = "M106 S0"
|
||||
|
||||
#Assign the variable values if "By Layer"-------------------------
|
||||
by_layer_or_feature = self.getSettingValueByKey("fan_layer_or_feature")
|
||||
if by_layer_or_feature == "by_layer":
|
||||
## By layer doesn't do any feature search so there is no need to look for combing moves
|
||||
feature_fan_combing = False
|
||||
fan_list[0] = self.getSettingValueByKey("layer_fan_1")
|
||||
fan_list[2] = self.getSettingValueByKey("layer_fan_2")
|
||||
fan_list[4] = self.getSettingValueByKey("layer_fan_3")
|
||||
fan_list[6] = self.getSettingValueByKey("layer_fan_4")
|
||||
fan_list[8] = self.getSettingValueByKey("layer_fan_5")
|
||||
fan_list[10] = self.getSettingValueByKey("layer_fan_6")
|
||||
fan_list[12] = self.getSettingValueByKey("layer_fan_7")
|
||||
fan_list[14] = self.getSettingValueByKey("layer_fan_8")
|
||||
## If there is no '/' delimiter then ignore the line else put the settings in a list
|
||||
for num in range(0,15,2):
|
||||
if "/" in fan_list[num]:
|
||||
fan_list[num + 1] = self._layer_checker(fan_list[num], "p", fan_mode)
|
||||
fan_list[num] = self._layer_checker(fan_list[num], "l", fan_mode)
|
||||
|
||||
## Assign the variable values if "By Feature"
|
||||
elif by_layer_or_feature == "by_feature":
|
||||
the_start_layer = self.getSettingValueByKey("feature_fan_start_layer") - 1
|
||||
the_end_layer = self.getSettingValueByKey("feature_fan_end_layer")
|
||||
try:
|
||||
if int(the_end_layer) != -1:
|
||||
## Catch a possible input error.
|
||||
if the_end_layer < the_start_layer:
|
||||
the_end_layer = the_start_layer
|
||||
except:
|
||||
the_end_layer = -1 ## If there is an input error default to the entire gcode file.
|
||||
|
||||
## Get the speed for each feature
|
||||
feature_name_list = []
|
||||
feature_speed_list = []
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_skirt"), fan_mode)); feature_name_list.append(";TYPE:SKIRT")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_wall_inner"), fan_mode)); feature_name_list.append(";TYPE:WALL-INNER")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_wall_outer"), fan_mode)); feature_name_list.append(";TYPE:WALL-OUTER")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_fill"), fan_mode)); feature_name_list.append(";TYPE:FILL")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_skin"), fan_mode)); feature_name_list.append(";TYPE:SKIN")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_support"), fan_mode)); feature_name_list.append(";TYPE:SUPPORT")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_support_interface"), fan_mode)); feature_name_list.append(";TYPE:SUPPORT-INTERFACE")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_prime_tower"), fan_mode)); feature_name_list.append(";TYPE:PRIME-TOWER")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_bridge"), fan_mode)); feature_name_list.append(";BRIDGE")
|
||||
feature_speed_list.append(self._feature_checker(self.getSettingValueByKey("feature_fan_feature_final"), fan_mode)); feature_name_list.append("FINAL_FAN")
|
||||
feature_fan_combing = self.getSettingValueByKey("feature_fan_combing")
|
||||
if the_end_layer > -1 and by_layer_or_feature == "by_feature":
|
||||
## Required so the final speed input can be determined
|
||||
the_end_is_enabled = True
|
||||
else:
|
||||
## There is no ending layer so do the whole file
|
||||
the_end_is_enabled = False
|
||||
if the_end_layer == -1 or the_end_is_enabled == False:
|
||||
the_end_layer = len(data) + 2
|
||||
|
||||
## Find the Layer0Index and the RaftIndex
|
||||
raft_start_index = 0
|
||||
number_of_raft_layers = 0
|
||||
layer_0_index = 0
|
||||
## Catch the number of raft layers.
|
||||
for l_num in range(1,10,1):
|
||||
layer = data[l_num]
|
||||
if ";LAYER:-" in layer:
|
||||
number_of_raft_layers += 1
|
||||
if raft_start_index == 0:
|
||||
raft_start_index = l_num
|
||||
if ";LAYER:0" in layer:
|
||||
layer_0_index = l_num
|
||||
break
|
||||
|
||||
## Is this a single extruder print on a multi-extruder printer? - get the correct fan number for the extruder being used.
|
||||
if is_multi_fan:
|
||||
T0_used = False
|
||||
T1_used = False
|
||||
T2_used = False
|
||||
T3_used = False
|
||||
## Bypass the file header and ending gcode.
|
||||
for num in range(1,len(data)-1,1):
|
||||
lines = data[num]
|
||||
if "T0" in lines:
|
||||
T0_used = True
|
||||
if "T1" in lines:
|
||||
T1_used = True
|
||||
if "T2" in lines:
|
||||
T2_used = True
|
||||
if "T3" in lines:
|
||||
T3_used = True
|
||||
is_multi_extr_print = True if sum([T0_used, T1_used, T2_used, T3_used]) > 1 else False
|
||||
|
||||
## On a multi-extruder printer and single extruder print find out which extruder starts the file.
|
||||
init_fan = t0_fan
|
||||
if not is_multi_extr_print:
|
||||
startup = data[1]
|
||||
lines = startup.split("\n")
|
||||
for line in lines:
|
||||
if line == "T1":
|
||||
t0_fan = t1_fan
|
||||
elif line == "T2":
|
||||
t0_fan = t2_fan
|
||||
elif line == "T3":
|
||||
t0_fan = t3_fan
|
||||
elif is_multi_extr_print:
|
||||
## On a multi-extruder printer and multi extruder print find out which extruder starts the file.
|
||||
startup = data[1]
|
||||
lines = startup.split("\n")
|
||||
for line in lines:
|
||||
if line == "T0":
|
||||
init_fan = t0_fan
|
||||
elif line == "T1":
|
||||
init_fan = t1_fan
|
||||
elif line == "T2":
|
||||
init_fan = t2_fan
|
||||
elif line == "T3":
|
||||
init_fan = t3_fan
|
||||
else:
|
||||
init_fan = ""
|
||||
## Assign the variable values if "Raft Enabled"
|
||||
raft_enabled = self.getSettingValueByKey("fan_enable_raft")
|
||||
if raft_enabled and bed_adhesion == "raft":
|
||||
fan_sp_raft = self._feature_checker(self.getSettingValueByKey("fan_raft_percent"), fan_mode)
|
||||
else:
|
||||
fan_sp_raft = "M106 S0"
|
||||
|
||||
# Start to alter the data-----------------------------------------
|
||||
## Strip the existing M106 lines from the file up to the end of the last layer. If a user wants to use more than one instance of this plugin then they won't want to erase the M106 lines that the preceding plugins inserted so 'delete_existing_m106' is an option.
|
||||
delete_existing_m106 = self.getSettingValueByKey("delete_existing_m106")
|
||||
if delete_existing_m106:
|
||||
## Start deleting from the beginning
|
||||
start_from = int(raft_start_index)
|
||||
else:
|
||||
if by_layer_or_feature == "by_layer":
|
||||
altered_start_layer = str(len(data))
|
||||
## The fan list layers don't need to be in ascending order. Get the lowest.
|
||||
for num in range(0,15,2):
|
||||
try:
|
||||
if int(fan_list[num]) < int(altered_start_layer):
|
||||
altered_start_layer = int(fan_list[num])
|
||||
except:
|
||||
pass
|
||||
elif by_layer_or_feature == "by_feature":
|
||||
altered_start_layer = int(the_start_layer) - 1
|
||||
start_from = int(layer_0_index) + int(altered_start_layer)
|
||||
## Strip the M106 and M107 lines from the file
|
||||
for l_index in range(int(start_from), len(data) - 1, 1):
|
||||
data[l_index] = re.sub(re.compile("M106(.*)\n"), "", data[l_index])
|
||||
data[l_index] = re.sub(re.compile("M107(.*)\n"), "", data[l_index])
|
||||
|
||||
## Deal with a raft and with One-At-A-Time print sequence
|
||||
if raft_enabled and bed_adhesion == "raft":
|
||||
if print_sequence == "one_at_a_time":
|
||||
for r_index in range(2,len(data)-2,1):
|
||||
lines = data[r_index].split("\n")
|
||||
if not raft_enabled or bed_adhesion != "raft":
|
||||
if ";LAYER:0" in data[r_index] or ";LAYER:-" in data[r_index]:
|
||||
lines.insert(1, "M106 S0" + str(t0_fan))
|
||||
if raft_enabled and bed_adhesion == "raft":
|
||||
if ";LAYER:-" in data[r_index]:
|
||||
## Turn the raft fan on
|
||||
lines.insert(1, fan_sp_raft + str(t0_fan))
|
||||
## Shut the raft fan off at layer 0
|
||||
if ";LAYER:0" in data[r_index]:
|
||||
lines.insert(1,"M106 S0" + str(t0_fan))
|
||||
data[r_index] = "\n".join(lines)
|
||||
elif print_sequence == "all_at_once":
|
||||
layer = data[raft_start_index]
|
||||
lines = layer.split("\n")
|
||||
if ";LAYER:-" in layer:
|
||||
## Turn the raft fan on
|
||||
lines.insert(1, fan_sp_raft + str(init_fan))
|
||||
layer = "\n".join(lines)
|
||||
data[raft_start_index] = layer
|
||||
layer = data[layer_0_index]
|
||||
lines = layer.split("\n")
|
||||
## Shut the raft fan off
|
||||
lines.insert(1, "M106 S0" + str(init_fan))
|
||||
data[layer_0_index] = "\n".join(lines)
|
||||
else:
|
||||
for r_index in range(2,len(data)-2,1):
|
||||
lines = data[r_index].split("\n")
|
||||
if ";LAYER:0" in data[r_index] or ";LAYER:-" in data[r_index]:
|
||||
if not "0" in fan_list:
|
||||
lines.insert(1, "M106 S0" + str(t0_fan))
|
||||
data[r_index] = "\n".join(lines)
|
||||
|
||||
## Turn off all fans at the end of data[1]. If more than one instance of this script is running then this will result in multiple M106 lines.
|
||||
temp_startup = data[1].split("\n")
|
||||
temp_startup.insert(len(temp_startup)-2,"M106 S0" + str(t0_fan))
|
||||
## If there are multiple cooling fans shut them all off
|
||||
if is_multi_fan:
|
||||
if extruder_count > 1 and t1_fan != t0_fan: temp_startup.insert(len(temp_startup)-2,"M106 S0" + str(t1_fan))
|
||||
if extruder_count > 2 and t2_fan != t1_fan and t2_fan != t0_fan: temp_startup.insert(len(temp_startup)-2,"M106 S0" + str(t2_fan))
|
||||
if extruder_count > 3 and t3_fan != t2_fan and t3_fan != t1_fan and t3_fan != t0_fan: temp_startup.insert(len(temp_startup)-2,"M106 S0" + str(t3_fan))
|
||||
data[1] = "\n".join(temp_startup)
|
||||
|
||||
## If 'feature_fan_combing' is True then add additional 'MESH:NONMESH' lines for travel moves over 5 lines long
|
||||
## For compatibility with 5.3.0 change any MESH:NOMESH to MESH:NONMESH.
|
||||
if feature_fan_combing:
|
||||
for layer_num in range(2,len(data)):
|
||||
layer = data[layer_num]
|
||||
data[layer_num] = re.sub(";MESH:NOMESH", ";MESH:NONMESH", layer)
|
||||
data = self._add_travel_comment(data, layer_0_index)
|
||||
|
||||
# Single Fan "By Layer"--------------------------------------------
|
||||
if by_layer_or_feature == "by_layer" and not is_multi_fan:
|
||||
return self._single_fan_by_layer(data, layer_0_index, fan_list, t0_fan)
|
||||
|
||||
# Multi-Fan "By Layer"---------------------------------------------
|
||||
if by_layer_or_feature == "by_layer" and is_multi_fan:
|
||||
return self._multi_fan_by_layer(data, layer_0_index, fan_list, t0_fan, t1_fan, t2_fan, t3_fan)
|
||||
|
||||
#Single Fan "By Feature"------------------------------------------
|
||||
if by_layer_or_feature == "by_feature" and (not is_multi_fan or not is_multi_extr_print):
|
||||
return self._single_fan_by_feature(data, layer_0_index, the_start_layer, the_end_layer, the_end_is_enabled, fan_list, t0_fan, feature_speed_list, feature_name_list, feature_fan_combing)
|
||||
|
||||
#Multi Fan "By Feature"-------------------------------------------
|
||||
if by_layer_or_feature == "by_feature" and is_multi_fan:
|
||||
return self._multi_fan_by_feature(data, layer_0_index, the_start_layer, the_end_layer, the_end_is_enabled, fan_list, t0_fan, t1_fan, t2_fan, t3_fan, feature_speed_list, feature_name_list, feature_fan_combing)
|
||||
|
||||
# The Single Fan "By Layer"----------------------------------------
|
||||
def _single_fan_by_layer(self, data: str, layer_0_index: int, fan_list: str, t0_fan: str)->str:
|
||||
layer_number = "0"
|
||||
single_fan_data = data
|
||||
for l_index in range(layer_0_index,len(single_fan_data)-1,1):
|
||||
layer = single_fan_data[l_index]
|
||||
fan_lines = layer.split("\n")
|
||||
for fan_line in fan_lines:
|
||||
if ";LAYER:" in fan_line:
|
||||
layer_number = str(fan_line.split(":")[1])
|
||||
## If there is a match for the current layer number make the insertion
|
||||
for num in range(0,15,2):
|
||||
if layer_number == str(fan_list[num]):
|
||||
layer = layer.replace(fan_lines[0],fan_lines[0] + "\n" + fan_list[num + 1] + str(t0_fan))
|
||||
single_fan_data[l_index] = layer
|
||||
return single_fan_data
|
||||
|
||||
# Multi-Fan "By Layer"-----------------------------------------
|
||||
def _multi_fan_by_layer(self, data: str, layer_0_index: int, fan_list: str, t0_fan: str, t1_fan: str, t2_fan: str, t3_fan: str)->str:
|
||||
multi_fan_data = data
|
||||
layer_number = "0"
|
||||
current_fan_speed = "0"
|
||||
prev_fan = str(t0_fan)
|
||||
this_fan = str(t0_fan)
|
||||
start_index = str(len(multi_fan_data))
|
||||
for num in range(0,15,2):
|
||||
## The fan_list may not be in ascending order. Get the lowest layer number
|
||||
try:
|
||||
if int(fan_list[num]) < int(start_index):
|
||||
start_index = str(fan_list[num])
|
||||
except:
|
||||
pass
|
||||
## Move the start point if delete_existing_m106 is false
|
||||
start_index = int(start_index) + int(layer_0_index)
|
||||
## Track the tool number
|
||||
for num in range(1,int(start_index),1):
|
||||
layer = multi_fan_data[num]
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line == "T0":
|
||||
prev_fan = this_fan
|
||||
this_fan = t0_fan
|
||||
elif line == "T1":
|
||||
prev_fan = this_fan
|
||||
this_fan = t1_fan
|
||||
elif line == "T2":
|
||||
prev_fan = this_fan
|
||||
this_fan = t2_fan
|
||||
elif line == "T3":
|
||||
prev_fan = this_fan
|
||||
this_fan = t3_fan
|
||||
for l_index in range(int(start_index),len(multi_fan_data)-1,1):
|
||||
modified_data = ""
|
||||
layer = multi_fan_data[l_index]
|
||||
fan_lines = layer.split("\n")
|
||||
for fan_line in fan_lines:
|
||||
## Prepare to shut down the previous fan and start the next one.
|
||||
if fan_line.startswith("T"):
|
||||
if fan_line == "T0": this_fan = str(t0_fan)
|
||||
if fan_line == "T1": this_fan = str(t1_fan)
|
||||
if fan_line == "T2": this_fan = str(t2_fan)
|
||||
if fan_line == "T3": this_fan = str(t3_fan)
|
||||
modified_data += "M106 S0" + prev_fan + "\n"
|
||||
modified_data += fan_line + "\n"
|
||||
modified_data += "M106 S" + str(current_fan_speed) + this_fan + "\n"
|
||||
prev_fan = this_fan
|
||||
elif ";LAYER:" in fan_line:
|
||||
modified_data += fan_line + "\n"
|
||||
layer_number = str(fan_line.split(":")[1])
|
||||
for num in range(0,15,2):
|
||||
if layer_number == str(fan_list[num]):
|
||||
modified_data += fan_list[num + 1] + this_fan + "\n"
|
||||
current_fan_speed = str(fan_list[num + 1].split("S")[1])
|
||||
current_fan_speed = str(current_fan_speed.split(" ")[0]) ## Just in case
|
||||
else:
|
||||
modified_data += fan_line + "\n"
|
||||
if modified_data.endswith("\n"): modified_data = modified_data[0:-1]
|
||||
multi_fan_data[l_index] = modified_data
|
||||
return multi_fan_data
|
||||
|
||||
# Single fan by feature-----------------------------------------------
|
||||
def _single_fan_by_feature(self, data: str, layer_0_index: int, the_start_layer: str, the_end_layer: str, the_end_is_enabled: str, fan_list: str, t0_fan: str, feature_speed_list: str, feature_name_list: str, feature_fan_combing: bool)->str:
|
||||
single_fan_data = data
|
||||
layer_number = "0"
|
||||
index = 1
|
||||
## Start with layer:0
|
||||
for l_index in range(layer_0_index,len(single_fan_data)-1,1):
|
||||
modified_data = ""
|
||||
layer = single_fan_data[l_index]
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if ";LAYER:" in line:
|
||||
layer_number = str(line.split(":")[1])
|
||||
if int(layer_number) >= int(the_start_layer) and int(layer_number) < int(the_end_layer)-1:
|
||||
temp = line.split(" ")[0]
|
||||
try:
|
||||
name_index = feature_name_list.index(temp)
|
||||
except:
|
||||
name_index = -1
|
||||
if name_index != -1:
|
||||
modified_data += feature_speed_list[name_index] + t0_fan + "\n"
|
||||
elif ";MESH:NONMESH" in line:
|
||||
if feature_fan_combing == True:
|
||||
modified_data += "M106 S0" + t0_fan + "\n"
|
||||
modified_data += line + "\n"
|
||||
## If an End Layer is defined and is less than the last layer then insert the Final Speed
|
||||
if line == ";LAYER:" + str(the_end_layer) and the_end_is_enabled == True:
|
||||
modified_data += feature_speed_list[len(feature_speed_list) - 1] + t0_fan + "\n"
|
||||
if modified_data.endswith("\n"): modified_data = modified_data[0: - 1]
|
||||
single_fan_data[l_index] = modified_data
|
||||
return single_fan_data
|
||||
|
||||
# Multi-fan by feature------------------------------------------------
|
||||
def _multi_fan_by_feature(self, data: str, layer_0_index: int, the_start_layer: str, the_end_layer: str, the_end_is_enabled: str, fan_list: str, t0_fan: str, t1_fan: str, t2_fan: str, t3_fan: str, feature_speed_list: str, feature_name_list: str, feature_fan_combing: bool)->str:
|
||||
multi_fan_data = data
|
||||
layer_number = "0"
|
||||
start_index = 1
|
||||
prev_fan = t0_fan
|
||||
this_fan = t0_fan
|
||||
modified_data = ""
|
||||
current_fan_speed = "0"
|
||||
for my_index in range(1, len(multi_fan_data) - 1, 1):
|
||||
layer = multi_fan_data[my_index]
|
||||
if ";LAYER:" + str(the_start_layer) + "\n" in layer:
|
||||
start_index = int(my_index) - 1
|
||||
break
|
||||
## Track the previous tool changes
|
||||
for num in range(1,start_index,1):
|
||||
layer = multi_fan_data[num]
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line == "T0":
|
||||
prev_fan = this_fan
|
||||
this_fan = t0_fan
|
||||
elif line == "T1":
|
||||
prev_fan = this_fan
|
||||
this_fan = t1_fan
|
||||
elif line == "T2":
|
||||
prev_fan = this_fan
|
||||
this_fan = t2_fan
|
||||
elif line == "T3":
|
||||
prev_fan = this_fan
|
||||
this_fan = t3_fan
|
||||
## Get the current tool.
|
||||
for l_index in range(start_index,start_index + 1,1):
|
||||
layer = multi_fan_data[l_index]
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line.startswith("T"):
|
||||
if line == "T0": this_fan = t0_fan
|
||||
if line == "T1": this_fan = t1_fan
|
||||
if line == "T2": this_fan = t2_fan
|
||||
if line == "T3": this_fan = t3_fan
|
||||
prev_fan = this_fan
|
||||
|
||||
## Start to make insertions-------------------------------------
|
||||
for l_index in range(start_index+1,len(multi_fan_data)-1,1):
|
||||
layer = multi_fan_data[l_index]
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line.startswith("T"):
|
||||
if line == "T0": this_fan = t0_fan
|
||||
if line == "T1": this_fan = t1_fan
|
||||
if line == "T2": this_fan = t2_fan
|
||||
if line == "T3": this_fan = t3_fan
|
||||
## Turn off the prev fan
|
||||
modified_data += "M106 S0" + prev_fan + "\n"
|
||||
modified_data += line + "\n"
|
||||
## Turn on the current fan
|
||||
modified_data += "M106 S" + str(current_fan_speed) + this_fan + "\n"
|
||||
prev_fan = this_fan
|
||||
if ";LAYER:" in line:
|
||||
layer_number = str(line.split(":")[1])
|
||||
modified_data += line + "\n"
|
||||
if int(layer_number) >= int(the_start_layer):
|
||||
temp = line.split(" ")[0]
|
||||
try:
|
||||
name_index = feature_name_list.index(temp)
|
||||
except:
|
||||
name_index = -1
|
||||
if name_index != -1:
|
||||
modified_data += line + "\n" + feature_speed_list[name_index] + this_fan + "\n"
|
||||
#modified_data += feature_speed_list[name_index] + this_fan + "\n"
|
||||
current_fan_speed = str(feature_speed_list[name_index].split("S")[1])
|
||||
elif ";MESH:NONMESH" in line:
|
||||
if feature_fan_combing == True:
|
||||
modified_data += line + "\n"
|
||||
modified_data += "M106 S0" + this_fan + "\n"
|
||||
current_fan_speed = "0"
|
||||
else:
|
||||
modified_data += line + "\n"
|
||||
## If an end layer is defined - Insert the final speed and set the other variables to Final Speed to finish the file
|
||||
## There cannot be a break here because if there are multiple fan numbers they still need to be shut off and turned on.
|
||||
elif line == ";LAYER:" + str(the_end_layer):
|
||||
modified_data += feature_speed_list[len(feature_speed_list) - 1] + this_fan + "\n"
|
||||
for set_speed in range(0, len(feature_speed_list) - 2):
|
||||
feature_speed_list[set_speed] = feature_speed_list[len(feature_speed_list) - 1]
|
||||
else:
|
||||
## Layer and Tool get inserted into modified_data above. All other lines go into modified_data here
|
||||
if not line.startswith("T") and not line.startswith(";LAYER:"): modified_data += line + "\n"
|
||||
if modified_data.endswith("\n"): modified_data = modified_data[0: - 1]
|
||||
multi_fan_data[l_index] = modified_data
|
||||
modified_data = ""
|
||||
return multi_fan_data
|
||||
|
||||
#Try to catch layer input errors, set the minimum speed to 12%, and put the strings together
|
||||
def _layer_checker(self, fan_string: str, ty_pe: str, fan_mode: bool) -> str:
|
||||
fan_string_l = str(fan_string.split("/")[0])
|
||||
try:
|
||||
if int(fan_string_l) <= 1: fan_string_l = "1"
|
||||
if fan_string_l == "": fan_string_l = str(len(data))
|
||||
except ValueError:
|
||||
fan_string_l = str(len(data))
|
||||
fan_string_l = str(int(fan_string_l) - 1)
|
||||
fan_string_p = str(fan_string.split("/")[1])
|
||||
if fan_string_p == "": fan_string_p = "0"
|
||||
try:
|
||||
if int(fan_string_p) < 0: fan_string_p = "0"
|
||||
if int(fan_string_p) > 100: fan_string_p = "100"
|
||||
except ValueError:
|
||||
fan_string_p = "0"
|
||||
## Set the minimum fan speed to 12%
|
||||
if int(fan_string_p) < 12 and int(fan_string_p) != 0:
|
||||
fan_string_p = "12"
|
||||
fan_layer_line = str(fan_string_l)
|
||||
if fan_mode:
|
||||
fan_percent_line = "M106 S" + str(round(int(fan_string_p) * 2.55))
|
||||
else:
|
||||
fan_percent_line = "M106 S" + str(round(int(fan_string_p) / 100, 1))
|
||||
if ty_pe == "l":
|
||||
return str(fan_layer_line)
|
||||
elif ty_pe == "p":
|
||||
return fan_percent_line
|
||||
|
||||
#Try to catch feature input errors, set the minimum speed to 12%, and put the strings together when 'By Feature'
|
||||
def _feature_checker(self, fan_feat_string: int, fan_mode: bool) -> str:
|
||||
if fan_feat_string < 0: fan_feat_string = 0
|
||||
## Set the minimum fan speed to 12%
|
||||
if fan_feat_string > 0 and fan_feat_string < 12: fan_feat_string = 12
|
||||
if fan_feat_string > 100: fan_feat_string = 100
|
||||
if fan_mode:
|
||||
fan_sp_feat = "M106 S" + str(round(fan_feat_string * 2.55))
|
||||
else:
|
||||
fan_sp_feat = "M106 S" + str(round(fan_feat_string / 100, 1))
|
||||
return fan_sp_feat
|
||||
|
||||
# Add additional travel comments to turn the fan off during combing.
|
||||
def _add_travel_comment(self, comment_data: str, lay_0_index: str) -> str:
|
||||
for lay_num in range(int(lay_0_index), len(comment_data)-1,1):
|
||||
layer = comment_data[lay_num]
|
||||
lines = layer.split("\n")
|
||||
## Copy the data to new_data and make the insertions there
|
||||
new_data = lines
|
||||
g0_count = 0
|
||||
g0_index = -1
|
||||
feature_type = ";TYPE:SUPPORT"
|
||||
is_travel = False
|
||||
for index, line in enumerate(lines):
|
||||
insert_index = 0
|
||||
if ";TYPE:" in line:
|
||||
feature_type = line
|
||||
is_travel = False
|
||||
g0_count = 0
|
||||
if ";MESH:NONMESH" in line:
|
||||
is_travel = True
|
||||
g0_count = 0
|
||||
if line.startswith("G0 ") and not is_travel:
|
||||
g0_count += 1
|
||||
if g0_index == -1:
|
||||
g0_index = lines.index(line)
|
||||
elif not line.startswith("G0 ") and not is_travel:
|
||||
## Add additional 'NONMESH' lines to shut the fan off during long combing moves--------
|
||||
if g0_count > 5:
|
||||
if not is_travel:
|
||||
new_data.insert(g0_index + insert_index, ";MESH:NONMESH")
|
||||
insert_index += 1
|
||||
## Add the feature_type at the end of the combing move to turn the fan back on
|
||||
new_data.insert(g0_index + g0_count + 1, feature_type)
|
||||
insert_index += 1
|
||||
g0_count = 0
|
||||
g0_index = -1
|
||||
is_travel = False
|
||||
elif g0_count <= 5:
|
||||
g0_count = 0
|
||||
g0_index = -1
|
||||
is_travel = False
|
||||
comment_data[lay_num] = "\n".join(new_data)
|
||||
return comment_data
|
|
@ -1,7 +1,7 @@
|
|||
# ChangeAtZ script - Change printing parameters at a given height
|
||||
# This script is the successor of the TweakAtZ plugin for legacy Cura.
|
||||
# It contains code from the TweakAtZ plugin V1.0-V4.x and from the ExampleScript by Jaime van Kessel, Ultimaker B.V.
|
||||
# It runs with the PostProcessingPlugin which is released under the terms of the AGPLv3 or higher.
|
||||
# It runs with the PostProcessingPlugin which is released under the terms of the LGPLv3 or higher.
|
||||
# This script is licensed under the Creative Commons - Attribution - Share Alike (CC BY-SA) terms
|
||||
|
||||
# Authors of the ChangeAtZ plugin / script:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# ColorMix script - 2-1 extruder color mix and blending
|
||||
# This script is specific for the Geeetech A10M dual extruder but should work with other Marlin printers.
|
||||
# It runs with the PostProcessingPlugin which is released under the terms of the AGPLv3 or higher.
|
||||
# It runs with the PostProcessingPlugin which is released under the terms of the LGPLv3 or higher.
|
||||
# This script is licensed under the Creative Commons - Attribution - Share Alike (CC BY-SA) terms
|
||||
|
||||
#Authors of the 2-1 ColorMix plug-in / script:
|
||||
|
@ -21,7 +21,7 @@
|
|||
# M163 - Set Mix Factor
|
||||
# M164 - Save Mix - saves to T2 as a unique mix
|
||||
|
||||
import re #To perform the search and replace.
|
||||
import re # To perform the search and replace.
|
||||
from ..Script import Script
|
||||
|
||||
class ColorMix(Script):
|
||||
|
|
|
@ -37,7 +37,7 @@ class CreateThumbnail(Script):
|
|||
|
||||
encoded_snapshot_length = len(encoded_snapshot)
|
||||
gcode.append(";")
|
||||
gcode.append("; thumbnail begin {} {} {}".format(
|
||||
gcode.append("; thumbnail begin {}x{} {}".format(
|
||||
width, height, encoded_snapshot_length))
|
||||
|
||||
chunks = ["; {}".format(encoded_snapshot[i:i+chunk_size])
|
||||
|
|
|
@ -3,21 +3,15 @@
|
|||
# Date: August 28, 2018
|
||||
# Modified: November 16, 2018 by Joshua Pope-Lewis
|
||||
|
||||
# Description: This plugin shows custom messages about your print on the Status bar...
|
||||
# Please look at the 5 options
|
||||
# - Scrolling (SCROLL_LONG_FILENAMES) if enabled in Marlin and you aren't printing a small item select this option.
|
||||
# - Name: By default it will use the name generated by Cura (EG: TT_Test_Cube) - Type a custom name in here
|
||||
# - Start Num: Choose which number you prefer for the initial layer, 0 or 1
|
||||
# - Max Layer: Enabling this will show how many layers are in the entire print (EG: Layer 1 of 265!)
|
||||
# - Add prefix 'Printing': Enabling this will add the prefix 'Printing'
|
||||
# Description: This plugin is now an option in 'Display Info on LCD'
|
||||
|
||||
from ..Script import Script
|
||||
from UM.Application import Application
|
||||
from UM.Message import Message
|
||||
|
||||
class DisplayFilenameAndLayerOnLCD(Script):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def initialize(self) -> None:
|
||||
Message(title = "[Display Filename and Layer on LCD]", text = "This script is now an option in 'Display Info on LCD'. This post processor no longer works.").show()
|
||||
|
||||
def getSettingDataString(self):
|
||||
return """{
|
||||
"name": "Display Filename And Layer On LCD",
|
||||
|
@ -26,40 +20,10 @@ class DisplayFilenameAndLayerOnLCD(Script):
|
|||
"version": 2,
|
||||
"settings":
|
||||
{
|
||||
"scroll":
|
||||
"enable_script":
|
||||
{
|
||||
"label": "Scroll enabled/Small layers?",
|
||||
"description": "If SCROLL_LONG_FILENAMES is enabled select this setting however, if the model is small disable this setting!",
|
||||
"type": "bool",
|
||||
"default_value": false
|
||||
},
|
||||
"name":
|
||||
{
|
||||
"label": "Text to display:",
|
||||
"description": "By default the current filename will be displayed on the LCD. Enter text here to override the filename and display something else.",
|
||||
"type": "str",
|
||||
"default_value": ""
|
||||
},
|
||||
"startNum":
|
||||
{
|
||||
"label": "Initial layer number:",
|
||||
"description": "Choose which number you prefer for the initial layer, 0 or 1",
|
||||
"type": "int",
|
||||
"default_value": 0,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 1
|
||||
},
|
||||
"maxlayer":
|
||||
{
|
||||
"label": "Display max layer?:",
|
||||
"description": "Display how many layers are in the entire print on status bar?",
|
||||
"type": "bool",
|
||||
"default_value": true
|
||||
},
|
||||
"addPrefixPrinting":
|
||||
{
|
||||
"label": "Add prefix 'Printing'?",
|
||||
"description": "This will add the prefix 'Printing'",
|
||||
"label": "Deprecated/Obsolete",
|
||||
"description": "This script is now included in 'Display Info on LCD'.",
|
||||
"type": "bool",
|
||||
"default_value": true
|
||||
}
|
||||
|
@ -67,43 +31,6 @@ class DisplayFilenameAndLayerOnLCD(Script):
|
|||
}"""
|
||||
|
||||
def execute(self, data):
|
||||
max_layer = 0
|
||||
lcd_text = "M117 "
|
||||
if self.getSettingValueByKey("name") != "":
|
||||
name = self.getSettingValueByKey("name")
|
||||
else:
|
||||
name = Application.getInstance().getPrintInformation().jobName
|
||||
if self.getSettingValueByKey("addPrefixPrinting"):
|
||||
lcd_text += "Printing "
|
||||
if not self.getSettingValueByKey("scroll"):
|
||||
lcd_text += "Layer "
|
||||
else:
|
||||
lcd_text += name + " - Layer "
|
||||
i = self.getSettingValueByKey("startNum")
|
||||
for layer in data:
|
||||
display_text = lcd_text + str(i)
|
||||
layer_index = data.index(layer)
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line.startswith(";LAYER_COUNT:"):
|
||||
max_layer = line
|
||||
max_layer = max_layer.split(":")[1]
|
||||
if self.getSettingValueByKey("startNum") == 0:
|
||||
max_layer = str(int(max_layer) - 1)
|
||||
if line.startswith(";LAYER:"):
|
||||
if self.getSettingValueByKey("maxlayer"):
|
||||
display_text = display_text + " of " + max_layer
|
||||
if not self.getSettingValueByKey("scroll"):
|
||||
display_text = display_text + " " + name
|
||||
else:
|
||||
if not self.getSettingValueByKey("scroll"):
|
||||
display_text = display_text + " " + name + "!"
|
||||
else:
|
||||
display_text = display_text + "!"
|
||||
line_index = lines.index(line)
|
||||
lines.insert(line_index + 1, display_text)
|
||||
i += 1
|
||||
final_lines = "\n".join(lines)
|
||||
data[layer_index] = final_lines
|
||||
|
||||
Message(title = "[Display Filename and Layer on LCD]", text = "This post is now included in 'Display Info on LCD'. This script will exit.").show()
|
||||
data[0] += "; [Display Filename and Layer on LCD] Did not run. It is now included in 'Display Info on LCD'.\n"
|
||||
return data
|
||||
|
|
483
plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py
Normal file
483
plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py
Normal file
|
@ -0,0 +1,483 @@
|
|||
# Display Filename and Layer on the LCD by Amanda de Castilho on August 28, 2018
|
||||
# Modified: Joshua Pope-Lewis on November 16, 2018
|
||||
# Display Progress on LCD by Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen, Inigo Martinez on July 31, 2019
|
||||
# Show Progress was adapted from Display Progress by Louis Wooters on January 6, 2020. His changes are included here.
|
||||
#---------------------------------------------------------------
|
||||
# DisplayNameOrProgressOnLCD.py
|
||||
# Cura Post-Process plugin
|
||||
# Combines 'Display Filename and Layer on the LCD' with 'Display Progress'
|
||||
# Combined and with additions by: GregValiant (Greg Foresi)
|
||||
# Date: September 8, 2023
|
||||
# NOTE: This combined post processor will make 'Display Filename and Layer on the LCD' and 'Display Progress' obsolete
|
||||
# Description: Display Filename and Layer options:
|
||||
# Status messages sent to the printer...
|
||||
# - Scrolling (SCROLL_LONG_FILENAMES) if enabled in Marlin and you aren't printing a small item select this option.
|
||||
# - Name: By default it will use the name generated by Cura (EG: TT_Test_Cube) - You may enter a custom name here
|
||||
# - Start Num: Choose which number you prefer for the initial layer, 0 or 1
|
||||
# - Max Layer: Enabling this will show how many layers are in the entire print (EG: Layer 1 of 265!)
|
||||
# - Add prefix 'Printing': Enabling this will add the prefix 'Printing'
|
||||
# - Example Line on LCD: Printing Layer 0 of 395 3DBenchy
|
||||
# Display Progress options:
|
||||
# - Display Total Layer Count
|
||||
# - Disply Time Remaining for the print
|
||||
# - Time Fudge Factor % - Divide the Actual Print Time by the Cura Estimate. Enter as a percentage and the displayed time will be adjusted. This allows you to bring the displayed time closer to reality (Ex: Entering 87.5 would indicate an adjustment to 87.5% of the Cura estimate).
|
||||
# - Example line on LCD: 1/479 | ET 2h13m
|
||||
# - Time to Pauses changes the M117/M118 lines to countdown to the next pause as 1/479 | TP 2h36m
|
||||
# - 'Add M118 Line' is available with either option. M118 will bounce the message back to a remote print server through the USB connection.
|
||||
# - 'Add M73 Line' is used by 'Display Progress' only. There are options to incluse M73 P(percent) and M73 R(time remaining)
|
||||
# - Enable 'Finish-Time' Message - when enabled, takes the Print Time and calculates when the print will end. It takes into account the Time Fudge Factor. The user may enter a print start time. This is also available for Display Filename.
|
||||
|
||||
from ..Script import Script
|
||||
from UM.Application import Application
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
import time
|
||||
import datetime
|
||||
import math
|
||||
from UM.Message import Message
|
||||
|
||||
class DisplayInfoOnLCD(Script):
|
||||
|
||||
def getSettingDataString(self):
|
||||
return """{
|
||||
"name": "Display Info on LCD",
|
||||
"key": "DisplayInfoOnLCD",
|
||||
"metadata": {},
|
||||
"version": 2,
|
||||
"settings":
|
||||
{
|
||||
"display_option":
|
||||
{
|
||||
"label": "LCD display option...",
|
||||
"description": "Display Filename and Layer was formerly 'Display Filename and Layer on LCD' post-processor. The message format on the LCD is 'Printing Layer 0 of 15 3D Benchy'. Display Progress is similar to the former 'Display Progress on LCD' post-processor. The display format is '1/16 | ET 2hr28m'. Display Progress includes a fudge factor for the print time estimate.",
|
||||
"type": "enum",
|
||||
"options": {
|
||||
"display_progress": "Display Progress",
|
||||
"filename_layer": "Filename and Layer"
|
||||
},
|
||||
"default_value": "display_progress"
|
||||
},
|
||||
"format_option":
|
||||
{
|
||||
"label": "Scroll enabled/Small layers?",
|
||||
"description": "If SCROLL_LONG_FILENAMES is enabled in your firmware select this setting.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "display_option == 'filename_layer'"
|
||||
},
|
||||
"file_name":
|
||||
{
|
||||
"label": "Text to display:",
|
||||
"description": "By default the current filename will be displayed on the LCD. Enter text here to override the filename and display something else.",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"enabled": "display_option == 'filename_layer'"
|
||||
},
|
||||
"startNum":
|
||||
{
|
||||
"label": "Initial layer number:",
|
||||
"description": "Choose which number you prefer for the initial layer, 0 or 1",
|
||||
"type": "int",
|
||||
"default_value": 0,
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 1,
|
||||
"enabled": "display_option == 'filename_layer'"
|
||||
},
|
||||
"maxlayer":
|
||||
{
|
||||
"label": "Display max layer?:",
|
||||
"description": "Display how many layers are in the entire print on status bar?",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "display_option == 'filename_layer'"
|
||||
},
|
||||
"addPrefixPrinting":
|
||||
{
|
||||
"label": "Add prefix 'Printing'?",
|
||||
"description": "This will add the prefix 'Printing'",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "display_option == 'filename_layer'"
|
||||
},
|
||||
"display_total_layers":
|
||||
{
|
||||
"label": "Display total layers",
|
||||
"description": "This setting adds the 'Total Layers' to the LCD message as '17/234'.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "display_option == 'display_progress'"
|
||||
},
|
||||
"display_remaining_time":
|
||||
{
|
||||
"label": "Display remaining time",
|
||||
"description": "This will add the remaining printing time to the LCD message.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "display_option == 'display_progress'"
|
||||
},
|
||||
"add_m118_line":
|
||||
{
|
||||
"label": "Add M118 Line",
|
||||
"description": "Adds M118 in addition to the M117. It will bounce the message back through the USB port to a computer print server (if a printer server like Octoprint or Pronterface is in use).",
|
||||
"type": "bool",
|
||||
"default_value": false
|
||||
},
|
||||
"add_m73_line":
|
||||
{
|
||||
"label": "Add M73 Line(s)",
|
||||
"description": "Adds M73 in addition to the M117. For some firmware this will set the printers time and or percentage.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "display_option == 'display_progress'"
|
||||
},
|
||||
"add_m73_percent":
|
||||
{
|
||||
"label": " Add M73 Percentage",
|
||||
"description": "Adds M73 with the P parameter. For some firmware this will set the printers 'percentage' of layers completed and it will count upward.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "add_m73_line and display_option == 'display_progress'"
|
||||
},
|
||||
"add_m73_time":
|
||||
{
|
||||
"label": " Add M73 Time",
|
||||
"description": "Adds M73 with the R parameter. For some firmware this will set the printers 'print time' and it will count downward.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "add_m73_line and display_option == 'display_progress'"
|
||||
},
|
||||
"speed_factor":
|
||||
{
|
||||
"label": "Time Fudge Factor %",
|
||||
"description": "When using 'Display Progress' tweak this value to get better estimates. ([Actual Print Time]/[Cura Estimate]) x 100 = Time Fudge Factor. If Cura estimated 9hr and the print actually took 10hr30min then enter 117 here to adjust any estimate closer to reality. This Fudge Factor is also used to calculate the print finish time.",
|
||||
"type": "float",
|
||||
"unit": "%",
|
||||
"default_value": 100,
|
||||
"enabled": "enable_end_message or display_option == 'display_progress'"
|
||||
},
|
||||
"countdown_to_pause":
|
||||
{
|
||||
"label": "Countdown to Pauses",
|
||||
"description": "Instead of the remaining print time the LCD will show the estimated time to pause (TP).",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "display_option == 'display_progress'"
|
||||
},
|
||||
"enable_end_message":
|
||||
{
|
||||
"label": "Enable 'Finish-Time' Message",
|
||||
"description": "Get a message when you save a fresh slice. It will show the estimated date and time that the print would finish.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": true
|
||||
},
|
||||
"print_start_time":
|
||||
{
|
||||
"label": "Print Start Time (Ex 16:45)",
|
||||
"description": "Use 'Military' time. 16:45 would be 4:45PM. 09:30 would be 9:30AM. If you leave this blank it will be assumed that the print will start Now. If you enter a guesstimate of your printer start time and that time is before 'Now' the guesstimate will consider that the print will start tomorrow at the entered time. ",
|
||||
"type": "str",
|
||||
"default_value": "",
|
||||
"unit": "hrs ",
|
||||
"enabled": "enable_end_message"
|
||||
}
|
||||
|
||||
}
|
||||
}"""
|
||||
|
||||
def execute(self, data):
|
||||
display_option = self.getSettingValueByKey("display_option")
|
||||
add_m118_line = self.getSettingValueByKey("add_m118_line")
|
||||
add_m73_line = self.getSettingValueByKey("add_m73_line")
|
||||
add_m73_time = self.getSettingValueByKey("add_m73_time")
|
||||
add_m73_percent = self.getSettingValueByKey("add_m73_percent")
|
||||
|
||||
# This is Display Filename and Layer on LCD---------------------------------------------------------
|
||||
if display_option == "filename_layer":
|
||||
max_layer = 0
|
||||
lcd_text = "M117 "
|
||||
if self.getSettingValueByKey("file_name") != "":
|
||||
file_name = self.getSettingValueByKey("file_name")
|
||||
else:
|
||||
file_name = Application.getInstance().getPrintInformation().jobName
|
||||
if self.getSettingValueByKey("addPrefixPrinting"):
|
||||
lcd_text += "Printing "
|
||||
if not self.getSettingValueByKey("scroll"):
|
||||
lcd_text += "Layer "
|
||||
else:
|
||||
lcd_text += file_name + " - Layer "
|
||||
i = self.getSettingValueByKey("startNum")
|
||||
for layer in data:
|
||||
display_text = lcd_text + str(i)
|
||||
layer_index = data.index(layer)
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line.startswith(";LAYER_COUNT:"):
|
||||
max_layer = line
|
||||
max_layer = max_layer.split(":")[1]
|
||||
if self.getSettingValueByKey("startNum") == 0:
|
||||
max_layer = str(int(max_layer) - 1)
|
||||
if line.startswith(";LAYER:"):
|
||||
if self.getSettingValueByKey("maxlayer"):
|
||||
display_text = display_text + " of " + max_layer
|
||||
if not self.getSettingValueByKey("scroll"):
|
||||
display_text = display_text + " " + file_name
|
||||
else:
|
||||
if not self.getSettingValueByKey("scroll"):
|
||||
display_text = display_text + " " + file_name + "!"
|
||||
else:
|
||||
display_text = display_text + "!"
|
||||
line_index = lines.index(line)
|
||||
lines.insert(line_index + 1, display_text)
|
||||
if add_m118_line:
|
||||
lines.insert(line_index + 2, str(display_text.replace("M117", "M118", 1)))
|
||||
i += 1
|
||||
final_lines = "\n".join(lines)
|
||||
data[layer_index] = final_lines
|
||||
if bool(self.getSettingValueByKey("enable_end_message")):
|
||||
message_str = self.message_to_user(self.getSettingValueByKey("speed_factor") / 100)
|
||||
Message(title = "Display Info on LCD - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show()
|
||||
return data
|
||||
|
||||
# Display Progress (from 'Show Progress' and 'Display Progress on LCD')---------------------------------------
|
||||
elif display_option == "display_progress":
|
||||
# get settings
|
||||
display_total_layers = self.getSettingValueByKey("display_total_layers")
|
||||
display_remaining_time = self.getSettingValueByKey("display_remaining_time")
|
||||
speed_factor = self.getSettingValueByKey("speed_factor") / 100
|
||||
m73_time = False
|
||||
m73_percent = False
|
||||
if add_m73_line and add_m73_time:
|
||||
m73_time = True
|
||||
if add_m73_line and add_m73_percent:
|
||||
m73_percent = True
|
||||
# initialize global variables
|
||||
first_layer_index = 0
|
||||
time_total = 0
|
||||
number_of_layers = 0
|
||||
time_elapsed = 0
|
||||
# if at least one of the settings is disabled, there is enough room on the display to display "layer"
|
||||
first_section = data[0]
|
||||
lines = first_section.split("\n")
|
||||
for line in lines:
|
||||
if line.startswith(";TIME:"):
|
||||
tindex = lines.index(line)
|
||||
cura_time = int(line.split(":")[1])
|
||||
print_time = cura_time * speed_factor
|
||||
hhh = print_time/3600
|
||||
hr = round(hhh // 1)
|
||||
mmm = round((hhh % 1) * 60)
|
||||
orig_hhh = cura_time/3600
|
||||
orig_hr = round(orig_hhh // 1)
|
||||
orig_mmm = math.floor((orig_hhh % 1) * 60)
|
||||
orig_sec = round((((orig_hhh % 1) * 60) % 1) * 60)
|
||||
if add_m118_line: lines.insert(tindex + 3,"M118 Adjusted Print Time " + str(hr) + "hr " + str(mmm) + "min")
|
||||
lines.insert(tindex + 3,"M117 ET " + str(hr) + "hr " + str(mmm) + "min")
|
||||
# add M73 line at beginning
|
||||
mins = int(60 * hr + mmm)
|
||||
if m73_time:
|
||||
lines.insert(tindex + 3, "M73 R{}".format(mins))
|
||||
if m73_percent:
|
||||
lines.insert(tindex + 3, "M73 P0")
|
||||
# If Countdonw to pause is enabled then count the pauses
|
||||
pause_str = ""
|
||||
if bool(self.getSettingValueByKey("countdown_to_pause")):
|
||||
pause_count = 0
|
||||
for num in range(2,len(data) - 1, 1):
|
||||
if "PauseAtHeight.py" in data[num]:
|
||||
pause_count += 1
|
||||
pause_str = f" with {pause_count} pause(s)"
|
||||
# This line goes in to convert seconds to hours and minutes
|
||||
lines.insert(tindex + 3, f";Cura Time Estimate: {cura_time}sec = {orig_hr}hr {orig_mmm}min {orig_sec}sec {pause_str}")
|
||||
data[0] = "\n".join(lines)
|
||||
data[len(data)-1] += "M117 Orig Cura Est " + str(orig_hr) + "hr " + str(orig_mmm) + "min\n"
|
||||
if add_m118_line: data[len(data)-1] += "M118 Est w/FudgeFactor " + str(speed_factor * 100) + "% was " + str(hr) + "hr " + str(mmm) + "min\n"
|
||||
if not display_total_layers or not display_remaining_time:
|
||||
base_display_text = "layer "
|
||||
else:
|
||||
base_display_text = ""
|
||||
layer = data[len(data)-1]
|
||||
data[len(data)-1] = layer.replace(";End of Gcode" + "\n", "")
|
||||
data[len(data)-1] += ";End of Gcode" + "\n"
|
||||
# Search for the number of layers and the total time from the start code
|
||||
for index in range(len(data)):
|
||||
data_section = data[index]
|
||||
# We have everything we need, save the index of the first layer and exit the loop
|
||||
if ";LAYER:" in data_section:
|
||||
first_layer_index = index
|
||||
break
|
||||
else:
|
||||
for line in data_section.split("\n"):
|
||||
if line.startswith(";LAYER_COUNT:"):
|
||||
number_of_layers = int(line.split(":")[1])
|
||||
elif line.startswith(";TIME:"):
|
||||
time_total = int(line.split(":")[1])
|
||||
# for all layers...
|
||||
current_layer = 0
|
||||
for layer_counter in range(len(data)-2):
|
||||
current_layer += 1
|
||||
layer_index = first_layer_index + layer_counter
|
||||
display_text = base_display_text
|
||||
display_text += str(current_layer)
|
||||
# create a list where each element is a single line of code within the layer
|
||||
lines = data[layer_index].split("\n")
|
||||
if not ";LAYER:" in data[layer_index]:
|
||||
current_layer -= 1
|
||||
continue
|
||||
# add the total number of layers if this option is checked
|
||||
if display_total_layers:
|
||||
display_text += "/" + str(number_of_layers)
|
||||
# if display_remaining_time is checked, it is calculated in this loop
|
||||
if display_remaining_time:
|
||||
time_remaining_display = " | ET " # initialize the time display
|
||||
m = (time_total - time_elapsed) // 60 # estimated time in minutes
|
||||
m *= speed_factor # correct for printing time
|
||||
m = int(m)
|
||||
h, m = divmod(m, 60) # convert to hours and minutes
|
||||
# add the time remaining to the display_text
|
||||
if h > 0: # if it's more than 1 hour left, display format = xhxxm
|
||||
time_remaining_display += str(h) + "h"
|
||||
if m < 10: # add trailing zero if necessary
|
||||
time_remaining_display += "0"
|
||||
time_remaining_display += str(m) + "m"
|
||||
else:
|
||||
time_remaining_display += str(m) + "m"
|
||||
display_text += time_remaining_display
|
||||
# find time_elapsed at the end of the layer (used to calculate the remaining time of the next layer)
|
||||
if not current_layer == number_of_layers:
|
||||
for line_index in range(len(lines) - 1, -1, -1):
|
||||
line = lines[line_index]
|
||||
if line.startswith(";TIME_ELAPSED:"):
|
||||
# update time_elapsed for the NEXT layer and exit the loop
|
||||
time_elapsed = int(float(line.split(":")[1]))
|
||||
break
|
||||
# insert the text AFTER the first line of the layer (in case other scripts use ";LAYER:")
|
||||
for l_index, line in enumerate(lines):
|
||||
if line.startswith(";LAYER:"):
|
||||
lines[l_index] += "\nM117 " + display_text
|
||||
# add M73 line
|
||||
mins = int(60 * h + m)
|
||||
if m73_time:
|
||||
lines[l_index] += "\nM73 R{}".format(mins)
|
||||
if m73_percent:
|
||||
lines[l_index] += "\nM73 P" + str(round(int(current_layer) / int(number_of_layers) * 100))
|
||||
if add_m118_line:
|
||||
lines[l_index] += "\nM118 " + display_text
|
||||
break
|
||||
# overwrite the layer with the modified layer
|
||||
data[layer_index] = "\n".join(lines)
|
||||
|
||||
# If enabled then change the ET to TP for 'Time To Pause'
|
||||
if bool(self.getSettingValueByKey("countdown_to_pause")):
|
||||
time_list = []
|
||||
time_list.append("0")
|
||||
time_list.append("0")
|
||||
this_time = 0
|
||||
pause_index = 1
|
||||
|
||||
# Get the layer times
|
||||
for num in range(2,len(data) - 1):
|
||||
layer = data[num]
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line.startswith(";TIME_ELAPSED:"):
|
||||
this_time = (float(line.split(":")[1]))*speed_factor
|
||||
time_list.append(str(this_time))
|
||||
if "PauseAtHeight.py" in layer:
|
||||
for qnum in range(num - 1, pause_index, -1):
|
||||
time_list[qnum] = str(float(this_time) - float(time_list[qnum])) + "P"
|
||||
pause_index = num-1
|
||||
|
||||
# Make the adjustments to the M117 (and M118) lines that are prior to a pause
|
||||
for num in range (2, len(data) - 1,1):
|
||||
layer = data[num]
|
||||
lines = layer.split("\n")
|
||||
for line in lines:
|
||||
if line.startswith("M117") and "|" in line and "P" in time_list[num]:
|
||||
M117_line = line.split("|")[0] + "| TP "
|
||||
alt_time = time_list[num][:-1]
|
||||
hhh = int(float(alt_time) / 3600)
|
||||
if hhh > 0:
|
||||
hhr = str(hhh) + "h"
|
||||
else:
|
||||
hhr = ""
|
||||
mmm = ((float(alt_time) / 3600) - (int(float(alt_time) / 3600))) * 60
|
||||
sss = int((mmm - int(mmm)) * 60)
|
||||
mmm = str(round(mmm)) + "m"
|
||||
time_to_go = str(hhr) + str(mmm)
|
||||
if hhr == "": time_to_go = time_to_go + str(sss) + "s"
|
||||
M117_line = M117_line + time_to_go
|
||||
layer = layer.replace(line, M117_line)
|
||||
if line.startswith("M118") and "|" in line and "P" in time_list[num]:
|
||||
M118_line = line.split("|")[0] + "| TP " + time_to_go
|
||||
layer = layer.replace(line, M118_line)
|
||||
data[num] = layer
|
||||
setting_data = ""
|
||||
if bool(self.getSettingValueByKey("enable_end_message")):
|
||||
message_str = self.message_to_user(speed_factor)
|
||||
Message(title = "[Display Info on LCD] - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show()
|
||||
return data
|
||||
|
||||
def message_to_user(self, speed_factor: float):
|
||||
# Message the user of the projected finish time of the print
|
||||
print_time = Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601)
|
||||
print_start_time = self.getSettingValueByKey("print_start_time")
|
||||
# If the user entered a print start time make sure it is in the correct format or ignore it.
|
||||
if print_start_time == "" or print_start_time == "0" or len(print_start_time) != 5 or not ":" in print_start_time:
|
||||
print_start_time = ""
|
||||
# Change the print start time to proper time format, or, use the current time
|
||||
if print_start_time != "":
|
||||
hr = int(print_start_time.split(":")[0])
|
||||
min = int(print_start_time.split(":")[1])
|
||||
sec = 0
|
||||
else:
|
||||
hr = int(time.strftime("%H"))
|
||||
min = int(time.strftime("%M"))
|
||||
sec = int(time.strftime("%S"))
|
||||
|
||||
#Get the current data/time info
|
||||
yr = int(time.strftime("%Y"))
|
||||
day = int(time.strftime("%d"))
|
||||
mo = int(time.strftime("%m"))
|
||||
|
||||
date_and_time = datetime.datetime(yr, mo, day, hr, min, sec)
|
||||
#Split the Cura print time
|
||||
pr_hr = int(print_time.split(":")[0])
|
||||
pr_min = int(print_time.split(":")[1])
|
||||
pr_sec = int(print_time.split(":")[2])
|
||||
#Adjust the print time if none was entered
|
||||
print_seconds = pr_hr*3600 + pr_min*60 + pr_sec
|
||||
#Adjust the total seconds by the Fudge Factor
|
||||
adjusted_print_time = print_seconds * speed_factor
|
||||
#Break down the adjusted seconds back into hh:mm:ss
|
||||
adj_hr = int(adjusted_print_time/3600)
|
||||
print_seconds = adjusted_print_time - (adj_hr * 3600)
|
||||
adj_min = int(print_seconds) / 60
|
||||
adj_sec = int(print_seconds - (adj_min * 60))
|
||||
#Get the print time to add to the start time
|
||||
time_change = datetime.timedelta(hours=adj_hr, minutes=adj_min, seconds=adj_sec)
|
||||
new_time = date_and_time + time_change
|
||||
#Get the day of the week that the print will end on
|
||||
week_day = str(["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][int(new_time.strftime("%w"))])
|
||||
#Get the month that the print will end in
|
||||
mo_str = str(["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][int(new_time.strftime("%m"))-1])
|
||||
#Make adjustments from 24hr time to 12hr time
|
||||
if int(new_time.strftime("%H")) > 12:
|
||||
show_hr = str(int(new_time.strftime("%H")) - 12) + ":"
|
||||
show_ampm = " PM"
|
||||
elif int(new_time.strftime("%H")) == 0:
|
||||
show_hr = "12:"
|
||||
show_ampm = " AM"
|
||||
else:
|
||||
show_hr = str(new_time.strftime("%H")) + ":"
|
||||
show_ampm = " AM"
|
||||
if print_start_time == "":
|
||||
start_str = "Now"
|
||||
else:
|
||||
start_str = "and your entered 'print start time' of " + print_start_time + "hrs."
|
||||
if print_start_time != "":
|
||||
print_start_str = "Print Start Time................." + str(print_start_time) + "hrs"
|
||||
else:
|
||||
print_start_str = "Print Start Time.................Now."
|
||||
estimate_str = "Cura Time Estimate.........." + str(print_time)
|
||||
adjusted_str = "Adjusted Time Estimate..." + str(time_change)
|
||||
finish_str = week_day + " " + str(mo_str) + " " + str(new_time.strftime("%d")) + ", " + str(new_time.strftime("%Y")) + " at " + str(show_hr) + str(new_time.strftime("%M")) + str(show_ampm)
|
||||
return finish_str, estimate_str, adjusted_str, print_start_str
|
|
@ -7,14 +7,13 @@
|
|||
|
||||
from ..Script import Script
|
||||
|
||||
import re
|
||||
import datetime
|
||||
from UM.Message import Message
|
||||
|
||||
class DisplayProgressOnLCD(Script):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def initialize(self) -> None:
|
||||
Message(title = "[Display Progress on LCD]", text = "This script is now an option in 'Display Info on LCD'. This post processor no longer works.").show()
|
||||
|
||||
def getSettingDataString(self):
|
||||
return """{
|
||||
"name": "Display Progress On LCD",
|
||||
|
@ -23,176 +22,17 @@ class DisplayProgressOnLCD(Script):
|
|||
"version": 2,
|
||||
"settings":
|
||||
{
|
||||
"time_remaining":
|
||||
"enable_script":
|
||||
{
|
||||
"label": "Time Remaining",
|
||||
"description": "Select to write remaining time to the display.Select to write remaining time on the display using M117 status line message (almost all printers) or using M73 command (Prusa and Marlin 2 if enabled).",
|
||||
"label": "Deprecated/Obsolete",
|
||||
"description": "This script is now included in 'Display Info on LCD'.",
|
||||
"type": "bool",
|
||||
"default_value": false
|
||||
},
|
||||
"time_remaining_method":
|
||||
{
|
||||
"label": "Time Reporting Method",
|
||||
"description": "How should remaining time be shown on the display? It could use a generic message command (M117, almost all printers), or a specialised time remaining command (M73, Prusa and Marlin 2).",
|
||||
"type": "enum",
|
||||
"options": {
|
||||
"m117":"M117 - All printers",
|
||||
"m73":"M73 - Prusa, Marlin 2",
|
||||
"m118":"M118 - Octoprint"
|
||||
},
|
||||
"enabled": "time_remaining",
|
||||
"default_value": "m117"
|
||||
},
|
||||
"update_frequency":
|
||||
{
|
||||
"label": "Update frequency",
|
||||
"description": "Update remaining time for every layer or periodically every minute or faster.",
|
||||
"type": "enum",
|
||||
"options": {"0":"Every layer","15":"Every 15 seconds","30":"Every 30 seconds","60":"Every minute"},
|
||||
"default_value": "0",
|
||||
"enabled": "time_remaining"
|
||||
},
|
||||
"percentage":
|
||||
{
|
||||
"label": "Percentage",
|
||||
"description": "When enabled, set the completion bar percentage on the LCD using Marlin's M73 command.",
|
||||
"type": "bool",
|
||||
"default_value": false
|
||||
"default_value": true
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
# Get the time value from a line as a float.
|
||||
# Example line ;TIME_ELAPSED:1234.6789 or ;TIME:1337
|
||||
def getTimeValue(self, line):
|
||||
list_split = re.split(":", line) # Split at ":" so we can get the numerical value
|
||||
return float(list_split[1]) # Convert the numerical portion to a float
|
||||
|
||||
def outputTime(self, lines, line_index, time_left, mode):
|
||||
# Do some math to get the time left in seconds into the right format. (HH,MM,SS)
|
||||
time_left = max(time_left, 0)
|
||||
m, s = divmod(time_left, 60)
|
||||
h, m = divmod(m, 60)
|
||||
# Create the string
|
||||
if mode == "m117":
|
||||
current_time_string = "{:d}h{:02d}m{:02d}s".format(int(h), int(m), int(s))
|
||||
# And now insert that into the GCODE
|
||||
lines.insert(line_index, "M117 Time Left {}".format(current_time_string))
|
||||
elif mode == "m118":
|
||||
current_time_string = "{:d}h{:02d}m{:02d}s".format(int(h), int(m), int(s))
|
||||
# And now insert that into the GCODE
|
||||
lines.insert(line_index, "M118 A1 P0 action:notification Time Left {}".format(current_time_string))
|
||||
else:
|
||||
mins = int(60 * h + m + s / 30)
|
||||
lines.insert(line_index, "M73 R{}".format(mins))
|
||||
|
||||
def execute(self, data):
|
||||
output_time = self.getSettingValueByKey("time_remaining")
|
||||
output_time_method = self.getSettingValueByKey("time_remaining_method")
|
||||
output_frequency = int(self.getSettingValueByKey("update_frequency"))
|
||||
output_percentage = self.getSettingValueByKey("percentage")
|
||||
line_set = {}
|
||||
if output_percentage or output_time:
|
||||
total_time = -1
|
||||
previous_layer_end_percentage = 0
|
||||
previous_layer_end_time = 0
|
||||
for layer in data:
|
||||
layer_index = data.index(layer)
|
||||
lines = layer.split("\n")
|
||||
|
||||
for line in lines:
|
||||
if (line.startswith(";TIME:") or line.startswith(";PRINT.TIME:")) and total_time == -1:
|
||||
# This line represents the total time required to print the gcode
|
||||
total_time = self.getTimeValue(line)
|
||||
line_index = lines.index(line)
|
||||
|
||||
# In the beginning we may have 2 M73 lines, but it makes logic less complicated
|
||||
if output_time:
|
||||
self.outputTime(lines, line_index, total_time, output_time_method)
|
||||
|
||||
if output_percentage:
|
||||
# Emit 0 percent to sure Marlin knows we are overriding the completion percentage
|
||||
if output_time_method == "m118":
|
||||
lines.insert(line_index, "M118 A1 P0 action:notification Data Left 0/100")
|
||||
else:
|
||||
lines.insert(line_index, "M73 P0")
|
||||
|
||||
elif line.startswith(";TIME_ELAPSED:"):
|
||||
# We've found one of the time elapsed values which are added at the end of layers
|
||||
|
||||
# If we have seen this line before then skip processing it. We can see lines multiple times because we are adding
|
||||
# intermediate percentages before the line being processed. This can cause the current line to shift back and be
|
||||
# encountered more than once
|
||||
if line in line_set:
|
||||
continue
|
||||
line_set[line] = True
|
||||
|
||||
# If total_time was not already found then noop
|
||||
if total_time == -1:
|
||||
continue
|
||||
|
||||
current_time = self.getTimeValue(line)
|
||||
line_index = lines.index(line)
|
||||
|
||||
if output_time:
|
||||
if output_frequency == 0:
|
||||
# Here we calculate remaining time
|
||||
self.outputTime(lines, line_index, total_time - current_time, output_time_method)
|
||||
else:
|
||||
# Here we calculate remaining time and how many outputs are expected for the layer
|
||||
layer_time_delta = int(current_time - previous_layer_end_time)
|
||||
layer_step_delta = int((current_time - previous_layer_end_time) / output_frequency)
|
||||
# If this layer represents less than 1 step then we don't need to emit anything, continue to the next layer
|
||||
if layer_step_delta != 0:
|
||||
# Grab the index of the current line and figure out how many lines represent one second
|
||||
step = line_index / layer_time_delta
|
||||
# Move new lines further as we add new lines above it
|
||||
lines_added = 1
|
||||
# Run through layer in seconds
|
||||
for seconds in range(1, layer_time_delta + 1):
|
||||
# Time from start to decide when to update
|
||||
line_time = int(previous_layer_end_time + seconds)
|
||||
# Output every X seconds and after last layer
|
||||
if line_time % output_frequency == 0 or line_time == total_time:
|
||||
# Line to add the output
|
||||
time_line_index = int((seconds * step) + lines_added)
|
||||
|
||||
# Insert remaining time into the GCODE
|
||||
self.outputTime(lines, time_line_index, total_time - line_time, output_time_method)
|
||||
# Next line will be again lower
|
||||
lines_added = lines_added + 1
|
||||
|
||||
previous_layer_end_time = int(current_time)
|
||||
|
||||
if output_percentage:
|
||||
# Calculate percentage value this layer ends at
|
||||
layer_end_percentage = int((current_time / total_time) * 100)
|
||||
|
||||
# Figure out how many percent of the total time is spent in this layer
|
||||
layer_percentage_delta = layer_end_percentage - previous_layer_end_percentage
|
||||
|
||||
# If this layer represents less than 1 percent then we don't need to emit anything, continue to the next layer
|
||||
if layer_percentage_delta != 0:
|
||||
# Grab the index of the current line and figure out how many lines represent one percent
|
||||
step = line_index / layer_percentage_delta
|
||||
|
||||
for percentage in range(1, layer_percentage_delta + 1):
|
||||
# We add the percentage value here as while processing prior lines we will have inserted
|
||||
# percentage lines before the current one. Failing to do this will upset the spacing
|
||||
percentage_line_index = int((percentage * step) + percentage)
|
||||
|
||||
# Due to integer truncation of the total time value in the gcode the percentage we
|
||||
# calculate may slightly exceed 100, as that is not valid we cap the value here
|
||||
output = min(percentage + previous_layer_end_percentage, 100)
|
||||
|
||||
# Now insert the sanitized percentage into the GCODE
|
||||
if output_time_method == "m118":
|
||||
lines.insert(percentage_line_index, "M118 A1 P0 action:notification Data Left {}/100".format(output))
|
||||
else:
|
||||
lines.insert(percentage_line_index, "M73 P{}".format(output))
|
||||
|
||||
previous_layer_end_percentage = layer_end_percentage
|
||||
|
||||
# Join up the lines for this layer again and store them in the data array
|
||||
data[layer_index] = "\n".join(lines)
|
||||
Message(title = "[Display Progress on LCD]", text = "This post is now included in 'Display Info on LCD'. This script will exit.").show()
|
||||
data[0] += "; [Display Progress on LCD] Did not run. It is now included in 'Display Info on LCD'.\n"
|
||||
return data
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
||||
# Copyright (c) 2023 Ultimaker B.V.
|
||||
# The PostProcessingPlugin is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
# Modification 06.09.2020
|
||||
# add checkbox, now you can choose and use configuration from the firmware itself.
|
||||
|
@ -7,7 +7,7 @@
|
|||
from typing import List
|
||||
from ..Script import Script
|
||||
|
||||
from UM.Application import Application #To get the current printer's settings.
|
||||
from UM.Application import Application # To get the current printer's settings.
|
||||
|
||||
class FilamentChange(Script):
|
||||
|
||||
|
@ -199,7 +199,7 @@ class FilamentChange(Script):
|
|||
if enable_before_macro:
|
||||
color_change = color_change + before_macro + "\n"
|
||||
|
||||
color_change = color_change + "M600\n"
|
||||
color_change = color_change + "M600"
|
||||
|
||||
if not firmware_config:
|
||||
if initial_retract is not None and initial_retract > 0.:
|
||||
|
@ -213,13 +213,15 @@ class FilamentChange(Script):
|
|||
|
||||
if x_pos is not None:
|
||||
color_change = color_change + (" X%.2f" % x_pos)
|
||||
|
||||
|
||||
if y_pos is not None:
|
||||
color_change = color_change + (" Y%.2f" % y_pos)
|
||||
|
||||
|
||||
if z_pos is not None and z_pos > 0.:
|
||||
color_change = color_change + (" Z%.2f" % z_pos)
|
||||
|
||||
color_change = color_change + "\n"
|
||||
|
||||
if enable_after_macro:
|
||||
color_change = color_change + after_macro + "\n"
|
||||
|
||||
|
|
357
plugins/PostProcessingPlugin/scripts/LimitXYAccelJerk.py
Normal file
357
plugins/PostProcessingPlugin/scripts/LimitXYAccelJerk.py
Normal file
|
@ -0,0 +1,357 @@
|
|||
# Limit XY Accel: Authored by: Greg Foresi (GregValiant)
|
||||
# July 2023
|
||||
# Sometimes bed-slinger printers need different Accel and Jerk values for the Y but Cura always makes them the same.
|
||||
# This script changes the Accel and/or Jerk from the beginning of the 'Start Layer' to the end of the 'End Layer'.
|
||||
# The existing M201 Max Accel will be changed to limit the Y (and/or X) accel at the printer. If you have Accel enabled in Cura and the XY Accel is set to 3000 then setting the Y limit to 1000 will result in the printer limiting the Y to 1000. This can keep tall skinny prints from breaking loose of the bed and failing. The script was not tested with Junction Deviation.
|
||||
# If enabled - the Jerk setting is changed line-by-line within the gcode as there is no "limit" on Jerk.
|
||||
# if 'Gradual ACCEL change' is enabled then the Accel is changed gradually from the Start to the End layer and that will be the final Accel setting in the file. If 'Gradual' is enabled then the Jerk settings will continue to be changed to the end of the file (rather than ending at the End layer).
|
||||
# This post is intended for printers with moving beds (bed slingers) so UltiMaker printers are excluded.
|
||||
# When setting an accel limit on multi-extruder printers ALL extruders are effected.
|
||||
# This post does not distinguish between Print Accel and Travel Accel. The limit is the limit for all regardless. Example: Skin Accel = 1000 and Outer Wall accel = 500. If the limit is set to 300 then both Skin and Outer Wall will be Accel = 300.
|
||||
# 9/15/2023 added support for RepRap M566 command for Jerk in mm/min
|
||||
# 2/4/2024 Added a block so the script doesn't run unless Accel Control is enabled in Cura. This should keep users from increasing the Accel Limits.
|
||||
|
||||
from ..Script import Script
|
||||
from cura.CuraApplication import CuraApplication
|
||||
import re
|
||||
from UM.Message import Message
|
||||
|
||||
class LimitXYAccelJerk(Script):
|
||||
|
||||
def initialize(self) -> None:
|
||||
super().initialize()
|
||||
# Get the Accel and Jerk and set the values in the setting boxes--
|
||||
mycura = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
extruder = mycura.extruderList
|
||||
accel_print = extruder[0].getProperty("acceleration_print", "value")
|
||||
accel_travel = extruder[0].getProperty("acceleration_travel", "value")
|
||||
jerk_print_old = extruder[0].getProperty("jerk_print", "value")
|
||||
jerk_travel_old = extruder[0].getProperty("jerk_travel", "value")
|
||||
self._instance.setProperty("x_accel_limit", "value", round(accel_print))
|
||||
self._instance.setProperty("y_accel_limit", "value", round(accel_print))
|
||||
self._instance.setProperty("x_jerk", "value", jerk_print_old)
|
||||
self._instance.setProperty("y_jerk", "value", jerk_print_old)
|
||||
ext_count = int(mycura.getProperty("machine_extruder_count", "value"))
|
||||
machine_name = str(mycura.getProperty("machine_name", "value"))
|
||||
if str(mycura.getProperty("machine_gcode_flavor", "value")) == "RepRap (RepRap)":
|
||||
self._instance.setProperty("jerk_cmd", "value", "reprap_flavor")
|
||||
else:
|
||||
self._instance.setProperty("jerk_cmd", "value", "marlin_flavor")
|
||||
firmware_flavor = str(mycura.getProperty("machine_gcode_flavor", "value"))
|
||||
|
||||
# Warn the user if the printer is an Ultimaker-------------------------
|
||||
if "Ultimaker" in machine_name or "UltiGCode" in firmware_flavor or "Griffin" in firmware_flavor:
|
||||
Message(text = "<NOTICE> [Limit the X-Y Accel/Jerk] DID NOT RUN because Ultimaker printers don't have sliding beds.").show()
|
||||
|
||||
# Warn the user if the printer is multi-extruder------------------
|
||||
if ext_count > 1:
|
||||
Message(text = "<NOTICE> 'Limit the X-Y Accel/Jerk': The post processor treats all extruders the same. If you have multiple extruders they will all be subject to the same Accel and Jerk limits imposed. If you have different Travel and Print Accel they will also be subject to the same limits. If that is not acceptable then you should not use this Post Processor.").show()
|
||||
|
||||
# Warn the user if Accel Control is not enabled in Cura. This keeps the script from being able to increase the Accel limits-----------
|
||||
if not bool(extruder[0].getProperty("acceleration_enabled", "value")):
|
||||
Message(title = "[Limit the X-Y Accel/Jerk]", text = "You must have 'Enable Acceleration Control' checked in Cura or the script will exit.").show()
|
||||
|
||||
def getSettingDataString(self):
|
||||
return """{
|
||||
"name": "Limit the X-Y Accel/Jerk (all extruders equal)",
|
||||
"key": "LimitXYAccelJerk",
|
||||
"metadata": {},
|
||||
"version": 2,
|
||||
"settings":
|
||||
{
|
||||
"type_of_change":
|
||||
{
|
||||
"label": "Immediate or Gradual change",
|
||||
"description": "An 'Immediate' change will insert the new numbers immediately at the Start Layer. A 'Gradual' change will transition from the starting Accel to the new Accel limit across a range of layers.",
|
||||
"type": "enum",
|
||||
"options": {
|
||||
"immediate_change": "Immediate",
|
||||
"gradual_change": "Gradual"},
|
||||
"default_value": "immediate_change"
|
||||
},
|
||||
"x_accel_limit":
|
||||
{
|
||||
"label": "X MAX Acceleration",
|
||||
"description": "If this number is lower than the 'X Print Accel' in Cura then this will limit the Accel on the X axis. Enter the Maximum Acceleration value for the X axis. This will affect both Print and Travel Accel. If you enable an End Layer then at the end of that layer the Accel Limit will be reset (unless you choose 'Gradual' in which case the new limit goes to the top layer).",
|
||||
"type": "int",
|
||||
"enabled": true,
|
||||
"minimum_value": 50,
|
||||
"unit": "mm/sec² ",
|
||||
"default_value": 500
|
||||
},
|
||||
"y_accel_limit":
|
||||
{
|
||||
"label": "Y MAX Acceleration",
|
||||
"description": "If this number is lower than the Y accel in Cura then this will limit the Accel on the Y axis. Enter the Maximum Acceleration value for the Y axis. This will affect both Print and Travel Accel. If you enable an End Layer then at the end of that layer the Accel Limit will be reset (unless you choose 'Gradual' in which case the new limit goes to the top layer).",
|
||||
"type": "int",
|
||||
"enabled": true,
|
||||
"minimum_value": 50,
|
||||
"unit": "mm/sec² ",
|
||||
"default_value": 500
|
||||
},
|
||||
"jerk_enable":
|
||||
{
|
||||
"label": "Change the Jerk",
|
||||
"description": "Whether to change the Jerk values.",
|
||||
"type": "bool",
|
||||
"enabled": true,
|
||||
"default_value": false
|
||||
},
|
||||
"jerk_cmd":
|
||||
{
|
||||
"label": "G-Code Jerk Command",
|
||||
"description": "Marlin uses M205. RepRap might use M566.",
|
||||
"type": "enum",
|
||||
"options": {
|
||||
"marlin_flavor": "M205",
|
||||
"reprap_flavor": "M566"},
|
||||
"default_value": "marlin_flavor",
|
||||
"enabled": "jerk_enable"
|
||||
},
|
||||
"x_jerk":
|
||||
{
|
||||
"label": " X jerk",
|
||||
"description": "Enter the Jerk value for the X axis. Enter '0' to use the existing X Jerk. This setting will affect both the Print and Travel jerk.",
|
||||
"type": "int",
|
||||
"enabled": "jerk_enable",
|
||||
"unit": "mm/sec ",
|
||||
"default_value": 8
|
||||
},
|
||||
"y_jerk":
|
||||
{
|
||||
"label": " Y jerk",
|
||||
"description": "Enter the Jerk value for the Y axis. Enter '0' to use the existing Y Jerk. This setting will affect both the Print and Travel jerk.",
|
||||
"type": "int",
|
||||
"enabled": "jerk_enable",
|
||||
"unit": "mm/sec ",
|
||||
"default_value": 8
|
||||
},
|
||||
"start_layer":
|
||||
{
|
||||
"label": "From Start of Layer:",
|
||||
"description": "Use the Cura Preview numbers. Enter the Layer to start the changes at. The minimum is Layer 1.",
|
||||
"type": "int",
|
||||
"default_value": 1,
|
||||
"minimum_value": 1,
|
||||
"unit": "Lay# ",
|
||||
"enabled": "type_of_change == 'immediate_change'"
|
||||
},
|
||||
"end_layer":
|
||||
{
|
||||
"label": "To End of Layer",
|
||||
"description": "Use the Cura Preview numbers. Enter '-1' for the entire file or enter a layer number. The changes will end at your 'End Layer' and revert back to the original numbers.",
|
||||
"type": "int",
|
||||
"default_value": -1,
|
||||
"minimum_value": -1,
|
||||
"unit": "Lay# ",
|
||||
"enabled": "type_of_change == 'immediate_change'"
|
||||
},
|
||||
"gradient_start_layer":
|
||||
{
|
||||
"label": " Gradual From Layer:",
|
||||
"description": "Use the Cura Preview numbers. Enter the Layer to start the changes at. The minimum is Layer 1.",
|
||||
"type": "int",
|
||||
"default_value": 1,
|
||||
"minimum_value": 1,
|
||||
"unit": "Lay# ",
|
||||
"enabled": "type_of_change == 'gradual_change'"
|
||||
},
|
||||
"gradient_end_layer":
|
||||
{
|
||||
"label": " Gradual To Layer",
|
||||
"description": "Use the Cura Preview numbers. Enter '-1' for the top layer or enter a layer number. The last 'Gradual' change will continue to the end of the file.",
|
||||
"type": "int",
|
||||
"default_value": -1,
|
||||
"minimum_value": -1,
|
||||
"unit": "Lay# ",
|
||||
"enabled": "type_of_change == 'gradual_change'"
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
def execute(self, data):
|
||||
mycura = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
extruder = mycura.extruderList
|
||||
machine_name = str(mycura.getProperty("machine_name", "value"))
|
||||
print_sequence = str(mycura.getProperty("print_sequence", "value"))
|
||||
acceleration_enabled = bool(extruder[0].getProperty("acceleration_enabled", "value"))
|
||||
|
||||
# Exit if acceleration control is not enabled----------------
|
||||
if not acceleration_enabled:
|
||||
Message(title = "[Limit the X-Y Accel/Jerk]", text = "DID NOT RUN. You must have 'Enable Acceleration Control' checked in Cura.").show()
|
||||
data[0] += "; [LimitXYAccelJerk] DID NOT RUN because 'Enable Acceleration Control' is not checked in Cura.\n"
|
||||
return data
|
||||
|
||||
# Exit if 'one_at_a_time' is enabled-------------------------
|
||||
if print_sequence == "one_at_a_time":
|
||||
Message(text = "<NOTICE> [Limit the X-Y Accel/Jerk] DID NOT RUN. This post processor is not compatible with 'One-at-a-Time' mode.").show()
|
||||
data[0] += "; [LimitXYAccelJerk] DID NOT RUN because Cura is set to 'One-at-a-Time' mode.\n"
|
||||
return data
|
||||
|
||||
# Exit if the printer is an Ultimaker-------------------------
|
||||
if "Ultimaker" in machine_name:
|
||||
Message(text = "<NOTICE> [Limit the X-Y Accel/Jerk] DID NOT RUN. This post processor is for bed slinger printers only.").show()
|
||||
data[0] += "; [LimitXYAccelJerk] DID NOT RUN because the printer doesn't have a sliding bed.\n"
|
||||
return data
|
||||
|
||||
type_of_change = str(self.getSettingValueByKey("type_of_change"))
|
||||
accel_print = extruder[0].getProperty("acceleration_print", "value")
|
||||
accel_travel = extruder[0].getProperty("acceleration_travel", "value")
|
||||
jerk_print_old = extruder[0].getProperty("jerk_print", "value")
|
||||
jerk_travel_old = extruder[0].getProperty("jerk_travel", "value")
|
||||
if int(accel_print) >= int(accel_travel):
|
||||
accel_old = accel_print
|
||||
else:
|
||||
accel_old = accel_travel
|
||||
jerk_travel = str(extruder[0].getProperty("jerk_travel", "value"))
|
||||
if int(jerk_print_old) >= int(jerk_travel_old):
|
||||
jerk_old = jerk_print_old
|
||||
else:
|
||||
jerk_old = jerk_travel_old
|
||||
|
||||
#Set the new Accel values----------------------------------------------------------
|
||||
x_accel = str(self.getSettingValueByKey("x_accel_limit"))
|
||||
y_accel = str(self.getSettingValueByKey("y_accel_limit"))
|
||||
x_jerk = int(self.getSettingValueByKey("x_jerk"))
|
||||
y_jerk = int(self.getSettingValueByKey("y_jerk"))
|
||||
if str(self.getSettingValueByKey("jerk_cmd")) == "reprap_flavor":
|
||||
jerk_cmd = "M566"
|
||||
x_jerk *= 60
|
||||
y_jerk *= 60
|
||||
jerk_old *= 60
|
||||
else:
|
||||
jerk_cmd = "M205"
|
||||
|
||||
# Put the strings together-------------------------------------------
|
||||
m201_limit_new = f"M201 X{x_accel} Y{y_accel}"
|
||||
m201_limit_old = f"M201 X{round(accel_old)} Y{round(accel_old)}"
|
||||
if x_jerk == 0:
|
||||
m205_jerk_pattern = r"Y(\d*)"
|
||||
m205_jerk_new = f"Y{y_jerk}"
|
||||
if y_jerk == 0:
|
||||
m205_jerk_pattern = r"X(\d*)"
|
||||
m205_jerk_new = f"X{x_jerk}"
|
||||
if x_jerk != 0 and y_jerk != 0:
|
||||
m205_jerk_pattern = jerk_cmd + r" X(\d*) Y(\d*)"
|
||||
m205_jerk_new = jerk_cmd + f" X{x_jerk} Y{y_jerk}"
|
||||
m205_jerk_old = jerk_cmd + f" X{jerk_old} Y{jerk_old}"
|
||||
type_of_change = self.getSettingValueByKey("type_of_change")
|
||||
|
||||
#Get the indexes of the start and end layers----------------------------------------
|
||||
if type_of_change == 'immediate_change':
|
||||
start_layer = int(self.getSettingValueByKey("start_layer"))-1
|
||||
end_layer = int(self.getSettingValueByKey("end_layer"))
|
||||
else:
|
||||
start_layer = int(self.getSettingValueByKey("gradient_start_layer"))-1
|
||||
end_layer = int(self.getSettingValueByKey("gradient_end_layer"))
|
||||
start_index = 2
|
||||
end_index = len(data)-2
|
||||
for num in range(2,len(data)-1):
|
||||
if ";LAYER:" + str(start_layer) + "\n" in data[num]:
|
||||
start_index = num
|
||||
break
|
||||
if int(end_layer) > 0:
|
||||
for num in range(3,len(data)-1):
|
||||
try:
|
||||
if ";LAYER:" + str(end_layer) + "\n" in data[num]:
|
||||
end_index = num
|
||||
break
|
||||
except:
|
||||
end_index = len(data)-2
|
||||
|
||||
#Add Accel limit and new Jerk at start layer-----------------------------------------------------
|
||||
if type_of_change == "immediate_change":
|
||||
layer = data[start_index]
|
||||
lines = layer.split("\n")
|
||||
for index, line in enumerate(lines):
|
||||
if lines[index].startswith(";LAYER:"):
|
||||
lines.insert(index+1,m201_limit_new)
|
||||
if self.getSettingValueByKey("jerk_enable"):
|
||||
lines.insert(index+2,m205_jerk_new)
|
||||
data[start_index] = "\n".join(lines)
|
||||
break
|
||||
|
||||
#Alter any existing jerk lines. Accel lines can be ignored-----------------------------------
|
||||
for num in range(start_index,end_index,1):
|
||||
layer = data[num]
|
||||
lines = layer.split("\n")
|
||||
for index, line in enumerate(lines):
|
||||
if line.startswith("M205") or line.startswith("M566"):
|
||||
lines[index] = re.sub(m205_jerk_pattern, m205_jerk_new, line)
|
||||
data[num] = "\n".join(lines)
|
||||
if end_layer != -1:
|
||||
try:
|
||||
layer = data[end_index-1]
|
||||
lines = layer.split("\n")
|
||||
lines.insert(len(lines)-2,m201_limit_old)
|
||||
lines.insert(len(lines)-2,m205_jerk_old)
|
||||
data[end_index-1] = "\n".join(lines)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
data[len(data)-1] = m201_limit_old + "\n" + m205_jerk_old + "\n" + data[len(data)-1]
|
||||
return data
|
||||
|
||||
elif type_of_change == "gradual_change":
|
||||
layer_spread = end_index - start_index
|
||||
if accel_old >= int(x_accel):
|
||||
x_accel_hyst = round((accel_old - int(x_accel)) / layer_spread)
|
||||
else:
|
||||
x_accel_hyst = round((int(x_accel) - accel_old) / layer_spread)
|
||||
if accel_old >= int(y_accel):
|
||||
y_accel_hyst = round((accel_old - int(y_accel)) / layer_spread)
|
||||
else:
|
||||
y_accel_hyst = round((int(y_accel) - accel_old) / layer_spread)
|
||||
|
||||
if accel_old >= int(x_accel):
|
||||
x_accel_start = round(round((accel_old - x_accel_hyst)/25)*25)
|
||||
else:
|
||||
x_accel_start = round(round((x_accel_hyst + accel_old)/25)*25)
|
||||
if accel_old >= int(y_accel):
|
||||
y_accel_start = round(round((accel_old - y_accel_hyst)/25)*25)
|
||||
else:
|
||||
y_accel_start = round(round((y_accel_hyst + accel_old)/25)*25)
|
||||
m201_limit_new = "M201 X" + str(x_accel_start) + " Y" + str(y_accel_start)
|
||||
#Add Accel limit and new Jerk at start layer-------------------------------------------------------------
|
||||
layer = data[start_index]
|
||||
lines = layer.split("\n")
|
||||
for index, line in enumerate(lines):
|
||||
if lines[index].startswith(";LAYER:"):
|
||||
lines.insert(index+1,m201_limit_new)
|
||||
if self.getSettingValueByKey("jerk_enable"):
|
||||
lines.insert(index+2,m205_jerk_new)
|
||||
data[start_index] = "\n".join(lines)
|
||||
break
|
||||
for num in range(start_index + 1, end_index,1):
|
||||
layer = data[num]
|
||||
lines = layer.split("\n")
|
||||
if accel_old >= int(x_accel):
|
||||
x_accel_start -= x_accel_hyst
|
||||
if x_accel_start < int(x_accel): x_accel_start = int(x_accel)
|
||||
else:
|
||||
x_accel_start += x_accel_hyst
|
||||
if x_accel_start > int(x_accel): x_accel_start = int(x_accel)
|
||||
if accel_old >= int(y_accel):
|
||||
y_accel_start -= y_accel_hyst
|
||||
if y_accel_start < int(y_accel): y_accel_start = int(y_accel)
|
||||
else:
|
||||
y_accel_start += y_accel_hyst
|
||||
if y_accel_start > int(y_accel): y_accel_start = int(y_accel)
|
||||
m201_limit_new = "M201 X" + str(round(round(x_accel_start/25)*25)) + " Y" + str(round(round(y_accel_start/25)*25))
|
||||
for index, line in enumerate(lines):
|
||||
if line.startswith(";LAYER:"):
|
||||
lines.insert(index+1, m201_limit_new)
|
||||
continue
|
||||
data[num] = "\n".join(lines)
|
||||
|
||||
#Alter any existing jerk lines. Accel lines can be ignored---------------
|
||||
if self.getSettingValueByKey("jerk_enable"):
|
||||
for num in range(start_index,len(data)-1,1):
|
||||
layer = data[num]
|
||||
lines = layer.split("\n")
|
||||
for index, line in enumerate(lines):
|
||||
if line.startswith("M205") or line.startswith("M566"):
|
||||
lines[index] = re.sub(m205_jerk_pattern, m205_jerk_new, line)
|
||||
data[num] = "\n".join(lines)
|
||||
data[len(data)-1] = m201_limit_old + "\n" + m205_jerk_old + "\n" + data[len(data)-1]
|
||||
return data
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
from ..Script import Script
|
||||
import re
|
||||
from UM.Application import Application #To get the current printer's settings.
|
||||
from UM.Application import Application # To get the current printer's settings.
|
||||
from UM.Logger import Logger
|
||||
|
||||
from typing import List, Tuple
|
||||
|
@ -46,7 +46,7 @@ class PauseAtHeight(Script):
|
|||
"pause_layer":
|
||||
{
|
||||
"label": "Pause Layer",
|
||||
"description": "Enter the Number of the LAST layer you want to finish prior to the pause (from the Cura preview).",
|
||||
"description": "Enter the Number of the LAST layer you want to finish prior to the pause. Note that 0 is the first layer printed.",
|
||||
"type": "int",
|
||||
"value": "math.floor((pause_height - 0.27) / 0.1) + 1",
|
||||
"minimum_value": "0",
|
||||
|
@ -67,7 +67,7 @@ class PauseAtHeight(Script):
|
|||
"label": "Keep motors engaged",
|
||||
"description": "Keep the steppers engaged to allow change of filament without moving the head. Applying too much force will move the head/bed anyway",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"default_value": false,
|
||||
"enabled": "pause_method != \\\"griffin\\\""
|
||||
},
|
||||
"disarm_timeout":
|
||||
|
@ -218,7 +218,7 @@ class PauseAtHeight(Script):
|
|||
"label": "Beep at pause",
|
||||
"description": "Make a beep when pausing",
|
||||
"type": "bool",
|
||||
"default_value": true
|
||||
"default_value": false
|
||||
},
|
||||
"beep_length":
|
||||
{
|
||||
|
@ -338,7 +338,7 @@ class PauseAtHeight(Script):
|
|||
nbr_negative_layers += 1
|
||||
|
||||
#Track the latest printing temperature in order to resume at the correct temperature.
|
||||
if line.startswith("T"):
|
||||
if re.match("T(\d*)", line):
|
||||
current_t = self.getValue(line, "T")
|
||||
m = self.getValue(line, "M")
|
||||
if m is not None and (m == 104 or m == 109) and self.getValue(line, "S") is not None:
|
||||
|
@ -478,10 +478,11 @@ class PauseAtHeight(Script):
|
|||
prepend_gcode += "M117 " + display_text + "\n"
|
||||
|
||||
# Set the disarm timeout
|
||||
if hold_steppers_on:
|
||||
prepend_gcode += self.putValue(M = 84, S = 3600) + " ; Keep steppers engaged for 1h\n"
|
||||
elif disarm_timeout > 0:
|
||||
prepend_gcode += self.putValue(M = 84, S = disarm_timeout) + " ; Set the disarm timeout\n"
|
||||
if pause_method != "griffin":
|
||||
if hold_steppers_on:
|
||||
prepend_gcode += self.putValue(M = 84, S = 3600) + " ; Keep steppers engaged for 1h\n"
|
||||
elif disarm_timeout > 0:
|
||||
prepend_gcode += self.putValue(M = 84, S = disarm_timeout) + " ; Set the disarm timeout\n"
|
||||
|
||||
# Beep at pause
|
||||
if beep_at_pause:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Copyright (c) 2023 UltiMaker B.V.
|
||||
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
||||
# The PostProcessingPlugin is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from ..Script import Script
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2017 Ghostkeeper
|
||||
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
||||
# The PostProcessingPlugin is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import re #To perform the search and replace.
|
||||
import re # To perform the search and replace.
|
||||
|
||||
from ..Script import Script
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# This PostProcessingPlugin script is released under the terms of the AGPLv3 or higher.
|
||||
# This PostProcessingPlugin script is released under the terms of the LGPLv3 or higher.
|
||||
"""
|
||||
Copyright (c) 2017 Christophe Baribaud 2017
|
||||
Python implementation of https://github.com/electrocbd/post_stretch
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue