Cura-MaterialPrintTempCorre.../TemperatureCalculatorPlugin.py

136 lines
5.7 KiB
Python

# Copyright (c) 2022 ManuelW
# The Temperature Profile Correction Plugin is released under the terms of the AGPLv3 or higher.
import re
from collections import OrderedDict
from UM.Extension import Extension
from UM.Application import Application
from UM.Settings.SettingDefinition import SettingDefinition
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Logger import Logger
class TemperatureCalculatorPlugin(Extension):
def __init__(self):
super().__init__()
self._application = Application.getInstance()
self._i18n_catalog = None
self._settings_dict = OrderedDict()
self._settings_dict["temp_calc_number"] = {
"label": "Material Temperature Correction",
"description": "A correction of the print temperature from Material settings. A negative value decreases the print temperature, a positive value will increase the print temperature.",
"type": "float",
"unit": "°C",
"default_value": 0,
"maximum_value_warning": 30,
"settable_per_mesh": False,
"settable_per_extruder": False,
"settable_per_meshgroup": False
}
ContainerRegistry.getInstance().containerLoadComplete.connect(self._onContainerLoadComplete)
self._application.getOutputDeviceManager().writeStarted.connect(self._filterGcode)
def _onContainerLoadComplete(self, container_id):
if not ContainerRegistry.getInstance().isLoaded(container_id):
# skip containers that could not be loaded, or subsequent findContainers() will cause an infinite loop
return
try:
container = ContainerRegistry.getInstance().findContainers(id = container_id)[0]
except IndexError:
# the container no longer exists
return
if not isinstance(container, DefinitionContainer):
# skip containers that are not definitions
return
if container.getMetaDataEntry("type") == "extruder":
# skip extruder definitions
return
material = container.findDefinitions(key="material")
temp_calc = container.findDefinitions(key=list(self._settings_dict.keys())[0])
if material and not temp_calc:
material = material[0]
for setting_key, setting_dict in self._settings_dict.items():
definition = SettingDefinition(setting_key, container, material, self._i18n_catalog)
definition.deserialize(setting_dict)
# add the setting to the already existing platform adhesion settingdefinition
# private member access is naughty, but the alternative is to serialise, nix and deserialise the whole thing,
# which breaks stuff
material._children.append(definition)
container._definition_cache[setting_key] = definition
container._updateRelations(definition)
def _filterGcode(self, output_device):
scene = self._application.getController().getScene()
global_container_stack = self._application.getGlobalContainerStack()
if not global_container_stack:
return
# get setting from Cura
temp_calc_value = global_container_stack.getProperty("temp_calc_number", "value")
if temp_calc_value == 0:
return
use_temp_calc = global_container_stack.getProperty("temp_calc_number", "value")
gcode_dict = getattr(scene, "gcode_dict", {})
if not gcode_dict: # this also checks for an empty dict
Logger.log("w", "Scene has no gcode to process")
return
dict_changed = False
hotend_temp_regex = re.compile(r"^(M104\sS)(\d{2,3})\Z") #grab only "M104 Sxx" Strings
for plate_id in gcode_dict:
gcode_list = gcode_dict[plate_id]
if len(gcode_list) < 2:
Logger.log("w", "Plate %s does not contain any layers", plate_id)
continue
if ";TEMPCALCPROCESSED\n" not in gcode_list[0]:
# look for the first line that contains a G0 or G1 move on the Z axis
# gcode_list[2] is the first layer, after the preamble and the start gcode
if ";LAYER:0\n" in gcode_list[1]:
# layer 0 somehow got appended to the start gcode chunk
chunks = gcode_list[1].split(";LAYER:0\n")
gcode_list[1] = chunks[0]
gcode_list.insert(2, ";LAYER:0\n" + chunks[1])
# process all G0/G1 lines and adjust the Z value
for n in range(2, len(gcode_list)): # all gcode lists / layers, start at layer 1 = gcode list 2
lines = gcode_list[n].split("\n")
for (line_nr, line) in enumerate(lines):
result = hotend_temp_regex.fullmatch(line)
if result:
try:
adjusted_temp = round(float(result.group(2)) + temp_calc_value, 5)
except ValueError:
Logger.log("e", "Unable to process temp in line %s", line)
continue
lines[line_nr] = result.group(1) + str(adjusted_temp) + " ;adjusted by temp calc"
gcode_list[n] = "\n".join(lines)
gcode_list[0] += ";TEMPCALCPROCESSED\n"
gcode_dict[plate_id] = gcode_list
dict_changed = True
else:
Logger.log("d", "Plate %s has already been processed", plate_id)
continue
if dict_changed:
setattr(scene, "gcode_dict", gcode_dict)