chore: normalize all tracked files to LF line endings
Mechanical application of the `.gitattributes` rules from the prior commit. All 50 files differ only in line endings — verified by `git diff --cached --ignore-all-space` being empty. Before: main had 42 CRLF + 27 LF Python files plus mixed-ending in YAML, templates, and shell scripts. After: every text file is LF (except the Windows-native *.ps1, *.bat scripts which remain CRLF per gitattributes). This eliminates the noisy-diff failure mode seen in PR #102, where a small logic change produced a 918-line diff due to whole-file CRLF→LF conversion.
This commit is contained in:
@@ -1,293 +1,293 @@
|
||||
"""
|
||||
Abstract base class for KiCAD API backends
|
||||
|
||||
Defines the interface that all KiCAD backends must implement.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KiCADBackend(ABC):
|
||||
"""Abstract base class for KiCAD API backends"""
|
||||
|
||||
@abstractmethod
|
||||
def connect(self) -> bool:
|
||||
"""
|
||||
Connect to KiCAD
|
||||
|
||||
Returns:
|
||||
True if connection successful, False otherwise
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def disconnect(self) -> None:
|
||||
"""Disconnect from KiCAD and clean up resources"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_connected(self) -> bool:
|
||||
"""
|
||||
Check if currently connected to KiCAD
|
||||
|
||||
Returns:
|
||||
True if connected, False otherwise
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_version(self) -> str:
|
||||
"""
|
||||
Get KiCAD version
|
||||
|
||||
Returns:
|
||||
Version string (e.g., "9.0.0")
|
||||
"""
|
||||
pass
|
||||
|
||||
# Project Operations
|
||||
@abstractmethod
|
||||
def create_project(self, path: Path, name: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Create a new KiCAD project
|
||||
|
||||
Args:
|
||||
path: Directory path for the project
|
||||
name: Project name
|
||||
|
||||
Returns:
|
||||
Dictionary with project info
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def open_project(self, path: Path) -> Dict[str, Any]:
|
||||
"""
|
||||
Open an existing KiCAD project
|
||||
|
||||
Args:
|
||||
path: Path to .kicad_pro file
|
||||
|
||||
Returns:
|
||||
Dictionary with project info
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save_project(self, path: Optional[Path] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Save the current project
|
||||
|
||||
Args:
|
||||
path: Optional new path to save to
|
||||
|
||||
Returns:
|
||||
Dictionary with save status
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def close_project(self) -> None:
|
||||
"""Close the current project"""
|
||||
pass
|
||||
|
||||
# Board Operations
|
||||
@abstractmethod
|
||||
def get_board(self) -> "BoardAPI":
|
||||
"""
|
||||
Get board API for current project
|
||||
|
||||
Returns:
|
||||
BoardAPI instance
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class BoardAPI(ABC):
|
||||
"""Abstract interface for board operations"""
|
||||
|
||||
@abstractmethod
|
||||
def set_size(self, width: float, height: float, unit: str = "mm") -> bool:
|
||||
"""
|
||||
Set board size
|
||||
|
||||
Args:
|
||||
width: Board width
|
||||
height: Board height
|
||||
unit: Unit of measurement ("mm" or "in")
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_size(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get current board size
|
||||
|
||||
Returns:
|
||||
Dictionary with width, height, unit
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def add_layer(self, layer_name: str, layer_type: str) -> bool:
|
||||
"""
|
||||
Add a layer to the board
|
||||
|
||||
Args:
|
||||
layer_name: Name of the layer
|
||||
layer_type: Type ("copper", "technical", "user")
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_components(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
List all components on the board
|
||||
|
||||
Returns:
|
||||
List of component dictionaries
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def place_component(
|
||||
self,
|
||||
reference: str,
|
||||
footprint: str,
|
||||
x: float,
|
||||
y: float,
|
||||
rotation: float = 0,
|
||||
layer: str = "F.Cu",
|
||||
value: str = "",
|
||||
) -> bool:
|
||||
"""
|
||||
Place a component on the board
|
||||
|
||||
Args:
|
||||
reference: Component reference (e.g., "R1")
|
||||
footprint: Footprint library path
|
||||
x: X position (mm)
|
||||
y: Y position (mm)
|
||||
rotation: Rotation angle (degrees)
|
||||
layer: Layer name
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
pass
|
||||
|
||||
# Routing Operations
|
||||
def add_track(
|
||||
self,
|
||||
start_x: float,
|
||||
start_y: float,
|
||||
end_x: float,
|
||||
end_y: float,
|
||||
width: float = 0.25,
|
||||
layer: str = "F.Cu",
|
||||
net_name: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Add a track (trace) to the board
|
||||
|
||||
Args:
|
||||
start_x: Start X position (mm)
|
||||
start_y: Start Y position (mm)
|
||||
end_x: End X position (mm)
|
||||
end_y: End Y position (mm)
|
||||
width: Track width (mm)
|
||||
layer: Layer name
|
||||
net_name: Optional net name
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_via(
|
||||
self,
|
||||
x: float,
|
||||
y: float,
|
||||
diameter: float = 0.8,
|
||||
drill: float = 0.4,
|
||||
net_name: Optional[str] = None,
|
||||
via_type: str = "through",
|
||||
) -> bool:
|
||||
"""
|
||||
Add a via to the board
|
||||
|
||||
Args:
|
||||
x: X position (mm)
|
||||
y: Y position (mm)
|
||||
diameter: Via diameter (mm)
|
||||
drill: Drill diameter (mm)
|
||||
net_name: Optional net name
|
||||
via_type: Via type ("through", "blind", "micro")
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
# Transaction support for undo/redo
|
||||
def begin_transaction(self, description: str = "MCP Operation") -> None:
|
||||
"""Begin a transaction for grouping operations."""
|
||||
pass # Optional - not all backends support this
|
||||
|
||||
def commit_transaction(self, description: str = "MCP Operation") -> None:
|
||||
"""Commit the current transaction."""
|
||||
pass # Optional
|
||||
|
||||
def rollback_transaction(self) -> None:
|
||||
"""Roll back the current transaction."""
|
||||
pass # Optional
|
||||
|
||||
def save(self) -> bool:
|
||||
"""Save the board."""
|
||||
raise NotImplementedError()
|
||||
|
||||
# Query operations
|
||||
def get_tracks(self) -> List[Dict[str, Any]]:
|
||||
"""Get all tracks on the board."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_vias(self) -> List[Dict[str, Any]]:
|
||||
"""Get all vias on the board."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_nets(self) -> List[Dict[str, Any]]:
|
||||
"""Get all nets on the board."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_selection(self) -> List[Dict[str, Any]]:
|
||||
"""Get currently selected items."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class BackendError(Exception):
|
||||
"""Base exception for backend errors"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ConnectionError(BackendError):
|
||||
"""Raised when connection to KiCAD fails"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class APINotAvailableError(BackendError):
|
||||
"""Raised when required API is not available"""
|
||||
|
||||
pass
|
||||
"""
|
||||
Abstract base class for KiCAD API backends
|
||||
|
||||
Defines the interface that all KiCAD backends must implement.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KiCADBackend(ABC):
|
||||
"""Abstract base class for KiCAD API backends"""
|
||||
|
||||
@abstractmethod
|
||||
def connect(self) -> bool:
|
||||
"""
|
||||
Connect to KiCAD
|
||||
|
||||
Returns:
|
||||
True if connection successful, False otherwise
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def disconnect(self) -> None:
|
||||
"""Disconnect from KiCAD and clean up resources"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def is_connected(self) -> bool:
|
||||
"""
|
||||
Check if currently connected to KiCAD
|
||||
|
||||
Returns:
|
||||
True if connected, False otherwise
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_version(self) -> str:
|
||||
"""
|
||||
Get KiCAD version
|
||||
|
||||
Returns:
|
||||
Version string (e.g., "9.0.0")
|
||||
"""
|
||||
pass
|
||||
|
||||
# Project Operations
|
||||
@abstractmethod
|
||||
def create_project(self, path: Path, name: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Create a new KiCAD project
|
||||
|
||||
Args:
|
||||
path: Directory path for the project
|
||||
name: Project name
|
||||
|
||||
Returns:
|
||||
Dictionary with project info
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def open_project(self, path: Path) -> Dict[str, Any]:
|
||||
"""
|
||||
Open an existing KiCAD project
|
||||
|
||||
Args:
|
||||
path: Path to .kicad_pro file
|
||||
|
||||
Returns:
|
||||
Dictionary with project info
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save_project(self, path: Optional[Path] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Save the current project
|
||||
|
||||
Args:
|
||||
path: Optional new path to save to
|
||||
|
||||
Returns:
|
||||
Dictionary with save status
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def close_project(self) -> None:
|
||||
"""Close the current project"""
|
||||
pass
|
||||
|
||||
# Board Operations
|
||||
@abstractmethod
|
||||
def get_board(self) -> "BoardAPI":
|
||||
"""
|
||||
Get board API for current project
|
||||
|
||||
Returns:
|
||||
BoardAPI instance
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class BoardAPI(ABC):
|
||||
"""Abstract interface for board operations"""
|
||||
|
||||
@abstractmethod
|
||||
def set_size(self, width: float, height: float, unit: str = "mm") -> bool:
|
||||
"""
|
||||
Set board size
|
||||
|
||||
Args:
|
||||
width: Board width
|
||||
height: Board height
|
||||
unit: Unit of measurement ("mm" or "in")
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_size(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get current board size
|
||||
|
||||
Returns:
|
||||
Dictionary with width, height, unit
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def add_layer(self, layer_name: str, layer_type: str) -> bool:
|
||||
"""
|
||||
Add a layer to the board
|
||||
|
||||
Args:
|
||||
layer_name: Name of the layer
|
||||
layer_type: Type ("copper", "technical", "user")
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_components(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
List all components on the board
|
||||
|
||||
Returns:
|
||||
List of component dictionaries
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def place_component(
|
||||
self,
|
||||
reference: str,
|
||||
footprint: str,
|
||||
x: float,
|
||||
y: float,
|
||||
rotation: float = 0,
|
||||
layer: str = "F.Cu",
|
||||
value: str = "",
|
||||
) -> bool:
|
||||
"""
|
||||
Place a component on the board
|
||||
|
||||
Args:
|
||||
reference: Component reference (e.g., "R1")
|
||||
footprint: Footprint library path
|
||||
x: X position (mm)
|
||||
y: Y position (mm)
|
||||
rotation: Rotation angle (degrees)
|
||||
layer: Layer name
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
pass
|
||||
|
||||
# Routing Operations
|
||||
def add_track(
|
||||
self,
|
||||
start_x: float,
|
||||
start_y: float,
|
||||
end_x: float,
|
||||
end_y: float,
|
||||
width: float = 0.25,
|
||||
layer: str = "F.Cu",
|
||||
net_name: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Add a track (trace) to the board
|
||||
|
||||
Args:
|
||||
start_x: Start X position (mm)
|
||||
start_y: Start Y position (mm)
|
||||
end_x: End X position (mm)
|
||||
end_y: End Y position (mm)
|
||||
width: Track width (mm)
|
||||
layer: Layer name
|
||||
net_name: Optional net name
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_via(
|
||||
self,
|
||||
x: float,
|
||||
y: float,
|
||||
diameter: float = 0.8,
|
||||
drill: float = 0.4,
|
||||
net_name: Optional[str] = None,
|
||||
via_type: str = "through",
|
||||
) -> bool:
|
||||
"""
|
||||
Add a via to the board
|
||||
|
||||
Args:
|
||||
x: X position (mm)
|
||||
y: Y position (mm)
|
||||
diameter: Via diameter (mm)
|
||||
drill: Drill diameter (mm)
|
||||
net_name: Optional net name
|
||||
via_type: Via type ("through", "blind", "micro")
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
# Transaction support for undo/redo
|
||||
def begin_transaction(self, description: str = "MCP Operation") -> None:
|
||||
"""Begin a transaction for grouping operations."""
|
||||
pass # Optional - not all backends support this
|
||||
|
||||
def commit_transaction(self, description: str = "MCP Operation") -> None:
|
||||
"""Commit the current transaction."""
|
||||
pass # Optional
|
||||
|
||||
def rollback_transaction(self) -> None:
|
||||
"""Roll back the current transaction."""
|
||||
pass # Optional
|
||||
|
||||
def save(self) -> bool:
|
||||
"""Save the board."""
|
||||
raise NotImplementedError()
|
||||
|
||||
# Query operations
|
||||
def get_tracks(self) -> List[Dict[str, Any]]:
|
||||
"""Get all tracks on the board."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_vias(self) -> List[Dict[str, Any]]:
|
||||
"""Get all vias on the board."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_nets(self) -> List[Dict[str, Any]]:
|
||||
"""Get all nets on the board."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_selection(self) -> List[Dict[str, Any]]:
|
||||
"""Get currently selected items."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class BackendError(Exception):
|
||||
"""Base exception for backend errors"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ConnectionError(BackendError):
|
||||
"""Raised when connection to KiCAD fails"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class APINotAvailableError(BackendError):
|
||||
"""Raised when required API is not available"""
|
||||
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user