From 8e2aaae7ece0e0d3a2d51ebd31f6d7efa669a961 Mon Sep 17 00:00:00 2001 From: Nicolas Predella Date: Tue, 14 Jul 2026 21:21:09 +0300 Subject: [PATCH] printer list ref --- core/nodeslicer/nodes/system.py | 113 ++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 21 deletions(-) diff --git a/core/nodeslicer/nodes/system.py b/core/nodeslicer/nodes/system.py index e4526c3..95db19e 100644 --- a/core/nodeslicer/nodes/system.py +++ b/core/nodeslicer/nodes/system.py @@ -14,8 +14,9 @@ from ....registry import register_class from .base import ExecSliceNode, SliceNode, SliceNodeJoiner, SlicingCollection, SlicingGroup @register_class -class SLICENODE_PrinterList(SliceNode): +class SLICENODE_PrinterList(ExecSliceNode): bl_label = "Printer List" + button_label = "Start Prints" def _refresh(self): import json @@ -38,22 +39,18 @@ class SLICENODE_PrinterList(SliceNode): for p_data in printers: name = p_data.get("name", "Unknown") printer_names.add(name) - sock = self.outputs.get(name) + sock = self.inputs.get(name) if not sock: - sock = self.add_output("SLICESOCK_Printer", name) + self.add_input("SLICESOCK_GcodeRo", name) - # Update properties - sock.label = name - sock.host = str(p_data.get("ip", "localhost")) - sock.port = int(p_data.get("port", 80)) - sock.prefix = str(p_data.get("prefix", "")) - sock.username = str(p_data.get("username", "")) - sock.password = str(p_data.get("password", "")) - sock.host_type = str(p_data.get("host_type", "PRUSALINK")).upper() - - # Remove stale sockets - for sock in list(self.outputs): + # Remove stale input sockets + for sock in list(self.inputs): if sock.name not in printer_names: + self.inputs.remove(sock) + + # Remove stale output sockets (except Execution Link) + for sock in list(self.outputs): + if sock.name != "Execution Link": self.outputs.remove(sock) def update_config(self, context): @@ -62,17 +59,91 @@ class SLICENODE_PrinterList(SliceNode): printers_json_path: NfStringProperty(name="Config File", default="", subtype='FILE_PATH', update=update_config) # pyright: ignore[reportInvalidTypeForm] def init(self, context): - pass + self._init_exec() def draw_buttons(self, context, layout): - layout.prop(self, "printers_json_path") # pyright: ignore[reportOptionalMemberAccess] + super().draw_buttons(context, layout) + layout.prop(self, "printers_json_path") - def evaluate_output(self, socket_name: str, **_kwargs) -> Any: + def evaluate(self, operator=None): + import json + from ....infra.networking.printer_backends import ( + PrusaLinkBackend, + CrealityBackend, + MoonrakerBackend + ) + self._refresh() - sock = self.outputs.get(socket_name) - if sock: - return sock.value - return None + + if not self.printers_json_path: + if operator: operator.report({'ERROR'}, "No Config File path provided") + return 'error' + + abs_path = bpy.path.abspath(self.printers_json_path) + if not os.path.exists(abs_path): + if operator: operator.report({'ERROR'}, f"Config file not found: {abs_path}") + return 'error' + + try: + with open(abs_path, 'r', encoding='utf-8') as f: + data = json.load(f) + except Exception as e: + if operator: operator.report({'ERROR'}, f"Failed to read config file: {str(e)}") + return 'error' + + printers = data.get("printers", []) + printer_configs = {p.get("name", "Unknown"): p for p in printers} + + any_printed = False + for sock in self.inputs: + if sock.name not in printer_configs: + continue + + gcode = self.resolve(sock.name) + if not gcode: + continue + + p_data = printer_configs[sock.name] + host = str(p_data.get("ip", "localhost")) + port = int(p_data.get("port", 80)) + prefix = str(p_data.get("prefix", "")) + password = str(p_data.get("password", "")) + host_type = str(p_data.get("host_type", "PRUSALINK")).upper() + + # Instantiate backend + backend = None + if host_type == 'PRUSALINK': + backend = PrusaLinkBackend(host, port, prefix, api_key=password) + elif host_type == 'CREALITY': + backend = CrealityBackend(host, port, prefix) + elif host_type == 'MOONRAKER': + backend = MoonrakerBackend(host, port, prefix) + + if not backend: + if operator: operator.report({'ERROR'}, f"Unsupported host type {host_type} for printer {sock.name}") + continue + + # Write to temp file to send + with tempfile.NamedTemporaryFile(mode='w', suffix='.gcode', delete=False) as tf: + tf.write(gcode) + temp_path = Path(tf.name) + + try: + print_name = f"NodeSlicer_{sock.name}.gcode".replace(" ", "_") + backend.start_print(temp_path, print_name) + if operator: operator.report({'INFO'}, f"Print started on {sock.name}") + any_printed = True + except Exception as e: + if operator: operator.report({'ERROR'}, f"Failed to start print on {sock.name}: {str(e)}") + finally: + if temp_path.exists(): + temp_path.unlink() + + if not any_printed: + if operator: operator.report({'WARNING'}, "No Gcode was sent to any printer") + return 'no gcode' + + return 'ok' @register_class class SLICENODE_PrusaslicerSlice(SliceNode):