update klipper

This commit is contained in:
whb0514
2025-07-29 15:43:51 +08:00
parent 0293507b71
commit f96c748538
7 changed files with 176 additions and 7 deletions

View 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)

View File

@@ -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)

View File

@@ -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
###################################################################### ######################################################################

View File

@@ -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)

View File

@@ -21,6 +21,9 @@
# Load "AHT10" # Load "AHT10"
[aht10] [aht10]
# Load "AHT20_F"
[aht20_f]
# Load "LM75" sensor # Load "LM75" sensor
[lm75] [lm75]

View File

@@ -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:

View File

@@ -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
###################################################################### ######################################################################