logical_layers/backend/serializer.py

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}"