# backend/serializer.py import pcbnew 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}" # Deserialize with KiCad version compatibility # KiCad 9+ requires (data, source_name), older versions take (data) try: lr = pcbnew.STRING_LINE_READER(sexpr, "restore") except: try: lr = pcbnew.STRING_LINE_READER(sexpr) except Exception as e: return None, f"STRING_LINE_READER init failed: {e}" item.Deserialize(lr) return item, None except Exception as e: return None, f"Deserialization error for {cls_name}: {e}"