import itertools import logging from inspect import signature import traceback GCODE_DOCUMENTATION = { "G0": "Linear Move", "G1": "Linear Move", "G2": "Arc or Circle Move", "G3": "Arc or Circle Move", "G4": "Dwell", "G5": "Bézier cubic spline", "G6": "Direct Stepper Move", "G10": "Retract", "G11": "Recover", "G12": "Clean the Nozzle", "G17": "CNC Workspace Planes", "G18": "CNC Workspace Planes", "G19": "CNC Workspace Planes", "G20": "Inch Units", "G21": "Millimeter Units", "G26": "Mesh Validation Pattern", "G27": "Park toolhead", "G28": "Auto Home", "G29": "Bed Leveling", "G29": "Bed Leveling (3-Point)", "G29": "Bed Leveling (Linear)", "G29": "Bed Leveling (Manual)", "G29": "Bed Leveling (Bilinear)", "G29": "Bed Leveling (Unified)", "G30": "Single Z-Probe", "G31": "Dock Sled", "G32": "Undock Sled", "G33": "Delta Auto Calibration", "G34": "Z Steppers Auto-Alignment", "G34": "Mechanical Gantry Calibration", "G35": "Tramming Assistant", "G38.2": "Probe target", "G38.3": "Probe target", "G38.4": "Probe target", "G38.5": "Probe target", "G42": "Move to mesh coordinate", "G53": "Move in Machine Coordinates", "G60": "Save Current Position", "G61": "Return to Saved Position", "G76": "Probe temperature calibration", "G80": "Cancel Current Motion Mode", "G90": "Absolute Positioning", "G91": "Relative Positioning", "G92": "Set Position", "G425": "Backlash Calibration", "M0": "Unconditional stop", "M1": "Unconditional stop", "M3": "Spindle CW / Laser On", "M4": "Spindle CCW / Laser On", "M5": "Spindle / Laser Off", "M7": "Coolant Controls", "M8": "Coolant Controls", "M9": "Coolant Controls", "M10": "Vacuum / Blower Control", "M11": "Vacuum / Blower Control", "M16": "Expected Printer Check", "M17": "Enable Steppers", "M18": "Disable steppers", "M84": "Disable steppers", "M20": "List SD Card", "M21": "Init SD card", "M22": "Release SD card", "M23": "Select SD file", "M24": "Start or Resume SD print", "M25": "Pause SD print", "M26": "Set SD position", "M27": "Report SD print status", "M28": "Start SD write", "M29": "Stop SD write", "M30": "Delete SD file", "M31": "Print time", "M32": "Select and Start", "M33": "Get Long Path", "M34": "SDCard Sorting", "M42": "Set Pin State", "M43": "Debug Pins", "M48": "Probe Repeatability Test", "M73": "Set Print Progress", "M75": "Start Print Job Timer", "M76": "Pause Print Job Timer", "M77": "Stop Print Job Timer", "M78": "Print Job Stats", "M80": "Power On", "M81": "Power Off", "M82": "E Absolute", "M83": "E Relative", "M85": "Inactivity Shutdown", "M86": "Hotend Idle Timeout", "M87": "Disable Hotend Idle Timeout", "M92": "Set Axis Steps-per-unit", "M100": "Free Memory", "M102": "Configure Bed Distance Sensor", "M104": "Set Hotend Temperature", "M105": "Report Temperatures", "M106": "Set Fan Speed", "M107": "Fan Off", "M108": "Break and Continue", "M109": "Wait for Hotend Temperature", "M110": "Set / Get Line Number", "M111": "Debug Level", "M112": "Full Shutdown", "M113": "Host Keepalive", "M114": "Get Current Position", "M115": "Firmware Info", "M117": "Set LCD Message", "M118": "Serial print", "M119": "Endstop States", "M120": "Enable Endstops", "M121": "Disable Endstops", "M122": "TMC Debugging", "M123": "Fan Tachometers", "M125": "Park Head", "M126": "Baricuda 1 Open", "M127": "Baricuda 1 Close", "M128": "Baricuda 2 Open", "M129": "Baricuda 2 Close", "M140": "Set Bed Temperature", "M141": "Set Chamber Temperature", "M143": "Set Laser Cooler Temperature", "M145": "Set Material Preset", "M149": "Set Temperature Units", "M150": "Set RGB(W) Color", "M154": "Position Auto-Report", "M155": "Temperature Auto-Report", "M163": "Set Mix Factor", "M164": "Save Mix", "M165": "Set Mix", "M166": "Gradient Mix", "M190": "Wait for Bed Temperature", "M191": "Wait for Chamber Temperature", "M192": "Wait for Probe temperature", "M193": "Set Laser Cooler Temperature", "M200": "Set Filament Diameter", "M201": "Print / Travel Move Limits", "M203": "Set Max Feedrate", "M204": "Set Starting Acceleration", "M205": "Set Advanced Settings", "M206": "Set Home Offsets", "M207": "Set Firmware Retraction", "M208": "Firmware Recover", "M209": "Set Auto Retract", "M211": "Software Endstops", "M217": "Filament swap parameters", "M218": "Set Hotend Offset", "M220": "Set Feedrate Percentage", "M221": "Set Flow Percentage", "M226": "Wait for Pin State", "M240": "Trigger Camera", "M250": "LCD Contrast", "M255": "LCD Sleep/Backlight Timeout", "M256": "LCD Brightness", "M260": "I2C Send", "M261": "I2C Request", "M280": "Servo Position", "M281": "Edit Servo Angles", "M282": "Detach Servo", "M290": "Babystep", "M300": "Play Tone", "M301": "Set Hotend PID", "M302": "Cold Extrude", "M303": "PID autotune", "M304": "Set Bed PID", "M305": "User Thermistor Parameters", "M306": "Model Predictive Temp. Control", "M350": "Set micro-stepping", "M351": "Set Microstep Pins", "M355": "Case Light Control", "M360": "SCARA Theta A", "M361": "SCARA Theta-B", "M362": "SCARA Psi-A", "M363": "SCARA Psi-B", "M364": "SCARA Psi-C", "M380": "Activate Solenoid", "M381": "Deactivate Solenoids", "M400": "Finish Moves", "M401": "Deploy Probe", "M402": "Stow Probe", "M403": "MMU2 Filament Type", "M404": "Set Filament Diameter", "M405": "Filament Width Sensor On", "M406": "Filament Width Sensor Off", "M407": "Filament Width", "M410": "Quickstop", "M412": "Filament Runout", "M413": "Power-loss Recovery", "M420": "Bed Leveling State", "M421": "Set Mesh Value", "M422": "Set Z Motor XY", "M423": "X Twist Compensation", "M425": "Backlash compensation", "M428": "Home Offsets Here", "M430": "Power Monitor", "M486": "Cancel Objects", "M493": "Fixed-Time Motion", "M500": "Save Settings", "M501": "Restore Settings", "M502": "Factory Reset", "M503": "Report Settings", "M504": "Validate EEPROM contents", "M510": "Lock Machine", "M511": "Unlock Machine", "M512": "Set Passcode", "M524": "Abort SD print", "M540": "Endstops Abort SD", "M569": "Set TMC stepping mode", "M575": "Serial baud rate", "M592": "Nonlinear Extrusion Control", "M593": "ZV Input Shaping", "M600": "Filament Change", "M603": "Configure Filament Change", "M605": "Multi Nozzle Mode", "M665": "Delta Configuration", "M665": "SCARA Configuration", "M666": "Set Delta endstop adjustments", "M666": "Set dual endstop offsets", "M672": "Duet Smart Effector sensitivity", "M701": "Load filament", "M702": "Unload filament", "M710": "Controller Fan settings", "M808": "Repeat Marker", "M851": "XYZ Probe Offset", "M852": "Bed Skew Compensation", "M871": "Probe temperature config", "M876": "Handle Prompt Response", "M900": "Linear Advance Factor", "M906": "Stepper Motor Current", "M907": "Set Motor Current", "M908": "Set Trimpot Pins", "M909": "DAC Print Values", "M910": "Commit DAC to EEPROM", "M911": "TMC OT Pre-Warn Condition", "M912": "Clear TMC OT Pre-Warn", "M913": "Set Hybrid Threshold Speed", "M914": "TMC Bump Sensitivity", "M915": "TMC Z axis calibration", "M916": "L6474 Thermal Warning Test", "M917": "L6474 Overcurrent Warning Test", "M918": "L6474 Speed Warning Test", "M919": "TMC Chopper Timing", "M928": "Start SD Logging", "M951": "Magnetic Parking Extruder", "M993": "Back up flash settings to SD", "M994": "Restore flash from SD", "M995": "Touch Screen Calibration", "M997": "Firmware update", "M999": "STOP Restart", "M7219": "MAX7219 Control", } class GCodeExecutor: def __init__(self): self._log = logging.getLogger( "octoprint.plugins.bambu_printer.BambuPrinter.gcode_executor" ) self.handler_names = set() self.gcode_handlers = {} self.gcode_handlers_no_data = {} def __contains__(self, item): return item in self.gcode_handlers or item in self.gcode_handlers_no_data def _get_required_args_count(self, func): sig = signature(func) required_count = sum( 1 for p in sig.parameters.values() if (p.kind == p.POSITIONAL_OR_KEYWORD or p.kind == p.POSITIONAL_ONLY) and p.default == p.empty ) return required_count def register(self, gcode): def decorator(func): required_count = self._get_required_args_count(func) if required_count == 1: self.gcode_handlers_no_data[gcode] = func elif required_count == 2: self.gcode_handlers[gcode] = func else: raise ValueError( f"Cannot register function with {required_count} required parameters" ) return func return decorator def register_no_data(self, gcode): def decorator(func): self.gcode_handlers_no_data[gcode] = func return func return decorator def execute(self, printer, gcode, data): gcode_info = self._gcode_with_info(gcode) try: if gcode in self.gcode_handlers: self._log.debug(f"Executing {gcode_info}") return self.gcode_handlers[gcode](printer, data) elif gcode in self.gcode_handlers_no_data: self._log.debug(f"Executing {gcode_info}") return self.gcode_handlers_no_data[gcode](printer) else: self._log.debug(f"ignoring {gcode_info} command.") return False except Exception as e: self._log.error(f"Error during gcode {gcode_info}") raise def _gcode_with_info(self, gcode): return f"{gcode} ({GCODE_DOCUMENTATION.get(gcode, 'Info not specified')})"