aboutsummaryrefslogtreecommitdiffstats
path: root/klippy
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2020-04-22 12:40:32 -0400
committerKevin O'Connor <kevin@koconnor.net>2020-05-05 11:08:11 -0400
commitddb8311890a130e3735d77fbb7fb24900a5152b2 (patch)
tree8b80d4f7f298fb0b536c6fb4dc5156e245c0cc25 /klippy
parent1eb2d4da902867b3bf4fdee1b2d272c170f93468 (diff)
downloadkutter-ddb8311890a130e3735d77fbb7fb24900a5152b2.tar.gz
kutter-ddb8311890a130e3735d77fbb7fb24900a5152b2.tar.xz
kutter-ddb8311890a130e3735d77fbb7fb24900a5152b2.zip
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>
Diffstat (limited to 'klippy')
-rw-r--r--klippy/extras/gcode_arcs.py3
-rw-r--r--klippy/extras/gcode_macro.py6
-rw-r--r--klippy/extras/homing_override.py2
-rw-r--r--klippy/extras/pause_resume.py2
-rw-r--r--klippy/extras/probe.py3
-rw-r--r--klippy/extras/safe_z_home.py6
-rw-r--r--klippy/gcode.py139
7 files changed, 102 insertions, 59 deletions
diff --git a/klippy/extras/gcode_arcs.py b/klippy/extras/gcode_arcs.py
index 61b1a0fc..eec746d1 100644
--- a/klippy/extras/gcode_arcs.py
+++ b/klippy/extras/gcode_arcs.py
@@ -54,7 +54,8 @@ class ArcSupport:
g1_params['E'] = asE / len(coords)
if asF is not None:
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()
# https://github.com/MarlinFirmware/Marlin
diff --git a/klippy/extras/gcode_macro.py b/klippy/extras/gcode_macro.py
index 2d4ebc75..b462e2cd 100644
--- a/klippy/extras/gcode_macro.py
+++ b/klippy/extras/gcode_macro.py
@@ -154,10 +154,10 @@ class GCodeMacro:
value,))
self.variables[variable] = literal
cmd_desc = "G-Code macro"
- def cmd(self, params):
+ def cmd(self, gcmd):
if self.in_script:
- raise self.gcode.error(
- "Macro %s called recursively" % (self.alias,))
+ raise gcmd.error("Macro %s called recursively" % (self.alias,))
+ params = gcmd.get_command_parameters()
kwparams = dict(self.kwparams)
kwparams.update(params)
kwparams.update(self.variables)
diff --git a/klippy/extras/homing_override.py b/klippy/extras/homing_override.py
index 8a40044c..b15b3b9f 100644
--- a/klippy/extras/homing_override.py
+++ b/klippy/extras/homing_override.py
@@ -54,7 +54,7 @@ class HomingOverride:
self.gcode.reset_last_position()
# Perform homing
kwparams = { 'printer': self.template.create_status_wrapper() }
- kwparams['params'] = params
+ kwparams['params'] = params.get_command_parameters()
try:
self.in_script = True
self.template.run_gcode_from_command(kwparams)
diff --git a/klippy/extras/pause_resume.py b/klippy/extras/pause_resume.py
index 4b3b88b5..b70895cd 100644
--- a/klippy/extras/pause_resume.py
+++ b/klippy/extras/pause_resume.py
@@ -57,7 +57,7 @@ class PauseResume:
self.pause_command_sent = False
if self.sd_paused:
# Printing from virtual sd, run pause command
- self.v_sd.cmd_M24({})
+ self.v_sd.cmd_M24(gcmd)
else:
self.gcode.respond_info("action:resumed")
def cmd_CLEAR_PAUSE(self, params):
diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py
index 703ef5b4..3d84aa9e 100644
--- a/klippy/extras/probe.py
+++ b/klippy/extras/probe.py
@@ -410,7 +410,8 @@ class ProbePointsHelper:
def _manual_probe_start(self):
done = self._move_next()
if not done:
- manual_probe.ManualProbeHelper(self.printer, {},
+ gcmd = self.gcode.create_gcode_command("", "", {})
+ manual_probe.ManualProbeHelper(self.printer, gcmd,
self._manual_probe_finalize)
def _manual_probe_finalize(self, kin_pos):
if kin_pos is None:
diff --git a/klippy/extras/safe_z_home.py b/klippy/extras/safe_z_home.py
index 4e0801c4..265cbf41 100644
--- a/klippy/extras/safe_z_home.py
+++ b/klippy/extras/safe_z_home.py
@@ -63,7 +63,8 @@ class SafeZHoming:
if need_y:
new_params['Y'] = '0'
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
if need_z:
# Move to safe XY homing position
@@ -75,7 +76,8 @@ class SafeZHoming:
toolhead.move(pos, self.speed)
self.gcode.reset_last_position()
# 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
pos = toolhead.get_position()
if self.z_hop:
diff --git a/klippy/gcode.py b/klippy/gcode.py
index 9077a667..3ea4d12c 100644
--- a/klippy/gcode.py
+++ b/klippy/gcode.py
@@ -1,11 +1,63 @@
# 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.
import os, re, logging, collections, shlex
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
class GCodeParser:
error = homing.CommandError
@@ -214,23 +266,26 @@ class GCodeParser:
cpos = line.find(';')
if cpos >= 0:
line = line[:cpos]
- # Break command into parts
- parts = self.args_r.split(line.upper())[1:]
+ # Break line into parts and determine command
+ parts = self.args_r.split(line.upper())
+ numparts = len(parts)
+ cmd = ""
+ if numparts >= 3 and parts[1] != 'N':
+ cmd = parts[1] + parts[2].strip()
+ elif numparts >= 5 and parts[1] == 'N':
+ # Skip line number at start of command
+ cmd = parts[3] + parts[4].strip()
+ # Build gcode "params" dictionary
params = { parts[i]: parts[i+1].strip()
- for i in range(0, len(parts), 2) }
+ for i in range(1, numparts, 2) }
params['#original'] = origline
- if parts and parts[0] == 'N':
- # Skip line number at start of command
- del parts[:2]
- if not parts:
- # Treat empty line as empty command
- parts = ['', '']
- params['#command'] = cmd = parts[0] + parts[1].strip()
+ params['#command'] = cmd
+ gcmd = GCodeCommand(self, cmd, origline, params)
# Invoke handler for command
self.need_ack = need_ack
handler = self.gcode_handlers.get(cmd, self.cmd_default)
try:
- handler(params)
+ handler(gcmd)
except self.error as e:
self._respond_error(str(e))
self.reset_last_position()
@@ -273,7 +328,7 @@ class GCodeParser:
# Check for M112 out-of-order
for line in lines:
if self.m112_r.match(line) is not None:
- self.cmd_M112({})
+ self.cmd_M112(None)
if self.is_processing_data:
if len(pending_commands) >= 20:
# Stop reading input
@@ -302,6 +357,8 @@ class GCodeParser:
self._process_commands(script.split('\n'), need_ack=False)
def get_mutex(self):
return self.mutex
+ def create_gcode_command(self, command, commandline, params):
+ return GCodeCommand(self, command, commandline, params)
# Response handling
def ack(self, msg=None):
if not self.need_ack:
@@ -340,56 +397,38 @@ class GCodeParser:
def _respond_state(self, state):
self.respond_info("Klipper state: %s" % (state,), log=False)
# Parameter parsing helpers
- class sentinel: pass
- def get_str(self, name, params, default=sentinel, parser=str,
+ def get_str(self, name, gcmd, default=GCodeCommand.sentinel, parser=str,
minval=None, maxval=None, above=None, below=None):
- if name not in params:
- if default is self.sentinel:
- raise self.error("Error on '%s': missing %s" % (
- params['#original'], name))
- return default
- 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,
+ return gcmd.get(name, default, parser, minval, maxval, above, below)
+ def get_int(self, name, gcmd, default=GCodeCommand.sentinel,
+ minval=None, maxval=None):
+ return gcmd.get_int(name, default, minval=minval, maxval=maxval)
+ def get_float(self, name, gcmd, default=GCodeCommand.sentinel,
minval=None, maxval=None, above=None, below=None):
- return self.get_str(name, params, default, parser=float, minval=minval,
- maxval=maxval, above=above, below=below)
+ return gcmd.get_float(name, default, minval=minval, maxval=maxval,
+ above=above, below=below)
extended_r = re.compile(
r'^\s*(?:N[0-9]+\s*)?'
r'(?P<cmd>[a-zA-Z_][a-zA-Z0-9_]+)(?:\s+|$)'
r'(?P<args>[^#*;]*?)'
r'\s*(?:[#*;].*)?$')
- def _get_extended_params(self, params):
- m = self.extended_r.match(params['#original'])
+ def _get_extended_params(self, gcmd):
+ m = self.extended_r.match(gcmd.get_commandline())
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')
try:
eparams = [earg.split('=', 1) for earg in shlex.split(eargs)]
eparams = { k.upper(): v for k, v in eparams }
- eparams.update({k: params[k] for k in params if k.startswith('#')})
- return eparams
+ eparams['#original'] = gcmd._params['#original']
+ eparams['#command'] = gcmd._params['#command']
+ gcmd._params.clear()
+ gcmd._params.update(eparams)
+ return gcmd
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
def cmd_default(self, params):
cmd = params.get('#command')