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:
@@ -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
|
||||||
|
|||||||
@@ -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** — как опция для удалённых клиентов.
|
||||||
|
|
||||||
|
|||||||
@@ -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).** Следующий шаг: оформление чертежа (размеры, рамка, штамп).
|
||||||
|
|||||||
@@ -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,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>();
|
||||||
|
|||||||
@@ -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); }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user