Skip to content

Commit

Permalink
Check firmware version before homing (#112)
Browse files Browse the repository at this point in the history
* feat(model): save firmware version used for calibration on model

* feat(model): validate against current firmware version

* fix: move mcu get_status to identify handler

* fixup! feat(model): validate against current firmware version

* fix: remove leftover adxl stuff

This became visible after adding the type to scanner

* chore: add recalibration docs url
  • Loading branch information
Jomik authored Nov 27, 2024
1 parent c1d8327 commit d2d5739
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 41 deletions.
88 changes: 58 additions & 30 deletions scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@

from . import bed_mesh, manual_probe, probe, thermistor

DOCS_TOUCH_CALIBRATION = "https://docs.cartographer3d.com/cartographer-probe/installation-and-setup/installation/touch-based-calibration"
DOCS_SCAN_CALIBRATION = "https://docs.cartographer3d.com/cartographer-probe/installation-and-setup/installation/scan-based-calibration"

STREAM_BUFFER_LIMIT_DEFAULT = 100
STREAM_TIMEOUT = 2.0

Expand Down Expand Up @@ -248,14 +251,14 @@ def __init__(self, config: ConfigWrapper):

mainsync = self.printer.lookup_object("mcu")._clocksync
mcu = config.get("mcu", None)
if mcu is not None:
if mcu is None:
self._mcu: MCU = MCU(config, SecondarySync(self.reactor, mainsync))
self.printer.add_object("mcu " + self.name, self._mcu)
else:
if mcu == "mcu":
self._mcu = self.printer.lookup_object("mcu")
else:
self._mcu = self.printer.lookup_object("mcu " + mcu)
else:
self._mcu = MCU(config, SecondarySync(self.reactor, mainsync))
self.printer.add_object("mcu " + self.name, self._mcu)
self.cmd_queue = self._mcu.alloc_command_queue()
self.mcu_probe = ScannerEndstopWrapper(self)

Expand Down Expand Up @@ -1220,6 +1223,7 @@ def _handle_mcu_identify(self):

self.toolhead = self.printer.lookup_object("toolhead")
self.trapq = self.toolhead.get_trapq()
self.fw_version = self._mcu.get_status()["mcu_version"]
except msgproto.error as e:
raise msgproto.error(str(e))

Expand Down Expand Up @@ -1582,9 +1586,11 @@ def cb(sample):
min(z_offset),
max(z_offset),
self.calibration_method,
0.0,
self.fw_version,
)
self.models[self.model.name] = self.model
self.model.save(self)
self.model.save()
self._apply_threshold()

toolhead.get_last_move_time()
Expand Down Expand Up @@ -1620,10 +1626,10 @@ def _apply_threshold(self, moving_up=False):
untrigger_c = int(self.freq_to_count(self.untrigger_freq))
self.scanner_set_threshold.send([trigger_c, untrigger_c])

def _register_model(self, name, model):
def register_model(self, name: str, model: "ScannerModel"):
if name in self.models:
raise self.printer.config_error(
"Multiple Scanner models with same" "name '%s'" % (name,)
"Multiple Scanner models with same name '%s'" % (name,)
)
self.models[name] = model

