Skip to content

Commit

Permalink
Merge branch 'main' into PP-512_Bad_print_quality_for_TPU_AA08_on_Fac…
Browse files Browse the repository at this point in the history
…tor_4
  • Loading branch information
HellAholic authored Sep 19, 2024
2 parents c11c97d + f0cbd8e commit 3b080f9
Show file tree
Hide file tree
Showing 29 changed files with 688 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/printer-linter-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- uses: technote-space/get-diff-action@v6
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/printer-linter-pr-diagnose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 2

Expand Down Expand Up @@ -55,7 +55,7 @@ jobs:
echo ${{ github.event.pull_request.head.repo.full_name }} > printer-linter-result/pr-head-repo.txt
echo ${{ github.event.pull_request.head.sha }} > printer-linter-result/pr-head-sha.txt
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: printer-linter-result
path: printer-linter-result/
Expand Down
2 changes: 1 addition & 1 deletion conandata.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ requirements:
- "pynest2d/5.3.0"
- "native_cad_plugin/2.0.0"
requirements_internal:
- "fdm_materials/(latest)@internal/testing"
- "fdm_materials/5.8.1"
- "cura_private_data/(latest)@internal/testing"
urls:
default:
Expand Down
1 change: 1 addition & 0 deletions cura/API/Account.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def _onLoginStateChanged(self, logged_in: bool = False, error_message: Optional[

def _onProfileChanged(self, profile: Optional[UserProfile]) -> None:
self._user_profile = profile
self._updatePermissions()
self.userProfileChanged.emit()

def _sync(self) -> None:
Expand Down
189 changes: 140 additions & 49 deletions plugins/CuraEngineBackend/StartSliceJob.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,20 @@ class StartJobResult(IntEnum):
ObjectsWithDisabledExtruder = 8


class GcodeStartEndFormatter(Formatter):
class GcodeConditionState(IntEnum):
OutsideCondition = 1
ConditionFalse = 2
ConditionTrue = 3
ConditionDone = 4


class GcodeInstruction(IntEnum):
Skip = 1
Evaluate = 2
EvaluateAndWrite = 3


class GcodeStartEndFormatter:
# Formatter class that handles token expansion in start/end gcode
# Example of a start/end gcode string:
# ```
Expand All @@ -63,22 +76,50 @@ class GcodeStartEndFormatter(Formatter):
# 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.

_extruder_regex = re.compile(r"^\s*(?P<expression>.*)\s*,\s*(?P<extruder_nr_expr>.*)\s*$")
_instruction_regex = re.compile(r"{(?P<condition>if|else|elif|endif)?\s*(?P<expression>.*?)\s*(?:,\s*(?P<extruder_nr_expr>.*))?\s*}(?P<end_of_line>\n?)")

def __init__(self, all_extruder_settings: Dict[str, Any], default_extruder_nr: int = -1) -> None:
def __init__(self, all_extruder_settings: Dict[str, Dict[str, Any]], default_extruder_nr: int = -1) -> None:
super().__init__()
self._all_extruder_settings: Dict[str, Any] = all_extruder_settings
self._all_extruder_settings: Dict[str, Dict[str, Any]] = all_extruder_settings
self._default_extruder_nr: int = default_extruder_nr
self._cura_application = CuraApplication.getInstance()
self._extruder_manager = ExtruderManager.getInstance()

def format(self, text: str) -> str:
remaining_text: str = text
result: str = ""

self._condition_state: GcodeConditionState = GcodeConditionState.OutsideCondition

while len(remaining_text) > 0:
next_code_match = self._instruction_regex.search(remaining_text)
if next_code_match is not None:
expression_start, expression_end = next_code_match.span()

if expression_start > 0:
result += self._process_statement(remaining_text[:expression_start])

def get_field(self, field_name, args: [str], kwargs: dict) -> Tuple[str, str]:
# get_field method parses all fields in the format-string and parses them individually to the get_value method.
# e.g. for a string "Hello {foo.bar}" would the complete field "foo.bar" would be passed to get_field, and then
# the individual parts "foo" and "bar" would be passed to get_value. This poses a problem for us, because want
# to parse the entire field as a single expression. To solve this, we override the get_field method and return
# the entire field as the expression.
return self.get_value(field_name, args, kwargs), field_name
result += self._process_code(next_code_match)

def get_value(self, expression: str, args: [str], kwargs: dict) -> str:
remaining_text = remaining_text[expression_end:]

else:
result += self._process_statement(remaining_text)
remaining_text = ""

return result

def _process_statement(self, statement: str) -> str:
if self._condition_state in [GcodeConditionState.OutsideCondition, GcodeConditionState.ConditionTrue]:
return statement
else:
return ""

def _process_code(self, code: re.Match) -> str:
condition: Optional[str] = code.group("condition")
expression: Optional[str] = code.group("expression")
extruder_nr_expr: Optional[str] = code.group("extruder_nr_expr")
end_of_line: Optional[str] = code.group("end_of_line")

# The following variables are not settings, but only become available after slicing.
# when these variables are encountered, we return them as-is. They are replaced later
Expand All @@ -87,53 +128,100 @@ def get_value(self, expression: str, args: [str], kwargs: dict) -> str:
if expression in post_slice_data_variables:
return f"{{{expression}}}"

extruder_nr = str(self._default_extruder_nr)
extruder_nr: str = str(self._default_extruder_nr)
instruction: GcodeInstruction = GcodeInstruction.Skip

# The settings may specify a specific extruder to use. This is done by
# formatting the expression as "{expression}, {extruder_nr_expr}". If the
# expression is formatted like this, we extract the extruder_nr and use
# it to get the value from the correct extruder stack.
match = self._extruder_regex.match(expression)
if match:
expression = match.group("expression")
extruder_nr_expr = match.group("extruder_nr_expr")
if condition is None:
# This is a classic statement
if self._condition_state in [GcodeConditionState.OutsideCondition, GcodeConditionState.ConditionTrue]:
# Skip and move to next
instruction = GcodeInstruction.EvaluateAndWrite
else:
# This is a condition statement, first check validity
if condition == "if":
if self._condition_state != GcodeConditionState.OutsideCondition:
raise SyntaxError("Nested conditions are not supported")
else:
if self._condition_state == GcodeConditionState.OutsideCondition:
raise SyntaxError("Condition should start with an 'if' statement")

if condition == "if":
# First instruction, just evaluate it
instruction = GcodeInstruction.Evaluate

if extruder_nr_expr.isdigit():
extruder_nr = extruder_nr_expr
else:
# We get the value of the extruder_nr_expr from `_all_extruder_settings` dictionary
# rather than the global container stack. The `_all_extruder_settings["-1"]` is a
# dict-representation of the global container stack, with additional properties such
# as `initial_extruder_nr`. As users may enter such expressions we can't use the
# global container stack.
extruder_nr = str(self._all_extruder_settings["-1"].get(extruder_nr_expr, "-1"))

if extruder_nr in self._all_extruder_settings:
additional_variables = self._all_extruder_settings[extruder_nr].copy()
else:
Logger.warning(f"Extruder {extruder_nr} does not exist, using global settings")
additional_variables = self._all_extruder_settings["-1"].copy()

# Add the arguments and keyword arguments to the additional settings. These
# are currently _not_ used, but they are added for consistency with the
# base Formatter class.
for key, value in enumerate(args):
additional_variables[key] = value
for key, value in kwargs.items():
additional_variables[key] = value

if extruder_nr == "-1":
container_stack = CuraApplication.getInstance().getGlobalContainerStack()
else:
container_stack = ExtruderManager.getInstance().getExtruderStack(extruder_nr)
if not container_stack:
if self._condition_state == GcodeConditionState.ConditionTrue:
# We have reached the next condition after a valid one has been found, skip the rest
self._condition_state = GcodeConditionState.ConditionDone

if condition == "elif":
if self._condition_state == GcodeConditionState.ConditionFalse:
# New instruction, and valid condition has not been reached so far => evaluate it
instruction = GcodeInstruction.Evaluate
else:
# New instruction, but valid condition has already been reached => skip it
instruction = GcodeInstruction.Skip

elif condition == "else":
instruction = GcodeInstruction.Skip # Never evaluate, expression should be empty
if self._condition_state == GcodeConditionState.ConditionFalse:
# Fallback instruction, and valid condition has not been reached so far => active next
self._condition_state = GcodeConditionState.ConditionTrue

elif condition == "endif":
instruction = GcodeInstruction.Skip # Never evaluate, expression should be empty
self._condition_state = GcodeConditionState.OutsideCondition

if instruction >= GcodeInstruction.Evaluate and extruder_nr_expr is not None:
extruder_nr_function = SettingFunction(extruder_nr_expr)
container_stack = self._cura_application.getGlobalContainerStack()

# We add the variables contained in `_all_extruder_settings["-1"]`, which is a dict-representation of the
# global container stack, with additional properties such as `initial_extruder_nr`. As users may enter such
# expressions we can't use the global container stack. The variables contained in the global container stack
# will then be inserted twice, which is not optimal but works well.
extruder_nr = str(extruder_nr_function(container_stack, additional_variables=self._all_extruder_settings["-1"]))

if instruction >= GcodeInstruction.Evaluate:
if extruder_nr in self._all_extruder_settings:
additional_variables = self._all_extruder_settings[extruder_nr].copy()
else:
Logger.warning(f"Extruder {extruder_nr} does not exist, using global settings")
container_stack = CuraApplication.getInstance().getGlobalContainerStack()
additional_variables = self._all_extruder_settings["-1"].copy()

setting_function = SettingFunction(expression)
value = setting_function(container_stack, additional_variables=additional_variables)
if extruder_nr == "-1":
container_stack = self._cura_application.getGlobalContainerStack()
else:
container_stack = self._extruder_manager.getExtruderStack(extruder_nr)
if not container_stack:
Logger.warning(f"Extruder {extruder_nr} does not exist, using global settings")
container_stack = self._cura_application.getGlobalContainerStack()

return value
setting_function = SettingFunction(expression)
value = setting_function(container_stack, additional_variables=additional_variables)

if instruction == GcodeInstruction.Evaluate:
if value:
self._condition_state = GcodeConditionState.ConditionTrue
else:
self._condition_state = GcodeConditionState.ConditionFalse

return ""
else:
value_str = str(value)

if end_of_line is not None:
# If we are evaluating an expression that is not a condition, restore the end of line
value_str += end_of_line

return value_str

else:
return ""


class StartSliceJob(Job):
Expand Down Expand Up @@ -470,6 +558,9 @@ def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))]
result["initial_extruder_nr"] = CuraApplication.getInstance().getExtruderManager().getInitialExtruderNr()

