Source code for gcode_reader.emulate.machines.small_scale_am

import re

from ..machine import Machine
from ..extruders import StepperExtruder, Extruder
from ..operations import AdditiveOperation
from .. import machine_options
from .. import commands
from ..registry import register

# PrusaSlicer embeds machine limits as "; key = value" lines between these markers.
_PRUSA_FOOTER_BEGIN = "prusaslicer_config = begin"
_PRUSA_FOOTER_END = "prusaslicer_config = end"
_PRUSA_LIMIT_KEYS = {
    "machine_max_feedrate_x": "max_feedrate",  # mm/s
    "machine_max_acceleration_x": "max_acceleration",  # mm/s²
    "machine_max_jerk_x": "max_jerk",  # mm/s
}
_PRUSA_FOOTER_RE = re.compile(r"^;\s*([\w]+)\s*=\s*([\d.]+)")


[docs] @register("desktop", aliases=["desktopam"]) class DesktopAM(Machine): """Base class for desktop AM machines with Marlin Firmware""" def __init__(self, extrusion_type=machine_options.REL_EXTRUSION, **kwargs): super().__init__( tools=(StepperExtruder()), extrusion_type=extrusion_type, **kwargs ) # Motion limits from explicit kwargs (e.g. DesktopAM(machine_max_feedrate_x=350)). # Values from PrusaSlicer config are in mm/s and mm/s²; no unit conversion needed. options = self.options.all_options if "machine_max_feedrate_x" in options: self.options.max_feedrate = float(options["machine_max_feedrate_x"]) if "machine_max_acceleration_x" in options: self.options.max_acceleration = float(options["machine_max_acceleration_x"]) if "machine_max_jerk_x" in options: self.options.max_jerk = float(options["machine_max_jerk_x"]) @property def relative_extrusion(self) -> bool: # NOTE: This returns True if all extruders are in relative mode. If there were ever multiple extruders with different relative/absolute states, we would need to do something different. extruders = [t for t in self.tools if isinstance(t, Extruder)] if not extruders: return False return all(e.relative_extrusion for e in extruders) @relative_extrusion.setter def relative_extrusion(self, val): for tool in self.tools: if isinstance(tool, Extruder): tool.relative_extrusion = val def _load_prusa_machine_limits(self, filepath: str) -> None: """Read machine kinematic limits from a PrusaSlicer gcode file footer. PrusaSlicer embeds full machine config between ``prusaslicer_config = begin`` and ``prusaslicer_config = end`` comment lines. This scans that section for the three limits we need (feedrate, acceleration, jerk in X) and populates ``self.options`` so ``get_motion_limits()`` can apply them to the motion profile. Values are stored in mm/s / mm/s² — no conversion needed. """ found = {} in_footer = False try: with open(filepath, "r", encoding="utf-8", errors="replace") as f: for line in f: low = line.lower() if _PRUSA_FOOTER_BEGIN in low: in_footer = True continue if _PRUSA_FOOTER_END in low: break if not in_footer: continue m = _PRUSA_FOOTER_RE.match(line) if m and m.group(1) in _PRUSA_LIMIT_KEYS: found[_PRUSA_LIMIT_KEYS[m.group(1)]] = float(m.group(2)) except (FileNotFoundError, PermissionError): return if "max_feedrate" in found: self.options.max_feedrate = found["max_feedrate"] if "max_acceleration" in found: self.options.max_acceleration = found["max_acceleration"] if "max_jerk" in found: self.options.max_jerk = found["max_jerk"]
[docs] def gcode_file_to_operation(self, filepath: str, operation_type=AdditiveOperation): self._load_prusa_machine_limits(filepath) return super().gcode_file_to_operation(filepath, operation_type)
def _config(self, command, process_data): # These are Marlin codes if isinstance(command, commands.M): if command.code == 82: self.relative_extrusion = False elif command.code == 83: self.relative_extrusion = True return super()._config(command, process_data)
[docs] @register("reprap") class RepRap(DesktopAM): """RepRap desktop AM""" def __init__(self, **kwargs): super().__init__(**kwargs) self.options = machine_options.RepRapOptions(**kwargs)