diff --git a/core/nodeslicer/nodes.py b/core/nodeslicer/nodes.py index cf437a2..313d8f9 100644 --- a/core/nodeslicer/nodes.py +++ b/core/nodeslicer/nodes.py @@ -1115,111 +1115,103 @@ class ExecSliceNode_DatabaseSubmit(ExecSliceNode): def init(self, context): self._init_exec() - self.add_input("SLICESOCK_Gcode") - self.add_input("SLICESOCK_String", "Filename") + self.add_multi_input("SLICESOCK_Gcode", "Gcode") def draw_buttons(self, context, layout): super().draw_buttons(context, layout) layout.prop(self, "record_id") - def _prepare_submission(self, operator=None) -> tuple[str, str, dict[str, str]]: - import re - import json - gcode: str = resolve_input(self.inputs.get("Gcode")) - if not gcode: - if operator: operator.report({'ERROR'}, "No Gcode to send") - return '', '', {} - - filename: str = resolve_input(self.inputs.get("Filename")) or "output.gcode" - if not filename.endswith(".gcode"): - filename += ".gcode" - - # Parse Metadata from Gcode - fields = {} - part_name = filename.split(".")[0] - fields["Part ID / Name"] = part_name - - # 1. Time per print (decimal hours) - time_match = re.search(r"; estimated printing time \(normal mode\) = (.*)", gcode) - if time_match: - time_str = time_match.group(1) - h = re.search(r"(\d+)h", time_str) - m = re.search(r"(\d+)m", time_str) - s = re.search(r"(\d+)s", time_str) - total_hours = 0.0 - if h: total_hours += float(h.group(1)) - if m: total_hours += float(m.group(1)) / 60.0 - if s: total_hours += float(s.group(1)) / 3600.0 - fields["Time per print"] = round(total_hours, 2) - - # 2. Parts per print - parts_match = re.search(r"; objects_info = (.*)", gcode) - if parts_match: - try: - info = json.loads(parts_match.group(1)) - fields["Parts per print"] = len(info.get("objects", [])) - except Exception: pass - - # 3. Weight per print - weight_match = re.search(r"; total filament used \[g\] = ([\d\.]+)", gcode) - if weight_match: - fields["Weight per print"] = int(float(weight_match.group(1))) - - return gcode, filename, fields - def evaluate(self, operator=None): from ...infra.networking.bgcode import convert_to_bgcode + from ...infra.networking.gcode_utils import get_gcode_name_from_metadata import tempfile import os from pathlib import Path - gcode, filename, fields = self._prepare_submission(operator) - if not gcode: return 'error' + gcodes: str = self.resolve_multi_input("Gcode") - # Detect binary trigger in gcode comments - is_binary = "; binary_gcode = 1" in gcode - - name_part = filename.split('.')[0] - filename = name_part + ".bgcode" if is_binary else ".gcode" - - token_path = getattr(self, self._token_prop_name, "") - token = "" - if token_path: - abs_token_path = bpy.path.abspath(token_path) - if os.path.exists(abs_token_path): + for gc in gcodes: + import re + import json + + if not gc: + if operator: operator.report({'ERROR'}, "No Gcode to send") + continue + + # Parse Metadata from Gcode + fields = {} + part_name = get_gcode_name_from_metadata(gc) + fields["Part ID / Name"] = part_name + + # 1. Time per print (decimal hours) + time_match = re.search(r"; estimated printing time \(normal mode\) = (.*)", gc) + if time_match: + time_str = time_match.group(1) + h = re.search(r"(\d+)h", time_str) + m = re.search(r"(\d+)m", time_str) + s = re.search(r"(\d+)s", time_str) + total_hours = 0.0 + if h: total_hours += float(h.group(1)) + if m: total_hours += float(m.group(1)) / 60.0 + if s: total_hours += float(s.group(1)) / 3600.0 + fields["Time per print"] = round(total_hours, 2) + + # 2. Parts per print + parts_match = re.search(r"; objects_info = (.*)", gc) + if parts_match: try: - with open(abs_token_path, 'r', encoding='utf-8') as f: - token = f.read().strip() - except Exception as e: - if operator: operator.report({'ERROR'}, f"Failed to read token file: {str(e)}") - return 'error' + info = json.loads(parts_match.group(1)) + fields["Parts per print"] = len(info.get("objects", [])) + except Exception: pass - if not self._check_config(token): - if operator: operator.report({'ERROR'}, f"{self._db_name} configuration incomplete") - return 'incomplete config' + # 3. Weight per print + weight_match = re.search(r"; total filament used \[g\] = ([\d\.]+)", gc) + if weight_match: + fields["Weight per print"] = int(float(weight_match.group(1))) - temp_dir = tempfile.TemporaryDirectory() - temp_path = Path(temp_dir.name) / filename + # Detect binary trigger in gcode comments + is_binary = "; binary_gcode = 1" in gc + + filename = part_name + ".bgcode" if is_binary else ".gcode" + + token_path = getattr(self, self._token_prop_name, "") + token = "" + if token_path: + abs_token_path = bpy.path.abspath(token_path) + if os.path.exists(abs_token_path): + try: + with open(abs_token_path, 'r', encoding='utf-8') as f: + token = f.read().strip() + except Exception as e: + if operator: operator.report({'ERROR'}, f"Failed to read token file: {str(e)}") + return 'error' - try: - if is_binary: - content = convert_to_bgcode(gcode, name_part, True) - with open(temp_path, "wb") as f: - f.write(content) - else: - with open(temp_path, 'w', encoding='utf-8') as f: - f.write(gcode) + if not self._check_config(token): + if operator: operator.report({'ERROR'}, f"{self._db_name} configuration incomplete") + return 'incomplete config' - record_id = self._submit_to_db(token, fields, temp_path) - if operator: operator.report({'INFO'}, f"Submitted to {self._db_name} (Record: {record_id})") - return 'ok' - except Exception as e: - if operator: operator.report({'ERROR'}, f"{self._db_name} Error: {str(e)}") - return 'error' - finally: - if temp_path.exists(): - temp_path.unlink() - temp_dir.cleanup() + temp_dir = tempfile.TemporaryDirectory() + temp_path = Path(temp_dir.name) / filename + + try: + if is_binary: + content = convert_to_bgcode(gc, part_name, True) + with open(temp_path, "wb") as f: + f.write(content) + else: + with open(temp_path, 'w', encoding='utf-8') as f: + f.write(gc) + + record_id = self._submit_to_db(token, fields, temp_path) + if operator: operator.report({'INFO'}, f"Submitted to {self._db_name} (Record: {record_id})") + return 'ok' + except Exception as e: + if operator: operator.report({'ERROR'}, f"{self._db_name} Error: {str(e)}") + return 'error' + finally: + if temp_path.exists(): + temp_path.unlink() + temp_dir.cleanup() def _check_config(self, token: str) -> bool: return False