103 lines
2.5 KiB
Python
103 lines
2.5 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from pathlib import Path
|
|
import re
|
|
from typing import Optional, Tuple
|
|
|
|
from attrs import define
|
|
|
|
|
|
OPTION_PATTERN = re.compile("; (?P<key>[a-z0-9_]+) = (?P<value>.*?)\n")
|
|
|
|
|
|
def parse_prusa_config_str(content: str) -> dict:
|
|
kvs = {}
|
|
iter = re.finditer(OPTION_PATTERN, content)
|
|
while m := next(iter, None):
|
|
if m.group("key") == "prusaslicer_config" and m.group("value") == "begin":
|
|
break
|
|
while m := next(iter, None):
|
|
if m.group("key") == "prusaslicer_config" and m.group("value") == "end":
|
|
break
|
|
else:
|
|
kvs[m.group("key")] = m.group("value")
|
|
|
|
return kvs
|
|
|
|
|
|
def parse_prusa_config(p: Path):
|
|
with open(p) as fp:
|
|
return parse_prusa_config_str(fp.read())
|
|
|
|
|
|
@define
|
|
class GcodeAnalysis:
|
|
max_x: int
|
|
max_y: int
|
|
max_z: int
|
|
max_bed: int
|
|
max_end: int
|
|
filament: str
|
|
nozzle: int
|
|
|
|
|
|
def parse_point(point: str) -> Tuple[int, int]:
|
|
a, b = point.split("x")
|
|
return int(a), int(b)
|
|
|
|
|
|
def parse_bed_shape(coords: str) -> Tuple[int, int]:
|
|
# Note, these are clockwise from 0, 0
|
|
a, b, c, d = coords.split(",")
|
|
dx, _ = parse_point(a)
|
|
_, dy = parse_point(b)
|
|
x, y = parse_point(c)
|
|
return x + dx, y + dy
|
|
|
|
|
|
def analyze_gcode_str(text: str) -> Optional[GcodeAnalysis]:
|
|
opts = parse_prusa_config_str(text)
|
|
|
|
kwargs = {}
|
|
if "bed_shape" in opts:
|
|
max_x, max_y = parse_bed_shape(opts["bed_shape"])
|
|
kwargs["max_x"] = max_x
|
|
kwargs["max_y"] = max_y
|
|
else:
|
|
return None
|
|
|
|
if "max_print_height" in opts:
|
|
kwargs["max_z"] = int(opts["max_print_height"])
|
|
else:
|
|
return None
|
|
|
|
if "first_layer_bed_temperature" in opts:
|
|
kwargs["max_bed"] = int(opts["first_layer_bed_temperature"])
|
|
elif "bed_temperature" in opts:
|
|
kwargs["max_bed"] = int(opts["bed_temperature"])
|
|
else:
|
|
return None
|
|
|
|
if "first_layer_temperature" in opts:
|
|
kwargs["max_end"] = int(opts["first_layer_temperature"])
|
|
elif "temperature" in opts:
|
|
kwargs["max_end"] = int(opts["temperature"])
|
|
else:
|
|
return None
|
|
|
|
if "filament_type" in opts:
|
|
kwargs["filament"] = opts["filament_type"]
|
|
else:
|
|
return None
|
|
|
|
if "nozzle_diameter" in opts:
|
|
kwargs["nozzle"] = float(opts["nozzle_diameter"])
|
|
else:
|
|
return None
|
|
|
|
return GcodeAnalysis(**kwargs)
|
|
|
|
|
|
def analyze_gcode_file(p: Path) -> Optional[GcodeAnalysis]:
|
|
with open(p) as fp:
|
|
return analyze_gcode_str(fp.read())
|