# backend/serializer.py import pcbnew import traceback class Serializer: @staticmethod def serialize(item): """ Returns (sexpr_string, error_message). """ if not item: return None, "Item is None" last_error = "" # Method 1: PCB_IO_KICAD_SEXPR (Robust, KiCad 6+) try: sf = pcbnew.STRING_FORMATTER() plugin = pcbnew.PCB_IO_KICAD_SEXPR() plugin.SetOutputFormatter(sf) plugin.Format(item) res = sf.GetString() if res and len(res) > 0: return res, None else: last_error = "Format returned empty string" except Exception as e: last_error = str(e) # Method 2: Fallback to direct Serialize try: sf = pcbnew.STRING_FORMATTER() if hasattr(item, 'Serialize'): item.Serialize(sf) res = sf.GetString() if res and len(res) > 0: return res, None except Exception as e: last_error = f"{last_error} | Fallback: {str(e)}" return None, last_error @staticmethod def deserialize(data, board): """ Returns (item, error_message). """ if not data: return None, "No data provided" cls_name = data.get('class') sexpr = data.get('sexpr') if not cls_name or not sexpr: return None, "Missing class or sexpr" try: if not hasattr(pcbnew, cls_name): return None, f"Class {cls_name} not found in pcbnew" cls = getattr(pcbnew, cls_name) item = None # Instantiate item try: item = cls(board) except: try: item = cls() except Exception as e: return None, f"Constructor failed for {cls_name}: {e}" # Ensure sexpr is a string if not isinstance(sexpr, str): sexpr = str(sexpr) # DEBUG LOGGING print(f"[DEBUG] Deserializing {cls_name}") print(f"[DEBUG] Sexpr (first 50 chars): {sexpr[:50]}") lr = None try: # Primary method: KiCad 9+ lr = pcbnew.STRING_LINE_READER(sexpr, "restore") except TypeError: # Fallback: Older KiCad versions (1 arg) try: lr = pcbnew.STRING_LINE_READER(sexpr) except Exception as e: return None, f"STRING_LINE_READER init failed (Fallback): {e}" except Exception as e: return None, f"STRING_LINE_READER init failed: {e}" if lr is None: return None, "STRING_LINE_READER is None" print(f"[DEBUG] LineReader created: {lr}") try: item.Deserialize(lr) except Exception as e: print(f"[DEBUG] Deserialize Exception: {e}") # Check for the specific Protobuf overload mismatch if "google::protobuf::Any" in str(e): print("[DEBUG] Protobuf overload mismatch detected. Attempting PCB_IO_KICAD_SEXPR Parse fallback...") try: # Hail Mary: Try using the IO plugin directly to parse the string # This bypasses the ambiguous Deserialize overload on the item itself plugin = pcbnew.PCB_IO_KICAD_SEXPR() if hasattr(plugin, 'Parse'): # Parse usually returns a new item parsed_item = plugin.Parse(sexpr) if parsed_item: print("[DEBUG] Fallback Parse successful!") return parsed_item, None except Exception as ex: print(f"[DEBUG] Fallback Parse failed: {ex}") # If fallback didn't work or wasn't applicable, re-raise raise e return item, None except Exception as e: return None, f"Deserialization error for {cls_name}: {e}"