mirror of
				https://github.com/Klipper3d/klipper.git
				synced 2025-10-31 18:36:09 +01:00 
			
		
		
		
	gcode: Create new wrapper class for gcode command parameters
Instead of passing a dictionary to the command handlers, create a wrapper class and pass that class to the command handlers. This can simplify the command handler code. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
		| @@ -54,7 +54,8 @@ class ArcSupport: | |||||||
|                 g1_params['E'] = asE / len(coords) |                 g1_params['E'] = asE / len(coords) | ||||||
|             if asF is not None: |             if asF is not None: | ||||||
|                 g1_params['F'] = asF |                 g1_params['F'] = asF | ||||||
|             self.gcode.cmd_G1(g1_params) |             g1_gcmd = self.gcode.create_gcode_command("G1", "G1", g1_params) | ||||||
|  |             self.gcode.cmd_G1(g1_gcmd) | ||||||
|  |  | ||||||
|     # function planArc() originates from marlin plan_arc() |     # function planArc() originates from marlin plan_arc() | ||||||
|     # https://github.com/MarlinFirmware/Marlin |     # https://github.com/MarlinFirmware/Marlin | ||||||
|   | |||||||
| @@ -154,10 +154,10 @@ class GCodeMacro: | |||||||
|                 value,)) |                 value,)) | ||||||
|         self.variables[variable] = literal |         self.variables[variable] = literal | ||||||
|     cmd_desc = "G-Code macro" |     cmd_desc = "G-Code macro" | ||||||
|     def cmd(self, params): |     def cmd(self, gcmd): | ||||||
|         if self.in_script: |         if self.in_script: | ||||||
|             raise self.gcode.error( |             raise gcmd.error("Macro %s called recursively" % (self.alias,)) | ||||||
|                 "Macro %s called recursively" % (self.alias,)) |         params = gcmd.get_command_parameters() | ||||||
|         kwparams = dict(self.kwparams) |         kwparams = dict(self.kwparams) | ||||||
|         kwparams.update(params) |         kwparams.update(params) | ||||||
|         kwparams.update(self.variables) |         kwparams.update(self.variables) | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ class HomingOverride: | |||||||
|         self.gcode.reset_last_position() |         self.gcode.reset_last_position() | ||||||
|         # Perform homing |         # Perform homing | ||||||
|         kwparams = { 'printer': self.template.create_status_wrapper() } |         kwparams = { 'printer': self.template.create_status_wrapper() } | ||||||
|         kwparams['params'] = params |         kwparams['params'] = params.get_command_parameters() | ||||||
|         try: |         try: | ||||||
|             self.in_script = True |             self.in_script = True | ||||||
|             self.template.run_gcode_from_command(kwparams) |             self.template.run_gcode_from_command(kwparams) | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ class PauseResume: | |||||||
|         self.pause_command_sent = False |         self.pause_command_sent = False | ||||||
|         if self.sd_paused: |         if self.sd_paused: | ||||||
|             # Printing from virtual sd, run pause command |             # Printing from virtual sd, run pause command | ||||||
|             self.v_sd.cmd_M24({}) |             self.v_sd.cmd_M24(gcmd) | ||||||
|         else: |         else: | ||||||
|             self.gcode.respond_info("action:resumed") |             self.gcode.respond_info("action:resumed") | ||||||
|     def cmd_CLEAR_PAUSE(self, params): |     def cmd_CLEAR_PAUSE(self, params): | ||||||
|   | |||||||
| @@ -410,7 +410,8 @@ class ProbePointsHelper: | |||||||
|     def _manual_probe_start(self): |     def _manual_probe_start(self): | ||||||
|         done = self._move_next() |         done = self._move_next() | ||||||
|         if not done: |         if not done: | ||||||
|             manual_probe.ManualProbeHelper(self.printer, {}, |             gcmd = self.gcode.create_gcode_command("", "", {}) | ||||||
|  |             manual_probe.ManualProbeHelper(self.printer, gcmd, | ||||||
|                                            self._manual_probe_finalize) |                                            self._manual_probe_finalize) | ||||||
|     def _manual_probe_finalize(self, kin_pos): |     def _manual_probe_finalize(self, kin_pos): | ||||||
|         if kin_pos is None: |         if kin_pos is None: | ||||||
|   | |||||||
| @@ -63,7 +63,8 @@ class SafeZHoming: | |||||||
|         if need_y: |         if need_y: | ||||||
|             new_params['Y'] = '0' |             new_params['Y'] = '0' | ||||||
|         if new_params: |         if new_params: | ||||||
|             self.prev_G28(new_params) |             g28_gcmd = self.gcode.create_gcode_command("G28", "G28", new_params) | ||||||
|  |             self.prev_G28(g28_gcmd) | ||||||
|         # Home Z axis if necessary |         # Home Z axis if necessary | ||||||
|         if need_z: |         if need_z: | ||||||
|             # Move to safe XY homing position |             # Move to safe XY homing position | ||||||
| @@ -75,7 +76,8 @@ class SafeZHoming: | |||||||
|             toolhead.move(pos, self.speed) |             toolhead.move(pos, self.speed) | ||||||
|             self.gcode.reset_last_position() |             self.gcode.reset_last_position() | ||||||
|             # Home Z |             # Home Z | ||||||
|             self.prev_G28({'Z': '0'}) |             g28_gcmd = self.gcode.create_gcode_command("G28", "G28", {'Z': '0'}) | ||||||
|  |             self.prev_G28(g28_gcmd) | ||||||
|             # Perform Z Hop again for pressure-based probes |             # Perform Z Hop again for pressure-based probes | ||||||
|             pos = toolhead.get_position() |             pos = toolhead.get_position() | ||||||
|             if self.z_hop: |             if self.z_hop: | ||||||
|   | |||||||
							
								
								
									
										141
									
								
								klippy/gcode.py
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								klippy/gcode.py
									
									
									
									
									
								
							| @@ -1,11 +1,63 @@ | |||||||
