gcode_macro: Evaluate macros using Jinja2 template engine
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
890a5ea6bb
commit
486c07c190
|
@ -1,40 +1,105 @@
|
||||||
# Add ability to define custom g-code macros
|
# Add ability to define custom g-code macros
|
||||||
#
|
#
|
||||||
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
# Copyright (C) 2018-2019 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 traceback, logging
|
import traceback, logging
|
||||||
|
import jinja2
|
||||||
|
|
||||||
DEFAULT_PREFIX = 'default_parameter_'
|
|
||||||
|
######################################################################
|
||||||
|
# Template handling
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
# Wrapper for "status" access to printer object get_status() methods
|
||||||
|
class StatusWrapper:
|
||||||
|
def __init__(self, printer, eventtime=None):
|
||||||
|
self.printer = printer
|
||||||
|
self.eventtime = eventtime
|
||||||
|
self.cache = {}
|
||||||
|
def __getitem__(self, val):
|
||||||
|
sval = str(val).strip()
|
||||||
|
if sval in self.cache:
|
||||||
|
return self.cache[sval]
|
||||||
|
po = self.printer.lookup_object(sval, None)
|
||||||
|
if po is None or not hasattr(po, 'get_status'):
|
||||||
|
raise KeyError(val)
|
||||||
|
if self.eventtime is None:
|
||||||
|
self.eventtime = self.printer.get_reactor().monotonic()
|
||||||
|
self.cache[sval] = res = dict(po.get_status(self.eventtime))
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Wrapper around a Jinja2 template
|
||||||
|
class TemplateWrapper:
|
||||||
|
def __init__(self, printer, env, name, script):
|
||||||
|
self.printer = printer
|
||||||
|
self.name = name
|
||||||
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
|
try:
|
||||||
|
self.template = env.from_string(script)
|
||||||
|
except Exception as e:
|
||||||
|
msg = "Error loading template '%s': %s" % (
|
||||||
|
name, traceback.format_exception_only(type(e), e)[-1])
|
||||||
|
logging.exception(msg)
|
||||||
|
raise printer.config_error(msg)
|
||||||
|
def create_status_wrapper(self, eventtime=None):
|
||||||
|
return StatusWrapper(self.printer, eventtime)
|
||||||
|
def render(self, context=None):
|
||||||
|
if context is None:
|
||||||
|
context = {'status': self.create_status_wrapper()}
|
||||||
|
try:
|
||||||
|
return str(self.template.render(context))
|
||||||
|
except Exception as e:
|
||||||
|
msg = "Error evaluating '%s': %s" % (
|
||||||
|
self.name, traceback.format_exception_only(type(e), e)[-1])
|
||||||
|
logging.exception(msg)
|
||||||
|
raise self.gcode.error(msg)
|
||||||
|
def run_gcode_from_command(self, context=None):
|
||||||
|
self.gcode.run_script_from_command(self.render(context))
|
||||||
|
|
||||||
|
# Main gcode macro template tracking
|
||||||
|
class PrinterGCodeMacro:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.env = jinja2.Environment('{%', '%}', '{', '}')
|
||||||
|
def load_template(self, config, option):
|
||||||
|
name = "%s:%s" % (config.get_name(), option)
|
||||||
|
script = config.get(option, '')
|
||||||
|
return TemplateWrapper(self.printer, self.env, name, script)
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return PrinterGCodeMacro(config)
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# GCode macro
|
||||||
|
######################################################################
|
||||||
|
|
||||||
class GCodeMacro:
|
class GCodeMacro:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.alias = config.get_name().split()[1].upper()
|
self.alias = config.get_name().split()[1].upper()
|
||||||
self.script = config.get('gcode')
|
|
||||||
printer = config.get_printer()
|
printer = config.get_printer()
|
||||||
|
config.get('gcode')
|
||||||
|
gcode_macro = printer.try_load_module(config, 'gcode_macro')
|
||||||
|
self.template = gcode_macro.load_template(config, 'gcode')
|
||||||
self.gcode = printer.lookup_object('gcode')
|
self.gcode = printer.lookup_object('gcode')
|
||||||
self.gcode.register_command(self.alias, self.cmd, desc=self.cmd_desc)
|
self.gcode.register_command(self.alias, self.cmd, desc=self.cmd_desc)
|
||||||
self.in_script = False
|
self.in_script = False
|
||||||
self.kwparams = { o[len(DEFAULT_PREFIX):].upper(): config.get(o)
|
prefix = 'default_parameter_'
|
||||||
for o in config.get_prefix_options(DEFAULT_PREFIX) }
|
self.kwparams = { o[len(prefix):].upper(): config.get(o)
|
||||||
|
for o in config.get_prefix_options(prefix) }
|
||||||
cmd_desc = "G-Code macro"
|
cmd_desc = "G-Code macro"
|
||||||
def cmd(self, params):
|
def cmd(self, params):
|
||||||
if self.in_script:
|
if self.in_script:
|
||||||
raise self.gcode.error(
|
raise self.gcode.error(
|
||||||
"Macro %s called recursively" % (self.alias,))
|
"Macro %s called recursively" % (self.alias,))
|
||||||
script = ""
|
|
||||||
kwparams = dict(self.kwparams)
|
kwparams = dict(self.kwparams)
|
||||||
kwparams.update(params)
|
kwparams.update(params)
|
||||||
try:
|
kwparams['status'] = self.template.create_status_wrapper()
|
||||||
script = self.script.format(**kwparams)
|
kwparams['params'] = params
|
||||||
except Exception as e:
|
|
||||||
msg = "Error evaluating %s: %s" % (
|
|
||||||
self.alias, traceback.format_exception_only(type(e), e)[-1])
|
|
||||||
logging.exception(msg)
|
|
||||||
raise self.gcode.error(msg)
|
|
||||||
self.in_script = True
|
self.in_script = True
|
||||||
try:
|
try:
|
||||||
self.gcode.run_script_from_command(script)
|
self.template.run_gcode_from_command(kwparams)
|
||||||
finally:
|
finally:
|
||||||
self.in_script = False
|
self.in_script = False
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,4 @@
|
||||||
cffi==1.12.2
|
cffi==1.12.2
|
||||||
pyserial==3.4
|
pyserial==3.4
|
||||||
greenlet==0.4.15
|
greenlet==0.4.15
|
||||||
|
Jinja2==2.10
|
||||||
|
|
Loading…
Reference in New Issue