Expand Down Expand Up @@ -2265,7 +2271,7 @@ def cmd_Z_OFFSET_APPLY_PROBE(self, gcmd: GCodeCommand):
)
else:
self.model.offset += offset
self.model.save(self, False)
self.model.save(False)
gcmd.respond_info(
f"Scanner model offset has been updated to {self.model.offset:.3f}.\n"
"You must run the SAVE_CONFIG command now to update the\n"
Expand Down Expand Up @@ -2315,19 +2321,36 @@ def __init__(
self.randomize = randomize


@final
class ScannerModel:
@classmethod
def load(cls, name, config, scanner):
_CONFIG_FW_VERSION = "model_fw_version"

@staticmethod
def load(name: str, config: ConfigWrapper, scanner: Scanner):
coef = config.getfloatlist("model_coef")
temp = config.getfloat("model_temp")
domain = config.getfloatlist("model_domain", count=2)
[min_z, max_z] = config.getfloatlist("model_range", count=2)
offset = config.getfloat("model_offset", 0.0)
mode = config.get("model_mode", "None")
fw_version = config.get(ScannerModel._CONFIG_FW_VERSION, "UNKNOWN")
poly = Polynomial(coef, domain)
return ScannerModel(name, scanner, poly, temp, min_z, max_z, mode, offset)
return ScannerModel(
name, scanner, poly, temp, min_z, max_z, mode, offset, fw_version
)

def __init__(self, name, scanner, poly, temp, min_z, max_z, mode, offset=0):
def __init__(
self,
name: str,
scanner: Scanner,
poly: Polynomial,
temp: float,
min_z: float,
max_z: float,
mode: str,
offset: float,
fw_version: str,
):
self.name = name
self.scanner = scanner
self.poly = poly
Expand All @@ -2336,24 +2359,38 @@ def __init__(self, name, scanner, poly, temp, min_z, max_z, mode, offset=0):
self.temp = temp
self.offset = offset
self.mode = mode
self.fw_version = fw_version

def save(self, scanner, show_message=True):
configfile = scanner.printer.lookup_object("configfile")
def save(self, show_message: bool = True):
configfile = self.scanner.printer.lookup_object("configfile")
section = "scanner model " + self.name
configfile.set(section, "model_coef", ",\n ".join(map(str, self.poly.coef)))
configfile.set(section, "model_domain", ",".join(map(str, self.poly.domain)))
configfile.set(section, "model_range", "%f,%f" % (self.min_z, self.max_z))
configfile.set(section, "model_temp", "%f" % (self.temp))
configfile.set(section, "model_offset", "%.5f" % (self.offset,))
configfile.set(section, "model_mode", "%s" % (self.scanner.calibration_method))
configfile.set(section, ScannerModel._CONFIG_FW_VERSION, self.fw_version)
if show_message:
scanner.gcode.respond_info(
self.scanner.gcode.respond_info(
"Scanner calibration for model '%s' has "
"been updated\nfor the current session. The SAVE_CONFIG "
"command will\nupdate the printer config file and restart "
"the printer." % (self.name,)
)

def validate(self) -> None:
cur_fw = self.scanner.fw_version
if cur_fw != self.fw_version:
url = DOCS_TOUCH_CALIBRATION
if self.mode == "scan":
url = DOCS_SCAN_CALIBRATION
raise self.scanner.printer.command_error(
f"Scanner model '{self.name}' was created with firmware version '{self.fw_version}', "
f"current firmware version is '{cur_fw}'. Please recalibrate the your threshold and model."
f" Click <a href='{url}'>HERE</a> for more information"
)

def freq_to_dist_raw(self, freq):
[begin, end] = self.poly.domain
invfreq = 1 / freq
Expand Down Expand Up @@ -2771,8 +2808,9 @@ def get_status(self, eventtime):
TRSYNC_SINGLE_MCU_TIMEOUT = 0.250


@final
class ScannerEndstopWrapper:
def __init__(self, scanner):
def __init__(self, scanner: Scanner):
self.scanner = scanner
self._mcu = scanner._mcu

Expand All @@ -2791,9 +2829,6 @@ def __init__(self, scanner):
printer.register_event_handler(
"homing:homing_move_begin", self._handle_homing_move_begin
)
printer.register_event_handler(
"homing:homing_move_end", self._handle_homing_move_end
)
self.z_homed = False
self.is_homing = False

Expand Down Expand Up @@ -2843,15 +2878,6 @@ def _handle_homing_move_begin(self, hmove):
self.scanner.trigger_method,
]
)
elif self.scanner.trigger_method == 2:
self.scanner.mcu_probe.probe_prepare(hmove)

def _handle_homing_move_end(self, hmove):
if (
self.scanner.mcu_probe in hmove.get_mcu_endstops()
and self.scanner.trigger_method == 2
):
self.scanner.mcu_probe.probe_finish(hmove)

def get_mcu(self):
return self._mcu
Expand Down Expand Up @@ -2882,6 +2908,8 @@ def get_steppers(self):
def home_start(
self, print_time, sample_time, sample_count, rest_time, triggered=True
):
if self.scanner.model is not None:
self.scanner.model.validate()
if self.scanner.model is None and self.scanner.trigger_method == 0:
raise self.scanner.printer.command_error("No Scanner model loaded")

Expand Down Expand Up @@ -3705,13 +3733,13 @@ def load_config(config: ConfigWrapper):
return scanner


def load_config_prefix(config):
def load_config_prefix(config: ConfigWrapper):
scanner = config.get_printer().lookup_object("scanner")
name = config.get_name()
if name.startswith("scanner model "):
name = name[14:]
model = ScannerModel.load(name, config, scanner)
scanner._register_model(name, model)
scanner.register_model(name, model)
return model
else:
raise config.error("Unknown scanner config directive '%s'" % (name[7:],))
10 changes: 9 additions & 1 deletion typings/clocksync.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
from typing import final
from reactor import Reactor

@final
class ClockSync:
pass

@final
class SecondarySync:
def __init__(self, _, __):
def __init__(self, reactor: Reactor, main_sync: ClockSync) -> None:
pass
12 changes: 12 additions & 0 deletions typings/configfile.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class sentinel:

@final
class ConfigWrapper:
error = configparser.Error
printer: Printer
def get_printer(self) -> Printer:
pass
Expand Down Expand Up @@ -166,3 +167,14 @@ class ConfigWrapper:
note_valid: bool = True,
) -> list[float] | None:
pass

