From aae29ba48b2b15594f3b39ca0d6a5df9263dff0a Mon Sep 17 00:00:00 2001
From: Kevin O'Connor <kevin@koconnor.net>
Date: Thu, 19 Dec 2024 11:45:13 -0500
Subject: [PATCH] heaters: Disable heater if it appears main thread has stopped
 updating

Update the heating code to periodically check that the main thread is
operating properly.  This is a mitigation for some rare cases where
the main thread may lockup while the background heater updating code
continues to run.  The goal is to detect these rare failures and
disable future heater updates.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
---
 klippy/extras/heaters.py | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py
index 7cb663a4a..145435427 100644
--- a/klippy/extras/heaters.py
+++ b/klippy/extras/heaters.py
@@ -14,6 +14,7 @@ KELVIN_TO_CELSIUS = -273.15
 MAX_HEAT_TIME = 5.0
 AMBIENT_TEMP = 25.
 PID_PARAM_BASE = 255.
+MAX_MAINTHREAD_TIME = 5.0
 
 class Heater:
     def __init__(self, config, sensor):
@@ -37,7 +38,7 @@ class Heater:
         self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
         self.smooth_time = config.getfloat('smooth_time', 1., above=0.)
         self.inv_smooth_time = 1. / self.smooth_time
-        self.is_shutdown = False
+        self.verify_mainthread_time = -999.
         self.lock = threading.Lock()
         self.last_temp = self.smoothed_temp = self.target_temp = 0.
         self.last_temp_time = 0.
@@ -66,7 +67,7 @@ class Heater:
         self.printer.register_event_handler("klippy:shutdown",
                                             self._handle_shutdown)
     def set_pwm(self, read_time, value):
-        if self.target_temp <= 0. or self.is_shutdown:
+        if self.target_temp <= 0. or read_time > self.verify_mainthread_time:
             value = 0.
         if ((read_time < self.next_pwm_time or not self.last_pwm_value)
             and abs(value - self.last_pwm_value) < 0.05):
@@ -91,7 +92,7 @@ class Heater:
             self.can_extrude = (self.smoothed_temp >= self.min_extrude_temp)
         #logging.debug("temp: %.3f %f = %f", read_time, temp)
     def _handle_shutdown(self):
-        self.is_shutdown = True
+        self.verify_mainthread_time = -999.
     # External commands
     def get_name(self):
         return self.name
@@ -129,6 +130,9 @@ class Heater:
             target_temp = max(self.min_temp, min(self.max_temp, target_temp))
         self.target_temp = target_temp
     def stats(self, eventtime):
+        est_print_time = self.mcu_pwm.get_mcu().estimated_print_time(eventtime)
+        if not self.printer.is_shutdown():
+            self.verify_mainthread_time = est_print_time + MAX_MAINTHREAD_TIME
         with self.lock:
             target_temp = self.target_temp
             last_temp = self.last_temp