From 98b79f25836b1cfcbbd7add5b9807035788023cf Mon Sep 17 00:00:00 2001 From: Lasse Dalegaard Date: Wed, 26 Jan 2022 20:06:59 +0100 Subject: [PATCH] Better support Cura and note detected slicer in output Adds support for Cura 4.13, which changes the metadata format somewhat. Also adds a post processing script that can be used in Cura(tested on Linux with Cura 4.13). Install to `scripts` directory under Cura resources, e.g. `~/.local/share/cura/4.13/scripts/`. Finally, we now append a comment to `post-process` output with tool version and detected slicer. --- compat/CuraKlipperEstimator.py | 67 ++++++++++++++++++++++++++++++++++ lib/src/slicer.rs | 31 ++++++++++++++-- tool/src/cmd/post_process.rs | 17 +++++++++ 3 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 compat/CuraKlipperEstimator.py diff --git a/compat/CuraKlipperEstimator.py b/compat/CuraKlipperEstimator.py new file mode 100644 index 0000000..920e85e --- /dev/null +++ b/compat/CuraKlipperEstimator.py @@ -0,0 +1,67 @@ +# Copyright (c) 2022 Lasse Dalegaard +# MIT licensed + +from ..Script import Script +from tempfile import TemporaryDirectory +import subprocess +import shutil +import os + +class KlipperEstimator(Script): + """ + Runs klipper_estimator on the resulting gcode. + """ + + def getSettingDataString(self): + return """{ + "name": "Klipper estimator", + "key": "KlipperEstimator", + "metadata": {}, + "version": 2, + "settings": + { + "path": + { + "label": "Path to klipper_estimator", + "description": "The path to the klipper_estimator binary.", + "type": "str", + "default_value": "" + }, + "config_kind": + { + "label": "Kind of config to use(file or moonraker_url)", + "description": "", + "type": "str", + "default_value": "" + }, + "config_arg": + { + "label": "Config argument", + "description": "Path for file, URL for Moonraker", + "type": "str", + "default_value": "" + } + } + }""" + + def execute(self, data): + with TemporaryDirectory() as work_dir: + filename = os.path.join(work_dir, "work.gcode") + with open(filename, 'w') as work_file: + for line in data: + work_file.write(line + "\n") + + args = [ + self.getSettingValueByKey("path"), + "--config_" + self.getSettingValueByKey("config_kind"), + self.getSettingValueByKey("config_arg"), + "post-process", + filename, + ] + + ret = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if ret.returncode != 0: + raise RuntimeError("Failed to run klipper_estimator\n%s" % (ret.stdout,)) + + with open(filename) as work_file: + return work_file.readlines() diff --git a/lib/src/slicer.rs b/lib/src/slicer.rs index bf54480..aec5210 100644 --- a/lib/src/slicer.rs +++ b/lib/src/slicer.rs @@ -5,14 +5,29 @@ pub enum SlicerPreset { PrusaSlicer { version: String }, SuperSlicer { version: String }, IdeaMaker { version: String }, - Cura { version: String }, + Cura { version: Option }, +} + +impl std::fmt::Display for SlicerPreset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SlicerPreset::PrusaSlicer { version } => write!(f, "PrusaSlicer {}", version), + SlicerPreset::SuperSlicer { version } => write!(f, "SuperSlicer {}", version), + SlicerPreset::IdeaMaker { version } => write!(f, "ideaMaker {}", version), + SlicerPreset::Cura { version: None } => write!(f, "Cura"), + SlicerPreset::Cura { + version: Some(version), + } => write!(f, "Cura {}", version), + } + } } impl SlicerPreset { pub fn determine(comment: &str) -> Option { None.or_else(|| Self::try_slic3r(comment)) .or_else(|| Self::try_ideamaker(comment)) - .or_else(|| Self::try_cura(comment)) + .or_else(|| Self::try_cura_old(comment)) + .or_else(|| Self::try_cura_new(comment)) } fn try_slic3r(comment: &str) -> Option { @@ -42,12 +57,20 @@ impl SlicerPreset { }) } - fn try_cura(comment: &str) -> Option { + fn try_cura_old(comment: &str) -> Option { lazy_static! { static ref RE: Regex = Regex::new(r"Generated with Cura_SteamEngine\s(.*)").unwrap(); } RE.captures(comment).map(|c| SlicerPreset::Cura { - version: c.get(1).unwrap().as_str().into(), + version: Some(c.get(1).unwrap().as_str().into()), }) } + + fn try_cura_new(comment: &str) -> Option { + lazy_static! { + static ref RE: Regex = Regex::new(r"GENERATOR.NAME:Cura_SteamEngine").unwrap(); + } + RE.captures(comment) + .map(|_| SlicerPreset::Cura { version: None }) + } } diff --git a/tool/src/cmd/post_process.rs b/tool/src/cmd/post_process.rs index e367c40..58100df 100644 --- a/tool/src/cmd/post_process.rs +++ b/tool/src/cmd/post_process.rs @@ -239,6 +239,11 @@ impl GCodeInterceptor for CuraGCodeInterceptor { op: GCodeOperation::Nop, comment: Some(format!("TIME:{:.0}", result.total_time.ceil())), }); + } else if com.starts_with("PRINT.TIME:") { + return Some(GCodeCommand { + op: GCodeOperation::Nop, + comment: Some(format!("PRINT.TIME:{:.0}", result.total_time.ceil())), + }); } else if com.starts_with("TIME_ELAPSED:") { if let Some(next) = self.time_buffer.pop_front() { return Some(GCodeCommand { @@ -385,6 +390,18 @@ impl PostProcessCmd { } } + write!( + wr, + "; Processed by klipper_estimator {}, {}", + env!("VERGEN_GIT_SEMVER_LIGHTWEIGHT"), + if let Some(slicer) = state.result.slicer { + format!("detected slicer {}", slicer) + } else { + "no slicer detected".into() + } + ) + .expect("IO error"); + // Flush output file before renaming wr.flush().expect("IO error"); std::fs::rename(&dst_path, &self.filename).expect("rename failed");