e4a80dc0e6
- Roadmap #22: File→Import JSON to restore a diagram (drag-drop or file picker) - Roadmap #23: Duplicate diagram from diagram list - Roadmap #24: DRC panel with collapsible categories (unconnected pins, dup refs, no length, no P/N) - Multi-view tabs: named views per diagram with independent device/wire layouts; snapshot-on-entry prevents bleed - Keyboard arrow nudge for selected device(s) - Undo for move, duplicate, label/property changes - Wire jump arcs toggle (⌒ Jumps button in toolbar) - Shift+click multi-wire selection; right-click bulk clear waypoints or auto-route - Auto-route: obstacle-avoiding ortho routing for single or bulk selected wires - Delete view tab with × button; right-click context menu for rename/delete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
219 lines
5.4 KiB
Python
219 lines
5.4 KiB
Python
from pydantic import BaseModel, field_validator
|
|
from typing import Optional, List, Any, Dict
|
|
from datetime import datetime
|
|
|
|
|
|
class DeviceBase(BaseModel):
|
|
device_type: str
|
|
label: str = ""
|
|
reference: str = ""
|
|
x: float = 100
|
|
y: float = 100
|
|
width: float = 120
|
|
height: float = 60
|
|
properties: Dict[str, Any] = {}
|
|
pins: List[Dict[str, Any]] = []
|
|
|
|
|
|
class DeviceCreate(DeviceBase):
|
|
diagram_id: int
|
|
|
|
|
|
class DeviceUpdate(BaseModel):
|
|
label: Optional[str] = None
|
|
reference: Optional[str] = None
|
|
x: Optional[float] = None
|
|
y: Optional[float] = None
|
|
width: Optional[float] = None
|
|
height: Optional[float] = None
|
|
properties: Optional[Dict[str, Any]] = None
|
|
pins: Optional[List[Dict[str, Any]]] = None
|
|
|
|
|
|
class Device(DeviceBase):
|
|
id: int
|
|
diagram_id: int
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class WireBase(BaseModel):
|
|
label: str = ""
|
|
from_device_id: Optional[int] = None
|
|
from_pin: str = ""
|
|
to_device_id: Optional[int] = None
|
|
to_pin: str = ""
|
|
color_primary: str = "#FF0000"
|
|
color_stripe: Optional[str] = None
|
|
gauge: str = "18 AWG"
|
|
length: Optional[float] = None
|
|
length_unit: str = "in"
|
|
notes: str = ""
|
|
waypoints: List[Dict[str, Any]] = []
|
|
twisted_pair: bool = False
|
|
twist_pitch: float = 16.0
|
|
shielded: bool = False
|
|
bundle_id: Optional[int] = None
|
|
show_size_label: bool = False
|
|
|
|
@field_validator("waypoints", mode="before")
|
|
@classmethod
|
|
def _coerce_waypoints(cls, v):
|
|
return v if v is not None else []
|
|
|
|
|
|
class WireCreate(WireBase):
|
|
diagram_id: int
|
|
|
|
|
|
class WireUpdate(BaseModel):
|
|
label: Optional[str] = None
|
|
from_device_id: Optional[int] = None
|
|
from_pin: Optional[str] = None
|
|
to_device_id: Optional[int] = None
|
|
to_pin: Optional[str] = None
|
|
color_primary: Optional[str] = None
|
|
color_stripe: Optional[str] = None
|
|
gauge: Optional[str] = None
|
|
length: Optional[float] = None
|
|
length_unit: Optional[str] = None
|
|
notes: Optional[str] = None
|
|
waypoints: Optional[List[Dict[str, Any]]] = None
|
|
twisted_pair: Optional[bool] = None
|
|
twist_pitch: Optional[float] = None
|
|
shielded: Optional[bool] = None
|
|
bundle_id: Optional[int] = None
|
|
show_size_label: Optional[bool] = None
|
|
|
|
|
|
class Wire(WireBase):
|
|
id: int
|
|
diagram_id: int
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# ── WireBundle ────────────────────────────────────────────────────────────────
|
|
|
|
class WireBundleBase(BaseModel):
|
|
label: str = ""
|
|
jacket_color: str = "#2a2a2a"
|
|
|
|
|
|
class WireBundleCreate(WireBundleBase):
|
|
diagram_id: int
|
|
|
|
|
|
class WireBundleUpdate(BaseModel):
|
|
label: Optional[str] = None
|
|
jacket_color: Optional[str] = None
|
|
|
|
|
|
class WireBundleOut(WireBundleBase):
|
|
id: int
|
|
diagram_id: int
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# ── Diagram ───────────────────────────────────────────────────────────────────
|
|
|
|
class DiagramBase(BaseModel):
|
|
name: str = "Untitled Diagram"
|
|
description: str = ""
|
|
|
|
|
|
class DiagramCreate(DiagramBase):
|
|
pass
|
|
|
|
|
|
class DiagramUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
|
|
|
|
class DiagramSummary(DiagramBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class DiagramFull(DiagramSummary):
|
|
devices: List[Device] = []
|
|
wires: List[Wire] = []
|
|
wire_bundles: List[WireBundleOut] = []
|
|
|
|
|
|
# ── DiagramView ───────────────────────────────────────────────────────────────
|
|
|
|
class DiagramViewBase(BaseModel):
|
|
name: str
|
|
route_mode: str = "direct"
|
|
|
|
|
|
class DiagramViewCreate(DiagramViewBase):
|
|
diagram_id: int
|
|
|
|
|
|
class DiagramViewUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
route_mode: Optional[str] = None
|
|
|
|
|
|
class DiagramViewOut(DiagramViewBase):
|
|
id: int
|
|
diagram_id: int
|
|
created_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class ViewDevicePositionUpdate(BaseModel):
|
|
x: float
|
|
y: float
|
|
|
|
|
|
class ViewWireWaypointsUpdate(BaseModel):
|
|
waypoints: List[Dict[str, Any]] = []
|
|
|
|
|
|
class ViewLayout(BaseModel):
|
|
device_positions: Dict[int, Dict[str, float]] = {}
|
|
wire_waypoints: Dict[int, List[Dict[str, Any]]] = {}
|
|
|
|
|
|
# ── CustomConnector ───────────────────────────────────────────────────────────
|
|
|
|
class CustomConnectorBase(BaseModel):
|
|
name: str
|
|
manufacturer: str = ""
|
|
part_number: str = ""
|
|
category: str = "Custom"
|
|
description: str = ""
|
|
pin_count: int
|
|
pin_labels: List[str] = []
|
|
|
|
|
|
class CustomConnectorCreate(CustomConnectorBase):
|
|
pass
|
|
|
|
|
|
class CustomConnectorUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
manufacturer: Optional[str] = None
|
|
part_number: Optional[str] = None
|
|
category: Optional[str] = None
|
|
description: Optional[str] = None
|
|
pin_count: Optional[int] = None
|
|
pin_labels: Optional[List[str]] = None
|
|
|
|
|
|
class CustomConnector(CustomConnectorBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|