diff --git a/python/commands/component_schematic.py b/python/commands/component_schematic.py index 8ed621a..682b7c1 100644 --- a/python/commands/component_schematic.py +++ b/python/commands/component_schematic.py @@ -66,7 +66,8 @@ class ComponentManager: } @classmethod - def get_or_create_template(cls, schematic: Schematic, comp_type: str, library: Optional[str] = None) -> str: + def get_or_create_template(cls, schematic: Schematic, comp_type: str, library: Optional[str] = None, + schematic_path: Optional[Path] = None) -> str: """ Get template reference for a component type, creating it dynamically if needed @@ -74,6 +75,7 @@ class ComponentManager: schematic: Schematic object comp_type: Component type (e.g., 'R', 'LED', 'STM32F103C8Tx') library: Optional library name (defaults to 'Device' for common types) + schematic_path: Optional path to schematic file (required for dynamic loading) Returns: Template reference string (e.g., '_TEMPLATE_R' or '_TEMPLATE_Device_R') @@ -97,31 +99,42 @@ class ComponentManager: logger.warning("Dynamic loader unavailable, using fallback template") return '_TEMPLATE_R' + # Check if schematic path is available + if schematic_path is None: + logger.warning("Dynamic loading requires schematic file path but none was provided") + return cls.TEMPLATE_MAP.get(comp_type, '_TEMPLATE_R') + # Determine library name if library is None: # Default library for common component types library = 'Device' # Most passives and basic components are in Device library try: - # Get schematic file path - # kicad-skip doesn't expose the file path directly, so we need to work around this - # For now, we'll need the caller to pass the schematic path - # TODO: Store schematic path in Schematic object or pass it separately + logger.info(f"Attempting dynamic load: {library}:{comp_type} from {schematic_path}") - logger.info(f"Attempting dynamic load: {library}:{comp_type}") + # Use dynamic symbol loader to inject symbol and create template + template_ref = loader.load_symbol_dynamically(schematic_path, library, comp_type) - # This is a limitation - we need the schematic file path - # For now, return a fallback - logger.warning("Dynamic loading requires schematic file path - feature not fully integrated yet") - return cls.TEMPLATE_MAP.get(comp_type, '_TEMPLATE_R') + logger.info(f"Successfully loaded symbol dynamically. Template ref: {template_ref}") + return template_ref except Exception as e: logger.error(f"Dynamic loading failed: {e}") + import traceback + logger.error(traceback.format_exc()) + # Fall back to static template if available return cls.TEMPLATE_MAP.get(comp_type, '_TEMPLATE_R') @staticmethod - def add_component(schematic: Schematic, component_def: dict): - """Add a component to the schematic by cloning from template""" + def add_component(schematic: Schematic, component_def: dict, schematic_path: Optional[Path] = None): + """ + Add a component to the schematic by cloning from template + + Args: + schematic: Schematic object to add component to + component_def: Component definition dictionary + schematic_path: Optional path to schematic file (enables dynamic symbol loading) + """ try: logger.info(f"Adding component: type={component_def.get('type')}, ref={component_def.get('reference')}") logger.debug(f"Full component_def: {component_def}") @@ -131,7 +144,7 @@ class ComponentManager: library = component_def.get('library', None) # Optional library specification # Get template reference (static or dynamic) - template_ref = ComponentManager.get_or_create_template(schematic, comp_type, library) + template_ref = ComponentManager.get_or_create_template(schematic, comp_type, library, schematic_path) # Check if schematic has template symbols if not hasattr(schematic.symbol, template_ref): diff --git a/python/kicad_interface.py b/python/kicad_interface.py index d5d0562..be6795c 100644 --- a/python/kicad_interface.py +++ b/python/kicad_interface.py @@ -549,32 +549,99 @@ class KiCADInterface: return {"success": False, "message": str(e)} def _handle_add_schematic_component(self, params): - """Add a component to a schematic""" + """Add a component to a schematic with dynamic symbol loading support""" logger.info("Adding component to schematic") try: + from pathlib import Path + schematic_path = params.get("schematicPath") component = params.get("component", {}) - + if not schematic_path: return {"success": False, "message": "Schematic path is required"} if not component: return {"success": False, "message": "Component definition is required"} - + + # Convert to Path object for dynamic loader + schematic_path_obj = Path(schematic_path) + + # Load schematic schematic = SchematicManager.load_schematic(schematic_path) if not schematic: return {"success": False, "message": "Failed to load schematic"} - - component_obj = ComponentManager.add_component(schematic, component) + + # Check if component type requires dynamic loading + comp_type = component.get('type', 'R') + library = component.get('library', 'Device') + + # Check if template exists in static templates + template_ref = ComponentManager.TEMPLATE_MAP.get(comp_type) + needs_dynamic_loading = False + + if template_ref: + # Check if template exists in schematic + if not hasattr(schematic.symbol, template_ref): + needs_dynamic_loading = True + logger.info(f"Static template {template_ref} not found in schematic, will try dynamic loading") + else: + # Not in static map, definitely needs dynamic loading + needs_dynamic_loading = True + logger.info(f"Component type {comp_type} not in static templates, will use dynamic loading") + + # If dynamic loading is needed and available + if needs_dynamic_loading: + try: + from commands.dynamic_symbol_loader import DynamicSymbolLoader + + loader = DynamicSymbolLoader() + + # Save current schematic first to preserve any changes + SchematicManager.save_schematic(schematic, schematic_path) + logger.info("Saved schematic before dynamic loading") + + # Dynamically load the symbol (injects into file and creates template) + logger.info(f"Dynamically loading symbol: {library}:{comp_type}") + template_ref = loader.load_symbol_dynamically(schematic_path_obj, library, comp_type) + logger.info(f"Dynamic loading successful. Template ref: {template_ref}") + + # Reload schematic to get the newly injected symbol + schematic = SchematicManager.load_schematic(schematic_path) + if not schematic: + return {"success": False, "message": "Failed to reload schematic after dynamic loading"} + logger.info("Reloaded schematic with new symbol definition") + + except ImportError: + logger.warning("Dynamic symbol loader not available, falling back to static templates") + except Exception as e: + logger.error(f"Dynamic loading failed: {e}") + logger.warning("Falling back to static templates") + + # Add component (now with template available in schematic) + component_obj = ComponentManager.add_component(schematic, component, schematic_path_obj) success = component_obj is not None - + if success: SchematicManager.save_schematic(schematic, schematic_path) - return {"success": True} + + # Prepare response with dynamic loading info + response = { + "success": True, + "component_reference": component.get('reference', 'unknown'), + "dynamic_loading_used": needs_dynamic_loading + } + + if needs_dynamic_loading: + response["symbol_source"] = f"{library}:{comp_type}" + response["template_reference"] = template_ref if 'template_ref' in locals() else "unknown" + + return response else: return {"success": False, "message": "Failed to add component"} except Exception as e: logger.error(f"Error adding component to schematic: {str(e)}") - return {"success": False, "message": str(e)} + import traceback + logger.error(traceback.format_exc()) + return {"success": False, "message": str(e), "errorDetails": traceback.format_exc()} def _handle_add_schematic_wire(self, params): """Add a wire to a schematic"""