Merge pull request #16962 from Ultimaker/CURA-11155-formulas-in-start-end-code

Formulas in start/end code
This commit is contained in:
Saumya Jain 2023-10-16 11:25:34 +02:00 committed by GitHub
commit 5a8b7e5740
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,4 +1,4 @@
# Copyright (c) 2021-2022 Ultimaker B.V. # Copyright (c) 2023 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import os import os
@ -24,6 +24,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.Scene import Scene #For typing. from UM.Scene.Scene import Scene #For typing.
from UM.Settings.Validator import ValidatorState from UM.Settings.Validator import ValidatorState
from UM.Settings.SettingRelation import RelationType from UM.Settings.SettingRelation import RelationType
from UM.Settings.SettingFunction import SettingFunction
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.Scene.CuraSceneNode import CuraSceneNode from cura.Scene.CuraSceneNode import CuraSceneNode
@ -46,44 +47,60 @@ class StartJobResult(IntEnum):
class GcodeStartEndFormatter(Formatter): class GcodeStartEndFormatter(Formatter):
"""Formatter class that handles token expansion in start/end gcode""" # Formatter class that handles token expansion in start/end gcode
# Example of a start/end gcode string:
# ```
# M104 S{material_print_temperature_layer_0, 0} ;pre-heat
# M140 S{material_bed_temperature_layer_0} ;heat bed
# M204 P{acceleration_print, 0} T{acceleration_travel, 0}
# M205 X{jerk_print, 0}
# ```
# Any expression between curly braces will be evaluated and replaced with the result, using the
# context of the provided default extruder. If no default extruder is provided, the global stack
# will be used. Alternatively, if the expression is formatted as "{[expression], [extruder_nr]}",
# then the expression will be evaluated with the extruder stack of the specified extruder_nr.
def __init__(self, default_extruder_nr: int = -1) -> None: _extruder_regex = re.compile(r"^\s*(?P<expression>.*)\s*,\s*(?P<extruder_nr>\d+)\s*$")
def __init__(self, default_extruder_nr: int = -1, *,
additional_per_extruder_settings: Optional[Dict[str, Dict[str, any]]] = None) -> None:
super().__init__() super().__init__()
self._default_extruder_nr = default_extruder_nr self._default_extruder_nr: int = default_extruder_nr
self._additional_per_extruder_settings: Optional[Dict[str, Dict[str, any]]] = additional_per_extruder_settings
def get_value(self, key: str, args: str, kwargs: dict) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
# The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key),
# and a default_extruder_nr to use when no extruder_nr is specified
def get_value(self, expression: str, args: [str], kwargs: dict) -> str:
extruder_nr = self._default_extruder_nr extruder_nr = self._default_extruder_nr
key_fragments = [fragment.strip() for fragment in key.split(",")] # The settings may specify a specific extruder to use. This is done by
if len(key_fragments) == 2: # formatting the expression as "{expression}, {extruder_nr}". If the
try: # expression is formatted like this, we extract the extruder_nr and use
extruder_nr = int(key_fragments[1]) # it to get the value from the correct extruder stack.
except ValueError: match = self._extruder_regex.match(expression)
try: if match:
extruder_nr = int(kwargs["-1"][key_fragments[1]]) # get extruder_nr values from the global stack #TODO: How can you ever provide the '-1' kwarg? expression = match.group("expression")
except (KeyError, ValueError): extruder_nr = int(match.group("extruder_nr"))
# either the key does not exist, or the value is not an int
Logger.log("w", "Unable to determine stack nr '%s' for key '%s' in start/end g-code, using global stack", key_fragments[1], key_fragments[0])
elif len(key_fragments) != 1:
Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end g-code", key)
return "{" + key + "}"
key = key_fragments[0] if self._additional_per_extruder_settings is not None and str(
extruder_nr) in self._additional_per_extruder_settings:
additional_variables = self._additional_per_extruder_settings[str(extruder_nr)]
else:
additional_variables = dict()
default_value_str = "{" + key + "}" # Add the arguments and keyword arguments to the additional settings. These
value = default_value_str # are currently _not_ used, but they are added for consistency with the
# "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value. # base Formatter class.
if key in kwargs["-1"]: for key, value in enumerate(args):
value = kwargs["-1"][key] additional_variables[key] = value
if str(extruder_nr) in kwargs and key in kwargs[str(extruder_nr)]: for key, value in kwargs.items():
value = kwargs[str(extruder_nr)][key] additional_variables[key] = value
if value == default_value_str: if extruder_nr == -1:
Logger.log("w", "Unable to replace '%s' placeholder in start/end g-code", key) container_stack = CuraApplication.getInstance().getGlobalContainerStack()
else:
container_stack = ExtruderManager.getInstance().getExtruderStack(extruder_nr)
setting_function = SettingFunction(expression)
value = setting_function(container_stack, additional_variables=additional_variables)
return value return value
@ -426,13 +443,14 @@ class StartSliceJob(Job):
self._cacheAllExtruderSettings() self._cacheAllExtruderSettings()
try: try:
# any setting can be used as a token # Get "replacement-keys" for the extruders. In the formatter the settings stack is used to get the
fmt = GcodeStartEndFormatter(default_extruder_nr = default_extruder_nr) # replacement values for the setting-keys. However, the values for `material_id`, `material_type`,
if self._all_extruders_settings is None: # etc are not in the settings stack.
return "" additional_per_extruder_settings = self._all_extruders_settings.copy()
settings = self._all_extruders_settings.copy() additional_per_extruder_settings["default_extruder_nr"] = default_extruder_nr
settings["default_extruder_nr"] = default_extruder_nr fmt = GcodeStartEndFormatter(default_extruder_nr=default_extruder_nr,
return str(fmt.format(value, **settings)) additional_per_extruder_settings=additional_per_extruder_settings)
return str(fmt.format(value))
except: except:
Logger.logException("w", "Unable to do token replacement on start/end g-code") Logger.logException("w", "Unable to do token replacement on start/end g-code")
return str(value) return str(value)