Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Qidi Plus 4 #6800

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion klippy/configfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ def __init__(self, printer):
gcode = self.printer.lookup_object('gcode')
gcode.register_command("SAVE_CONFIG", self.cmd_SAVE_CONFIG,
desc=self.cmd_SAVE_CONFIG_help)
gcode.register_command("SAVE_CONFIG_QD", self.cmd_SAVE_CONFIG_QD,
desc=self.cmd_SAVE_CONFIG_QD_help)
def _find_autosave_data(self, data):
regular_data = data
autosave_data = ""
Expand Down Expand Up @@ -397,16 +399,66 @@ def cmd_SAVE_CONFIG(self, gcmd):
try:
f = open(temp_name, 'w')
f.write(data)
f.flush()
f.close()
os.rename(cfgname, backup_name)
os.rename(temp_name, cfgname)
os.system("sync")
except:
msg = "Unable to write config file during SAVE_CONFIG"
logging.exception(msg)
raise gcmd.error(msg)
# Request a restart
gcode = self.printer.lookup_object('gcode')
gcode.request_restart('restart')
gcode.request_restart('firmware_restart')

cmd_SAVE_CONFIG_QD_help = "Overwrite config file and restart"
def cmd_SAVE_CONFIG_QD(self, gcmd):
if not self.autosave.fileconfig.sections():
return
gcode = self.printer.lookup_object('gcode')
# Create string containing autosave data
autosave_data = self._build_config_string(self.autosave)
lines = [('#*# ' + l).strip()
for l in autosave_data.split('\n')]
lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip())
lines.append("")
autosave_data = '\n'.join(lines)
# Read in and validate current config file
cfgname = self.printer.get_start_args()['config_file']
try:
data = self._read_config_file(cfgname)
regular_data, old_autosave_data = self._find_autosave_data(data)
config = self._build_config_wrapper(regular_data, cfgname)
except error as e:
msg = "Unable to parse existing config on SAVE_CONFIG"
logging.exception(msg)
raise gcode.error(msg)
regular_data = self._strip_duplicates(regular_data, self.autosave)
self._disallow_include_conflicts(regular_data, cfgname, gcode)
data = regular_data.rstrip() + autosave_data
# Determine filenames
datestr = time.strftime("-%Y%m%d_%H%M%S")
backup_name = cfgname + datestr
temp_name = cfgname + "_autosave"
if cfgname.endswith(".cfg"):
backup_name = cfgname[:-4] + datestr + ".cfg"
temp_name = cfgname[:-4] + "_autosave.cfg"
# Create new config file with temporary name and swap with main config
logging.info("SAVE_CONFIG to '%s' (backup in '%s')",
cfgname, backup_name)
try:
f = open(temp_name, 'w')
f.write(data)
f.flush()
f.close()
os.rename(cfgname, backup_name)
os.rename(temp_name, cfgname)
os.system("sync")
except:
msg = "Unable to write config file during SAVE_CONFIG"
logging.exception(msg)
raise gcode.error(msg)


######################################################################
Expand Down Expand Up @@ -518,6 +570,11 @@ def deprecate(self, section, option, value=None, msg=None):
self.deprecate_warnings.append(res)
self.status_warnings = self.runtime_warnings + self.deprecate_warnings
# Status reporting
def runtime_warning(self, msg):
logging.warning(msg)
res = {'type': 'runtime_warning', 'message': msg}
self.runtime_warnings.append(res)
self.status_warnings = self.runtime_warnings + self.deprecate_warnings
def _build_status_config(self, config):
self.status_raw_config = {}
for section in config.get_prefix_sections(''):
Expand Down
52 changes: 52 additions & 0 deletions klippy/extras/chamber_fan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from . import fan

PIN_MIN_TIME = 0.100

class ChamberFan:
def __init__(self, config):
self.printer = config.get_printer()
self.printer.register_event_handler("klippy:ready", self.handle_ready)
self.printer.register_event_handler("klippy:connect",
self.handle_connect)
self.printer.load_object(config, 'heaters')
self.heaters = []
self.fan = fan.Fan(config)
self.fan_speed = config.getfloat('fan_speed', default=1.,
minval=0., maxval=1.)
self.idle_speed = config.getfloat(
'idle_speed', default=self.fan_speed, minval=0., maxval=1.)
self.idle_timeout = config.getint("idle_timeout", default=30, minval=0)
self.heater_names = config.getlist("heater", ())
self.last_on = self.idle_timeout
self.last_speed = 0.
def handle_connect(self):
# Heater lookup
pheaters = self.printer.lookup_object('heaters')
self.heaters = [pheaters.lookup_heater(n) for n in self.heater_names]
def handle_ready(self):
reactor = self.printer.get_reactor()
reactor.register_timer(self.callback, reactor.monotonic()+PIN_MIN_TIME)
def get_status(self, eventtime):
return self.fan.get_status(eventtime)
def callback(self, eventtime):
speed = 0.
active = False
for heater in self.heaters:
_, target_temp = heater.get_temp(eventtime)
if target_temp:
active = True
if active:
self.last_on = 0
speed = self.fan_speed
elif self.last_on < self.idle_timeout:
speed = self.idle_speed
self.last_on += 1
if speed != self.last_speed:
self.last_speed = speed
curtime = self.printer.get_reactor().monotonic()
print_time = self.fan.get_mcu().estimated_print_time(curtime)
self.fan.set_speed(print_time + PIN_MIN_TIME, speed)
return eventtime + 1.

def load_config_prefix(config):
return ChamberFan(config)
15 changes: 15 additions & 0 deletions klippy/extras/gcode_macro_break.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class GCodeMacroBreaker:
def __init__(self, config):
# Gcode macro interupt
self.printer = config.get_printer()
webhooks = self.printer.lookup_object('webhooks')
webhooks.register_endpoint("breakmacro", self._handle_breakmacro)
webhooks.register_endpoint("resumemacro", self._handle_resumemacro)
self.gcode = self.printer.lookup_object('gcode')
def _handle_breakmacro(self, web_request):
self.gcode.break_flag = True
def _handle_resumemacro(self, web_request):
self.gcode.break_flag = False

def load_config(config):
return GCodeMacroBreaker(config)
24 changes: 24 additions & 0 deletions klippy/extras/gcode_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ def __init__(self, config):
self.saved_states = {}
self.move_transform = self.move_with_transform = None
self.position_with_transform = (lambda: [0., 0., 0., 0.])
# Save and load z offset
gcode.register_command(
'SAVE_ZOFFSET_TO_VARIABLE',
self.cmd_SAVE_ZOFFSET_TO_VARIABLE
)
gcode.register_command(
'LOAD_ZOFFSET_FROM_VARIABLE',
self.cmd_LOAD_ZOFFSET_FROM_VARIABLE
)
def _handle_ready(self):
self.is_printer_ready = True
if self.move_transform is None:
Expand Down Expand Up @@ -272,5 +281,20 @@ def cmd_GET_POSITION(self, gcmd):
% (mcu_pos, stepper_pos, kin_pos, toolhead_pos,
gcode_pos, base_pos, homing_pos))

def cmd_SAVE_ZOFFSET_TO_VARIABLE(self, gcmd):
variables = self.printer.lookup_object("save_variables")
gcode_move = self.printer.lookup_object("gcode_move")
variables.save_variable(
'Variables', 'z_offset',
gcode_move.homing_position[2]
)

def cmd_LOAD_ZOFFSET_FROM_VARIABLE(self, gcmd):
variables = self.printer.lookup_object("save_variables")
gcode_move = self.printer.lookup_object("gcode_move")
gcode_move.homing_position[2] = float(
variables.load_variable('Variables', 'z_offset')
)

def load_config(config):
return GCodeMove(config)
87 changes: 87 additions & 0 deletions klippy/extras/gcode_shell_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Run a shell command via gcode
#
# Copyright (C) 2019 Eric Callahan <arksine.code@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import os
import shlex
import subprocess
import logging

class ShellCommand:
def __init__(self, config):
self.name = config.get_name().split()[-1]
self.printer = config.get_printer()
self.gcode = self.printer.lookup_object('gcode')
cmd = config.get('command')
cmd = os.path.expanduser(cmd)
self.command = shlex.split(cmd)
self.timeout = config.getfloat('timeout', 2., above=0.)
self.verbose = config.getboolean('verbose', True)
self.proc_fd = None
self.partial_output = ""
self.gcode.register_mux_command(
"RUN_SHELL_COMMAND", "CMD", self.name,
self.cmd_RUN_SHELL_COMMAND,
desc=self.cmd_RUN_SHELL_COMMAND_help)

def _process_output(self, eventime):
if self.proc_fd is None:
return
try:
data = os.read(self.proc_fd, 4096)
except Exception:
pass
data = self.partial_output + data.decode()
if '\n' not in data:
self.partial_output = data
return
elif data[-1] != '\n':
split = data.rfind('\n') + 1
self.partial_output = data[split:]
data = data[:split]
else:
self.partial_output = ""
self.gcode.respond_info(data)

cmd_RUN_SHELL_COMMAND_help = "Run a linux shell command"
def cmd_RUN_SHELL_COMMAND(self, params):
gcode_params = params.get('PARAMS','')
gcode_params = shlex.split(gcode_params)
reactor = self.printer.get_reactor()
try:
proc = subprocess.Popen(
self.command + gcode_params, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except Exception:
logging.exception(
"shell_command: Command {%s} failed" % (self.name))
raise self.gcode.error("Error running command {%s}" % (self.name))
if self.verbose:
self.proc_fd = proc.stdout.fileno()
self.gcode.respond_info("Running Command {%s}...:" % (self.name))
hdl = reactor.register_fd(self.proc_fd, self._process_output)
eventtime = reactor.monotonic()
endtime = eventtime + self.timeout
complete = False
while eventtime < endtime:
eventtime = reactor.pause(eventtime + .05)
if proc.poll() is not None:
complete = True
break
if not complete:
proc.terminate()
if self.verbose:
if self.partial_output:
self.gcode.respond_info(self.partial_output)
self.partial_output = ""
if complete:
msg = "Command {%s} finished\n" % (self.name)
else:
msg = "Command {%s} timed out" % (self.name)
self.gcode.respond_info(msg)
reactor.unregister_fd(hdl)
self.proc_fd = None


def load_config_prefix(config):
return ShellCommand(config)
16 changes: 15 additions & 1 deletion klippy/extras/heaters.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@ def __init__(self, config):
gcode.register_command("M105", self.cmd_M105, when_not_ready=True)
gcode.register_command("TEMPERATURE_WAIT", self.cmd_TEMPERATURE_WAIT,
desc=self.cmd_TEMPERATURE_WAIT_help)
# Wait heater interupt
webhooks = self.printer.lookup_object('webhooks')
webhooks.register_endpoint("breakheater", self._handle_breakheater)
self.break_flag=False
def _handle_breakheater(self,web_request):
reactor = self.printer.get_reactor()
for heater in self.heaters.values():
eventtime = reactor.monotonic()
if heater.check_busy(eventtime):
self.break_flag = True
def load_config(self, config):
self.have_load_sensors = True
# Load default temperature sensors
Expand Down Expand Up @@ -345,7 +355,11 @@ def _wait_for_temperature(self, heater):
gcode = self.printer.lookup_object("gcode")
reactor = self.printer.get_reactor()
eventtime = reactor.monotonic()
self.break_flag = False
while not self.printer.is_shutdown() and heater.check_busy(eventtime):
if self.break_flag:
self.break_flag = False
break
print_time = toolhead.get_last_move_time()
gcode.respond_raw(self._get_temp(eventtime))
eventtime = reactor.pause(eventtime + 1.)
Expand Down Expand Up @@ -374,7 +388,7 @@ def cmd_TEMPERATURE_WAIT(self, gcmd):
toolhead = self.printer.lookup_object("toolhead")
reactor = self.printer.get_reactor()
eventtime = reactor.monotonic()
while not self.printer.is_shutdown():
while not self.printer.is_shutdown() and not self.break_flag:
temp, target = sensor.get_temp(eventtime)
if temp >= min_temp and temp <= max_temp:
return
Expand Down
4 changes: 2 additions & 2 deletions klippy/extras/homing.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ def probing_move(self, mcu_probe, pos, speed):
"Probing failed due to printer shutdown")
raise
if hmove.check_no_movement() is not None:
raise self.printer.command_error(
"Probe triggered prior to movement")
gcode = self.printer.lookup_object('gcode')
gcode.respond_info('Probe triggered prior to movement')
return epos
def cmd_G28(self, gcmd):
# Move to origin
Expand Down
Loading
Loading