| # Parse gcode commands | # Parse gcode commands | ||||||
| # | # | ||||||
| # Copyright (C) 2016-2019  Kevin O'Connor <kevin@koconnor.net> | # Copyright (C) 2016-2020  Kevin O'Connor <kevin@koconnor.net> | ||||||
| # | # | ||||||
| # This file may be distributed under the terms of the GNU GPLv3 license. | # This file may be distributed under the terms of the GNU GPLv3 license. | ||||||
| import os, re, logging, collections, shlex | import os, re, logging, collections, shlex | ||||||
| import homing | import homing | ||||||
|  |  | ||||||
|  | class GCodeCommand: | ||||||
|  |     error = homing.CommandError | ||||||
|  |     def __init__(self, gcode, command, commandline, params): | ||||||
|  |         self._command = command | ||||||
|  |         self._commandline = commandline | ||||||
|  |         self._params = params | ||||||
|  |         # Method wrappers | ||||||
|  |         self.respond_info = gcode.respond_info | ||||||
|  |         self.respond_raw = gcode.respond_raw | ||||||
|  |         self.__contains__ = self._params.__contains__ | ||||||
|  |         self.__getitem__ = self._params.__getitem__ | ||||||
|  |     def get_command(self): | ||||||
|  |         return self._command | ||||||
|  |     def get_commandline(self): | ||||||
|  |         return self._commandline | ||||||
|  |     def get_command_parameters(self): | ||||||
|  |         return self._params | ||||||
|  |     # Parameter parsing helpers | ||||||
|  |     class sentinel: pass | ||||||
|  |     def get(self, name, default=sentinel, parser=str, minval=None, maxval=None, | ||||||
|  |             above=None, below=None): | ||||||
|  |         value = self._params.get(name) | ||||||
|  |         if value is None: | ||||||
|  |             if default is self.sentinel: | ||||||
|  |                 raise self.error("Error on '%s': missing %s" | ||||||
|  |                                  % (self._commandline, name)) | ||||||
|  |             return default | ||||||
|  |         try: | ||||||
|  |             value = parser(value) | ||||||
|  |         except: | ||||||
|  |             raise self.error("Error on '%s': unable to parse %s" | ||||||
|  |                              % (self._commandline, value)) | ||||||
|  |         if minval is not None and value < minval: | ||||||
|  |             raise self.error("Error on '%s': %s must have minimum of %s" | ||||||
|  |                              % (self._commandline, name, minval)) | ||||||
|  |         if maxval is not None and value > maxval: | ||||||
|  |             raise self.error("Error on '%s': %s must have maximum of %s" | ||||||
|  |                              % (self._commandline, name, maxval)) | ||||||
|  |         if above is not None and value <= above: | ||||||
|  |             raise self.error("Error on '%s': %s must be above %s" | ||||||
|  |                              % (self._commandline, name, above)) | ||||||
|  |         if below is not None and value >= below: | ||||||
|  |             raise self.error("Error on '%s': %s must be below %s" | ||||||
|  |                              % (self._commandline, name, below)) | ||||||
|  |         return value | ||||||
|  |     def get_int(self, name, default=sentinel, minval=None, maxval=None): | ||||||
|  |         return self.get(name, default, parser=int, minval=minval, maxval=maxval) | ||||||
|  |     def get_float(self, name, default=sentinel, minval=None, maxval=None, | ||||||
|  |                   above=None, below=None): | ||||||
|  |         return self.get(name, default, parser=float, minval=minval, | ||||||
|  |                         maxval=maxval, above=above, below=below) | ||||||
|  |  | ||||||
| # Parse and handle G-Code commands | # Parse and handle G-Code commands | ||||||
| class GCodeParser: | class GCodeParser: | ||||||
|     error = homing.CommandError |     error = homing.CommandError | ||||||
| @@ -214,23 +266,26 @@ class GCodeParser: | |||||||
|             cpos = line.find(';') |             cpos = line.find(';') | ||||||
|             if cpos >= 0: |             if cpos >= 0: | ||||||
|                 line = line[:cpos] |                 line = line[:cpos] | ||||||
|             # Break command into parts |             # Break line into parts and determine command | ||||||
|             parts = self.args_r.split(line.upper())[1:] |             parts = self.args_r.split(line.upper()) | ||||||
|             params = { parts[i]: parts[i+1].strip() |             numparts = len(parts) | ||||||
|                        for i in range(0, len(parts), 2) } |             cmd = "" | ||||||
|             params['#original'] = origline |             if numparts >= 3 and parts[1] != 'N': | ||||||
|             if parts and parts[0] == 'N': |                 cmd = parts[1] + parts[2].strip() | ||||||
|  |             elif numparts >= 5 and parts[1] == 'N': | ||||||
|                 # Skip line number at start of command |                 # Skip line number at start of command | ||||||
|                 del parts[:2] |                 cmd = parts[3] + parts[4].strip() | ||||||
|             if not parts: |             # Build gcode "params" dictionary | ||||||
|                 # Treat empty line as empty command |             params = { parts[i]: parts[i+1].strip() | ||||||
|                 parts = ['', ''] |                        for i in range(1, numparts, 2) } | ||||||
|             params['#command'] = cmd = parts[0] + parts[1].strip() |             params['#original'] = origline | ||||||
|  |             params['#command'] = cmd | ||||||
|  |             gcmd = GCodeCommand(self, cmd, origline, params) | ||||||
|             # Invoke handler for command |             # Invoke handler for command | ||||||
|             self.need_ack = need_ack |             self.need_ack = need_ack | ||||||
|             handler = self.gcode_handlers.get(cmd, self.cmd_default) |             handler = self.gcode_handlers.get(cmd, self.cmd_default) | ||||||
|             try: |             try: | ||||||
|                 handler(params) |                 handler(gcmd) | ||||||
|             except self.error as e: |             except self.error as e: | ||||||
|                 self._respond_error(str(e)) |                 self._respond_error(str(e)) | ||||||
|                 self.reset_last_position() |                 self.reset_last_position() | ||||||
| @@ -273,7 +328,7 @@ class GCodeParser: | |||||||
|                 # Check for M112 out-of-order |                 # Check for M112 out-of-order | ||||||
|                 for line in lines: |                 for line in lines: | ||||||
|                     if self.m112_r.match(line) is not None: |                     if self.m112_r.match(line) is not None: | ||||||
|                         self.cmd_M112({}) |                         self.cmd_M112(None) | ||||||
|             if self.is_processing_data: |             if self.is_processing_data: | ||||||
|                 if len(pending_commands) >= 20: |                 if len(pending_commands) >= 20: | ||||||
|                     # Stop reading input |                     # Stop reading input | ||||||
| @@ -302,6 +357,8 @@ class GCodeParser: | |||||||
|             self._process_commands(script.split('\n'), need_ack=False) |             self._process_commands(script.split('\n'), need_ack=False) | ||||||
|     def get_mutex(self): |     def get_mutex(self): | ||||||
|         return self.mutex |         return self.mutex | ||||||
|  |     def create_gcode_command(self, command, commandline, params): | ||||||
|  |         return GCodeCommand(self, command, commandline, params) | ||||||
|     # Response handling |     # Response handling | ||||||
|     def ack(self, msg=None): |     def ack(self, msg=None): | ||||||
|         if not self.need_ack: |         if not self.need_ack: | ||||||
| @@ -340,56 +397,38 @@ class GCodeParser: | |||||||
|     def _respond_state(self, state): |     def _respond_state(self, state): | ||||||
|         self.respond_info("Klipper state: %s" % (state,), log=False) |         self.respond_info("Klipper state: %s" % (state,), log=False) | ||||||
|     # Parameter parsing helpers |     # Parameter parsing helpers | ||||||
|     class sentinel: pass |     def get_str(self, name, gcmd, default=GCodeCommand.sentinel, parser=str, | ||||||
|     def get_str(self, name, params, default=sentinel, parser=str, |  | ||||||
|                 minval=None, maxval=None, above=None, below=None): |                 minval=None, maxval=None, above=None, below=None): | ||||||
|         if name not in params: |         return gcmd.get(name, default, parser, minval, maxval, above, below) | ||||||
|             if default is self.sentinel: |     def get_int(self, name, gcmd, default=GCodeCommand.sentinel, | ||||||
|                 raise self.error("Error on '%s': missing %s" % ( |                 minval=None, maxval=None): | ||||||
|                     params['#original'], name)) |         return gcmd.get_int(name, default, minval=minval, maxval=maxval) | ||||||
|             return default |     def get_float(self, name, gcmd, default=GCodeCommand.sentinel, | ||||||
|         try: |  | ||||||
|             value = parser(params[name]) |  | ||||||
|         except: |  | ||||||
|             raise self.error("Error on '%s': unable to parse %s" % ( |  | ||||||
|                 params['#original'], params[name])) |  | ||||||
|         if minval is not None and value < minval: |  | ||||||
|             raise self.error("Error on '%s': %s must have minimum of %s" % ( |  | ||||||
|                 params['#original'], name, minval)) |  | ||||||
|         if maxval is not None and value > maxval: |  | ||||||
|             raise self.error("Error on '%s': %s must have maximum of %s" % ( |  | ||||||
|                 params['#original'], name, maxval)) |  | ||||||
|         if above is not None and value <= above: |  | ||||||
|             raise self.error("Error on '%s': %s must be above %s" % ( |  | ||||||
|                 params['#original'], name, above)) |  | ||||||
|         if below is not None and value >= below: |  | ||||||
|             raise self.error("Error on '%s': %s must be below %s" % ( |  | ||||||
|                 params['#original'], name, below)) |  | ||||||
|         return value |  | ||||||
|     def get_int(self, name, params, default=sentinel, minval=None, maxval=None): |  | ||||||
|         return self.get_str(name, params, default, parser=int, |  | ||||||
|                             minval=minval, maxval=maxval) |  | ||||||
|     def get_float(self, name, params, default=sentinel, |  | ||||||
|                   minval=None, maxval=None, above=None, below=None): |                   minval=None, maxval=None, above=None, below=None): | ||||||
|         return self.get_str(name, params, default, parser=float, minval=minval, |         return gcmd.get_float(name, default, minval=minval, maxval=maxval, | ||||||
|                             maxval=maxval, above=above, below=below) |                               above=above, below=below) | ||||||
|     extended_r = re.compile( |     extended_r = re.compile( | ||||||
|         r'^\s*(?:N[0-9]+\s*)?' |         r'^\s*(?:N[0-9]+\s*)?' | ||||||
|         r'(?P<cmd>[a-zA-Z_][a-zA-Z0-9_]+)(?:\s+|$)' |         r'(?P<cmd>[a-zA-Z_][a-zA-Z0-9_]+)(?:\s+|$)' | ||||||
|         r'(?P<args>[^#*;]*?)' |         r'(?P<args>[^#*;]*?)' | ||||||
|         r'\s*(?:[#*;].*)?$') |         r'\s*(?:[#*;].*)?$') | ||||||
|     def _get_extended_params(self, params): |     def _get_extended_params(self, gcmd): | ||||||
|         m = self.extended_r.match(params['#original']) |         m = self.extended_r.match(gcmd.get_commandline()) | ||||||
|         if m is None: |         if m is None: | ||||||
|             raise self.error("Malformed command '%s'" % (params['#original'],)) |             raise self.error("Malformed command '%s'" | ||||||
|  |                              % (gcmd.get_commandline(),)) | ||||||
|         eargs = m.group('args') |         eargs = m.group('args') | ||||||
|         try: |         try: | ||||||
|             eparams = [earg.split('=', 1) for earg in shlex.split(eargs)] |             eparams = [earg.split('=', 1) for earg in shlex.split(eargs)] | ||||||
|             eparams = { k.upper(): v for k, v in eparams } |             eparams = { k.upper(): v for k, v in eparams } | ||||||
|             eparams.update({k: params[k] for k in params if k.startswith('#')}) |             eparams['#original'] = gcmd._params['#original'] | ||||||
|             return eparams |             eparams['#command'] = gcmd._params['#command'] | ||||||
|  |             gcmd._params.clear() | ||||||
|  |             gcmd._params.update(eparams) | ||||||
|  |             return gcmd | ||||||
|         except ValueError as e: |         except ValueError as e: | ||||||
|             raise self.error("Malformed command '%s'" % (params['#original'],)) |             raise self.error("Malformed command '%s'" | ||||||
|  |                              % (gcmd.get_commandline(),)) | ||||||
|     # G-Code special command handlers |     # G-Code special command handlers | ||||||
|     def cmd_default(self, params): |     def cmd_default(self, params): | ||||||
|         cmd = params.get('#command') |         cmd = params.get('#command') | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user