bed_mesh: Move profile management to its own class
This streamlines the BedMeshCalibrate class, making it only resposible for configuring and executing the calibration procedure. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
dbec03abd9
commit
4795c0896d
|
@ -74,6 +74,10 @@ class BedMesh:
|
||||||
self.fade_target = 0.
|
self.fade_target = 0.
|
||||||
self.gcode = self.printer.lookup_object('gcode')
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
self.splitter = MoveSplitter(config, self.gcode)
|
self.splitter = MoveSplitter(config, self.gcode)
|
||||||
|
# setup persistent storage
|
||||||
|
self.pmgr = ProfileManager(config, self)
|
||||||
|
self.save_profile = self.pmgr.save_profile
|
||||||
|
# register gcodes
|
||||||
self.gcode.register_command(
|
self.gcode.register_command(
|
||||||
'BED_MESH_OUTPUT', self.cmd_BED_MESH_OUTPUT,
|
'BED_MESH_OUTPUT', self.cmd_BED_MESH_OUTPUT,
|
||||||
desc=self.cmd_BED_MESH_OUTPUT_help)
|
desc=self.cmd_BED_MESH_OUTPUT_help)
|
||||||
|
@ -86,7 +90,8 @@ class BedMesh:
|
||||||
self.gcode.set_move_transform(self)
|
self.gcode.set_move_transform(self)
|
||||||
def handle_ready(self):
|
def handle_ready(self):
|
||||||
self.toolhead = self.printer.lookup_object('toolhead')
|
self.toolhead = self.printer.lookup_object('toolhead')
|
||||||
self.bmc.handle_ready()
|
self.bmc.print_generated_points(logging.info)
|
||||||
|
self.pmgr.initialize()
|
||||||
def set_mesh(self, mesh):
|
def set_mesh(self, mesh):
|
||||||
if mesh is not None and self.fade_end != self.FADE_DISABLE:
|
if mesh is not None and self.fade_end != self.FADE_DISABLE:
|
||||||
self.log_fade_complete = True
|
self.log_fade_complete = True
|
||||||
|
@ -189,12 +194,14 @@ class BedMesh:
|
||||||
mesh_max = (params['max_x'], params['max_y'])
|
mesh_max = (params['max_x'], params['max_y'])
|
||||||
probed_matrix = self.z_mesh.get_probed_matrix()
|
probed_matrix = self.z_mesh.get_probed_matrix()
|
||||||
mesh_matrix = self.z_mesh.get_mesh_matrix()
|
mesh_matrix = self.z_mesh.get_mesh_matrix()
|
||||||
status['profile_name'] = self.bmc.current_profile
|
status['profile_name'] = self.pmgr.get_current_profile()
|
||||||
status['mesh_min'] = mesh_min
|
status['mesh_min'] = mesh_min
|
||||||
status['mesh_max'] = mesh_max
|
status['mesh_max'] = mesh_max
|
||||||
status['probed_matrix'] = probed_matrix
|
status['probed_matrix'] = probed_matrix
|
||||||
status['mesh_matrix'] = mesh_matrix
|
status['mesh_matrix'] = mesh_matrix
|
||||||
return status
|
return status
|
||||||
|
def get_mesh(self):
|
||||||
|
return self.z_mesh
|
||||||
cmd_BED_MESH_OUTPUT_help = "Retrieve interpolated grid of probed z-points"
|
cmd_BED_MESH_OUTPUT_help = "Retrieve interpolated grid of probed z-points"
|
||||||
def cmd_BED_MESH_OUTPUT(self, gcmd):
|
def cmd_BED_MESH_OUTPUT(self, gcmd):
|
||||||
if gcmd.get_int('PGP', 0):
|
if gcmd.get_int('PGP', 0):
|
||||||
|
@ -225,13 +232,10 @@ class BedMeshCalibrate:
|
||||||
ALGOS = ['lagrange', 'bicubic']
|
ALGOS = ['lagrange', 'bicubic']
|
||||||
def __init__(self, config, bedmesh):
|
def __init__(self, config, bedmesh):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.name = config.get_name()
|
|
||||||
self.current_profile = ""
|
|
||||||
self.radius = self.origin = None
|
self.radius = self.origin = None
|
||||||
self.relative_reference_index = config.getint(
|
self.relative_reference_index = config.getint(
|
||||||
'relative_reference_index', None)
|
'relative_reference_index', None)
|
||||||
self.bedmesh = bedmesh
|
self.bedmesh = bedmesh
|
||||||
self.z_mesh = None
|
|
||||||
self.mesh_config = collections.OrderedDict()
|
self.mesh_config = collections.OrderedDict()
|
||||||
self.points = self._generate_points(config)
|
self.points = self._generate_points(config)
|
||||||
self._init_mesh_config(config, self.points)
|
self._init_mesh_config(config, self.points)
|
||||||
|
@ -239,22 +243,10 @@ class BedMeshCalibrate:
|
||||||
config, self.probe_finalize, self.points)
|
config, self.probe_finalize, self.points)
|
||||||
self.probe_helper.minimum_points(3)
|
self.probe_helper.minimum_points(3)
|
||||||
self.probe_helper.use_xy_offsets(True)
|
self.probe_helper.use_xy_offsets(True)
|
||||||
# setup persistent storage
|
|
||||||
self.profiles = {}
|
|
||||||
self.incompatible_profiles = []
|
|
||||||
self._load_storage(config)
|
|
||||||
self.gcode = self.printer.lookup_object('gcode')
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
self.gcode.register_command(
|
self.gcode.register_command(
|
||||||
'BED_MESH_CALIBRATE', self.cmd_BED_MESH_CALIBRATE,
|
'BED_MESH_CALIBRATE', self.cmd_BED_MESH_CALIBRATE,
|
||||||
desc=self.cmd_BED_MESH_CALIBRATE_help)
|
desc=self.cmd_BED_MESH_CALIBRATE_help)
|
||||||
self.gcode.register_command(
|
|
||||||
'BED_MESH_PROFILE', self.cmd_BED_MESH_PROFILE,
|
|
||||||
desc=self.cmd_BED_MESH_PROFILE_help)
|
|
||||||
def handle_ready(self):
|
|
||||||
self.print_generated_points(logging.info)
|
|
||||||
self._check_incompatible_profiles()
|
|
||||||
if "default" in self.profiles:
|
|
||||||
self.load_profile("default")
|
|
||||||
def _generate_points(self, config):
|
def _generate_points(self, config):
|
||||||
self.radius = config.getfloat('mesh_radius', None, above=0.)
|
self.radius = config.getfloat('mesh_radius', None, above=0.)
|
||||||
if self.radius is not None:
|
if self.radius is not None:
|
||||||
|
@ -375,120 +367,6 @@ class BedMeshCalibrate:
|
||||||
params['algo'] = 'lagrange'
|
params['algo'] = 'lagrange'
|
||||||
params['tension'] = config.getfloat(
|
params['tension'] = config.getfloat(
|
||||||
'bicubic_tension', .2, minval=0., maxval=2.)
|
'bicubic_tension', .2, minval=0., maxval=2.)
|
||||||
def _check_incompatible_profiles(self):
|
|
||||||
if self.incompatible_profiles:
|
|
||||||
configfile = self.printer.lookup_object('configfile')
|
|
||||||
for profile in self.incompatible_profiles:
|
|
||||||
configfile.remove_section('bed_mesh ' + profile)
|
|
||||||
self.gcode.respond_info(
|
|
||||||
"The following incompatible profiles have been detected\n"
|
|
||||||
"and are scheduled for removal:\n%s\n"
|
|
||||||
"The SAVE_CONFIG command will update the printer config\n"
|
|
||||||
"file and restart the printer" %
|
|
||||||
(('\n').join(self.incompatible_profiles)))
|
|
||||||
def _load_storage(self, config):
|
|
||||||
stored_profs = config.get_prefix_sections(self.name)
|
|
||||||
# Remove primary bed_mesh section, as it is not a stored profile
|
|
||||||
stored_profs = [s for s in stored_profs
|
|
||||||
if s.get_name() != self.name]
|
|
||||||
for profile in stored_profs:
|
|
||||||
name = profile.get_name().split(' ', 1)[1]
|
|
||||||
version = profile.getint('version', 0)
|
|
||||||
if version != PROFILE_VERSION:
|
|
||||||
logging.info(
|
|
||||||
"bed_mesh: Profile [%s] not compatible with this version\n"
|
|
||||||
"of bed_mesh. Profile Version: %d Current Version: %d "
|
|
||||||
% (name, version, PROFILE_VERSION))
|
|
||||||
self.incompatible_profiles.append(name)
|
|
||||||
continue
|
|
||||||
self.profiles[name] = {}
|
|
||||||
z_values = profile.get('points').split('\n')
|
|
||||||
self.profiles[name]['points'] = \
|
|
||||||
[[float(pt.strip()) for pt in line.split(',')]
|
|
||||||
for line in z_values if line.strip()]
|
|
||||||
self.profiles[name]['mesh_params'] = params = \
|
|
||||||
collections.OrderedDict()
|
|
||||||
for key, t in PROFILE_OPTIONS.items():
|
|
||||||
if t is int:
|
|
||||||
params[key] = profile.getint(key)
|
|
||||||
elif t is float:
|
|
||||||
params[key] = profile.getfloat(key)
|
|
||||||
elif t is str:
|
|
||||||
params[key] = profile.get(key)
|
|
||||||
def save_profile(self, prof_name):
|
|
||||||
if self.z_mesh is None:
|
|
||||||
self.gcode.respond_info(
|
|
||||||
"Unable to save to profile [%s], the bed has not been probed"
|
|
||||||
% (prof_name))
|
|
||||||
return
|
|
||||||
probed_matrix = self.z_mesh.get_probed_matrix()
|
|
||||||
mesh_params = self.z_mesh.get_mesh_params()
|
|
||||||
configfile = self.printer.lookup_object('configfile')
|
|
||||||
cfg_name = self.name + " " + prof_name
|
|
||||||
# set params
|
|
||||||
z_values = ""
|
|
||||||
for line in probed_matrix:
|
|
||||||
z_values += "\n "
|
|
||||||
for p in line:
|
|
||||||
z_values += "%.6f, " % p
|
|
||||||
z_values = z_values[:-2]
|
|
||||||
configfile.set(cfg_name, 'version', PROFILE_VERSION)
|
|
||||||
configfile.set(cfg_name, 'points', z_values)
|
|
||||||
for key, value in mesh_params.items():
|
|
||||||
configfile.set(cfg_name, key, value)
|
|
||||||
# save copy in local storage
|
|
||||||
self.profiles[prof_name] = profile = {}
|
|
||||||
profile['points'] = probed_matrix
|
|
||||||
profile['mesh_params'] = collections.OrderedDict(mesh_params)
|
|
||||||
self.gcode.respond_info(
|
|
||||||
"Bed Mesh state has been saved to profile [%s]\n"
|
|
||||||
"for the current session. The SAVE_CONFIG command will\n"
|
|
||||||
"update the printer config file and restart the printer."
|
|
||||||
% (prof_name))
|
|
||||||
def load_profile(self, prof_name):
|
|
||||||
profile = self.profiles.get(prof_name, None)
|
|
||||||
if profile is None:
|
|
||||||
raise self.gcode.error(
|
|
||||||
"bed_mesh: Unknown profile [%s]" % prof_name)
|
|
||||||
probed_matrix = profile['points']
|
|
||||||
mesh_params = profile['mesh_params']
|
|
||||||
self.z_mesh = ZMesh(mesh_params)
|
|
||||||
try:
|
|
||||||
self.z_mesh.build_mesh(probed_matrix)
|
|
||||||
except BedMeshError as e:
|
|
||||||
raise self.gcode.error(e.message)
|
|
||||||
self.current_profile = prof_name
|
|
||||||
self.bedmesh.set_mesh(self.z_mesh)
|
|
||||||
def remove_profile(self, prof_name):
|
|
||||||
if prof_name in self.profiles:
|
|
||||||
configfile = self.printer.lookup_object('configfile')
|
|
||||||
configfile.remove_section('bed_mesh ' + prof_name)
|
|
||||||
del self.profiles[prof_name]
|
|
||||||
self.gcode.respond_info(
|
|
||||||
"Profile [%s] removed from storage for this session.\n"
|
|
||||||
"The SAVE_CONFIG command will update the printer\n"
|
|
||||||
"configuration and restart the printer" % (prof_name))
|
|
||||||
else:
|
|
||||||
self.gcode.respond_info(
|
|
||||||
"No profile named [%s] to remove" % (prof_name))
|
|
||||||
cmd_BED_MESH_PROFILE_help = "Bed Mesh Persistent Storage management"
|
|
||||||
def cmd_BED_MESH_PROFILE(self, gcmd):
|
|
||||||
options = collections.OrderedDict({
|
|
||||||
'LOAD': self.load_profile,
|
|
||||||
'SAVE': self.save_profile,
|
|
||||||
'REMOVE': self.remove_profile
|
|
||||||
})
|
|
||||||
for key in options:
|
|
||||||
name = gcmd.get(key, None)
|
|
||||||
if name is not None:
|
|
||||||
if name == "default" and key == 'SAVE':
|
|
||||||
gcmd.respond_info(
|
|
||||||
"Profile 'default' is reserved, please choose"
|
|
||||||
" another profile name.")
|
|
||||||
else:
|
|
||||||
options[key](name)
|
|
||||||
return
|
|
||||||
gcmd.respond_info("Invalid syntax '%s'" % (gcmd.get_commandline(),))
|
|
||||||
cmd_BED_MESH_CALIBRATE_help = "Perform Mesh Bed Leveling"
|
cmd_BED_MESH_CALIBRATE_help = "Perform Mesh Bed Leveling"
|
||||||
def cmd_BED_MESH_CALIBRATE(self, gcmd):
|
def cmd_BED_MESH_CALIBRATE(self, gcmd):
|
||||||
self.bedmesh.set_mesh(None)
|
self.bedmesh.set_mesh(None)
|
||||||
|
@ -561,15 +439,14 @@ class BedMeshCalibrate:
|
||||||
"Probed table length: %d Probed Table:\n%s") %
|
"Probed table length: %d Probed Table:\n%s") %
|
||||||
(len(probed_matrix), str(probed_matrix)))
|
(len(probed_matrix), str(probed_matrix)))
|
||||||
|
|
||||||
self.z_mesh = ZMesh(params)
|
z_mesh = ZMesh(params)
|
||||||
try:
|
try:
|
||||||
self.z_mesh.build_mesh(probed_matrix)
|
z_mesh.build_mesh(probed_matrix)
|
||||||
except BedMeshError as e:
|
except BedMeshError as e:
|
||||||
raise self.gcode.error(e.message)
|
raise self.gcode.error(e.message)
|
||||||
self.current_profile = "default"
|
self.bedmesh.set_mesh(z_mesh)
|
||||||
self.bedmesh.set_mesh(self.z_mesh)
|
|
||||||
self.gcode.respond_info("Mesh Bed Leveling Complete")
|
self.gcode.respond_info("Mesh Bed Leveling Complete")
|
||||||
self.save_profile("default")
|
self.bedmesh.save_profile("default")
|
||||||
|
|
||||||
|
|
||||||
class MoveSplitter:
|
class MoveSplitter:
|
||||||
|
@ -923,5 +800,141 @@ class ZMesh:
|
||||||
return a + b + c + d
|
return a + b + c + d
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileManager:
|
||||||
|
def __init__(self, config, bedmesh):
|
||||||
|
self.name = config.get_name()
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
|
self.bedmesh = bedmesh
|
||||||
|
self.profiles = {}
|
||||||
|
self.current_profile = ""
|
||||||
|
self.incompatible_profiles = []
|
||||||
|
# Fetch stored profiles from Config
|
||||||
|
stored_profs = config.get_prefix_sections(self.name)
|
||||||
|
stored_profs = [s for s in stored_profs
|
||||||
|
if s.get_name() != self.name]
|
||||||
|
for profile in stored_profs:
|
||||||
|
name = profile.get_name().split(' ', 1)[1]
|
||||||
|
version = profile.getint('version', 0)
|
||||||
|
if version != PROFILE_VERSION:
|
||||||
|
logging.info(
|
||||||
|
"bed_mesh: Profile [%s] not compatible with this version\n"
|
||||||
|
"of bed_mesh. Profile Version: %d Current Version: %d "
|
||||||
|
% (name, version, PROFILE_VERSION))
|
||||||
|
self.incompatible_profiles.append(name)
|
||||||
|
continue
|
||||||
|
self.profiles[name] = {}
|
||||||
|
z_values = profile.get('points').split('\n')
|
||||||
|
self.profiles[name]['points'] = \
|
||||||
|
[[float(pt.strip()) for pt in line.split(',')]
|
||||||
|
for line in z_values if line.strip()]
|
||||||
|
self.profiles[name]['mesh_params'] = params = \
|
||||||
|
collections.OrderedDict()
|
||||||
|
for key, t in PROFILE_OPTIONS.items():
|
||||||
|
if t is int:
|
||||||
|
params[key] = profile.getint(key)
|
||||||
|
elif t is float:
|
||||||
|
params[key] = profile.getfloat(key)
|
||||||
|
elif t is str:
|
||||||
|
params[key] = profile.get(key)
|
||||||
|
# Register GCode
|
||||||
|
self.gcode.register_command(
|
||||||
|
'BED_MESH_PROFILE', self.cmd_BED_MESH_PROFILE,
|
||||||
|
desc=self.cmd_BED_MESH_PROFILE_help)
|
||||||
|
def initialize(self):
|
||||||
|
self._check_incompatible_profiles()
|
||||||
|
if "default" in self.profiles:
|
||||||
|
self.load_profile("default")
|
||||||
|
def get_current_profile(self):
|
||||||
|
return self.current_profile
|
||||||
|
def _check_incompatible_profiles(self):
|
||||||
|
if self.incompatible_profiles:
|
||||||
|
configfile = self.printer.lookup_object('configfile')
|
||||||
|
for profile in self.incompatible_profiles:
|
||||||
|
configfile.remove_section('bed_mesh ' + profile)
|
||||||
|
self.gcode.respond_info(
|
||||||
|
"The following incompatible profiles have been detected\n"
|
||||||
|
"and are scheduled for removal:\n%s\n"
|
||||||
|
"The SAVE_CONFIG command will update the printer config\n"
|
||||||
|
"file and restart the printer" %
|
||||||
|
(('\n').join(self.incompatible_profiles)))
|
||||||
|
def save_profile(self, prof_name):
|
||||||
|
z_mesh = self.bedmesh.get_mesh()
|
||||||
|
if z_mesh is None:
|
||||||
|
self.gcode.respond_info(
|
||||||
|
"Unable to save to profile [%s], the bed has not been probed"
|
||||||
|
% (prof_name))
|
||||||
|
return
|
||||||
|
probed_matrix = z_mesh.get_probed_matrix()
|
||||||
|
mesh_params = z_mesh.get_mesh_params()
|
||||||
|
configfile = self.printer.lookup_object('configfile')
|
||||||
|
cfg_name = self.name + " " + prof_name
|
||||||
|
# set params
|
||||||
|
z_values = ""
|
||||||
|
for line in probed_matrix:
|
||||||
|
z_values += "\n "
|
||||||
|
for p in line:
|
||||||
|
z_values += "%.6f, " % p
|
||||||
|
z_values = z_values[:-2]
|
||||||
|
configfile.set(cfg_name, 'version', PROFILE_VERSION)
|
||||||
|
configfile.set(cfg_name, 'points', z_values)
|
||||||
|
for key, value in mesh_params.items():
|
||||||
|
configfile.set(cfg_name, key, value)
|
||||||
|
# save copy in local storage
|
||||||
|
self.profiles[prof_name] = profile = {}
|
||||||
|
profile['points'] = probed_matrix
|
||||||
|
profile['mesh_params'] = collections.OrderedDict(mesh_params)
|
||||||
|
self.current_profile = prof_name
|
||||||
|
self.gcode.respond_info(
|
||||||
|
"Bed Mesh state has been saved to profile [%s]\n"
|
||||||
|
"for the current session. The SAVE_CONFIG command will\n"
|
||||||
|
"update the printer config file and restart the printer."
|
||||||
|
% (prof_name))
|
||||||
|
def load_profile(self, prof_name):
|
||||||
|
profile = self.profiles.get(prof_name, None)
|
||||||
|
if profile is None:
|
||||||
|
raise self.gcode.error(
|
||||||
|
"bed_mesh: Unknown profile [%s]" % prof_name)
|
||||||
|
probed_matrix = profile['points']
|
||||||
|
mesh_params = profile['mesh_params']
|
||||||
|
z_mesh = ZMesh(mesh_params)
|
||||||
|
try:
|
||||||
|
z_mesh.build_mesh(probed_matrix)
|
||||||
|
except BedMeshError as e:
|
||||||
|
raise self.gcode.error(e.message)
|
||||||
|
self.current_profile = prof_name
|
||||||
|
self.bedmesh.set_mesh(z_mesh)
|
||||||
|
def remove_profile(self, prof_name):
|
||||||
|
if prof_name in self.profiles:
|
||||||
|
configfile = self.printer.lookup_object('configfile')
|
||||||
|
configfile.remove_section('bed_mesh ' + prof_name)
|
||||||
|
del self.profiles[prof_name]
|
||||||
|
self.gcode.respond_info(
|
||||||
|
"Profile [%s] removed from storage for this session.\n"
|
||||||
|
"The SAVE_CONFIG command will update the printer\n"
|
||||||
|
"configuration and restart the printer" % (prof_name))
|
||||||
|
else:
|
||||||
|
self.gcode.respond_info(
|
||||||
|
"No profile named [%s] to remove" % (prof_name))
|
||||||
|
cmd_BED_MESH_PROFILE_help = "Bed Mesh Persistent Storage management"
|
||||||
|
def cmd_BED_MESH_PROFILE(self, gcmd):
|
||||||
|
options = collections.OrderedDict({
|
||||||
|
'LOAD': self.load_profile,
|
||||||
|
'SAVE': self.save_profile,
|
||||||
|
'REMOVE': self.remove_profile
|
||||||
|
})
|
||||||
|
for key in options:
|
||||||
|
name = gcmd.get(key, None)
|
||||||
|
if name is not None:
|
||||||
|
if name == "default" and key == 'SAVE':
|
||||||
|
gcmd.respond_info(
|
||||||
|
"Profile 'default' is reserved, please choose"
|
||||||
|
" another profile name.")
|
||||||
|
else:
|
||||||
|
options[key](name)
|
||||||
|
return
|
||||||
|
gcmd.respond_info("Invalid syntax '%s'" % (gcmd.get_commandline(),))
|
||||||
|
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return BedMesh(config)
|
return BedMesh(config)
|
||||||
|
|
Loading…
Reference in New Issue