kcaa 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kcaa-0.2.2 → kcaa-0.2.3}/.gitignore +1 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/PKG-INFO +8 -5
- {kcaa-0.2.2 → kcaa-0.2.3}/README.md +7 -4
- {kcaa-0.2.2 → kcaa-0.2.3}/README_CN.md +7 -4
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/server.py +14 -8
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/pcb_edit_tools.py +99 -26
- kcaa-0.2.3/kcaa/tools/pcb_library_tools.py +907 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/pcb_placement_tools.py +139 -72
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/pcb_query_tools.py +152 -93
- kcaa-0.2.3/kcaa/tools/project_tools.py +132 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/sheet_tools.py +33 -17
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/symbol_edit_tools.py +159 -88
- kcaa-0.2.3/kcaa/tools/version_tools.py +139 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/wire_edit_tools.py +79 -2
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/footprint_database.py +198 -59
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/footprint_index_manager.py +127 -23
- kcaa-0.2.3/kcaa/utils/fp_lib_table_utils.py +137 -0
- kcaa-0.2.3/kcaa/utils/pcb_footprint_utils.py +639 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/pcb_library_utils.py +1 -0
- kcaa-0.2.3/kcaa/utils/version_manager.py +250 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/pyproject.toml +1 -1
- kcaa-0.2.2/kcaa/tools/pcb_library_tools.py +0 -422
- kcaa-0.2.2/kcaa/tools/project_tools.py +0 -62
- kcaa-0.2.2/kcaa/tools/version_tools.py +0 -134
- kcaa-0.2.2/kcaa/utils/pcb_footprint_utils.py +0 -240
- kcaa-0.2.2/kcaa/utils/version_manager.py +0 -143
- {kcaa-0.2.2 → kcaa-0.2.3}/LICENSE +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/__init__.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/context.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/prompts/__init__.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/prompts/bom_prompts.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/prompts/drc_prompt.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/prompts/pattern_prompts.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/prompts/templates.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/resources/__init__.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/resources/bom_resources.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/resources/drc_resources.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/resources/files.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/resources/netlist_resources.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/resources/pattern_resources.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/resources/projects.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/router/__init__.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/router/a_star.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/router/grid_a_star.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/router/path_postprocess.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/router/router.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/router/via_check.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/router/visibility_graph.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/router/world_model.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/__init__.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/analysis_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/bom_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/drc_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/export_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/kipy_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/netlist_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/pattern_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/pcb_group_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/pcb_placement_helpers.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/pcb_routing_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/pcb_zone_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/placement_helpers.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/schematic_group_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/skill_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/symbol_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/tools/validation_tools.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/__init__.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/boundary_validator.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/component_utils.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/config.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/file_utils.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/ipc_drc.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/kicad_api_detection.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/kicad_cli.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/kicad_utils.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/kipy_reload.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/net_settings.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/netlist_parser.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/path_validator.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/pattern_recognition.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/pcb_board_utils.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/pcb_design_rules.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/pcb_sexp_utils.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/schematic_sexp_utils.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/secure_subprocess.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/skip_compat.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/skip_helpers.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/symbol_database.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/symbol_extractor.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/symbol_geometry.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/symbol_index_manager.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/symbol_index_reader.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kcaa/utils/temp_dir_manager.py +0 -0
- {kcaa-0.2.2 → kcaa-0.2.3}/kicad_plugin/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: kcaa
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Model Context Protocol (MCP) server for KiCad electronic design automation (EDA) files
|
|
5
5
|
Project-URL: Homepage, https://github.com/paul356/KiCad-AI-Assistant
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/paul356/KiCad-AI-Assistant/issues
|
|
@@ -292,6 +292,9 @@ kcaa
|
|
|
292
292
|
| `list_footprint_libraries` | List all available footprint libraries |
|
|
293
293
|
| `search_footprints` | Search footprints by name, description, or tag |
|
|
294
294
|
| `get_footprint_details` | Get footprint details (pads, bounding box, etc.) |
|
|
295
|
+
| `find_footprints_not_in_libraries` | List board footprints missing from every indexed library |
|
|
296
|
+
| `create_footprint_library` | Create a global 3rdparty or project-local footprint library |
|
|
297
|
+
| `add_footprints_to_library` | Export explicitly named board footprints into a library |
|
|
295
298
|
|
|
296
299
|
### PCB Query
|
|
297
300
|
|
|
@@ -323,7 +326,7 @@ kcaa
|
|
|
323
326
|
|
|
324
327
|
| Tool | Description |
|
|
325
328
|
|------|-------------|
|
|
326
|
-
| `set_footprint_position` | Move and/or rotate
|
|
329
|
+
| `set_footprint_position` | Move and/or rotate one or more footprints (via `items`) |
|
|
327
330
|
| `flip_footprint` | Flip a footprint between top and bottom layer |
|
|
328
331
|
| `align_footprints` | Align footprints to the same axis |
|
|
329
332
|
| `distribute_footprints` | Distribute footprints evenly along an axis |
|
|
@@ -371,9 +374,9 @@ kcaa
|
|
|
371
374
|
|
|
372
375
|
| Tool | Description |
|
|
373
376
|
|------|-------------|
|
|
374
|
-
| `
|
|
375
|
-
| `
|
|
376
|
-
| `
|
|
377
|
+
| `save_project_version` | Save a project version archive (schematic+PCB+project file) |
|
|
378
|
+
| `list_project_versions` | List saved project version archives |
|
|
379
|
+
| `restore_project_version` | Restore project files together to a saved archive |
|
|
377
380
|
| `generate_pcb_thumbnail` | Render a PCB thumbnail image |
|
|
378
381
|
| `generate_project_thumbnail` | Render a project thumbnail |
|
|
379
382
|
|
|
@@ -253,6 +253,9 @@ kcaa
|
|
|
253
253
|
| `list_footprint_libraries` | List all available footprint libraries |
|
|
254
254
|
| `search_footprints` | Search footprints by name, description, or tag |
|
|
255
255
|
| `get_footprint_details` | Get footprint details (pads, bounding box, etc.) |
|
|
256
|
+
| `find_footprints_not_in_libraries` | List board footprints missing from every indexed library |
|
|
257
|
+
| `create_footprint_library` | Create a global 3rdparty or project-local footprint library |
|
|
258
|
+
| `add_footprints_to_library` | Export explicitly named board footprints into a library |
|
|
256
259
|
|
|
257
260
|
### PCB Query
|
|
258
261
|
|
|
@@ -284,7 +287,7 @@ kcaa
|
|
|
284
287
|
|
|
285
288
|
| Tool | Description |
|
|
286
289
|
|------|-------------|
|
|
287
|
-
| `set_footprint_position` | Move and/or rotate
|
|
290
|
+
| `set_footprint_position` | Move and/or rotate one or more footprints (via `items`) |
|
|
288
291
|
| `flip_footprint` | Flip a footprint between top and bottom layer |
|
|
289
292
|
| `align_footprints` | Align footprints to the same axis |
|
|
290
293
|
| `distribute_footprints` | Distribute footprints evenly along an axis |
|
|
@@ -332,9 +335,9 @@ kcaa
|
|
|
332
335
|
|
|
333
336
|
| Tool | Description |
|
|
334
337
|
|------|-------------|
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
338
|
+
| `save_project_version` | Save a project version archive (schematic+PCB+project file) |
|
|
339
|
+
| `list_project_versions` | List saved project version archives |
|
|
340
|
+
| `restore_project_version` | Restore project files together to a saved archive |
|
|
338
341
|
| `generate_pcb_thumbnail` | Render a PCB thumbnail image |
|
|
339
342
|
| `generate_project_thumbnail` | Render a project thumbnail |
|
|
340
343
|
|
|
@@ -252,6 +252,9 @@ kcaa
|
|
|
252
252
|
| `list_footprint_libraries` | 列出所有可用封装库 |
|
|
253
253
|
| `search_footprints` | 按名称/描述/标签搜索封装 |
|
|
254
254
|
| `get_footprint_details` | 获取封装详情(焊盘、边界框等) |
|
|
255
|
+
| `find_footprints_not_in_libraries` | 列出板上缺失于所有已索引库的封装 |
|
|
256
|
+
| `create_footprint_library` | 创建全局 3rdparty 或项目级封装库 |
|
|
257
|
+
| `add_footprints_to_library` | 将显式指定的板载封装导出到库 |
|
|
255
258
|
|
|
256
259
|
### PCB 查询
|
|
257
260
|
|
|
@@ -283,7 +286,7 @@ kcaa
|
|
|
283
286
|
|
|
284
287
|
| 工具 | 说明 |
|
|
285
288
|
|------|------|
|
|
286
|
-
| `set_footprint_position` |
|
|
289
|
+
| `set_footprint_position` | 批量移动/旋转一个或多个封装 |
|
|
287
290
|
| `flip_footprint` | 将封装在顶层/底层之间翻转 |
|
|
288
291
|
| `align_footprints` | 将封装对齐到同一坐标轴 |
|
|
289
292
|
| `distribute_footprints` | 沿坐标轴等间距分布封装 |
|
|
@@ -331,9 +334,9 @@ kcaa
|
|
|
331
334
|
|
|
332
335
|
| 工具 | 说明 |
|
|
333
336
|
|------|------|
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
+
| `save_project_version` | 保存项目版本归档(原理图+PCB+项目文件) |
|
|
338
|
+
| `list_project_versions` | 列出已保存的项目版本归档 |
|
|
339
|
+
| `restore_project_version` | 将项目文件一起恢复到已保存的归档 |
|
|
337
340
|
| `generate_pcb_thumbnail` | 渲染 PCB 缩略图 |
|
|
338
341
|
| `generate_project_thumbnail` | 渲染项目缩略图 |
|
|
339
342
|
|
|
@@ -6,11 +6,12 @@ Two server profiles are supported, selected via the KICAD_MCP_PROFILE environmen
|
|
|
6
6
|
full (default) — registers all tools, resources, and prompts. Intended for
|
|
7
7
|
standalone MCP clients such as Claude Desktop.
|
|
8
8
|
|
|
9
|
-
plugin — registers
|
|
10
|
-
|
|
11
|
-
no prompts, no kicad-cli-dependent tools.
|
|
12
|
-
KiCad plugin integration where the
|
|
13
|
-
context directly and the LLM
|
|
9
|
+
plugin — registers the skip-based schematic/PCB editing tools
|
|
10
|
+
plus the ``get_project_structure`` query tool. No
|
|
11
|
+
resources, no prompts, no kicad-cli-dependent tools.
|
|
12
|
+
Intended for the KiCad plugin integration where the
|
|
13
|
+
plugin supplies project context directly and the LLM
|
|
14
|
+
drives editing via tool calls.
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
17
|
import atexit
|
|
@@ -40,6 +41,7 @@ from kcaa.tools.pcb_query_tools import register_pcb_query_tools
|
|
|
40
41
|
from kcaa.tools.pcb_routing_tools import register_pcb_routing_tools
|
|
41
42
|
from kcaa.tools.pcb_zone_tools import register_pcb_zone_tools
|
|
42
43
|
from kcaa.tools.placement_helpers import register_placement_helpers
|
|
44
|
+
from kcaa.tools.project_tools import register_project_tools
|
|
43
45
|
from kcaa.tools.schematic_group_tools import register_schematic_group_tools
|
|
44
46
|
from kcaa.tools.sheet_tools import register_sheet_tools
|
|
45
47
|
from kcaa.tools.skill_tools import register_skill_tools
|
|
@@ -110,9 +112,12 @@ def register_signal_handlers(server: FastMCP) -> None:
|
|
|
110
112
|
|
|
111
113
|
|
|
112
114
|
def _register_plugin_profile(mcp: FastMCP) -> None:
|
|
113
|
-
"""Register
|
|
115
|
+
"""Register the skip-based schematic/PCB editing tools + IPC-based tools for the plugin profile."""
|
|
114
116
|
logging.info("Registering plugin profile: schematic + PCB tools")
|
|
115
117
|
register_netlist_tools(mcp)
|
|
118
|
+
# Project structure query for the plugin LLM; keep full-profile
|
|
119
|
+
# management tools (list_projects/open_project) out of this profile.
|
|
120
|
+
register_project_tools(mcp, tools=("get_project_structure",))
|
|
116
121
|
register_symbol_tools(mcp)
|
|
117
122
|
register_symbol_edit_tools(mcp)
|
|
118
123
|
register_sheet_tools(mcp)
|
|
@@ -205,8 +210,9 @@ def create_server(profile: ServerProfile = "full") -> FastMCP:
|
|
|
205
210
|
Args:
|
|
206
211
|
profile: Server capability profile.
|
|
207
212
|
"full" — all tools, resources, and prompts (default; for Claude Desktop etc.)
|
|
208
|
-
"plugin" — plugin-facing profile:
|
|
209
|
-
|
|
213
|
+
"plugin" — plugin-facing profile: skip-based schematic/PCB editing
|
|
214
|
+
tools plus the get_project_structure query tool; no
|
|
215
|
+
resources, no prompts, no kicad-cli-dependent tools.
|
|
210
216
|
Override via KICAD_MCP_PROFILE env var.
|
|
211
217
|
"""
|
|
212
218
|
# Allow env var to override the profile argument; strip whitespace/case variants
|
|
@@ -309,47 +309,120 @@ def register_pcb_edit_tools(mcp: FastMCP) -> None:
|
|
|
309
309
|
@mcp.tool()
|
|
310
310
|
async def set_footprint_property(
|
|
311
311
|
pcb_path: str,
|
|
312
|
-
|
|
313
|
-
property_name: str,
|
|
314
|
-
value: str,
|
|
312
|
+
items: list[dict[str, Any]],
|
|
315
313
|
ctx: Context | None,
|
|
316
|
-
) -> dict[str,
|
|
317
|
-
"""Update a property
|
|
314
|
+
) -> dict[str, Any]:
|
|
315
|
+
"""Update a property on footprints of the PCB board.
|
|
318
316
|
|
|
319
317
|
Common property names: ``Reference``, ``Value``, ``Datasheet``,
|
|
320
318
|
``Description``. Custom user fields are also supported.
|
|
321
319
|
|
|
320
|
+
Each item in *items* carries its own property and value
|
|
321
|
+
(``{"reference": ..., "property_name": ..., "value": ...}``).
|
|
322
|
+
All items are processed in one parse + one save (single ``.bak``).
|
|
323
|
+
Partial-apply: a footprint that cannot be found or lacks the
|
|
324
|
+
property keeps its own error in ``results`` while the remaining
|
|
325
|
+
footprints are still applied and saved. Empty, duplicate, or
|
|
326
|
+
malformed items are rejected up front.
|
|
327
|
+
|
|
322
328
|
A .kicad_pcb.bak backup is created before writing.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
pcb_path: Absolute path to the .kicad_pcb file.
|
|
332
|
+
items: List of per-footprint specs, e.g. ``[{"reference":
|
|
333
|
+
"R1", "property_name": "Value", "value": "22k"}]``.
|
|
334
|
+
Keys: ``reference`` (str, required, unique),
|
|
335
|
+
``property_name`` (str, required),
|
|
336
|
+
``value`` (str, required).
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
dict with: results (per-footprint {reference, property_name,
|
|
340
|
+
previous_value, new_value} or {reference, error}),
|
|
341
|
+
success (all applied), count, applied_count, failure_count,
|
|
342
|
+
backup_path, pcb_path.
|
|
323
343
|
"""
|
|
344
|
+
_ITEM_KEYS = {"reference", "property_name", "value"}
|
|
345
|
+
if not items:
|
|
346
|
+
return {"error": "items must not be empty"}
|
|
347
|
+
for item in items:
|
|
348
|
+
if not isinstance(item, dict):
|
|
349
|
+
return {"error": "each item must be a dict with reference, property_name, value"}
|
|
350
|
+
unknown = set(item) - _ITEM_KEYS
|
|
351
|
+
if unknown:
|
|
352
|
+
return {
|
|
353
|
+
"error": f"Unknown item fields: {sorted(unknown)}. Valid: {sorted(_ITEM_KEYS)}"
|
|
354
|
+
}
|
|
355
|
+
ref = item.get("reference")
|
|
356
|
+
if not isinstance(ref, str) or not ref:
|
|
357
|
+
return {"error": "items must not contain empty or missing references"}
|
|
358
|
+
if not isinstance(item.get("property_name"), str) or not item["property_name"]:
|
|
359
|
+
return {"error": "items must contain a non-empty property_name"}
|
|
360
|
+
if not isinstance(item.get("value"), str):
|
|
361
|
+
return {"error": "items must contain a string value"}
|
|
362
|
+
refs = [item["reference"] for item in items]
|
|
363
|
+
if len(set(refs)) != len(refs):
|
|
364
|
+
return {"error": "items must not contain duplicate references"}
|
|
365
|
+
|
|
324
366
|
data = load_pcb(pcb_path)
|
|
325
|
-
try:
|
|
326
|
-
fp = find_footprint(data, reference)
|
|
327
|
-
except KeyError as exc:
|
|
328
|
-
return {"error": str(exc)}
|
|
329
367
|
|
|
330
|
-
|
|
331
|
-
|
|
368
|
+
def apply_one(item: dict[str, Any]) -> dict[str, Any]:
|
|
369
|
+
ref = item["reference"]
|
|
370
|
+
name = item["property_name"]
|
|
371
|
+
val = item["value"]
|
|
372
|
+
try:
|
|
373
|
+
fp = find_footprint(data, ref)
|
|
374
|
+
except KeyError as exc:
|
|
375
|
+
return {"error": str(exc), "reference": ref}
|
|
376
|
+
|
|
377
|
+
old_value = get_fp_property(fp, name)
|
|
378
|
+
if old_value is None:
|
|
379
|
+
return {
|
|
380
|
+
"error": (
|
|
381
|
+
f"Property '{name}' not found on footprint '{ref}'. "
|
|
382
|
+
f"Use get_footprint to see available properties."
|
|
383
|
+
),
|
|
384
|
+
"reference": ref,
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
updated = set_fp_property(fp, name, val)
|
|
388
|
+
if not updated:
|
|
389
|
+
return {
|
|
390
|
+
"error": f"Failed to update property '{name}' on '{ref}'.",
|
|
391
|
+
"reference": ref,
|
|
392
|
+
}
|
|
393
|
+
|
|
332
394
|
return {
|
|
333
|
-
"
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
395
|
+
"success": True,
|
|
396
|
+
"reference": ref,
|
|
397
|
+
"property_name": name,
|
|
398
|
+
"previous_value": old_value,
|
|
399
|
+
"new_value": val,
|
|
337
400
|
}
|
|
338
401
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
402
|
+
results: list[dict[str, Any]] = []
|
|
403
|
+
for item in items:
|
|
404
|
+
ref = item["reference"]
|
|
405
|
+
try:
|
|
406
|
+
results.append(apply_one(item))
|
|
407
|
+
except Exception as exc:
|
|
408
|
+
log.warning("set_footprint_property: reference %r failed: %s", ref, exc)
|
|
409
|
+
results.append({"error": f"{ref}: {exc}", "reference": ref})
|
|
342
410
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
411
|
+
applied_count = sum(1 for r in results if r.get("success"))
|
|
412
|
+
|
|
413
|
+
backup_path: str | None = None
|
|
414
|
+
if applied_count > 0:
|
|
415
|
+
try:
|
|
416
|
+
backup_path = save_pcb(pcb_path, data)
|
|
417
|
+
except OSError as exc:
|
|
418
|
+
return {"error": f"Failed to write PCB file: {exc}"}
|
|
347
419
|
|
|
348
420
|
return {
|
|
349
|
-
"
|
|
350
|
-
"
|
|
351
|
-
"
|
|
352
|
-
"
|
|
421
|
+
"success": applied_count == len(results) and len(results) > 0,
|
|
422
|
+
"results": results,
|
|
423
|
+
"count": len(results),
|
|
424
|
+
"applied_count": applied_count,
|
|
425
|
+
"failure_count": len(results) - applied_count,
|
|
353
426
|
"backup_path": backup_path,
|
|
354
427
|
"pcb_path": pcb_path,
|
|
355
428
|
}
|