Merge feat/drawing-standard-views: стандартные виды чертежа (веха 2D-чертёж)

drawing_create_standard_views (инкремент 1 вехи 2D-ЧЕРТЁЖ): стандартные
ассоциативные виды детали/сборки на чертеже (API7 IViews.AddStandartViews).
73 инструмента, 177 тестов (116 unit + 61 integration). Спек + ревью Codex
(спека и реализации) учтены.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 14:44:28 +03:00
13 changed files with 487 additions and 16 deletions
+3 -2
View File
File diff suppressed because one or more lines are too long
+3 -2
View File
@@ -39,7 +39,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe
} }
``` ```
## Инструменты (72 инструмента) ## Инструменты (73 инструмента)
| Группа | Инструменты | | Группа | Инструменты |
|---|---| |---|---|
@@ -54,6 +54,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe
| Variables | `create_variable`, `set_variable`, `delete_variable` | | Variables | `create_variable`, `set_variable`, `delete_variable` |
| Conversion | `import_step`, `export_step` | | Conversion | `import_step`, `export_step` |
| Assembly | `assembly_add_component`, `assembly_add_mate` | | Assembly | `assembly_add_component`, `assembly_add_mate` |
| Drawing | `drawing_create_standard_views` |
| Validation | `validate_part` | | Validation | `validate_part` |
### Типовой сценарий ### Типовой сценарий
@@ -72,7 +73,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe
``` ```
src/Kompas.Mcp.Core/ COM-слой: STA-диспетчер, подключение, документы, эскизы/операции, конвертация, снимок, инспекция модели src/Kompas.Mcp.Core/ COM-слой: STA-диспетчер, подключение, документы, эскизы/операции, конвертация, снимок, инспекция модели
src/Kompas.Mcp.Host/ MCP-сервер (stdio) + определения инструментов src/Kompas.Mcp.Host/ MCP-сервер (stdio) + определения инструментов
tests/Kompas.Mcp.Tests/ 156 тестов: unit + integration (integration требуют КОМПАС) tests/Kompas.Mcp.Tests/ 177 тестов: unit + integration (integration требуют КОМПАС)
libs/kompas-interop/ вендорские interop-сборки КОМПАС (из SDK Samples/Common) libs/kompas-interop/ вендорские interop-сборки КОМПАС (из SDK Samples/Common)
docs/ архитектура, план, презентация, MD-база знаний SDK (Kompas3D_SDK/) docs/ архитектура, план, презентация, MD-база знаний SDK (Kompas3D_SDK/)
usecases/ полигон обкатки подходов (в .gitignore); приёмы поднимаются в навык kompas-3d usecases/ полигон обкатки подходов (в .gitignore); приёмы поднимаются в навык kompas-3d
+9 -6
View File
@@ -1,6 +1,6 @@
# Архитектура MCP-сервера КОМПАС-3D (предлагаемый вариант) # Архитектура MCP-сервера КОМПАС-3D (предлагаемый вариант)
> Статус: **реализовано и работает** (v1+v2+STEP/assembly+direct-edit+inspection+package-A «богатые эскизы»+package-B «shell+rib+sweep+loft»+package-C «массивы и зеркало»+package-D start «переменные»+package-E «offset plane»+`hole`+`hole_counterbore`+`hole_countersink`+`hole_conic`+`draft`+`assembly_add_component`+`assembly_add_mate`). Актуализировано по коду. > Статус: **реализовано и работает** (v1+v2+STEP/assembly+direct-edit+inspection+package-A «богатые эскизы»+package-B «shell+rib+sweep+loft»+package-C «массивы и зеркало»+package-D start «переменные»+package-E «offset plane»+`hole`+`hole_counterbore`+`hole_countersink`+`hole_conic`+`draft`+`assembly_add_component`+`assembly_add_mate`+`drawing_create_standard_views` (начало вехи «2D-чертёж»)). Актуализировано по коду.
> Сопутствующий контекст по COM API КОМПАС — в [`../CLAUDE.md`](../CLAUDE.md). > Сопутствующий контекст по COM API КОМПАС — в [`../CLAUDE.md`](../CLAUDE.md).
> Имена интерфейсов и перечислений сверены со справкой SDK по MD-базе знаний `docs/Kompas3D_SDK/` (навык `kompas-sdk-research`). > Имена интерфейсов и перечислений сверены со справкой SDK по MD-базе знаний `docs/Kompas3D_SDK/` (навык `kompas-sdk-research`).
@@ -112,8 +112,8 @@ MCP client ──stdio──> Host (поток-пул, async)
| Проект | Ответственность | | Проект | Ответственность |
|---|---| |---|---|
| **Kompas.Mcp.Host** | Точка входа. Сборка MCP-сервера (stdio), DI (в т.ч. `ConversionService`), логи в stderr, старт и владение STA-потоком, graceful shutdown. Определения инструментов по категориям (System / Documents / Sketch / Features / Query / Conversion). **Тонкий слой**: валидация аргументов → постановка задачи на STA-поток → форматирование результата/ошибки в MCP-ответ. | | **Kompas.Mcp.Host** | Точка входа. Сборка MCP-сервера (stdio), DI (в т.ч. `ConversionService`), логи в stderr, старт и владение STA-потоком, graceful shutdown. Определения инструментов по категориям (System / Documents / Sketch / Features / Query / Conversion). **Тонкий слой**: валидация аргументов → постановка задачи на STA-поток → форматирование результата/ошибки в MCP-ответ. |
| **Kompas.Mcp.Core** | COM-слой. Менеджер соединения, менеджер документов, построитель эскизов и операций, `ConversionService` (STEP импорт/экспорт), `QueryService` (МЦХ, грани, рёбра, компоненты), `ModelInspectionService` (дерево операций, тела, переменные, drill-down, измерения), `AssemblyService` (вставка компонентов в сборку, namespace `Kompas.Mcp.Core.Assemblies`), снимок. Все паттерны API5/API7 живут здесь. | | **Kompas.Mcp.Core** | COM-слой. Менеджер соединения, менеджер документов, построитель эскизов и операций, `ConversionService` (STEP импорт/экспорт), `QueryService` (МЦХ, грани, рёбра, компоненты), `ModelInspectionService` (дерево операций, тела, переменные, drill-down, измерения), `AssemblyService` (вставка компонентов в сборку, namespace `Kompas.Mcp.Core.Assemblies`), `DrawingService` (стандартные виды чертежа, namespace `Kompas.Mcp.Core.Drawings`), снимок. Все паттерны API5/API7 живут здесь. |
| **Kompas.Mcp.Tests** | Юнит (без COM) + интеграционные (требуют КОМПАС). 156 тестов (98 unit + 58 integration). Все интеграционные классы наследуют `IntegrationTestBase` (`IAsyncLifetime`), который вызывает `DocumentService.CloseAllAsync` после каждого теста. | | **Kompas.Mcp.Tests** | Юнит (без COM) + интеграционные (требуют КОМПАС). 177 тестов (116 unit + 61 integration). Все интеграционные классы наследуют `IntegrationTestBase` (`IAsyncLifetime`), который вызывает `DocumentService.CloseAllAsync` после каждого теста. |
Принцип: **только `Core` знает про COM**. `Host`/Tools оперируют доменными DTO и вызывают `Core`; `Host` не содержит бизнес-логики. Принцип: **только `Core` знает про COM**. `Host`/Tools оперируют доменными DTO и вызывают `Core`; `Host` не содержит бизнес-логики.
@@ -121,7 +121,7 @@ MCP client ──stdio──> Host (поток-пул, async)
--- ---
## 5. Карта инструментов (72 инструмента) ## 5. Карта инструментов (73 инструмента)
Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — `snake_case`. Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — `snake_case`.
@@ -208,6 +208,9 @@ MCP client ──stdio──> Host (поток-пул, async)
- `assembly_add_component` — вставить компонент (.m3d деталь или .a3d подсборка) из файла в активную сборку; задать положение origin (x,y,z мм, мировая СК сборки). Возвращает индекс и имя вставленного компонента. Реализовано через API7: `IParts7.AddFromFile(path, ExternalFile=true, Redraw=true)``IPart7``IPlacement3D.SetOrigin(x,y,z)``UpdatePlacement(true)``RebuildModel(true)`. `RequireActiveAssembly` проверяет `doc.DocumentType == ksDocumentAssembly` до обращения к `TopPart`. Откат при неуспехе через `IFeature7.Delete`. DI: `AddSingleton<AssemblyService>()` в `Program.cs`. - `assembly_add_component` — вставить компонент (.m3d деталь или .a3d подсборка) из файла в активную сборку; задать положение origin (x,y,z мм, мировая СК сборки). Возвращает индекс и имя вставленного компонента. Реализовано через API7: `IParts7.AddFromFile(path, ExternalFile=true, Redraw=true)``IPart7``IPlacement3D.SetOrigin(x,y,z)``UpdatePlacement(true)``RebuildModel(true)`. `RequireActiveAssembly` проверяет `doc.DocumentType == ksDocumentAssembly` до обращения к `TopPart`. Откат при неуспехе через `IFeature7.Delete`. DI: `AddSingleton<AssemblyService>()` в `Program.cs`.
- `assembly_add_mate` — наложить сопряжение между гранями двух компонентов активной сборки по мировым точкам. Параметры: `mateType` (`coincidence` — грани заподлицо | `distance` — зазор `value` мм), `x1,y1,z1`, `x2,y2,z2`, `value=0`. Решатель позиционирует компоненты; возвращает статус `valid`. Реализовано через API7: `top.MateConstraints` (`IMateConstraints3D`) → `Add(MateConstraintType)``BaseObject1/2` (грани через `top.FindObjectsByPoint(x,y,z,FirstLevel=false)`) → `ParamValue``Update()``RebuildModel(true)`; проверка `mate.Valid` (false → ошибка + откат `mate.Owner.Delete`). Enum `MateType { Coincidence, Distance }` в `src/Kompas.Mcp.Core/Assemblies/MateType.cs`; маппинг на `MateConstraintType` (`mc_Coincidence=0`, `mc_Distance=5`). Scope: `coincidence`/`distance` проверены вживую; прочие типы (`parallel`, `concentric`, …) — будущие инкременты. - `assembly_add_mate` — наложить сопряжение между гранями двух компонентов активной сборки по мировым точкам. Параметры: `mateType` (`coincidence` — грани заподлицо | `distance` — зазор `value` мм), `x1,y1,z1`, `x2,y2,z2`, `value=0`. Решатель позиционирует компоненты; возвращает статус `valid`. Реализовано через API7: `top.MateConstraints` (`IMateConstraints3D`) → `Add(MateConstraintType)``BaseObject1/2` (грани через `top.FindObjectsByPoint(x,y,z,FirstLevel=false)`) → `ParamValue``Update()``RebuildModel(true)`; проверка `mate.Valid` (false → ошибка + откат `mate.Owner.Delete`). Enum `MateType { Coincidence, Distance }` в `src/Kompas.Mcp.Core/Assemblies/MateType.cs`; маппинг на `MateConstraintType` (`mc_Coincidence=0`, `mc_Distance=5`). Scope: `coincidence`/`distance` проверены вживую; прочие типы (`parallel`, `concentric`, …) — будущие инкременты.
**Drawing — 2D-чертёж (`DrawingService`, `DrawingTools`, namespace `Kompas.Mcp.Core.Drawings`)**
- `drawing_create_standard_views(partFilePath, scale, x, y)` — создать стандартные ассоциативные виды (спереди/сверху/слева, `ksPtFront/ksPtUp/ksPtLeft`) сохранённой 3D-модели (.m3d/.a3d) на активном чертеже. Реализовано через API7 `IKompasDocument2D.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди", object[]{1,3,5}, x, y, scale, dx=20, dy=20)``object[]` передаётся как SAFEARRAY VT_I4. Проверки: `created==3` строго; непустота — сумма `IView.ObjectCount` новых видов (по разнице `IView.Number`) `>0`, иначе ошибка + откат (`IView.Delete`). Возвращает `DrawingViewsResult{Created,Total}`. Модель должна быть сохранена до вызова.
**Vision — визуальная обратная связь (fallback)** **Vision — визуальная обратная связь (fallback)**
- `model_snapshot` — отрендерить активный 3D-документ в PNG и вернуть **как image-контент MCP**, - `model_snapshot` — отрендерить активный 3D-документ в PNG и вернуть **как image-контент MCP**,
чтобы мультимодальный агент *увидел* промежуточный результат. Использовать для визуально-пространственных чтобы мультимодальный агент *увидел* промежуточный результат. Использовать для визуально-пространственных
@@ -341,14 +344,14 @@ dotnet build -c Release -r win-x64 # сборка
## 10. Дорожная карта ## 10. Дорожная карта
**Реализовано (v1+v2+STEP/assembly+direct-edit+inspection+package-A+package-B+package-C+package-D start+package-E start+hole+hole_counterbore+hole_countersink+hole_conic+draft+assembly_add_component+assembly_add_mate):** документы, эскизы (полный набор 2D-примитивов: линия, окружность, дуга, дуга по 3 точкам, прямоугольник, эллипс, ломаная, правильный многоугольник, сплайн NURBS, точка), **вспомогательная геометрия (`sketch_create_on_offset_plane`)**, выдавливание/вырез, вращение, скругление/фаска, **оболочка (`shell`)**, **ребро жёсткости (`rib`)**, **кинематическая операция (`sweep`)**, **операция по сечениям (`loft`)**, **линейный/круговой массив и зеркало (`linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body`)**, **отверстие (`hole`, API7)**, **цековка (`hole_counterbore`, API7)**, **зенковка (`hole_countersink`, API7)**, **коническое отверстие (`hole_conic`, API7)**, **уклон (`draft`, API5 `ksInclineDefinition`)**, снимок; `get_part_info`, `get_bounding_box`, `list_faces`, `list_edges`; `import_step`, `export_step`, `list_components`; **`move_face`**, **`split_solid_by_plane`**, **`move_body`**, **`boolean_union`** (прямое редактирование и булевы операции); **`describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure`** (структурный осмотр модели); **`create_variable`, `set_variable`, `delete_variable`** (управление переменными, package D start); **`assembly_add_component`** (вставка компонента в сборку, API7, `AssemblyService`); **`assembly_add_mate`** (сопряжения `coincidence`/`distance`, API7, `IMateConstraints3D`). **Реализовано (v1+v2+STEP/assembly+direct-edit+inspection+package-A+package-B+package-C+package-D start+package-E start+hole+hole_counterbore+hole_countersink+hole_conic+draft+assembly_add_component+assembly_add_mate+drawing_create_standard_views):** документы, эскизы (полный набор 2D-примитивов: линия, окружность, дуга, дуга по 3 точкам, прямоугольник, эллипс, ломаная, правильный многоугольник, сплайн NURBS, точка), **вспомогательная геометрия (`sketch_create_on_offset_plane`)**, выдавливание/вырез, вращение, скругление/фаска, **оболочка (`shell`)**, **ребро жёсткости (`rib`)**, **кинематическая операция (`sweep`)**, **операция по сечениям (`loft`)**, **линейный/круговой массив и зеркало (`linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body`)**, **отверстие (`hole`, API7)**, **цековка (`hole_counterbore`, API7)**, **зенковка (`hole_countersink`, API7)**, **коническое отверстие (`hole_conic`, API7)**, **уклон (`draft`, API5 `ksInclineDefinition`)**, снимок; `get_part_info`, `get_bounding_box`, `list_faces`, `list_edges`; `import_step`, `export_step`, `list_components`; **`move_face`**, **`split_solid_by_plane`**, **`move_body`**, **`boolean_union`** (прямое редактирование и булевы операции); **`describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure`** (структурный осмотр модели); **`create_variable`, `set_variable`, `delete_variable`** (управление переменными, package D start); **`assembly_add_component`** (вставка компонента в сборку, API7, `AssemblyService`); **`assembly_add_mate`** (сопряжения `coincidence`/`distance`, API7, `IMateConstraints3D`); **`drawing_create_standard_views`** (стандартные ассоциативные виды чертежа, API7, `DrawingService`).
**Следующие приоритеты:** **Следующие приоритеты:**
1. Пакет D «параметрика» (продолжение) — связь размеров эскизов с переменными (параметрические эскизы через API2D). ⚠️ **Исследовано**: `ksCDimWithVariable` недоступен из внешней автоматизации — см. `docs/superpowers/specs/2026-05-27-parametric-sketch-findings.md`. 1. Пакет D «параметрика» (продолжение) — связь размеров эскизов с переменными (параметрические эскизы через API2D). ⚠️ **Исследовано**: `ksCDimWithVariable` недоступен из внешней автоматизации — см. `docs/superpowers/specs/2026-05-27-parametric-sketch-findings.md`.
2. Пакет E «вспомогательная геометрия» (продолжение) — ось, точка, плоскость по трём точкам / по углу. 2. Пакет E «вспомогательная геометрия» (продолжение) — ось, точка, плоскость по трём точкам / по углу.
3. Рассечение/перемещение тела как MCP-инструменты (`SplitSolids`/`BodyRepositions`) — механика есть, продуктизация не закончена. 3. Рассечение/перемещение тела как MCP-инструменты (`SplitSolids`/`BodyRepositions`) — механика есть, продуктизация не закончена.
4. Свойства документа: `IPropertyMng` / `IPropertyKeeper`. 4. Свойства документа: `IPropertyMng` / `IPropertyKeeper`.
5. Полноценное 2D-черчение: виды, линии/дуги/окружности, размеры (`ILinearDimension`, …), штриховки, тексты. 5. **2D-чертёж (продолжение):** оформление — размеры (`ILinearDimension`, …), рамка, штамп, штриховки, тексты. Стандартные виды реализованы (`drawing_create_standard_views`).
6. Сборки — продолжение: дополнительные типы сопряжений (`parallel`, `perpendicular`, `concentric`, `angle`, `tangency`; `coincidence`/`distance` реализованы); авто-позиционирование компонентов; спецификации (`ISpecification`). 6. Сборки — продолжение: дополнительные типы сопряжений (`parallel`, `perpendicular`, `concentric`, `angle`, `tangency`; `coincidence`/`distance` реализованы); авто-позиционирование компонентов; спецификации (`ISpecification`).
7. Транспорт **HTTP/SSE** — как опция для удалённых клиентов. 7. Транспорт **HTTP/SSE** — как опция для удалённых клиентов.
+3
View File
@@ -188,3 +188,6 @@ stateful-сессии? Пока полагаемся на последовате
### ✅ Сопряжения в сборках — реализованы (coincidence/distance) ### ✅ Сопряжения в сборках — реализованы (coincidence/distance)
**Решено (инкремент 2).** Новый инструмент `assembly_add_mate(mateType, x1,y1,z1, x2,y2,z2, value=0)`. Паттерн API7: `top.MateConstraints` (`IMateConstraints3D`) → `Add(MateConstraintType)``BaseObject1/2` (грани через `top.FindObjectsByPoint(x,y,z,FirstLevel=false)`) → `ParamValue``Update()``RebuildModel(true)`; проверка `mate.Valid`. Типы `mc_Coincidence=0`/`mc_Distance=5` проверены вживую. Enum `MateType` + static `Mates` в `src/Kompas.Mcp.Core/Assemblies/MateType.cs`. Итог: **72 инструмента, 156 тестов (98 unit + 58 integration).** Следующий шаг: прочие типы сопряжений (`parallel`, `perpendicular`, `concentric`, `angle`, `tangency`). **Решено (инкремент 2).** Новый инструмент `assembly_add_mate(mateType, x1,y1,z1, x2,y2,z2, value=0)`. Паттерн API7: `top.MateConstraints` (`IMateConstraints3D`) → `Add(MateConstraintType)``BaseObject1/2` (грани через `top.FindObjectsByPoint(x,y,z,FirstLevel=false)`) → `ParamValue``Update()``RebuildModel(true)`; проверка `mate.Valid`. Типы `mc_Coincidence=0`/`mc_Distance=5` проверены вживую. Enum `MateType` + static `Mates` в `src/Kompas.Mcp.Core/Assemblies/MateType.cs`. Итог: **72 инструмента, 156 тестов (98 unit + 58 integration).** Следующий шаг: прочие типы сопряжений (`parallel`, `perpendicular`, `concentric`, `angle`, `tangency`).
### ✅ Стандартные виды чертежа — реализованы (начало вехи «2D-чертёж»)
**Решено.** Новый инструмент `drawing_create_standard_views(partFilePath, scale, x, y)` — создаёт ассоциативные виды (спереди/сверху/слева) на активном чертеже. Реализовано через API7 `DrawingService` (namespace `Kompas.Mcp.Core.Drawings`), `DrawingTools`. Паттерн: `IKompasDocument2D.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди", object[]{1,3,5}, x, y, scale, 20, 20)``object[]` как SAFEARRAY VT_I4. Проверено вживую: `Views.Count` 1→4, `ObjectCount>0` сразу (rebuild не нужен). Итог: **73 инструмента, 177 тестов (116 unit + 61 integration).** Следующий шаг: оформление чертежа (размеры, рамка, штамп).
+8 -6
View File
@@ -128,7 +128,7 @@
<!-- HERO --> <!-- HERO -->
<header class="hero"> <header class="hero">
<div class="wrap"> <div class="wrap">
<span class="badge"><span class="dot" style="background:var(--green);box-shadow:0 0 10px var(--green)"></span> Статус: v3+ работает — эскиз→операции→STEP→сборка→move_face→структурный осмотр→богатые эскизы→shell+rib+sweep+loft+массивы+зеркало+hole+hole_counterbore+hole_countersink+hole_conic+draft+переменные+assembly_add_component+assembly_add_mate (72 инструмента)</span> <span class="badge"><span class="dot" style="background:var(--green);box-shadow:0 0 10px var(--green)"></span> Статус: v3+ работает — эскиз→операции→STEP→сборка→move_face→структурный осмотр→богатые эскизы→shell+rib+sweep+loft+массивы+зеркало+hole+hole_counterbore+hole_countersink+hole_conic+draft+переменные+assembly_add_component+assembly_add_mate+drawing_create_standard_views (73 инструмента)</span>
<h1>КОМПАС-3D <span class="g">MCP-сервер</span><br>управление CAD языком LLM</h1> <h1>КОМПАС-3D <span class="g">MCP-сервер</span><br>управление CAD языком LLM</h1>
<p class="lead">MCP-сервер, который превращает операции КОМПАС-3D — создание документов, <p class="lead">MCP-сервер, который превращает операции КОМПАС-3D — создание документов,
эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом — эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом —
@@ -261,7 +261,7 @@
впереди.</p> впереди.</p>
<div class="prog"> <div class="prog">
<div class="progrow"><span>Общий прогресс</span><span>v3+: STEP · move_face · структурный осмотр · богатые эскизы (пакет A) · shell+rib+sweep+loft (пакет B) · массивы+зеркало (пакет C) · переменные (пакет D start) · offset-plane (пакет E start) · hole (API7) · hole_counterbore · hole_countersink · hole_conic · draft (API5) · assembly_add_component · assembly_add_mate · навык kompas-3d · 72 инструмента · 156 тестов</span></div> <div class="progrow"><span>Общий прогресс</span><span>v3+: STEP · move_face · структурный осмотр · богатые эскизы (пакет A) · shell+rib+sweep+loft (пакет B) · массивы+зеркало (пакет C) · переменные (пакет D start) · offset-plane (пакет E start) · hole (API7) · hole_counterbore · hole_countersink · hole_conic · draft (API5) · assembly_add_component · assembly_add_mate · drawing_create_standard_views · навык kompas-3d · 73 инструмента · 177 тестов</span></div>
<div class="bar"><i style="width:97%"></i></div> <div class="bar"><i style="width:97%"></i></div>
</div> </div>
@@ -297,7 +297,7 @@
<!-- TOOLS --> <!-- TOOLS -->
<div class="panel"> <div class="panel">
<h3>Инструменты v3+ <span class="tag done">ГОТОВО</span></h3> <h3>Инструменты v3+ <span class="tag done">ГОТОВО</span></h3>
<p class="meta">72 инструмента, отдаются по MCP-протоколу</p> <p class="meta">73 инструмента, отдаются по MCP-протоколу</p>
<ul class="list"> <ul class="list">
<li class="muted"><span class="mark ok"></span><span class="t"><code>System</code>: connect · status · set_visible</span></li> <li class="muted"><span class="mark ok"></span><span class="t"><code>System</code>: connect · status · set_visible</span></li>
<li class="muted"><span class="mark ok"></span><span class="t"><code>Documents</code>: create · open · save · save_as · close · active</span></li> <li class="muted"><span class="mark ok"></span><span class="t"><code>Documents</code>: create · open · save · save_as · close · active</span></li>
@@ -310,7 +310,8 @@
<li class="muted"><span class="mark ok"></span><span class="t"><code>Variables</code>: <b>create_variable</b> · <b>set_variable</b> · <b>delete_variable</b> (пакет D)</span></li> <li class="muted"><span class="mark ok"></span><span class="t"><code>Variables</code>: <b>create_variable</b> · <b>set_variable</b> · <b>delete_variable</b> (пакет D)</span></li>
<li class="muted"><span class="mark ok"></span><span class="t"><code>Conversion</code>: import_step · export_step</span></li> <li class="muted"><span class="mark ok"></span><span class="t"><code>Conversion</code>: import_step · export_step</span></li>
<li class="muted"><span class="mark ok"></span><span class="t"><code>Assembly</code>: <b>assembly_add_component</b> (вставка компонента .m3d/.a3d в сборку, API7) · <b>assembly_add_mate</b> (сопряжения coincidence/distance, API7)</span></li> <li class="muted"><span class="mark ok"></span><span class="t"><code>Assembly</code>: <b>assembly_add_component</b> (вставка компонента .m3d/.a3d в сборку, API7) · <b>assembly_add_mate</b> (сопряжения coincidence/distance, API7)</span></li>
<li><span class="mark wip"></span><span>пакет E (ось, угловая плоскость) · пакет D параметрика: параметрические эскизы (API2D) — <b>исследовано, недоступно через COM API</b> · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency)</span></li> <li class="muted"><span class="mark ok"></span><span class="t"><code>Drawing</code>: <b>drawing_create_standard_views</b> (стандартные ассоциативные виды чертежа, API7)</span></li>
<li><span class="mark wip"></span><span>2D-чертёж: оформление (размеры, рамка, штамп) · пакет E (ось, угловая плоскость) · пакет D параметрика: параметрические эскизы (API2D) — <b>исследовано, недоступно через COM API</b> · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency)</span></li>
</ul> </ul>
</div> </div>
@@ -321,7 +322,7 @@
<ul class="list"> <ul class="list">
<li class="muted"><span class="mark ok"></span><span class="t">Unit-тесты — без COM: диспетчер, enum-маппинг, StepFormat, InspectionText</span></li> <li class="muted"><span class="mark ok"></span><span class="t">Unit-тесты — без COM: диспетчер, enum-маппинг, StepFormat, InspectionText</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Интеграционные тесты с КОМПАС: подключение, документы, снимок, цикл, STEP round-trip, сборка, move_face, инспекция</span></li> <li class="muted"><span class="mark ok"></span><span class="t">Интеграционные тесты с КОМПАС: подключение, документы, снимок, цикл, STEP round-trip, сборка, move_face, инспекция</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Итого 156 тестов зелёных (98 unit + 58 integration)</span></li> <li class="muted"><span class="mark ok"></span><span class="t">Итого 177 тестов зелёных (116 unit + 61 integration)</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Сборка <code>kompas-mcp.exe</code>, README с конфигом клиента</span></li> <li class="muted"><span class="mark ok"></span><span class="t">Сборка <code>kompas-mcp.exe</code>, README с конфигом клиента</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Навык <code>kompas-3d</code> (методика) + полигон <code>usecases/</code></span></li> <li class="muted"><span class="mark ok"></span><span class="t">Навык <code>kompas-3d</code> (методика) + полигон <code>usecases/</code></span></li>
<li><span class="mark todo"></span><span>In-process тест MCP-клиента, релизный publish</span></li> <li><span class="mark todo"></span><span>In-process тест MCP-клиента, релизный publish</span></li>
@@ -350,7 +351,8 @@
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">отверстия под крепёж · готово ✓</div><h4>Цековка, зенковка и конус</h4><p><b>hole_counterbore</b> (ksHTCounterbore) · <b>hole_countersink</b> (ksHTCountersinking) · <b>hole_conic</b> (ksHTConic, IConicHoleParameters) · HoleCore рефакторен (configure-колбэк) · 70 инструментов · 119 тестов</p></div> <div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">отверстия под крепёж · готово ✓</div><h4>Цековка, зенковка и конус</h4><p><b>hole_counterbore</b> (ksHTCounterbore) · <b>hole_countersink</b> (ksHTCountersinking) · <b>hole_conic</b> (ksHTConic, IConicHoleParameters) · HoleCore рефакторен (configure-колбэк) · 70 инструментов · 119 тестов</p></div>
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">сборки start · готово ✓</div><h4>Вставка компонентов</h4><p><b>assembly_add_component</b> (API7 IParts7.AddFromFile + IPlacement3D)</p></div> <div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">сборки start · готово ✓</div><h4>Вставка компонентов</h4><p><b>assembly_add_component</b> (API7 IParts7.AddFromFile + IPlacement3D)</p></div>
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">сборки инкремент 2 · готово ✓</div><h4>Сопряжения</h4><p><b>assembly_add_mate</b> (coincidence/distance, API7 IMateConstraints3D) · IntegrationTestBase · 72 инструмента · 156 тестов</p></div> <div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">сборки инкремент 2 · готово ✓</div><h4>Сопряжения</h4><p><b>assembly_add_mate</b> (coincidence/distance, API7 IMateConstraints3D) · IntegrationTestBase · 72 инструмента · 156 тестов</p></div>
<div class="tl cur"><div class="ph">далее · в работе</div><h4>пакет E продолжение</h4><p>вспомогательная геометрия (ось, угловая плоскость) · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency) · пакет D параметрика — параметрические эскизы недоступны через COM API</p></div> <div class="tl cur"><div class="ph">2D-чертёж · начато ▶</div><h4>Стандартные виды</h4><p><b>drawing_create_standard_views</b> (AddStandartViews, API7) · 73 инструмента · 177 тестов · далее: оформление (размеры, рамка, штамп)</p></div>
<div class="tl"><div class="ph">далее · в очереди</div><h4>пакет E + прочее</h4><p>вспомогательная геометрия (ось, угловая плоскость) · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency) · пакет D параметрика — параметрические эскизы недоступны через COM API</p></div>
</div> </div>
</div> </div>
</section> </section>
@@ -0,0 +1,128 @@
# Дизайн: стандартные виды чертежа (drawing_create_standard_views) через API7
**Дата:** 2026-05-27
**Статус:** реализовано и проверено (3 интеграционных + 18 unit, всего 177); ревью Codex спека (10) и реализации (7) учтено
## Правки по ревью реализации Codex (7 замечаний)
- **#1** непустоту проверяем у ВНОВЬ созданных видов (по разнице `IView.Number` до/после), а не по
всей коллекции — на непустом чертеже прежняя геометрия не маскирует пустые новые виды.
- **#3** откат: при `created != 3` или пустых видах удаляем созданные виды (`IView.Delete`) → чертёж
не остаётся в полу-состоянии.
- **#2** ленивость `ObjectCount` опровергнута эмпирически (happy-тест зелёный = геометрия есть сразу,
rebuild не нужен). **#7** добавлен unit-тест нормализации кириллического пути.
- **#4** повторный вызов намеренно добавляет ещё набор (дубликаты в разных `x,y` легитимны) —
отражено в описании инструмента. **#5** обход `IViews` уточнён (по `Number`); точечный release RCW
отклонён (v2-2). **#6** доп. негативы (.a3d, пустой валидный .m3d) — маргинальны, отложены.
## Решения по ревью Codex
- **#1** файл модели: `File.Exists` И `FileInfo.Length > 0` (несохранённый/битый файл).
- **#3/#5/#6** непустота видов — гарантия В СЕРВИСЕ: после создания суммируем `IView.ObjectCount`
по коллекции; если 0 → ошибка «виды пусты (модель не спроецировалась)». Это и проверяет, что
rebuild не нужен (геометрия уже есть). Координатные проверки (`GetProjectionPoint`) — избыточны.
- **#8** строгий контракт: `created == 3` (иначе ошибка с `before/after/ok`), а не `> 0`.
- **#9** позиция главного вида — опциональные `x`,`y` (дефолт 100/150), чтобы повторный вызов не
клал виды стопкой; `dx`/`dy`=20 фиксированы (YAGNI).
- **#10** возврат — record `DrawingViewsResult { Created, Total }` (не голый int) для отчёта и
будущей адресации видов.
- **#2** негативный unit-тест на чужое расширение (`.step`) — в плане.
- **#4** точечный release RCW отклонён (конвенция v2-2, как `AssemblyService`). **#7** неверный
`ProjectionName` не тестируем — `"#Спереди"` захардкожен, не управляется пользователем.
## Цель
Веха 2 — **2D-ЧЕРТЁЖ** (новый класс функционала после деталей и сборок). Инкремент 1: создать на
активном чертеже **стандартные ассоциативные виды** (спереди/сверху/слева) по сохранённой 3D-модели
`.m3d`/`.a3d`. Это фундамент чертежа — без видов лист пуст. Чертёж проверяется не объёмом, а
**числом созданных видов**.
## Спайк: механизм подтверждён вживую (НЕ разучивать)
Спайк (`DrawingSpikeTests`, зелёный) подтвердил путь API7:
```
IKompasDocument2D doc2d = (IKompasDocument2D)app.ActiveDocument; // активный чертёж
IViews views = doc2d.ViewsAndLayersManager.Views; // COM-свойства (не QI)
object[] projTypes = { 1, 3, 5 }; // ksPtFront=1, ksPtUp=3, ksPtLeft=5 (SAFEARRAY VT_I4)
bool ok = views.AddStandartViews(path, "#Спереди", projTypes, X:100, Y:150, Scale:1.0, DX:20, DY:20);
int count = views.Count;
```
**Проверенные факты:**
- `object[] { 1, 3, 5 }` **маршалится в SAFEARRAY VT_I4 без проблем** (риск из ревью SDK снят).
- Новый чертёж стартует с `Views.Count == 1` (системный вид #0). После `AddStandartViews` с тремя
кодами проекций `Count == 4`**создано ровно 3 вида** (спереди/сверху/слева). `ok == True`.
- `ProjectionName = "#Спереди"` — ориентация главного вида (стандартное имя проекции 3D-модели).
- `IKompasDocument2D` напрямую (без QI к `IKompasDocument2D1`) даёт `ViewsAndLayersManager.Views`
(подтверждено рефлексией interop — отличие от ревью SDK).
- Явный rebuild/save после `AddStandartViews` НЕ нужен — виды уже в документе (`Count` сразу 4).
**Параметры `AddStandartViews(FileName, ProjectionName, ProjectionsTypes, X, Y, Scale, DX, DY)`:**
- `FileName` — путь к `.m3d`/`.a3d`.
- `ProjectionName` — ориентация главного вида (`"#Спереди"`).
- `ProjectionsTypes``SAFEARRAY VT_I4` кодов `ksRelativeProjectionTypeEnum`: `ksPtFront=1`,
`ksPtRear=2`, `ksPtUp=3`, `ksPtDown=4`, `ksPtLeft=5`, `ksPtRight=6`, `ksPtIsoXYZ=7`.
- `X, Y` — точка привязки главного вида на листе, мм.
- `Scale` — масштаб (1.0 = 1:1).
- `DX, DY` — зазоры между видами, мм. Возврат: `bool` (успех).
## Архитектура
Новый класс (как сборки — отдельный от `PartModeler`). Сервис `DrawingService` (API7), инструменты
`DrawingTools`, namespace `Kompas.Mcp.Core.Drawings`.
- `src/Kompas.Mcp.Core/Drawings/DrawingService.cs` — сервис.
- `src/Kompas.Mcp.Core/Drawings/DrawingValidation.cs` — чистые static-валидаторы (unit).
- `src/Kompas.Mcp.Host/Tools/DrawingTools.cs` — MCP-инструмент.
- DI: `AddSingleton<DrawingService>()` в `Program.cs`.
## MCP-инструмент
| Инструмент | Параметры | Поведение |
|---|---|---|
| `drawing_create_standard_views` | `partFilePath: string`, `scale=1.0` | На активном чертеже создать стандартные ассоциативные виды (спереди/сверху/слева) сохранённой детали/сборки. Возвращает число созданных видов. |
Контракт: активный документ — чертёж (`ksDocumentDrawing`); файл модели существует. Набор видов
фиксирован (спереди+сверху+слева, 3 вида); позиция/зазоры — дефолты (X=100, Y=150, DX=20, DY=20 мм).
Выбор набора видов / позиции / изометрии — будущие инкременты (YAGNI).
## Валидация (чистые static, unit-тест)
`DrawingValidation`:
- `NormalizeModelPath(string? path)``Trim` → непустота → расширение `.m3d`/`.a3d``Path.GetFullPath`.
- `RequireScale(double scale)` — конечный и `> 0`.
`File.Exists` — в сервисе (`FileNotFoundException` с абсолютным путём).
## Проверка активного документа
`RequireActiveDrawing()` — раздельные ошибки: нет документа / тип != `ksDocumentDrawing` / не
приводится к `IKompasDocument2D`.
## Реализация
`CreateStandardViewsAsync(partFilePath, scale, ct)` — на STA-потоке: валидация (`NormalizeModelPath`,
`RequireScale`) → `File.Exists``RequireActiveDrawing``views.Count` (before) →
`AddStandartViews(path, "#Спереди", {1,3,5}, 100, 150, scale, 20, 20)` (результат проверяем: FALSE →
ошибка) → `created = views.Count - before` (ожидаем 3; если 0 — ошибка «виды не созданы»). Возврат —
число созданных видов. Транзитные RCW не освобождаем точечно (долг v2-2).
## Тестирование
### Unit (`DrawingValidationTests`)
- `NormalizeModelPath` — бросает на пустом/чужом расширении (`.step`); принимает `.m3d`/`.a3d`
(регистр, пробелы), возвращает абсолютный путь.
- `RequireScale` — бросает на 0/отрицательном/NaN/Infinity; принимает положительный.
### Integration (`DrawingTests`)
Паттерн: построить деталь 40×30×20 → `SaveAsAsync(.m3d)` → проверить файл → `CloseAsync` → создать
чертёж → `CreateStandardViewsAsync` → проверить → `finally CloseAsync` (база `IntegrationTestBase`).
1. **Создание видов**: `AddStandartViews` → возврат `== 3`; `Views.Count` вырос на 3 (с 1 до 4).
2. **Не-чертёж**: активный документ — деталь → `CreateStandardViewsAsync` бросает (понятная ошибка).
(Сценарий 1 = промотированный спайк; спайк-файл удаляется.)
## Дальнейшее (вне спека)
- Выбор набора видов (+изометрия, +сечения), позиция/масштаб листа, размеры/обозначения на видах,
рамка/основная надпись.
@@ -0,0 +1,112 @@
using System.Runtime.Versioning;
using Kompas.Mcp.Core.Threading;
using Kompas6Constants;
using KompasAPI7;
namespace Kompas.Mcp.Core.Drawings;
/// <summary>
/// Операции с 2D-чертежами через API7 (<c>IViews</c>). Все вызовы — на STA-потоке
/// <see cref="KompasDispatcher"/>.
/// </summary>
[SupportedOSPlatform("windows")]
public sealed class DrawingService
{
private readonly KompasSession _session;
private readonly KompasDispatcher _dispatcher;
public DrawingService(KompasSession session, KompasDispatcher dispatcher)
{
_session = session ?? throw new ArgumentNullException(nameof(session));
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
}
/// <summary>
/// Создать на активном чертеже стандартные ассоциативные виды (спереди/сверху/слева) сохранённой
/// 3D-модели <paramref name="partFilePath"/> (.m3d/.a3d). Главный вид привязывается к точке
/// (<paramref name="x"/>,<paramref name="y"/>) листа, мм; масштаб — <paramref name="scale"/>.
/// </summary>
public Task<DrawingViewsResult> CreateStandardViewsAsync(string partFilePath, double scale,
double x, double y, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() => CreateStandardViewsCore(partFilePath, scale, x, y), ct);
// Стандартная тройка проекций: ksPtFront=1, ksPtUp=3, ksPtLeft=5 (ksRelativeProjectionTypeEnum).
private static readonly object[] StandardProjections = { 1, 3, 5 };
private const int ExpectedCreated = 3;
private const double GapMm = 20; // зазоры между видами (DX=DY)
private DrawingViewsResult CreateStandardViewsCore(string partFilePath, double scale, double x, double y)
{
var path = DrawingValidation.NormalizeModelPath(partFilePath);
DrawingValidation.RequireScale(scale);
if (!double.IsFinite(x) || !double.IsFinite(y))
throw new ArgumentException("Координаты привязки вида должны быть конечными числами.");
if (!File.Exists(path))
throw new FileNotFoundException($"Файл модели не найден: {path}", path);
if (new FileInfo(path).Length == 0)
throw new InvalidOperationException($"Файл модели пуст (не сохранён?): {path}");
var views = RequireActiveDrawingViews();
// Снимок номеров существующих видов — чтобы потом отличить созданные ИМЕННО этим вызовом
// (на чертеже уже есть системный вид и могут быть прежние виды).
var beforeNumbers = CollectViewNumbers(views);
if (!views.AddStandartViews(path, "#Спереди", StandardProjections, x, y, scale, GapMm, GapMm))
throw new InvalidOperationException(
$"AddStandartViews вернул FALSE — виды не созданы (файл модели или проекция недоступны): {path}");
var newViews = CollectNewViews(views, beforeNumbers);
try
{
if (newViews.Count != ExpectedCreated)
throw new InvalidOperationException(
$"Ожидалось {ExpectedCreated} вида, создано {newViews.Count}.");
// Гарантия непустоты: у созданных видов должна быть геометрия (иначе модель не
// спроецировалась). Проверяем ИМЕННО новые виды, не всю коллекцию.
if (newViews.Sum(v => v.ObjectCount) <= 0)
throw new InvalidOperationException(
"Стандартные виды созданы, но пусты (модель не спроецировалась). Проверьте файл модели.");
return new DrawingViewsResult { Created = newViews.Count, Total = views.Count };
}
catch
{
// Откат: удаляем виды, созданные этим вызовом, чтобы чертёж не остался в полу-состоянии.
foreach (var v in newViews)
try { v.Delete(); } catch { /* лучшее усилие */ }
throw;
}
}
private IViews RequireActiveDrawingViews()
{
var doc = _session.Application.ActiveDocument
?? throw new InvalidOperationException("Нет активного документа. Создайте чертёж: document_create drawing.");
if (doc.DocumentType != DocumentTypeEnum.ksDocumentDrawing)
throw new InvalidOperationException(
$"Активный документ не является чертежом (тип: {doc.DocumentType}). Создайте чертёж: document_create drawing.");
if (doc is not IKompasDocument2D doc2d)
throw new InvalidOperationException("Активный документ не приводится к IKompasDocument2D.");
return doc2d.ViewsAndLayersManager?.Views
?? throw new InvalidOperationException("Не удалось получить коллекцию видов чертежа.");
}
private static HashSet<int> CollectViewNumbers(IViews views)
{
var set = new HashSet<int>();
foreach (var o in views)
if (o is IView v)
set.Add(v.Number);
return set;
}
private static List<IView> CollectNewViews(IViews views, HashSet<int> beforeNumbers)
{
var list = new List<IView>();
foreach (var o in views)
if (o is IView v && !beforeNumbers.Contains(v.Number))
list.Add(v);
return list;
}
}
@@ -0,0 +1,35 @@
namespace Kompas.Mcp.Core.Drawings;
/// <summary>Чистые валидаторы операций чертежа (без COM) — выделены для unit-тестов.</summary>
public static class DrawingValidation
{
/// <summary>Допустимые расширения файла-источника модели: деталь (.m3d) и сборка (.a3d).</summary>
private static readonly HashSet<string> AllowedExtensions =
new(StringComparer.OrdinalIgnoreCase) { ".m3d", ".a3d" };
/// <summary>
/// Нормализовать путь к файлу 3D-модели: обрезать пробелы, проверить непустоту и расширение
/// (.m3d/.a3d), вернуть абсолютный путь. Существование файла НЕ проверяет (это делает сервис).
/// </summary>
public static string NormalizeModelPath(string? path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException("Путь к файлу модели не задан.", nameof(path));
var trimmed = path.Trim();
var ext = Path.GetExtension(trimmed);
if (!AllowedExtensions.Contains(ext))
throw new ArgumentException(
$"Недопустимое расширение файла модели '{ext}'. Ожидается .m3d (деталь) или .a3d (сборка).",
nameof(path));
return Path.GetFullPath(trimmed);
}
/// <summary>Проверить масштаб чертежа: конечный и строго положительный.</summary>
public static void RequireScale(double scale)
{
if (!double.IsFinite(scale) || scale <= 0)
throw new ArgumentOutOfRangeException(nameof(scale), "Масштаб должен быть конечным и > 0.");
}
}
@@ -0,0 +1,11 @@
namespace Kompas.Mcp.Core.Drawings;
/// <summary>Результат создания стандартных видов на чертеже.</summary>
public sealed record DrawingViewsResult
{
/// <summary>Сколько видов создано этим вызовом.</summary>
public required int Created { get; init; }
/// <summary>Общее число видов на чертеже после операции (включая системный вид).</summary>
public required int Total { get; init; }
}
+2
View File
@@ -2,6 +2,7 @@ using Kompas.Mcp.Core;
using Kompas.Mcp.Core.Assemblies; using Kompas.Mcp.Core.Assemblies;
using Kompas.Mcp.Core.Conversion; using Kompas.Mcp.Core.Conversion;
using Kompas.Mcp.Core.Documents; using Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Drawings;
using Kompas.Mcp.Core.Editing; using Kompas.Mcp.Core.Editing;
using Kompas.Mcp.Core.Modeling; using Kompas.Mcp.Core.Modeling;
using Kompas.Mcp.Core.Query; using Kompas.Mcp.Core.Query;
@@ -25,6 +26,7 @@ builder.Services.AddSingleton<DocumentService>();
builder.Services.AddSingleton<PartModeler>(); builder.Services.AddSingleton<PartModeler>();
builder.Services.AddSingleton<HoleService>(); builder.Services.AddSingleton<HoleService>();
builder.Services.AddSingleton<AssemblyService>(); builder.Services.AddSingleton<AssemblyService>();
builder.Services.AddSingleton<DrawingService>();
builder.Services.AddSingleton<VariableService>(); builder.Services.AddSingleton<VariableService>();
builder.Services.AddSingleton<SnapshotService>(); builder.Services.AddSingleton<SnapshotService>();
builder.Services.AddSingleton<QueryService>(); builder.Services.AddSingleton<QueryService>();
+29
View File
@@ -0,0 +1,29 @@
using System.ComponentModel;
using Kompas.Mcp.Core;
using Kompas.Mcp.Core.Drawings;
using ModelContextProtocol.Server;
namespace Kompas.Mcp.Host.Tools;
/// <summary>Инструменты 2D-чертежа (виды, оформление).</summary>
[McpServerToolType]
public sealed class DrawingTools(KompasSession session, DrawingService drawing)
{
[McpServerTool(Name = "drawing_create_standard_views")]
[Description("На активном чертеже создать стандартные ассоциативные виды (спереди, сверху, слева) " +
"сохранённой 3D-модели (.m3d деталь / .a3d сборка). Активный документ должен быть чертежом " +
"(document_create drawing); модель должна быть сохранена в файл (document_save_as). " +
"scale — масштаб (1.0 = 1:1); x,y — точка привязки главного вида на листе (мм). " +
"Повторный вызов добавит ещё один набор видов — задайте другие x,y, чтобы не накладывались. " +
"Возвращает число созданных видов.")]
public async Task<string> CreateStandardViews(
[Description("Путь к файлу 3D-модели (.m3d/.a3d)")] string partFilePath,
[Description("Масштаб видов (1.0 = 1:1)")] double scale = 1.0,
[Description("X привязки главного вида на листе, мм")] double x = 100,
[Description("Y привязки главного вида на листе, мм")] double y = 150)
{
await session.ConnectAsync();
var r = await drawing.CreateStandardViewsAsync(partFilePath, scale, x, y);
return $"Создано {r.Created} стандартных вида на чертеже (всего видов: {r.Total}, масштаб {scale}).";
}
}
@@ -0,0 +1,56 @@
using Kompas.Mcp.Core.Drawings;
namespace Kompas.Mcp.Tests;
[Trait("Category", "Unit")]
public sealed class DrawingValidationTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void NormalizeModelPath_throws_on_empty(string? path)
=> Assert.Throws<ArgumentException>(() => DrawingValidation.NormalizeModelPath(path));
[Theory]
[InlineData(@"C:\m\part.step")]
[InlineData(@"C:\m\part.txt")]
[InlineData(@"C:\m\part")]
public void NormalizeModelPath_throws_on_wrong_extension(string path)
=> Assert.Throws<ArgumentException>(() => DrawingValidation.NormalizeModelPath(path));
[Theory]
[InlineData(@"C:\m\part.m3d", ".m3d")]
[InlineData(@"C:\m\frame.a3d", ".a3d")]
[InlineData(@" C:\m\part.M3D ", ".m3d")]
[InlineData(@"C:\модели\деталь.m3d", ".m3d")] // кириллица в пути
public void NormalizeModelPath_accepts_model_files(string path, string expectedExt)
{
var result = DrawingValidation.NormalizeModelPath(path);
Assert.True(Path.IsPathFullyQualified(result));
Assert.EndsWith(expectedExt, result, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void NormalizeModelPath_preserves_cyrillic()
{
var result = DrawingValidation.NormalizeModelPath(@"C:\модели\деталь.m3d");
Assert.Contains("деталь", result);
Assert.Contains("модели", result);
}
[Theory]
[InlineData(0)]
[InlineData(-1)]
[InlineData(double.NaN)]
[InlineData(double.PositiveInfinity)]
public void RequireScale_throws_on_non_positive_or_non_finite(double scale)
=> Assert.Throws<ArgumentOutOfRangeException>(() => DrawingValidation.RequireScale(scale));
[Theory]
[InlineData(1.0)]
[InlineData(0.5)]
[InlineData(2.0)]
public void RequireScale_accepts_positive(double scale)
=> Assert.Null(Record.Exception(() => DrawingValidation.RequireScale(scale)));
}
@@ -0,0 +1,88 @@
using Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Drawings;
using Kompas.Mcp.Core.Modeling;
namespace Kompas.Mcp.Tests.Integration;
/// <summary>Интеграция: стандартные ассоциативные виды чертежа (API7 IViews.AddStandartViews).</summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class DrawingTests : IntegrationTestBase
{
private readonly DocumentService _docs;
private readonly PartModeler _modeler;
private readonly DrawingService _drawing;
public DrawingTests(KompasFixture fx) : base(fx)
{
_docs = new DocumentService(fx.Session, fx.Dispatcher);
_modeler = new PartModeler(fx.Session, fx.Dispatcher);
_drawing = new DrawingService(fx.Session, fx.Dispatcher);
}
private async Task<string> BuildAndSaveBoxAsync()
{
var path = TestPaths.NewFile(".m3d");
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
await _modeler.AddRectangleAsync(s, 0, 0, 40, 30);
await _modeler.CloseSketchAsync(s);
await _modeler.ExtrudeAsync(s, depth: 20);
await _modeler.RebuildAsync();
await _docs.SaveAsAsync(path);
}
finally { await _docs.CloseAsync(save: false); }
Assert.True(File.Exists(path), $"Файл детали не сохранён: {path}");
Assert.True(new FileInfo(path).Length > 0, "Файл детали пуст.");
return path;
}
[Fact]
public async Task Creates_three_standard_views_on_drawing()
{
var partFile = await BuildAndSaveBoxAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
var r = await _drawing.CreateStandardViewsAsync(partFile, scale: 1.0, x: 100, y: 150);
// Создано ровно 3 вида (спереди/сверху/слева); всего на чертеже +3 к системному виду.
Assert.Equal(3, r.Created);
Assert.True(r.Total >= 4, $"Ожидалось ≥4 видов всего (системный + 3), получено {r.Total}.");
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Rejects_non_drawing_document()
{
// Активный документ — деталь (не чертёж): понятная ошибка.
var partFile = await BuildAndSaveBoxAsync();
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
await Assert.ThrowsAsync<InvalidOperationException>(
() => _drawing.CreateStandardViewsAsync(partFile, scale: 1.0, x: 100, y: 150));
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Missing_model_file_throws()
{
var missing = Path.Combine(TestPaths.Scratch, $"no_such_{Guid.NewGuid():N}.m3d");
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
await Assert.ThrowsAsync<FileNotFoundException>(
() => _drawing.CreateStandardViewsAsync(missing, scale: 1.0, x: 100, y: 150));
}
finally { await _docs.CloseAsync(save: false); }
}
}