@final
class PrinterConfig:
def get_printer(self) -> Printer:
pass
def deprecate(self, option: str, value: str | None = None) -> None:
pass
def set(self, section: str, option: str, value: object) -> None:
pass
def remove_section(self, section: str) -> None:
pass
26 changes: 19 additions & 7 deletions typings/klippy.pyi
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from typing import Callable, TypeVar, final
from typing import Callable, Literal, TypeVar, final, overload

from configfile import ConfigWrapper, sentinel
import configfile
import gcode
from configfile import ConfigWrapper, PrinterConfig, sentinel
from mcu import MCU
from reactor import Reactor

from scanner import Scanner

T = TypeVar("T")

@final
Expand All @@ -13,10 +16,6 @@ class Printer:
command_error = gcode.CommandError
def add_object(self, name: str, obj: object) -> None:
pass

def lookup_object(self, name: str, default: T | type[sentinel] = sentinel) -> T:
pass

def load_object(
self,
config: ConfigWrapper,
Expand All @@ -31,5 +30,18 @@ class Printer:
pass
def get_reactor(self) -> Reactor:
pass
def register_event_handler(self, event: str, callback: Callable[[], None]) -> None:
def register_event_handler(self, event: str, callback: Callable[..., None]) -> None:
pass

@overload
def lookup_object(self, name: Literal["configfile"]) -> PrinterConfig:
pass
@overload
def lookup_object(self, name: Literal["mcu"]) -> MCU:
pass
@overload
def lookup_object(self, name: Literal["scanner"]) -> Scanner:
pass
@overload
def lookup_object(self, name: str, default: T | type[sentinel] = sentinel) -> T:
pass
16 changes: 13 additions & 3 deletions typings/mcu.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from clocksync import SecondarySync
from typing import TypedDict
from clocksync import ClockSync, SecondarySync
from configfile import ConfigWrapper

class MCUStatus(TypedDict):
mcu_version: str

class MCU:
_mcu_freq = 42
def __init__(self, _, sync: SecondarySync):
_mcu_freq: float
_clocksync: ClockSync
def __init__(self, config: ConfigWrapper, sync: SecondarySync) -> None:
pass
def alloc_command_queue(self):
pass
Expand All @@ -24,6 +30,10 @@ class MCU:
pass
def get_printer(self):
pass
def get_status(self) -> MCUStatus:
pass
def is_fileoutput(self) -> bool:
pass

class MCU_trsync:
REASON_ENDSTOP_HIT = 1
Expand Down

0 comments on commit d2d5739

Please sign in to comment.