# If adding or changing a setting here, please update the associated wiki page
# https://github.com/Ultimaker/Cura/wiki/Start-End-G%E2%80%90Code

return result

def _cacheAllExtruderSettings(self):
Expand Down
16 changes: 14 additions & 2 deletions plugins/MachineSettingsAction/MachineSettingsExtruderTab.qml
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Item
Cura.GcodeTextArea // "Extruder Start G-code"
{
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottom: buttonLearnMore.top
anchors.bottomMargin: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
width: base.columnWidth - UM.Theme.getSize("default_margin").width
Expand All @@ -196,7 +196,7 @@ Item
Cura.GcodeTextArea // "Extruder End G-code"
{
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottom: buttonLearnMore.top
anchors.bottomMargin: UM.Theme.getSize("default_margin").height
anchors.right: parent.right
width: base.columnWidth - UM.Theme.getSize("default_margin").width
Expand All @@ -206,5 +206,17 @@ Item
settingKey: "machine_extruder_end_code"
settingStoreIndex: propertyStoreIndex
}

Cura.TertiaryButton
{
id: buttonLearnMore

text: catalog.i18nc("@button", "Learn more")
iconSource: UM.Theme.getIcon("LinkExternal")
isIconOnRightSide: true
onClicked: Qt.openUrlExternally("https://github.com/Ultimaker/Cura/wiki/Start-End-G%E2%80%90Code")
anchors.bottom: parent.bottom
anchors.right: parent.right
}
}
}
16 changes: 15 additions & 1 deletion plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ Item
anchors
{
top: upperBlock.bottom
bottom: parent.bottom
bottom: buttonLearnMore.top
left: parent.left
right: parent.right
margins: UM.Theme.getSize("default_margin").width
Expand All @@ -403,5 +403,19 @@ Item
settingKey: "machine_end_gcode"
settingStoreIndex: propertyStoreIndex
}

}

