mirror of
https://github.com/QIDITECH/klipper.git
synced 2026-01-30 23:48:43 +03:00
update klipper
This commit is contained in:
110
klippy/extras/dynamaic_config_loader.py
Normal file
110
klippy/extras/dynamaic_config_loader.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# Dynamic configuration loader for Klipper
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023 Your Name <your.email@example.com>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import configparser
|
||||||
|
import importlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
class DynamicConfigLoader:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.config_path = config.get('config_path')
|
||||||
|
self.loaded_configs = {}
|
||||||
|
|
||||||
|
# 注册命令
|
||||||
|
gcode = self.printer.lookup_object('gcode')
|
||||||
|
gcode.register_command('LOAD_CONFIG', self.cmd_LOAD_CONFIG,
|
||||||
|
desc="动态加载配置文件")
|
||||||
|
gcode.register_command('UNLOAD_CONFIG', self.cmd_UNLOAD_CONFIG,
|
||||||
|
desc="卸载动态加载的配置")
|
||||||
|
gcode.register_command('LIST_LOADED_CONFIGS', self.cmd_LIST_LOADED_CONFIGS,
|
||||||
|
desc="列出所有已加载的动态配置")
|
||||||
|
|
||||||
|
# 在printer对象初始化完成后加载默认配置
|
||||||
|
self.printer.register_event_handler("klippy:connect", self.handle_connect)
|
||||||
|
|
||||||
|
def handle_connect(self):
|
||||||
|
if self.config_path and os.path.exists(self.config_path):
|
||||||
|
self._load_config_file(self.config_path)
|
||||||
|
|
||||||
|
def _load_config_file(self, config_path):
|
||||||
|
try:
|
||||||
|
config_parser = configparser.ConfigParser()
|
||||||
|
config_parser.read(config_path)
|
||||||
|
|
||||||
|
for section in config_parser.sections():
|
||||||
|
module_name = section.split()[0]
|
||||||
|
|
||||||
|
# 尝试导入模块
|
||||||
|
try:
|
||||||
|
module = importlib.import_module('extras.' + module_name)
|
||||||
|
if hasattr(module, 'load_config'):
|
||||||
|
# 创建配置对象
|
||||||
|
config_obj = self.printer.lookup_object('configfile').create_config_wrapper(
|
||||||
|
config_parser, section)
|
||||||
|
# 调用模块的load_config函数
|
||||||
|
obj = module.load_config(config_obj)
|
||||||
|
if obj is not None:
|
||||||
|
# 将对象添加到printer
|
||||||
|
self.printer.add_object(section, obj)
|
||||||
|
self.loaded_configs[section] = {
|
||||||
|
'module': module_name,
|
||||||
|
'object': obj
|
||||||
|
}
|
||||||
|
logging.info("动态加载配置: %s", section)
|
||||||
|
except ImportError:
|
||||||
|
logging.warning("无法导入模块 %s", module_name)
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("加载配置 %s 时出错: %s", section, str(e))
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("加载配置文件 %s 时出错: %s", config_path, str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def cmd_LOAD_CONFIG(self, gcmd):
|
||||||
|
config_path = gcmd.get('PATH')
|
||||||
|
if not os.path.exists(config_path):
|
||||||
|
raise gcmd.error("配置文件不存在: %s" % config_path)
|
||||||
|
|
||||||
|
success = self._load_config_file(config_path)
|
||||||
|
if success:
|
||||||
|
gcmd.respond_info("成功加载配置文件: %s" % config_path)
|
||||||
|
else:
|
||||||
|
raise gcmd.error("加载配置文件失败: %s" % config_path)
|
||||||
|
|
||||||
|
def cmd_UNLOAD_CONFIG(self, gcmd):
|
||||||
|
section = gcmd.get('SECTION')
|
||||||
|
if section not in self.loaded_configs:
|
||||||
|
raise gcmd.error("未找到已加载的配置: %s" % section)
|
||||||
|
|
||||||
|
# 从printer中移除对象
|
||||||
|
try:
|
||||||
|
# 某些对象可能需要特殊的清理逻辑
|
||||||
|
obj = self.loaded_configs[section]['object']
|
||||||
|
if hasattr(obj, 'shutdown'):
|
||||||
|
obj.shutdown()
|
||||||
|
|
||||||
|
# 从loaded_configs中移除
|
||||||
|
del self.loaded_configs[section]
|
||||||
|
gcmd.respond_info("成功卸载配置: %s" % section)
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("卸载配置 %s 时出错: %s", section, str(e))
|
||||||
|
raise gcmd.error("卸载配置失败: %s" % section)
|
||||||
|
|
||||||
|
def cmd_LIST_LOADED_CONFIGS(self, gcmd):
|
||||||
|
if not self.loaded_configs:
|
||||||
|
gcmd.respond_info("没有动态加载的配置")
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = "已加载的动态配置:\n"
|
||||||
|
for section, info in self.loaded_configs.items():
|
||||||
|
msg += " %s (模块: %s)\n" % (section, info['module'])
|
||||||
|
gcmd.respond_info(msg)
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return DynamicConfigLoader(config)
|
||||||
@@ -27,6 +27,7 @@ class EncoderSensor:
|
|||||||
self.estimated_print_time = None
|
self.estimated_print_time = None
|
||||||
# Initialise internal state
|
# Initialise internal state
|
||||||
self.filament_runout_pos = None
|
self.filament_runout_pos = None
|
||||||
|
self.button_state = 0
|
||||||
# Register commands and event handlers
|
# Register commands and event handlers
|
||||||
self.printer.register_event_handler('klippy:ready',
|
self.printer.register_event_handler('klippy:ready',
|
||||||
self._handle_ready)
|
self._handle_ready)
|
||||||
@@ -36,12 +37,19 @@ class EncoderSensor:
|
|||||||
self._handle_not_printing)
|
self._handle_not_printing)
|
||||||
self.printer.register_event_handler('idle_timeout:idle',
|
self.printer.register_event_handler('idle_timeout:idle',
|
||||||
self._handle_not_printing)
|
self._handle_not_printing)
|
||||||
|
self.gcode = self.printer.lookup_object('gcode')
|
||||||
|
self.gcode.register_command(
|
||||||
|
'CLEAR_MOTION_DATA',
|
||||||
|
self.cmd_CLEAR_MOTION_DATA
|
||||||
|
)
|
||||||
def _update_filament_runout_pos(self, eventtime=None):
|
def _update_filament_runout_pos(self, eventtime=None):
|
||||||
if eventtime is None:
|
if eventtime is None:
|
||||||
eventtime = self.reactor.monotonic()
|
eventtime = self.reactor.monotonic()
|
||||||
self.filament_runout_pos = (
|
self.filament_runout_pos = (
|
||||||
self._get_extruder_pos(eventtime) +
|
self._get_extruder_pos(eventtime) +
|
||||||
self.detection_length)
|
self.detection_length)
|
||||||
|
def cmd_CLEAR_MOTION_DATA(self, gcmd):
|
||||||
|
self._update_filament_runout_pos()
|
||||||
def _handle_ready(self):
|
def _handle_ready(self):
|
||||||
self.extruder = self.printer.lookup_object(self.extruder_name)
|
self.extruder = self.printer.lookup_object(self.extruder_name)
|
||||||
self.estimated_print_time = (
|
self.estimated_print_time = (
|
||||||
@@ -69,6 +77,7 @@ class EncoderSensor:
|
|||||||
def encoder_event(self, eventtime, state):
|
def encoder_event(self, eventtime, state):
|
||||||
if self.extruder is not None:
|
if self.extruder is not None:
|
||||||
self._update_filament_runout_pos(eventtime)
|
self._update_filament_runout_pos(eventtime)
|
||||||
|
self.button_state = state
|
||||||
# Check for filament insertion
|
# Check for filament insertion
|
||||||
# Filament is always assumed to be present on an encoder event
|
# Filament is always assumed to be present on an encoder event
|
||||||
self.runout_helper.note_filament_present(True)
|
self.runout_helper.note_filament_present(True)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import os, logging, threading
|
|||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
KELVIN_TO_CELSIUS = -273.15
|
KELVIN_TO_CELSIUS = -273.15
|
||||||
MAX_HEAT_TIME = 5.0
|
MAX_HEAT_TIME = 10.0
|
||||||
AMBIENT_TEMP = 25.
|
AMBIENT_TEMP = 25.
|
||||||
PID_PARAM_BASE = 255.
|
PID_PARAM_BASE = 255.
|
||||||
|
|
||||||
@@ -65,6 +65,7 @@ class Heater:
|
|||||||
gcode.register_mux_command("SET_HEATER_TEMPERATURE", "HEATER",
|
gcode.register_mux_command("SET_HEATER_TEMPERATURE", "HEATER",
|
||||||
self.name, self.cmd_SET_HEATER_TEMPERATURE,
|
self.name, self.cmd_SET_HEATER_TEMPERATURE,
|
||||||
desc=self.cmd_SET_HEATER_TEMPERATURE_help)
|
desc=self.cmd_SET_HEATER_TEMPERATURE_help)
|
||||||
|
self.can_wait = True
|
||||||
def set_pwm(self, read_time, value):
|
def set_pwm(self, read_time, value):
|
||||||
if self.target_temp <= 0.:
|
if self.target_temp <= 0.:
|
||||||
value = 0.
|
value = 0.
|
||||||
@@ -166,7 +167,12 @@ class Heater:
|
|||||||
msg = "The hotbed temperature is too low, and it will automatically heat up to " + str((temp+self.heat_with_heater_bed_tem_add))
|
msg = "The hotbed temperature is too low, and it will automatically heat up to " + str((temp+self.heat_with_heater_bed_tem_add))
|
||||||
gcmd.respond_info(msg)
|
gcmd.respond_info(msg)
|
||||||
else:
|
else:
|
||||||
pheaters.set_temperature(self, temp)
|
wait = bool(gcmd.get_int('WAIT', 0))
|
||||||
|
if not self.can_wait:
|
||||||
|
wait = False
|
||||||
|
pheaters.set_temperature(self, temp, wait)
|
||||||
|
def set_can_wait(self, value):
|
||||||
|
self.can_wait = value
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|||||||
@@ -11,6 +11,28 @@ class SaveVariables:
|
|||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.filename = os.path.expanduser(config.get('filename'))
|
self.filename = os.path.expanduser(config.get('filename'))
|
||||||
self.allVariables = {}
|
self.allVariables = {}
|
||||||
|
|
||||||
|
self.default_values = {
|
||||||
|
'auto_reload_detect': 0, 'auto_read_rfid': 0, 'auto_init_detect': 0,
|
||||||
|
'box_count': 0, 'enable_box': 0,'load_retry_num': 0,
|
||||||
|
'slot_sync': "", 'retry_step': "", 'last_load_slot': ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in range(16):
|
||||||
|
self.default_values[f'filament_slot{i}'] = 0
|
||||||
|
self.default_values[f'color_slot{i}'] = 0
|
||||||
|
self.default_values[f'vendor_slot{i}'] = 0
|
||||||
|
self.default_values[f'slot{i}'] = 0
|
||||||
|
self.default_values[f'value_t{i}'] = ""
|
||||||
|
self.default_values[f'runout_{i}'] = 0
|
||||||
|
|
||||||
|
self.default_values['filament_slot16'] = 0
|
||||||
|
self.default_values['color_slot16'] = 0
|
||||||
|
self.default_values['vendor_slot16'] = 0
|
||||||
|
|
||||||
|
for key, value in self.default_values.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(self.filename):
|
if not os.path.exists(self.filename):
|
||||||
open(self.filename, "w").close()
|
open(self.filename, "w").close()
|
||||||
@@ -20,6 +42,7 @@ class SaveVariables:
|
|||||||
gcode = self.printer.lookup_object('gcode')
|
gcode = self.printer.lookup_object('gcode')
|
||||||
gcode.register_command('SAVE_VARIABLE', self.cmd_SAVE_VARIABLE,
|
gcode.register_command('SAVE_VARIABLE', self.cmd_SAVE_VARIABLE,
|
||||||
desc=self.cmd_SAVE_VARIABLE_help)
|
desc=self.cmd_SAVE_VARIABLE_help)
|
||||||
|
|
||||||
def load_variable(self, section, option):
|
def load_variable(self, section, option):
|
||||||
varfile = configparser.ConfigParser()
|
varfile = configparser.ConfigParser()
|
||||||
try:
|
try:
|
||||||
@@ -29,6 +52,7 @@ class SaveVariables:
|
|||||||
msg = "Unable to parse existing variable file"
|
msg = "Unable to parse existing variable file"
|
||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise self.printer.command_error(msg)
|
raise self.printer.command_error(msg)
|
||||||
|
|
||||||
def save_variable(self, section, option, value):
|
def save_variable(self, section, option, value):
|
||||||
varfile = configparser.ConfigParser()
|
varfile = configparser.ConfigParser()
|
||||||
try:
|
try:
|
||||||
@@ -43,6 +67,7 @@ class SaveVariables:
|
|||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise self.printer.command_error(msg)
|
raise self.printer.command_error(msg)
|
||||||
self.loadVariables()
|
self.loadVariables()
|
||||||
|
|
||||||
def loadVariables(self):
|
def loadVariables(self):
|
||||||
allvars = {}
|
allvars = {}
|
||||||
varfile = configparser.ConfigParser()
|
varfile = configparser.ConfigParser()
|
||||||
@@ -56,7 +81,12 @@ class SaveVariables:
|
|||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise self.printer.command_error(msg)
|
raise self.printer.command_error(msg)
|
||||||
self.allVariables = allvars
|
self.allVariables = allvars
|
||||||
|
|
||||||
|
for key, default in self.default_values.items():
|
||||||
|
setattr(self, key, self.allVariables.get(key, default))
|
||||||
|
|
||||||
cmd_SAVE_VARIABLE_help = "Save arbitrary variables to disk"
|
cmd_SAVE_VARIABLE_help = "Save arbitrary variables to disk"
|
||||||
|
|
||||||
def cmd_SAVE_VARIABLE(self, gcmd):
|
def cmd_SAVE_VARIABLE(self, gcmd):
|
||||||
varname = gcmd.get('VARIABLE')
|
varname = gcmd.get('VARIABLE')
|
||||||
value = gcmd.get('VALUE')
|
value = gcmd.get('VALUE')
|
||||||
@@ -80,8 +110,14 @@ class SaveVariables:
|
|||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise gcmd.error(msg)
|
raise gcmd.error(msg)
|
||||||
self.loadVariables()
|
self.loadVariables()
|
||||||
|
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return {'variables': self.allVariables}
|
status = {'variables': self.allVariables}
|
||||||
|
|
||||||
|
for key in self.default_values.keys():
|
||||||
|
status[key] = getattr(self, key)
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
return SaveVariables(config)
|
return SaveVariables(config)
|
||||||
@@ -21,6 +21,9 @@
|
|||||||
# Load "AHT10"
|
# Load "AHT10"
|
||||||
[aht10]
|
[aht10]
|
||||||
|
|
||||||
|
# Load "AHT20_F"
|
||||||
|
[aht20_f]
|
||||||
|
|
||||||
# Load "LM75" sensor
|
# Load "LM75" sensor
|
||||||
[lm75]
|
[lm75]
|
||||||
|
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ class GCodeDispatch:
|
|||||||
not gcmd.get_float('S', 1.) or self.is_fileinput)):
|
not gcmd.get_float('S', 1.) or self.is_fileinput)):
|
||||||
# Don't warn about requests to turn off fan when fan not present
|
# Don't warn about requests to turn off fan when fan not present
|
||||||
return
|
return
|
||||||
gcmd.respond_info('Unknown command:"%s"' % (cmd,))
|
#gcmd.respond_info('Unknown command:"%s"' % (cmd,))
|
||||||
def _cmd_mux(self, command, gcmd):
|
def _cmd_mux(self, command, gcmd):
|
||||||
key, values = self.mux_commands[command]
|
key, values = self.mux_commands[command]
|
||||||
if None in values:
|
if None in values:
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
import sys, os, gc, optparse, logging, time, collections, importlib
|
import sys, os, gc, optparse, logging, time, collections, importlib
|
||||||
import util, reactor, queuelogger, msgproto
|
import util, reactor, queuelogger, msgproto
|
||||||
import gcode, configfile, pins, mcu, toolhead, webhooks
|
import gcode, configfile, pins, mcu, toolhead, webhooks
|
||||||
|
from extras import box_detect
|
||||||
|
import pyudev, shutil, configparser
|
||||||
|
|
||||||
message_ready = "Printer is ready"
|
message_ready = "Printer is ready"
|
||||||
|
|
||||||
@@ -112,9 +114,10 @@ class Printer:
|
|||||||
module_name = module_parts[0]
|
module_name = module_parts[0]
|
||||||
py_name = os.path.join(os.path.dirname(__file__),
|
py_name = os.path.join(os.path.dirname(__file__),
|
||||||
'extras', module_name + '.py')
|
'extras', module_name + '.py')
|
||||||
|
so_name = os.path.join(os.path.dirname(__file__), 'extras', module_name + '.so')
|
||||||
py_dirname = os.path.join(os.path.dirname(__file__),
|
py_dirname = os.path.join(os.path.dirname(__file__),
|
||||||
'extras', module_name, '__init__.py')
|
'extras', module_name, '__init__.py')
|
||||||
if not os.path.exists(py_name) and not os.path.exists(py_dirname):
|
if not os.path.exists(py_name) and not os.path.exists(so_name) and not os.path.exists(py_dirname):
|
||||||
if default is not configfile.sentinel:
|
if default is not configfile.sentinel:
|
||||||
return default
|
return default
|
||||||
raise self.config_error("Unable to load module '%s'" % (section,))
|
raise self.config_error("Unable to load module '%s'" % (section,))
|
||||||
@@ -139,6 +142,8 @@ class Printer:
|
|||||||
m.add_printer_objects(config)
|
m.add_printer_objects(config)
|
||||||
for section_config in config.get_prefix_sections(''):
|
for section_config in config.get_prefix_sections(''):
|
||||||
self.load_object(config, section_config.get_name(), None)
|
self.load_object(config, section_config.get_name(), None)
|
||||||
|
for m in [box_detect]:
|
||||||
|
m.add_printer_objects(config)
|
||||||
for m in [toolhead]:
|
for m in [toolhead]:
|
||||||
m.add_printer_objects(config)
|
m.add_printer_objects(config)
|
||||||
# Validate that there are no undefined parameters in the config file
|
# Validate that there are no undefined parameters in the config file
|
||||||
@@ -172,6 +177,7 @@ class Printer:
|
|||||||
return "\n".join(msg)
|
return "\n".join(msg)
|
||||||
def _connect(self, eventtime):
|
def _connect(self, eventtime):
|
||||||
try:
|
try:
|
||||||
|
box_detect.monitor_serial_devices(self)
|
||||||
self._read_config()
|
self._read_config()
|
||||||
self.send_event("klippy:mcu_identify")
|
self.send_event("klippy:mcu_identify")
|
||||||
for cb in self.event_handlers.get("klippy:connect", []):
|
for cb in self.event_handlers.get("klippy:connect", []):
|
||||||
@@ -266,7 +272,6 @@ class Printer:
|
|||||||
self.run_result = result
|
self.run_result = result
|
||||||
self.reactor.end()
|
self.reactor.end()
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Startup
|
# Startup
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|||||||
Reference in New Issue
Block a user