120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
# 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}" |