Cura.TertiaryButton
{
id: buttonLearnMore

text: catalog.i18nc("@button", "Learn more")
iconSource: UM.Theme.getIcon("LinkExternal")
isIconOnRightSide: true
onClicked: Qt.openUrlExternally("https://github.com/Ultimaker/Cura/wiki/Start-End-G%E2%80%90Code")
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: UM.Theme.getSize("default_margin").width
}
}
5 changes: 2 additions & 3 deletions plugins/XmlMaterialProfile/XmlMaterialProfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1084,9 +1084,8 @@ def _addSettingElement(self, builder, instance):
# Skip material properties (eg diameter) or metadata (eg GUID)
return

truth_map = { True: "yes", False: "no" }
if tag_name != "cura:setting" and instance.value in truth_map:
data = truth_map[instance.value]
if tag_name != "cura:setting" and isinstance(instance.value, bool):
data = "yes" if instance.value else "no"
else:
data = str(instance.value)

Expand Down
2 changes: 1 addition & 1 deletion resources/conandata.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version: "5.8.0"
version: "5.8.1"
6 changes: 3 additions & 3 deletions resources/definitions/creality_ender3v3se.def.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@
},
"machine_heated_bed": { "default_value": true },
"machine_height": { "default_value": 250 },
"machine_max_acceleration_e": { "value": 5000 },
"machine_max_acceleration_x": { "value": 5000.0 },
"machine_max_acceleration_y": { "value": 5000.0 },
"machine_max_acceleration_e": { "value": 2500 },
"machine_max_acceleration_x": { "value": 2500.0 },
"machine_max_acceleration_y": { "value": 2500.0 },
"machine_max_acceleration_z": { "value": 500.0 },
"machine_max_feedrate_e": { "value": 100 },
"machine_max_feedrate_x": { "value": 500 },
Expand Down
9 changes: 0 additions & 9 deletions resources/qml/Actions.qml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ Item

property alias showProfileFolder: showProfileFolderAction
property alias documentation: documentationAction
property alias showTroubleshooting: showTroubleShootingAction
property alias openSponsershipPage: openSponsershipPageAction
property alias reportBug: reportBugAction
property alias whatsNew: whatsNewAction
Expand All @@ -86,14 +85,6 @@ Item

UM.I18nCatalog{id: catalog; name: "cura"}


Action
{
id: showTroubleShootingAction
onTriggered: Qt.openUrlExternally("https://ultimaker.com/en/troubleshooting?utm_source=cura&utm_medium=software&utm_campaign=dropdown-troubleshooting")
text: catalog.i18nc("@action:inmenu", "Show Online Troubleshooting")
}

Action
{
id: openSponsershipPageAction
Expand Down
Loading

0 comments on commit 3b080f9

Please sign in to comment.