mirror of https://github.com/Desuuuu/klipper.git
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:
parent
1eb2d4da90
commit
ddb8311890
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue