2018-03-11 06:22:44 +01:00
|
|
|
# Heater/sensor verification code
|
|
|
|
#
|
|
|
|
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
|
|
|
#
|
|
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
import logging
|
|
|
|
|
2018-03-13 04:21:54 +01:00
|
|
|
HINT_THERMAL = """
|
|
|
|
See the 'verify_heater' section in config/example-extras.cfg
|
|
|
|
for the parameters that control this check.
|
|
|
|
"""
|
|
|
|
|
2018-03-11 06:22:44 +01:00
|
|
|
class HeaterCheck:
|
|
|
|
def __init__(self, config):
|
|
|
|
self.printer = config.get_printer()
|
2019-01-08 17:09:55 +01:00
|
|
|
self.printer.register_event_handler("klippy:connect",
|
|
|
|
self.handle_connect)
|
2019-01-08 15:15:40 +01:00
|
|
|
self.printer.register_event_handler("klippy:shutdown",
|
|
|
|
self.handle_shutdown)
|
2018-03-11 06:22:44 +01:00
|
|
|
self.heater_name = config.get_name().split()[1]
|
|
|
|
self.heater = None
|
2018-03-12 00:16:55 +01:00
|
|
|
self.hysteresis = config.getfloat('hysteresis', 5., minval=0.)
|
|
|
|
self.max_error = config.getfloat('max_error', 120., minval=0.)
|
2018-03-11 06:22:44 +01:00
|
|
|
self.heating_gain = config.getfloat('heating_gain', 2., above=0.)
|
|
|
|
default_gain_time = 20.
|
|
|
|
if self.heater_name == 'heater_bed':
|
|
|
|
default_gain_time = 60.
|
|
|
|
self.check_gain_time = config.getfloat(
|
|
|
|
'check_gain_time', default_gain_time, minval=1.)
|
2019-03-01 00:16:22 +01:00
|
|
|
self.approaching_target = self.starting_approach = False
|
2018-03-12 00:16:55 +01:00
|
|
|
self.last_target = self.goal_temp = self.error = 0.
|
2019-03-01 00:16:22 +01:00
|
|
|
self.goal_systime = self.printer.get_reactor().NEVER
|
2018-09-28 20:50:42 +02:00
|
|
|
self.check_timer = None
|
2019-01-08 17:09:55 +01:00
|
|
|
def handle_connect(self):
|
|
|
|
if self.printer.get_start_args().get('debugoutput') is not None:
|
|
|
|
# Disable verify_heater if outputting to a debug file
|
|
|
|
return
|
|
|
|
pheater = self.printer.lookup_object('heater')
|
|
|
|
self.heater = pheater.lookup_heater(self.heater_name)
|
|
|
|
logging.info("Starting heater checks for %s", self.heater_name)
|
|
|
|
reactor = self.printer.get_reactor()
|
|
|
|
self.check_timer = reactor.register_timer(self.check_event, reactor.NOW)
|
2019-01-08 15:15:40 +01:00
|
|
|
def handle_shutdown(self):
|
|
|
|
if self.check_timer is not None:
|
2018-09-28 20:50:42 +02:00
|
|
|
reactor = self.printer.get_reactor()
|
|
|
|
reactor.update_timer(self.check_timer, reactor.NEVER)
|
2018-03-11 06:22:44 +01:00
|
|
|
def check_event(self, eventtime):
|
|
|
|
temp, target = self.heater.get_temp(eventtime)
|
|
|
|
if temp >= target - self.hysteresis:
|
|
|
|
# Temperature near target - reset checks
|
2019-03-01 00:16:22 +01:00
|
|
|
if self.approaching_target and target:
|
2018-03-11 06:22:44 +01:00
|
|
|
logging.info("Heater %s within range of %.3f",
|
|
|
|
self.heater_name, target)
|
2019-03-01 00:16:22 +01:00
|
|
|
self.approaching_target = self.starting_approach = False
|
2019-02-28 19:40:57 +01:00
|
|
|
if temp <= target + self.hysteresis:
|
|
|
|
self.error = 0.
|
2019-03-01 00:16:22 +01:00
|
|
|
self.last_target = target
|
|
|
|
return eventtime + 1.
|
|
|
|
self.error += (target - self.hysteresis) - temp
|
|
|
|
if not self.approaching_target:
|
2018-03-11 06:22:44 +01:00
|
|
|
if target != self.last_target:
|
|
|
|
# Target changed - reset checks
|
|
|
|
logging.info("Heater %s approaching new target of %.3f",
|
|
|
|
self.heater_name, target)
|
2019-03-01 00:16:22 +01:00
|
|
|
self.approaching_target = self.starting_approach = True
|
2018-03-11 06:22:44 +01:00
|
|
|
self.goal_temp = temp + self.heating_gain
|
2019-03-01 00:16:22 +01:00
|
|
|
self.goal_systime = eventtime + self.check_gain_time
|
2018-03-12 00:16:55 +01:00
|
|
|
elif self.error >= self.max_error:
|
2018-03-11 06:22:44 +01:00
|
|
|
# Failure due to inability to maintain target temperature
|
|
|
|
return self.heater_fault()
|
|
|
|
elif temp >= self.goal_temp:
|
|
|
|
# Temperature approaching target - reset checks
|
2019-02-28 23:43:32 +01:00
|
|
|
self.starting_approach = False
|
2019-03-01 00:16:22 +01:00
|
|
|
self.error = 0.
|
2018-03-11 06:22:44 +01:00
|
|
|
self.goal_temp = temp + self.heating_gain
|
2019-03-01 00:16:22 +01:00
|
|
|
self.goal_systime = eventtime + self.check_gain_time
|
|
|
|
elif eventtime >= self.goal_systime:
|
|
|
|
# Temperature is no longer approaching target
|
|
|
|
self.approaching_target = False
|
|
|
|
logging.info("Heater %s no longer approaching target %.3f",
|
|
|
|
self.heater_name, target)
|
2019-02-28 23:43:32 +01:00
|
|
|
elif self.starting_approach:
|
|
|
|
self.goal_temp = min(self.goal_temp, temp + self.heating_gain)
|
2018-03-11 06:22:44 +01:00
|
|
|
self.last_target = target
|
|
|
|
return eventtime + 1.
|
|
|
|
def heater_fault(self):
|
2018-03-13 04:21:54 +01:00
|
|
|
msg = "Heater %s not heating at expected rate" % (self.heater_name,)
|
|
|
|
logging.error(msg)
|
|
|
|
self.printer.invoke_shutdown(msg + HINT_THERMAL)
|
2018-03-11 06:22:44 +01:00
|
|
|
return self.printer.get_reactor().NEVER
|
|
|
|
|
|
|
|
def load_config_prefix(config):
|
|
|
|
return HeaterCheck(config)
|