diff --git a/CLAUDE.md b/CLAUDE.md index b82a972..23f4cec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,12 +24,12 @@ The base is committed to the repo (canonical) — no regeneration step. It cover ## Current state **Status:** v1 + v2 + STEP/assembly + direct B-rep edit + structural inspection + 2D drawings — implemented and working (full sketch→feature→inspect→STEP round-trip + `move_face` + `describe_model` validated end-to-end). -Stack: **.NET 8 (`net8.0-windows`, x64), C#**, MCP via the official `ModelContextProtocol` SDK over **stdio**. **82 MCP tools, 308 tests green (188 unit + 120 integration).** +Stack: **.NET 8 (`net8.0-windows`, x64), C#**, MCP via the official `ModelContextProtocol` SDK over **stdio**. **83 MCP tools, 331 tests green (201 unit + 130 integration).** Where to look (single source of truth — do **not** duplicate these lists here): -- **Full tool catalog** (by group, all 82) → [`README.md`](README.md) §«Инструменты». +- **Full tool catalog** (by group, all 83) → [`README.md`](README.md) §«Инструменты». - **Roadmap / what's already done** → [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) §10. -- **Backlog / what's left** → [`docs/TODO.md`](docs/TODO.md) (canonical; top of «2D-ЧЕРТЁЖ» = next priority. Associative diametral/radial→circle binding (`associate` flag) and sheet format/orientation (`drawing_set_sheet_format`) done — next: arc/angular/rough binding, leaders, bases, tolerances; plus extra mate types, package E continuation). +- **Backlog / what's left** → [`docs/TODO.md`](docs/TODO.md) (canonical; top of «2D-ЧЕРТЁЖ» = next priority. Associative diametral/radial→circle binding (`associate` flag), sheet format (`drawing_set_sheet_format`), leaders (`drawing_add_leader`) done — next: bases, tolerances, arc/angular/rough binding; plus extra mate types, package E continuation). - **Design decisions / caveats** → [`docs/OPEN_QUESTIONS.md`](docs/OPEN_QUESTIONS.md). Known caveat («Ревью v2»): boss/cut direction on a selected face depends on face-normal orientation — `forward` may need flipping; the agent picks via snapshot or `describe_face` normal. - **Verified COM call-chains & gotchas** → «Key implementation facts» below + memory files (`kompas-*-api*.md`). @@ -88,6 +88,7 @@ dotnet run --project src/Kompas.Mcp.Host # start the MCP server (stdio) - `drawing_add_text` — **text lives in `IDrawingContainer`, NOT `ISymbols2DContainer`, and NOT in `IView.ObjectCount`**: `(IDrawingContainer)view.DrawingTexts.Add()`/`IDrawingText` (`X/Y/Angle`) + `(IText)dt.Str` = text; count via `DrawingTexts.Count`. Do NOT set `Height` (that's block height, not font). - `drawing_set_technical_requirements` — **document-level**: `(IDrawingDocument)doc.TechnicalDemand`/`ITechnicalDemand`: `td.Text.Str = text` → `td.Update()` (`IsCreated` False→True on first set; overwrites; `\n` multiline). - `DrawingAnnotationResult{Value(read-back), ViewNumber}` is the shared result for rough/text. + - `drawing_add_leader` — `ISymbols2DContainer.Leaders.Add(ksDrLeader)` → `IBaseLeader`. **КРИТИЧЕСКИЙ ПОРЯДОК (спайк):** новая выноска имеет 0 ответвлений → `(IBranchs)bl.AddBranchByPoint(0,x,y)` (остриё) ДО `SetBranchTextPosition(textX,textY)`/`Update`, иначе КОМПАС падает `RPC_E_SERVERFAULT`. Текст — `(ILeader)bl.TextOnShelf.Str`; `ShelfDirection` (enum `ShelfDirection{Auto,Right,Left,Up,Down}`, Auto = не задавать). `AddBranchByPoint`/`SetBranchTextPosition` возвращают bool — проверять. Результат — общий `DrawingAnnotationResult`. - `drawing_set_sheet_format` — `RequireLayoutSheet(n).Format` (`ISheetFormat`): `Format`=`ksDocumentFormatEnum` (`PaperFormat{A0..A5,User}`+`PaperFormats.Parse/ToKompas/FromKompas`), standard → set `VerticalOrientation=!landscape` (W/H auto-computed from enum+orientation, e.g. A3 landscape→420×297); **User → set `FormatWidth/Height` only, КОМПАС auto-derives `VerticalOrientation` from W/H and ignores the flag, never swaps W/H (spike)** → `sheet.Update()`. New drawing defaults A4 portrait. `ValidateFormatDimensions`: User needs W/H>0, standard forbids W/H (must be 0). - **Richer sketch primitives (API5 `ksDocument2D` wrappers)**: `ksArcBy3Points`; `ksArcByAngle` (centre/radius/start-end angles in degrees + counterClockwise flag); `ksEllipse` via `ksEllipseParam` (`KompasObject.GetParamStruct(ko_EllipseParam=22)`; **property names are `A`/`B` UPPERCASE in the interop**); `ksRegularPolygon` via `ksRegularPolygonParam` (`ko_RegularPolygonParam=92`; `describe = !inscribed`); NURBS spline via `ksNurbs(order=4)` + `ksNurbsPoint` loop + `ksEndObj`; `ksPoint`. Param structs released after use. `PartModeler` is partial: `.cs` (registry/helpers/`NewParam`/reset), `.Sketch.cs` (2D primitives), `.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` (`Core/Modeling`) holds enum maps + validators (`RequirePositive`, `RequireVertexCount`, `RequirePoints`, `RequireMin`). Point lists use `record SketchPoint(X, Y)` (JSON names `x`/`y`). diff --git a/README.md b/README.md index 58b02a3..d0534d1 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe } ``` -## Инструменты (82 инструмента) +## Инструменты (83 инструмента) | Группа | Инструменты | |---|---| @@ -56,7 +56,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe | Variables | `create_variable`, `set_variable`, `delete_variable` | | Conversion | `import_step`, `export_step` | | Assembly | `assembly_add_component`, `assembly_add_mate` | -| Drawing | `drawing_create_standard_views`, `drawing_fill_title_block`, `drawing_add_linear_dimension`, `drawing_add_diametral_dimension`, `drawing_add_radial_dimension`, `drawing_add_angular_dimension`, `drawing_add_rough`, `drawing_add_text`, `drawing_set_technical_requirements`, `drawing_set_sheet_format` | +| Drawing | `drawing_create_standard_views`, `drawing_fill_title_block`, `drawing_add_linear_dimension`, `drawing_add_diametral_dimension`, `drawing_add_radial_dimension`, `drawing_add_angular_dimension`, `drawing_add_rough`, `drawing_add_text`, `drawing_set_technical_requirements`, `drawing_set_sheet_format`, `drawing_add_leader` | | Validation | `validate_part` | ### Типовой сценарий @@ -75,7 +75,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe ``` src/Kompas.Mcp.Core/ COM-слой: STA-диспетчер, подключение, документы, эскизы/операции, конвертация, снимок, инспекция модели src/Kompas.Mcp.Host/ MCP-сервер (stdio) + определения инструментов -tests/Kompas.Mcp.Tests/ 308 тестов: unit + integration (integration требуют КОМПАС) +tests/Kompas.Mcp.Tests/ 331 тест: unit + integration (integration требуют КОМПАС) libs/kompas-interop/ вендорские interop-сборки КОМПАС (из SDK Samples/Common) docs/ архитектура, план, презентация, MD-база знаний SDK (Kompas3D_SDK/) usecases/ полигон обкатки подходов (в .gitignore); приёмы поднимаются в навык kompas-3d diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index de87c4c..caea6ac 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Архитектура 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`+`drawing_create_standard_views`+`drawing_fill_title_block`+`drawing_add_linear_dimension`+`drawing_add_diametral_dimension`+`drawing_add_radial_dimension`+`drawing_add_angular_dimension`+`drawing_add_rough`+`drawing_add_text`+`drawing_set_technical_requirements`+ассоциативная привязка диаметрального/радиального к окружности (веха «2D-чертёж», инкремент 7)+`drawing_set_sheet_format` (формат и ориентация листа, инкремент 8)). Актуализировано по коду. +> Статус: **реализовано и работает** (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`+`drawing_fill_title_block`+`drawing_add_linear_dimension`+`drawing_add_diametral_dimension`+`drawing_add_radial_dimension`+`drawing_add_angular_dimension`+`drawing_add_rough`+`drawing_add_text`+`drawing_set_technical_requirements`+ассоциативная привязка диаметрального/радиального к окружности (веха «2D-чертёж», инкремент 7)+`drawing_set_sheet_format` (формат и ориентация листа, инкремент 8)+`drawing_add_leader` (линия-выноска с текстом, инкремент 9)). Актуализировано по коду. > Сопутствующий контекст по COM API КОМПАС — в [`../CLAUDE.md`](../CLAUDE.md). > Имена интерфейсов и перечислений сверены со справкой SDK по MD-базе знаний `docs/Kompas3D_SDK/` (субагент `kompas-sdk-research`). @@ -113,7 +113,7 @@ 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.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) + интеграционные (требуют КОМПАС). 308 тестов (188 unit + 120 integration). Все интеграционные классы наследуют `IntegrationTestBase` (`IAsyncLifetime`), который вызывает `DocumentService.CloseAllAsync` после каждого теста. | +| **Kompas.Mcp.Tests** | Юнит (без COM) + интеграционные (требуют КОМПАС). 331 тест (201 unit + 130 integration). Все интеграционные классы наследуют `IntegrationTestBase` (`IAsyncLifetime`), который вызывает `DocumentService.CloseAllAsync` после каждого теста. | Принцип: **только `Core` знает про COM**. `Host`/Tools оперируют доменными DTO и вызывают `Core`; `Host` не содержит бизнес-логики. @@ -121,7 +121,7 @@ MCP client ──stdio──> Host (поток-пул, async) --- -## 5. Карта инструментов (82 инструмента) +## 5. Карта инструментов (83 инструмента) Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — `snake_case`. @@ -219,6 +219,7 @@ MCP client ──stdio──> Host (поток-пул, async) - `drawing_add_text(x,y, text, angle, viewNumber)` — свободная текстовая надпись на виде стилем по умолчанию. `x,y` — точка привязки (СК вида, мм); `text` — содержимое (`\n` — многострочно); `angle` — угол (°, по умолч. 0); `viewNumber` — номер вида. Реализовано: `(IDrawingContainer)view.DrawingTexts.Add()` → `IDrawingText` (`X/Y/Angle`) → `(IText)dt.Str = text` → `Update()` → `Valid`. ВАЖНО: текст живёт в `IDrawingContainer` (контейнер геометрии вида), НЕ в `ISymbols2DContainer`; попадание проверяется через `DrawingTexts.Count`. Возвращает `DrawingAnnotationResult { Value, ViewNumber }`. - `drawing_set_technical_requirements(text)` — технические требования чертежа (единый текстовый блок, уровень документа, не вида). `text` — строки через `\n`. Реализовано: `(IDrawingDocument)doc.TechnicalDemand` → `td.Text.Str = text` → `td.Update()`. Перезаписывает прежние (не добавляет). Возвращает число строк. - `drawing_set_sheet_format(format, landscape, width?, height?, sheetNumber=1)` — задать формат и ориентацию листа активного чертежа. `format`: `"A0"|"A1"|"A2"|"A3"|"A4"|"A5"|"user"`; `landscape`: `false`=книжная/`true`=альбомная; `width`/`height` только для `format=user` (мм, >0, для стандартного должны быть 0). Для `format=user` КОМПАС выводит ориентацию из соотношения сторон, флаг `landscape` игнорируется. Возвращает итоговые формат, ширину, высоту, ориентацию (read-back из COM). Реализовано: `ILayoutSheet.Format` (`ISheetFormat`: `Format=ksDocumentFormatEnum`, `VerticalOrientation`, `FormatWidth/Height`) → `sheet.Update()`. Новые: enum `PaperFormat {A0,A1,A2,A3,A4,A5,User}` + `PaperFormats.Parse/ToKompas/FromKompas`; record `SheetFormatResult {Format,Width,Height,Landscape}`; валидатор `DrawingValidation.ValidateFormatDimensions`; хелпер `RequireLayoutSheet`. API7, `DrawingService`. +- `drawing_add_leader(x,y, textX,textY, text, shelfDirection, viewNumber)` — поставить линию-выноску с текстом на виде активного чертежа. `x,y` — остриё (точка, куда указывает стрелка); `textX,textY` — положение полки/якоря текста; `text` — надпись на полке; `shelfDirection`: `"auto"|"right"|"left"|"up"|"down"` (направление полки; `auto` = по умолчанию КОМПАС); `viewNumber` — номер вида (0 = первый). Координаты в локальной СК вида (мм). Точка острия должна отличаться от точки полки. Возвращает `DrawingAnnotationResult { Value, ViewNumber }`. Реализовано: `ISymbols2DContainer.Leaders.Add(ksDrLeader)` → `IBaseLeader`; `(IBranchs)bl.AddBranchByPoint(0,x,y)` (остриё — обязательно до `Update`, иначе `RPC_E_SERVERFAULT`); `SetBranchTextPosition(textX,textY)`; `(ILeader)bl.TextOnShelf.Str = text`; опционально `ShelfDirection`; `Update()`. Новый enum `ShelfDirection {Auto,Right,Left,Up,Down}` + `ShelfDirections.Parse/ToKompas` в `Drawings/ShelfDirection.cs`; переиспользует `DrawingAnnotationResult`. API7, `DrawingService`. **Vision — визуальная обратная связь (fallback)** - `model_snapshot` — отрендерить активный 3D-документ в PNG и вернуть **как image-контент MCP**, @@ -353,14 +354,14 @@ dotnet build -c Release -r win-x64 # сборка ## 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+drawing_create_standard_views+drawing_fill_title_block+drawing_add_linear_dimension+drawing_add_diametral_dimension+drawing_add_radial_dimension+drawing_add_angular_dimension+drawing_add_rough+drawing_add_text+drawing_set_technical_requirements+associate для диаметрального/радиального+drawing_set_sheet_format):** документы, эскизы (полный набор 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`); **`drawing_fill_title_block`** (заполнение основной надписи/штампа: обозначение, наименование, материал; `IStamp.Text[id].Str`, API7, `DrawingService`); **`drawing_add_linear_dimension`** (линейный размер на виде чертежа: `ISymbols2DContainer`/`LineDimensions.Add`/`ILineDimension`, координаты в локальной СК вида, API7, `DrawingService`); **`drawing_add_diametral_dimension`** (диаметральный размер Ø: `ISymbols2DContainer.DiametralDimensions.Add`/`IDiametralDimension`, Xc/Yc/Radius/Angle в радианах, API7, `DrawingService`; **`associate=true`** — ассоциативная привязка к спроецированной окружности через `dim.BaseObject`, `CircularObjectMatch`); **`drawing_add_radial_dimension`** (радиальный размер R: `ISymbols2DContainer.RadialDimensions.Add`/`IRadialDimension`, значение = радиус, API7, `DrawingService`; **`associate=true`** — ассоциативная привязка аналогично диаметральному); **`drawing_add_angular_dimension`** (угловой размер: `ISymbols2DContainer.AngleDimensions.Add`/`IAngleDimension`, `angleType`=min/max/more, значение в градусах, API7, `DrawingService`); **`drawing_add_rough`** (знак шероховатости: `ISymbols2DContainer.Roughs.Add`/`IRough`/`IRoughParams`, `signType`=delete/without/none, API7, `DrawingService`); **`drawing_add_text`** (свободная текстовая надпись на виде: `IDrawingContainer.DrawingTexts.Add`/`IDrawingText`/`IText`, API7, `DrawingService`); **`drawing_set_technical_requirements`** (технические требования чертежа: `IDrawingDocument.TechnicalDemand`/`ITechnicalDemand`/`IText`, уровень документа, API7, `DrawingService`); **`drawing_set_sheet_format`** (формат и ориентация листа: `ILayoutSheet.Format`/`ISheetFormat`, `ksDocumentFormatEnum`, `VerticalOrientation`/`FormatWidth`/`FormatHeight`, `PaperFormat` enum, API7, `DrawingService`). Класс «2D-чертёж» (инкремент 6): стандартные виды, основная надпись, размеры (линейный, диаметральный, радиальный, угловой), текстовые обозначения (шероховатость, свободный текст, технические требования). **Инкремент 7**: ассоциативная привязка диаметрального и радиального размеров к спроецированной окружности (`IDrawingContainer.Circles`, поиск по центру+радиусу с допуском 1 мм, `CircularObjectMatch`, `dim.BaseObject = circle`); первый шаг к «умному» чертежу. **Инкремент 8**: задание формата и ориентации листа (`drawing_set_sheet_format`); для `format=user` ориентация определяется КОМПАС из соотношения сторон. +**Реализовано (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+drawing_fill_title_block+drawing_add_linear_dimension+drawing_add_diametral_dimension+drawing_add_radial_dimension+drawing_add_angular_dimension+drawing_add_rough+drawing_add_text+drawing_set_technical_requirements+associate для диаметрального/радиального+drawing_set_sheet_format+drawing_add_leader):** документы, эскизы (полный набор 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`); **`drawing_fill_title_block`** (заполнение основной надписи/штампа: обозначение, наименование, материал; `IStamp.Text[id].Str`, API7, `DrawingService`); **`drawing_add_linear_dimension`** (линейный размер на виде чертежа: `ISymbols2DContainer`/`LineDimensions.Add`/`ILineDimension`, координаты в локальной СК вида, API7, `DrawingService`); **`drawing_add_diametral_dimension`** (диаметральный размер Ø: `ISymbols2DContainer.DiametralDimensions.Add`/`IDiametralDimension`, Xc/Yc/Radius/Angle в радианах, API7, `DrawingService`; **`associate=true`** — ассоциативная привязка к спроецированной окружности через `dim.BaseObject`, `CircularObjectMatch`); **`drawing_add_radial_dimension`** (радиальный размер R: `ISymbols2DContainer.RadialDimensions.Add`/`IRadialDimension`, значение = радиус, API7, `DrawingService`; **`associate=true`** — ассоциативная привязка аналогично диаметральному); **`drawing_add_angular_dimension`** (угловой размер: `ISymbols2DContainer.AngleDimensions.Add`/`IAngleDimension`, `angleType`=min/max/more, значение в градусах, API7, `DrawingService`); **`drawing_add_rough`** (знак шероховатости: `ISymbols2DContainer.Roughs.Add`/`IRough`/`IRoughParams`, `signType`=delete/without/none, API7, `DrawingService`); **`drawing_add_text`** (свободная текстовая надпись на виде: `IDrawingContainer.DrawingTexts.Add`/`IDrawingText`/`IText`, API7, `DrawingService`); **`drawing_set_technical_requirements`** (технические требования чертежа: `IDrawingDocument.TechnicalDemand`/`ITechnicalDemand`/`IText`, уровень документа, API7, `DrawingService`); **`drawing_set_sheet_format`** (формат и ориентация листа: `ILayoutSheet.Format`/`ISheetFormat`, `ksDocumentFormatEnum`, `VerticalOrientation`/`FormatWidth`/`FormatHeight`, `PaperFormat` enum, API7, `DrawingService`); **`drawing_add_leader`** (линия-выноска с текстом на виде: `ISymbols2DContainer.Leaders.Add(ksDrLeader)`/`IBaseLeader`/`IBranchs.AddBranchByPoint`/`ILeader.TextOnShelf.Str`, `ShelfDirection` enum, API7, `DrawingService`). Класс «2D-чертёж» (инкремент 6): стандартные виды, основная надпись, размеры (линейный, диаметральный, радиальный, угловой), текстовые обозначения (шероховатость, свободный текст, технические требования). **Инкремент 7**: ассоциативная привязка диаметрального и радиального размеров к спроецированной окружности (`IDrawingContainer.Circles`, поиск по центру+радиусу с допуском 1 мм, `CircularObjectMatch`, `dim.BaseObject = circle`); первый шаг к «умному» чертежу. **Инкремент 8**: задание формата и ориентации листа (`drawing_set_sheet_format`); для `format=user` ориентация определяется КОМПАС из соотношения сторон. **Инкремент 9**: линия-выноска с текстом (`drawing_add_leader`); ключевая тонкость: `AddBranchByPoint` обязателен до `Update` (без ответвления — `RPC_E_SERVERFAULT`). **Следующие приоритеты:** 1. Пакет D «параметрика» (продолжение) — связь размеров эскизов с переменными (параметрические эскизы через API2D). ⚠️ **Исследовано**: `ksCDimWithVariable` недоступен из внешней автоматизации — см. `docs/superpowers/specs/2026-05-27-parametric-sketch-findings.md`. 2. Пакет E «вспомогательная геометрия» (продолжение) — ось, точка, плоскость по трём точкам / по углу. 3. Рассечение/перемещение тела как MCP-инструменты (`SplitSolids`/`BodyRepositions`) — механика есть, продуктизация не закончена. 4. Свойства документа: `IPropertyMng` / `IPropertyKeeper`. -5. **2D-чертёж (продолжение):** инкремент 8 добавил формат и ориентацию листа (`drawing_set_sheet_format`). Нереализовано: рамка по ГОСТ-стилю (`LayoutLibraryFileName`/`LayoutStyleNumber`), несколько листов; привязка к дуге (`Arcs`), ассоциативный угловой размер (BaseObject1/2, отрезки), ассоциативная шероховатость (`IRough.BaseObject`); выноски (`Leaders`), обозначения баз (`Bases`), допуски формы (`Tolerances`). +5. **2D-чертёж (продолжение):** инкременты 8–9 добавили формат листа и линию-выноску. Нереализовано: рамка по ГОСТ-стилю (`LayoutLibraryFileName`/`LayoutStyleNumber`), несколько листов; привязка к дуге (`Arcs`), ассоциативный угловой размер (BaseObject1/2, отрезки), ассоциативная шероховатость (`IRough.BaseObject`); обозначения баз (`Bases`), допуски формы (`Tolerances`); специальные выноски (позиция, клеймо, маркер); шероховатость в условиях окончательной обработки. 6. Сборки — продолжение: дополнительные типы сопряжений (`parallel`, `perpendicular`, `concentric`, `angle`, `tangency`; `coincidence`/`distance` реализованы); авто-позиционирование компонентов; спецификации (`ISpecification`). 7. Транспорт **HTTP/SSE** — как опция для удалённых клиентов. diff --git a/docs/OPEN_QUESTIONS.md b/docs/OPEN_QUESTIONS.md index 477a6e8..7381ca8 100644 --- a/docs/OPEN_QUESTIONS.md +++ b/docs/OPEN_QUESTIONS.md @@ -230,3 +230,8 @@ stateful-сессии? Пока полагаемся на последовате **Решено.** Новый инструмент `drawing_set_sheet_format(format, landscape, width?, height?, sheetNumber=1)` — задаёт формат и ориентацию активного листа чертежа. Паттерн API7: `doc2d.LayoutSheets.ItemByNumber[sheetNumber]` → `ILayoutSheet.Format` (`ISheetFormat`: `Format=ksDocumentFormatEnum`, `VerticalOrientation=!landscape`, `FormatWidth/Height` только для `format=user`) → `sheet.Update()`. Дефолтный лист нового чертежа — A4 книжный. Новые типы: enum `PaperFormat {A0,A1,A2,A3,A4,A5,User}` + `PaperFormats.Parse/ToKompas/FromKompas`; record `SheetFormatResult`; валидатор `DrawingValidation.ValidateFormatDimensions`; хелпер `RequireLayoutSheet`. Ключевая тонкость: для `format=user` КОМПАС выводит `VerticalOrientation` из соотношения сторон (`height>width`), флаг `landscape` в этом случае игнорируется — результат читается read-back из COM. Для стандартных форматов `width`/`height` запрещены (должны быть 0). +20 unit-тестов (`PaperFormats.Parse`, `ValidateFormatDimensions`) + 10 интеграционных (`DrawingSheetFormatTests`). Итог: **82 инструмента, 308 тестов (188 unit + 120 integration).** Нереализовано: рамка по ГОСТ-стилю (`LayoutLibraryFileName`/`LayoutStyleNumber`), несколько листов. + +### ✅ Линия-выноска с текстом — реализована (инкремент 9 вехи «2D-чертёж») +**Решено.** Новый инструмент `drawing_add_leader(x,y, textX,textY, text, shelfDirection, viewNumber)` — ставит линию-выноску с надписью на полке на виде активного чертежа. Паттерн API7: `ISymbols2DContainer.Leaders.Add(ksDrLeader)` → `IBaseLeader` → `(IBranchs)bl.AddBranchByPoint(0,x,y)` (остриё, ответвление ответвлений — обязательно до `Update`, иначе `RPC_E_SERVERFAULT`) → `SetBranchTextPosition(textX,textY)` → `(ILeader)bl.TextOnShelf.Str = text` → опционально `ShelfDirection` → `Update()` → `Valid`. Новый enum `ShelfDirection {Auto,Right,Left,Up,Down}` + `ShelfDirections.Parse/ToKompas` в `src/Kompas.Mcp.Core/Drawings/ShelfDirection.cs`; переиспользует `DrawingAnnotationResult { Value, ViewNumber }`. +13 unit-тестов (`ShelfDirections.Parse`) + 10 интеграционных (`DrawingLeaderTests`, включая параметризацию направлений полки). Итог: **83 инструмента, 331 тест (201 unit + 130 integration).** + +Нереализовано: обозначения баз (`Bases`), допуски формы (`Tolerances`); привязка к дуге (`Arcs`), ассоциативный угловой размер, ассоциативная шероховатость (`IRough.BaseObject`); рамка по ГОСТ-стилю; специальные выноски (позиция, клеймо, маркер). diff --git a/docs/TODO.md b/docs/TODO.md index 7b7f465..5232eed 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -25,7 +25,9 @@ - ✓ **Формат/ориентация листа** (A0–A5/user, книжная/альбомная) — `drawing_set_sheet_format` (инкремент 8); `ILayoutSheet.Format`/`ISheetFormat`. Остаётся: ☐ рамка/основная надпись по конкретному ГОСТ-стилю оформления (`LayoutLibraryFileName`/`LayoutStyleNumber`), несколько листов. -- ☐ **Выноски** (`Leaders`) — `ISymbols2DContainer.Leaders` (полки-выноски с текстом). +- ✓ **Выноски** (`Leaders`) — `drawing_add_leader` (инкремент 9): остриё + полка с текстом, + `shelfDirection`. Остаётся: ☐ многострочный/под-полкой текст (`TextUnderShelf`), несколько + ответвлений, привязка к геометрии (`IBranchs.SetBaseObject`), спец. выноски (позиция/клеймо/маркер). - ☐ **Обозначения баз** (`Bases`) — `ISymbols2DContainer.Bases`. - ☐ **Допуски формы и расположения** (`Tolerances`) — `ISymbols2DContainer.Tolerances` (обычно в связке с базой). - ☐ (опц.) **Доп. виды**: разрез/сечение (`CutLines`), выносной элемент (`RemoteElements`), местный вид. diff --git a/docs/presentation.html b/docs/presentation.html index 279c663..c0fc21c 100644 --- a/docs/presentation.html +++ b/docs/presentation.html @@ -128,7 +128,7 @@
- Статус: 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+drawing_fill_title_block+drawing_add_linear_dimension+drawing_add_diametral_dimension+drawing_add_radial_dimension+drawing_add_angular_dimension+drawing_add_rough+drawing_add_text+drawing_set_technical_requirements+associate+drawing_set_sheet_format (82 инструмента) + Статус: 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+drawing_fill_title_block+drawing_add_linear_dimension+drawing_add_diametral_dimension+drawing_add_radial_dimension+drawing_add_angular_dimension+drawing_add_rough+drawing_add_text+drawing_set_technical_requirements+associate+drawing_set_sheet_format+drawing_add_leader (83 инструмента)

КОМПАС-3D MCP-сервер
управление CAD языком LLM

MCP-сервер, который превращает операции КОМПАС-3D — создание документов, эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом — @@ -261,7 +261,7 @@ впереди.

-
Общий прогресс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 · drawing_fill_title_block · drawing_add_linear_dimension · drawing_add_diametral_dimension · drawing_add_radial_dimension · drawing_add_angular_dimension · drawing_add_rough · drawing_add_text · drawing_set_technical_requirements · associate (ассоциативная привязка диам./радиального) · drawing_set_sheet_format (формат листа) · навык kompas-3d · 82 инструмента · 308 тестов
+
Общий прогресс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 · drawing_fill_title_block · drawing_add_linear_dimension · drawing_add_diametral_dimension · drawing_add_radial_dimension · drawing_add_angular_dimension · drawing_add_rough · drawing_add_text · drawing_set_technical_requirements · associate (ассоциативная привязка диам./радиального) · drawing_set_sheet_format (формат листа) · drawing_add_leader (выноска) · навык kompas-3d · 83 инструмента · 331 тест
@@ -297,7 +297,7 @@

Инструменты v3+ ГОТОВО

-

82 инструмента, отдаются по MCP-протоколу

+

83 инструмента, отдаются по MCP-протоколу

  • System: connect · status · set_visible
  • Documents: create · open · save · save_as · close · active
  • @@ -310,8 +310,8 @@
  • Variables: create_variable · set_variable · delete_variable (пакет D)
  • Conversion: import_step · export_step
  • Assembly: assembly_add_component (вставка компонента .m3d/.a3d в сборку, API7) · assembly_add_mate (сопряжения coincidence/distance, API7)
  • -
  • Drawing: drawing_create_standard_views (стандартные ассоциативные виды чертежа, API7) · drawing_fill_title_block (основная надпись/штамп: обозначение, наименование, материал, API7) · drawing_add_linear_dimension (линейный размер на виде, ISymbols2DContainer, API7) · drawing_add_diametral_dimension (диаметральный размер Ø, IDiametralDimension, associate=true для ассоциативной привязки к окружности, API7) · drawing_add_radial_dimension (радиальный размер R, IRadialDimension, associate=true аналогично, API7) · drawing_add_angular_dimension (угловой размер, IAngleDimension, angleType=min/max/more, API7) · drawing_add_rough (знак шероховатости, IRough/IRoughParams, signType=delete/without/none, API7) · drawing_add_text (свободный текст на виде, IDrawingContainer/IDrawingText, API7) · drawing_set_technical_requirements (тех. требования документа, IDrawingDocument/TechnicalDemand, API7) · drawing_set_sheet_format (формат и ориентация листа, ILayoutSheet/ISheetFormat, PaperFormat A0–A5/user, API7)
  • -
  • 2D-чертёж: рамка/основная надпись по ГОСТ-стилю (LayoutLibraryFileName/LayoutStyleNumber), несколько листов · привязка к дуге (Arcs), ассоциативный угловой размер, ассоциативная шероховатость (IRough.BaseObject) · выноски (Leaders), обозначения баз (Bases), допуски формы (Tolerances) · пакет E (ось, угловая плоскость) · пакет D параметрика: параметрические эскизы (API2D) — исследовано, недоступно через COM API · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency)
  • +
  • Drawing: drawing_create_standard_views (стандартные ассоциативные виды чертежа, API7) · drawing_fill_title_block (основная надпись/штамп: обозначение, наименование, материал, API7) · drawing_add_linear_dimension (линейный размер на виде, ISymbols2DContainer, API7) · drawing_add_diametral_dimension (диаметральный размер Ø, IDiametralDimension, associate=true для ассоциативной привязки к окружности, API7) · drawing_add_radial_dimension (радиальный размер R, IRadialDimension, associate=true аналогично, API7) · drawing_add_angular_dimension (угловой размер, IAngleDimension, angleType=min/max/more, API7) · drawing_add_rough (знак шероховатости, IRough/IRoughParams, signType=delete/without/none, API7) · drawing_add_text (свободный текст на виде, IDrawingContainer/IDrawingText, API7) · drawing_set_technical_requirements (тех. требования документа, IDrawingDocument/TechnicalDemand, API7) · drawing_set_sheet_format (формат и ориентация листа, ILayoutSheet/ISheetFormat, PaperFormat A0–A5/user, API7) · drawing_add_leader (линия-выноска с текстом, ISymbols2DContainer/IBaseLeader/IBranchs/ILeader, ShelfDirection enum, API7)
  • +
  • 2D-чертёж: рамка/основная надпись по ГОСТ-стилю (LayoutLibraryFileName/LayoutStyleNumber), несколько листов · привязка к дуге (Arcs), ассоциативный угловой размер, ассоциативная шероховатость (IRough.BaseObject) · обозначения баз (Bases), допуски формы (Tolerances), специальные выноски (позиция/клеймо/маркер) · пакет E (ось, угловая плоскость) · пакет D параметрика: параметрические эскизы (API2D) — исследовано, недоступно через COM API · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency)
@@ -322,7 +322,7 @@
  • Unit-тесты — без COM: диспетчер, enum-маппинг, StepFormat, InspectionText
  • Интеграционные тесты с КОМПАС: подключение, документы, снимок, цикл, STEP round-trip, сборка, move_face, инспекция
  • -
  • Итого 308 тестов зелёных (188 unit + 120 integration)
  • +
  • Итого 331 тест зелёных (201 unit + 130 integration)
  • Сборка kompas-mcp.exe, README с конфигом клиента
  • Навык kompas-3d (методика) + полигон usecases/
  • In-process тест MCP-клиента, релизный publish
  • @@ -358,7 +358,8 @@
    2D-чертёж · инкремент 6 ✓

    Текстовые обозначения

    drawing_add_rough (IRough/IRoughParams, signType=delete/without/none, API7) · drawing_add_text (IDrawingContainer/IDrawingText/IText, API7) · drawing_set_technical_requirements (IDrawingDocument/TechnicalDemand, уровень документа, API7) · DrawingAnnotationResult · RoughSignType enum · 81 инструмент · 267 тестов

    2D-чертёж · инкремент 7 ✓

    Ассоциативная привязка к окружности

    associate=true у drawing_add_diametral_dimension и drawing_add_radial_dimension · CircularObjectMatch (поиск по центру+радиусу, допуск 1 мм) · RequireViewContainers · dim.BaseObject=circle · значение из геометрии · 81 инструмент · 278 тестов

    2D-чертёж · инкремент 8 ✓

    Формат и ориентация листа

    drawing_set_sheet_format (ILayoutSheet.Format / ISheetFormat, ksDocumentFormatEnum, A0–A5/user, VerticalOrientation) · PaperFormat enum · SheetFormatResult · ValidateFormatDimensions · 82 инструмента · 308 тестов

    -
    далее · в работе ▶

    Оформление чертежа

    Рамка/надпись по ГОСТ-стилю (LayoutLibraryFileName) · несколько листов · привязка к дуге (Arcs) · ассоциативный угловой размер · ассоциативная шероховатость (IRough.BaseObject) · выноски (Leaders) · обозначения баз (Bases) · допуски формы (Tolerances) · пакет E (ось, угловая плоскость) · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency) · пакет D параметрика — параметрические эскизы недоступны через COM API

    +
    2D-чертёж · инкремент 9 ✓

    Линия-выноска

    drawing_add_leader (ISymbols2DContainer.Leaders.Add / IBaseLeader / IBranchs.AddBranchByPoint / ILeader.TextOnShelf, ShelfDirection enum auto/right/left/up/down, API7) · DrawingAnnotationResult · 83 инструмента · 331 тест

    +
    далее · в работе ▶

    Оформление чертежа

    Рамка/надпись по ГОСТ-стилю (LayoutLibraryFileName) · несколько листов · привязка к дуге (Arcs) · ассоциативный угловой размер · ассоциативная шероховатость (IRough.BaseObject) · обозначения баз (Bases) · допуски формы (Tolerances) · специальные выноски (позиция/клеймо/маркер) · пакет E (ось, угловая плоскость) · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency) · пакет D параметрика — параметрические эскизы недоступны через COM API

diff --git a/docs/superpowers/specs/2026-05-28-drawing-leader-design.md b/docs/superpowers/specs/2026-05-28-drawing-leader-design.md new file mode 100644 index 0000000..3d1f6e0 --- /dev/null +++ b/docs/superpowers/specs/2026-05-28-drawing-leader-design.md @@ -0,0 +1,119 @@ +# Дизайн: линия-выноска с текстом (drawing_add_leader) через API7 + +**Дата:** 2026-05-28 +**Статус:** дизайн согласован, спайк проведён, ревью Codex + pi/glm-5.1 + pi/kimi-k2.6 учтено — к реализации + +## Правки по ревью реализации (Codex + pi/glm-5.1 + pi/kimi-k2.6) + +- Проверка возврата `AddBranchByPoint`/`SetBranchTextPosition` (оба `bool`) — ранняя диагностика + вместо позднего `RPC_E_SERVERFAULT` (Codex, glm). +- RU-алиасы в сообщении `ShelfDirections.Parse` и в `[Description]` инструмента (glm, kimi). +- Интеграционный тест направления полки параметризован `Right/Left/Up/Down` — покрывает всю + поверхность `ToKompas` на реальном COM (glm, kimi). +- Отклонено (обоснование): unit-тест `ToKompas` (граница проекта — тест не ссылается на + `Kompas6Constants`); e2e-тест tool-обёртки (тонкая, `Parse`+сервис покрыты); `angleDeg` + (у `ILeader`/`IBaseLeader` нет свойства Angle — ориентация геометрическая); выравнивание default + `text` у `AddText` (вне объёма; required-контракт `AddLeader` корректнее). + +## Правки по ревью спека (Codex + pi/glm-5.1 + pi/kimi-k2.6) + +- **Сигнатура** `DrawingValidation.RequireNonEmptyText(text, nameof(text))` (двухаргументный — как есть). +- **QI с диагностикой:** `bl as IBranchs ?? throw`, `bl as ILeader ?? throw` (не прямой cast); read-back + `leader.TextOnShelf?.Str ?? string.Empty`; двухшаговый null-check `Leaders` (коллекция) и `Add()` — + как `AddRoughCore`. +- **`ShelfDirections.ToKompas(Auto)` бросает `ArgumentOutOfRangeException`** (Auto обрабатывается в сервисе + ДО вызова — «не задавать ShelfDirection»; прямой вызов для Auto — ошибка). RU-алиасы `Parse`: + `авто/вправо/влево/вверх/вниз`. +- **Вырожденная выноска:** `RequireDistinctPoints(x, y, textX, textY)` (остриё ≠ полка; есть валидатор). +- **Критический порядок** (комментарий в коде): `AddBranchByPoint` ДО `SetBranchTextPosition`/`Update` + (иначе RPC_E_SERVERFAULT — выноска без ветвей). +- **Тесты:** пустой текст — `""` И `" "`; `viewNumber=0` резолвится в первый вид; путь `shelfDirection=auto` + (ShelfDirection не задаётся). XML-`` членов `ShelfDirection` — «в ЛОКАЛЬНОЙ СК вида». +- Нейминг COM-объекта — `baseLeader` (не `bl`). `GetViewLeaderCountAsync` считает ВСЕ подтипы выносок + (для v1 ок; фильтр — при добавлении ksDrPosLeader/… — в «Дальнейшее»). + +## Цель + +Веха 2D-ЧЕРТЁЖ, инкремент 9. Поставить на виде **линию-выноску** (leader) с текстовой надписью на полке — +поясняющее обозначение, указывающее стрелкой на элемент (примечание, материал детали, и т.п.). + +## Спайк: последовательность подтверждена вживую (НЕ разучивать) + +Спайк (`_SpikeLeader`, прогнан на реальном КОМПАС, затем удалён). **Ключ:** только что созданная +выноска имеет **0 ответвлений** — любые операции над ответвлением (`SetSignPosition`) и `Update` на +выноске без ветвей падают с `RPC_E_SERVERFAULT`; сначала нужно добавить ответвление через `IBranchs`. + +``` +IBaseLeader bl = symbols.Leaders.Add(DrawingObjectTypeEnum.ksDrLeader); // ksDrLeader=20 +((IBranchs)bl).AddBranchByPoint(0, x, y); // QI; ответвление 0 — остриё (куда указывает стрелка) +bl.SetBranchTextPosition(textX, textY); // положение полки/текста +((ILeader)bl).TextOnShelf.Str = text; // надпись на полке (.Str — наш interop) +// опц.: ((ILeader)bl).ShelfDirection = ksShelfDirectionEnum.ksLSRight; +bl.Update(); // True, bl.Valid == True +``` + +**Проверено:** `AddBranchByPoint(0,x,y)` → `BranchCount=1`; `Update=True`, `Valid=True`, +`TextOnShelf.Str` round-trip; `Leaders.Count` +1. Работает с явной `ShelfDirection` и без неё. +`BaseLeader` реализует `IBaseLeader`/`IDrawingObject`; `IBranchs` и `ILeader` — COM-QI от него. + +Интерфейсы: `ILeaders.Add(DrawingObjectTypeEnum)` → `BaseLeader` (для обычной выноски `ksDrLeader=20`); +`IBranchs.AddBranchByPoint(int index, double x, double y)`; `IBaseLeader.SetBranchTextPosition(x,y)` / +`Update` / `Valid` / `Delete` / `ArrowType` (`ksArrowEnum`); `ILeader.TextOnShelf` (`IText`), +`ShelfDirection` (`ksShelfDirectionEnum`: `ksLSNone=0`/`ksLSRight=1`/`ksLSUp=2`/`ksLSDown=3`/`ksLSLeft=-1`). + +## MCP-инструмент + +| Инструмент | Параметры | Поведение | +|---|---|---| +| `drawing_add_leader` | `x,y` (остриё — куда указывает стрелка), `textX,textY` (полка/якорь текста), `text`, `shelfDirection="auto"` (auto\|right\|left\|up\|down), `viewNumber=0` | Поставить выноску на виде: стрелка из полки указывает в (x,y), текст `text` на полке у (textX,textY). Координаты — ЛОКАЛЬНАЯ СК вида (мм). `shelfDirection` — направление полки (auto = по умолчанию КОМПАС). Возвращает текст (read-back) и номер вида. | + +## Архитектура + +В `DrawingService` (namespace `Kompas.Mcp.Core.Drawings`). +- `AddLeaderAsync(viewNumber, x, y, textX, textY, text, shelfDirection, ct)` → `DrawingAnnotationResult` + (переиспользуем: `Value`=read-back текст, `ViewNumber`). +- Использует существующий `RequireSymbols2DContainer(viewNumber)` (выноски в `ISymbols2DContainer.Leaders`). +- Новый файл `src/Kompas.Mcp.Core/Drawings/ShelfDirection.cs`: `enum ShelfDirection {Auto,Right,Left,Up,Down}` + (XML-`` на членах) + `ShelfDirections.Parse(string)` / `ToKompas(ShelfDirection)→ksShelfDirectionEnum` + по образцу `AngleDimensionTypes`. `Auto` → не задавать `ShelfDirection` (оставить дефолт КОМПАС). +- Счётчик `GetViewLeaderCountAsync(viewNumber)` (для теста попадания в вид). + +## Реализация + +`AddLeaderCore`: `RequireNonEmptyText(text)` + `RequireFiniteCoords(x,y,textX,textY)` → +`RequireSymbols2DContainer` → `symbols.Leaders` (null-check) → `.Add(ksDrLeader)` (null-check) → +**внутри `try/catch`+`Delete`**: `((IBranchs)bl).AddBranchByPoint(0, x, y)` (QI null-check), +`bl.SetBranchTextPosition(textX, textY)`, `((ILeader)bl).TextOnShelf` (null-check) `.Str = text`, +если `shelfDirection != Auto`: `((ILeader)bl).ShelfDirection = ShelfDirections.ToKompas(...)` → +`bl.Update()` (FALSE → откат) → `bl.Valid` (false → откат) → read-back `((ILeader)bl).TextOnShelf.Str` +→ `DrawingAnnotationResult{Value=read-back, ViewNumber}`. + +RCW точечно не освобождаем (консистентно; долг v2-2). + +## Валидация (чистые static, unit) + +- `RequireNonEmptyText(text, nameof(text))`, `RequireFiniteCoords(x,y,textX,textY)`, + `RequireDistinctPoints(x, y, textX, textY)` (остриё ≠ полка) — все уже есть. +- `ShelfDirections.Parse` — `auto/right/left/up/down` (+ рус. `авто/вправо/влево/вверх/вниз`, регистр/пробелы); + неизвестное → `ArgumentException`; null → `ArgumentNullException`. `ToKompas` (→ `ksShelfDirectionEnum`) + НЕ покрываем unit (граница проекта); бросает `ArgumentOutOfRangeException` на `Auto`. + +## Тестирование + +### Unit (`ShelfDirectionsTests`) +- `Parse`: `auto/right/left/up/down` (+ регистр, рус.) → enum; неизвестное → `ArgumentException`; null → `ArgumentNullException`. + +### Integration (`DrawingLeaderTests`; модель — коробка + стандартные виды) +1. **Ставится + round-trip + viewNumber=0**: `text="Примечание 1"`, `shelfDirection=auto`, `viewNumber=0` + → `Value=="Примечание 1"` (read-back), `viewNumber=0` резолвится в первый вид (`r.ViewNumber` совпал), + `Leaders.Count` целевого вида +1. +2. **shelfDirection=left** (не-auto путь) на целевом виде → ставится, `Leaders.Count` +1. +3. **Пустой текст** (`""` и `" "`) → `ArgumentException` (до Add). +4. **Вырожденная** (остриё совпадает с полкой) → `ArgumentException` (до Add). +5. **Нет видов** → понятная ошибка. 6. **Несуществующий `viewNumber>0`** → понятная ошибка. + +## Дальнейшее (вне спека) +- Многострочный текст полки / текст под полкой (`TextUnderShelf`); несколько ответвлений + (`AddBranchByPoint` с разными индексами); привязка к геометрии (`IBranchs.SetBaseObject`); тип стрелки + (`ArrowType`); специальные выноски (позиция/клеймо/маркер: `ksDrPosLeader/ksDrBrandLeader/ksDrMarkerLeader`). + Базы (`Bases`), допуски формы (`Tolerances`). diff --git a/src/Kompas.Mcp.Core/Drawings/DrawingService.cs b/src/Kompas.Mcp.Core/Drawings/DrawingService.cs index bbacfa3..3d73e10 100644 --- a/src/Kompas.Mcp.Core/Drawings/DrawingService.cs +++ b/src/Kompas.Mcp.Core/Drawings/DrawingService.cs @@ -482,6 +482,66 @@ public sealed class DrawingService public Task GetViewTextCountAsync(int viewNumber, CancellationToken ct = default) => _dispatcher.InvokeAsync(() => RequireDrawingContainer(viewNumber).Container.DrawingTexts?.Count ?? 0, ct); + /// + /// Поставить линию-выноску на виде: стрелка из полки указывает в (,), + /// текст на полке у (,) — всё в + /// ЛОКАЛЬНОЙ СК вида (мм). — направление полки (Auto = по умолчанию КОМПАС). + /// Возвращает текст (read-back) и номер вида. + /// + public Task AddLeaderAsync(int viewNumber, double x, double y, double textX, + double textY, string text, ShelfDirection shelfDirection, CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => AddLeaderCore(viewNumber, x, y, textX, textY, text, shelfDirection), ct); + + private DrawingAnnotationResult AddLeaderCore(int viewNumber, double x, double y, double textX, + double textY, string text, ShelfDirection shelfDirection) + { + DrawingValidation.RequireNonEmptyText(text, nameof(text)); + DrawingValidation.RequireFiniteCoords(x, y, textX, textY); + DrawingValidation.RequireDistinctPoints(x, y, textX, textY); // остриё ≠ полка (иначе вырождение) + + var (view, symbols) = RequireSymbols2DContainer(viewNumber); + var leaders = symbols.Leaders + ?? throw new InvalidOperationException("Коллекция Leaders недоступна на виде."); + var baseLeader = leaders.Add(DrawingObjectTypeEnum.ksDrLeader) + ?? throw new InvalidOperationException("Leaders.Add вернул null."); + try + { + // КРИТИЧЕСКИЙ ПОРЯДОК: AddBranchByPoint ДОЛЖЕН предшествовать SetBranchTextPosition/Update — + // выноска без ответвлений валит КОМПАС (RPC_E_SERVERFAULT). Проверено спайком. + var branches = baseLeader as IBranchs + ?? throw new InvalidOperationException("Выноска не приводится к IBranchs."); + if (!branches.AddBranchByPoint(0, x, y)) // остриё ответвления 0 (куда указывает стрелка) + throw new InvalidOperationException("AddBranchByPoint вернул FALSE — ответвление выноски не создано."); + if (!baseLeader.SetBranchTextPosition(textX, textY)) // положение полки/текста + throw new InvalidOperationException("SetBranchTextPosition вернул FALSE — полка выноски не задана."); + + var leader = baseLeader as ILeader + ?? throw new InvalidOperationException("Выноска не приводится к ILeader."); + var shelf = leader.TextOnShelf + ?? throw new InvalidOperationException("Текст полки выноски недоступен (TextOnShelf == null)."); + shelf.Str = text; + if (shelfDirection != ShelfDirection.Auto) + leader.ShelfDirection = ShelfDirections.ToKompas(shelfDirection); + + if (!baseLeader.Update()) + throw new InvalidOperationException("ILeader.Update вернул FALSE — выноска не построена."); + if (!baseLeader.Valid) + throw new InvalidOperationException("Выноска построена невалидной."); + + var readBack = leader.TextOnShelf?.Str ?? string.Empty; // read-back из COM + return new DrawingAnnotationResult { Value = readBack, ViewNumber = view.Number }; + } + catch + { + try { baseLeader.Delete(); } catch { /* лучшее усилие */ } + throw; + } + } + + /// Число выносок на виде (для проверки попадания в вид; считает все подтипы выносок). + public Task GetViewLeaderCountAsync(int viewNumber, CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => RequireSymbols2DContainer(viewNumber).Symbols.Leaders?.Count ?? 0, ct); + /// /// Задать технические требования активного чертежа (единый блок над основной надписью; перезаписывает /// прежние). — строки через \n. Возвращает число строк. diff --git a/src/Kompas.Mcp.Core/Drawings/ShelfDirection.cs b/src/Kompas.Mcp.Core/Drawings/ShelfDirection.cs new file mode 100644 index 0000000..398b58f --- /dev/null +++ b/src/Kompas.Mcp.Core/Drawings/ShelfDirection.cs @@ -0,0 +1,52 @@ +using Kompas6Constants; + +namespace Kompas.Mcp.Core.Drawings; + +/// Направление полки выноски (обёртка над ). +public enum ShelfDirection +{ + /// Авто — направление полки оставляет КОМПАС (свойство не задаётся). + Auto, + /// Полка вправо (в ЛОКАЛЬНОЙ СК вида). + Right, + /// Полка влево (в ЛОКАЛЬНОЙ СК вида). + Left, + /// Полка вверх (в ЛОКАЛЬНОЙ СК вида). + Up, + /// Полка вниз (в ЛОКАЛЬНОЙ СК вида). + Down, +} + +/// Маппинг направления полки ↔ перечисление КОМПАС и разбор строки. +public static class ShelfDirections +{ + /// В . Бросает на — + /// «авто» обрабатывается в сервисе (свойство не задаётся), сюда передавать нельзя. + public static ksShelfDirectionEnum ToKompas(ShelfDirection direction) => direction switch + { + ShelfDirection.Right => ksShelfDirectionEnum.ksLSRight, + ShelfDirection.Left => ksShelfDirectionEnum.ksLSLeft, + ShelfDirection.Up => ksShelfDirectionEnum.ksLSUp, + ShelfDirection.Down => ksShelfDirectionEnum.ksLSDown, + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, + "ShelfDirection.Auto не имеет COM-значения (направление не задаётся)."), + }; + + /// Разобрать строку направления (англ./рус., регистр/пробелы) в . + public static ShelfDirection Parse(string value) + { + ArgumentNullException.ThrowIfNull(value); + return value.Trim().ToLowerInvariant() switch + { + "auto" or "авто" => ShelfDirection.Auto, + "right" or "вправо" => ShelfDirection.Right, + "left" or "влево" => ShelfDirection.Left, + "up" or "вверх" => ShelfDirection.Up, + "down" or "вниз" => ShelfDirection.Down, + _ => throw new ArgumentException( + $"Неизвестное направление полки '{value}'. Допустимо: auto/авто, right/вправо, " + + "left/влево, up/вверх, down/вниз.", + nameof(value)), + }; + } +} diff --git a/src/Kompas.Mcp.Host/Tools/DrawingTools.cs b/src/Kompas.Mcp.Host/Tools/DrawingTools.cs index c4e5907..676c694 100644 --- a/src/Kompas.Mcp.Host/Tools/DrawingTools.cs +++ b/src/Kompas.Mcp.Host/Tools/DrawingTools.cs @@ -207,4 +207,26 @@ public sealed class DrawingTools(KompasSession session, DrawingService drawing) var orient = r.Landscape ? "альбомная" : "книжная"; return $"Формат листа {sheetNumber}: {r.Format}, {r.Width:0.#}×{r.Height:0.#} мм, {orient}."; } + + [McpServerTool(Name = "drawing_add_leader")] + [Description("Поставить линию-выноску с текстом на виде активного чертежа. Стрелка выноски указывает " + + "в точку (x,y); текст text размещается на полке у точки (textX,textY) — всё в ЛОКАЛЬНОЙ СК вида (мм). " + + "shelfDirection: auto/авто (по умолчанию — КОМПАС сам) | right/вправо | left/влево | up/вверх | " + + "down/вниз — направление полки. " + + "viewNumber — номер вида из drawing_create_standard_views (0 = первый/главный). Точка острия (x,y) " + + "должна отличаться от точки полки (textX,textY). Возвращает поставленный текст и номер вида.")] + public async Task AddLeader( + [Description("X острия — куда указывает стрелка (СК вида, мм)")] double x, + [Description("Y острия")] double y, + [Description("X полки/якоря текста")] double textX, + [Description("Y полки/якоря текста")] double textY, + [Description("Текст надписи на полке")] string text, + [Description("Направление полки: auto | right | left | up | down")] string shelfDirection = "auto", + [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0) + { + await session.ConnectAsync(); + var dir = ShelfDirections.Parse(shelfDirection); + var r = await drawing.AddLeaderAsync(viewNumber, x, y, textX, textY, text, dir); + return $"Выноска поставлена на виде {r.ViewNumber}: «{r.Value}»."; + } } diff --git a/tests/Kompas.Mcp.Tests/Integration/DrawingLeaderTests.cs b/tests/Kompas.Mcp.Tests/Integration/DrawingLeaderTests.cs new file mode 100644 index 0000000..c41e13c --- /dev/null +++ b/tests/Kompas.Mcp.Tests/Integration/DrawingLeaderTests.cs @@ -0,0 +1,146 @@ +using Kompas.Mcp.Core.Documents; +using Kompas.Mcp.Core.Drawings; +using Kompas.Mcp.Core.Modeling; + +namespace Kompas.Mcp.Tests.Integration; + +/// Интеграция: линия-выноска с текстом на виде (API7 ISymbols2DContainer.Leaders + IBranchs). +[Trait("Category", "Integration")] +[Collection(KompasCollection.Name)] +public sealed class DrawingLeaderTests : IntegrationTestBase +{ + private readonly DocumentService _docs; + private readonly PartModeler _modeler; + private readonly DrawingService _drawing; + + public DrawingLeaderTests(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 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); } + return path; + } + + [Fact] + public async Task Leader_is_placed_round_trip_and_resolves_default_view() + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + var first = views.ViewNumbers[0]; + + var before = await _drawing.GetViewLeaderCountAsync(first); + // viewNumber=0 → первый несистемный вид (= first) + var r = await _drawing.AddLeaderAsync(0, x: 5, y: 5, textX: 25, textY: 20, + text: "Примечание 1", shelfDirection: ShelfDirection.Auto); + var after = await _drawing.GetViewLeaderCountAsync(first); + + Assert.Equal(first, r.ViewNumber); + Assert.Equal("Примечание 1", r.Value); // read-back из COM + Assert.Equal(before + 1, after); + } + finally { await _docs.CloseAsync(save: false); } + } + + [Theory] + [InlineData(ShelfDirection.Right)] + [InlineData(ShelfDirection.Left)] + [InlineData(ShelfDirection.Up)] + [InlineData(ShelfDirection.Down)] + public async Task Leader_with_explicit_shelf_direction(ShelfDirection dir) + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + var target = views.ViewNumbers[0]; + var before = await _drawing.GetViewLeaderCountAsync(target); + // Покрывает всю поверхность ShelfDirections.ToKompas (Right/Left/Up/Down) на реальном COM. + var r = await _drawing.AddLeaderAsync(target, x: 10, y: 8, textX: 30, textY: 25, + text: "Полка", shelfDirection: dir); + var after = await _drawing.GetViewLeaderCountAsync(target); + Assert.Equal(before + 1, after); + Assert.Equal("Полка", r.Value); + } + finally { await _docs.CloseAsync(save: false); } + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + public async Task Empty_text_throws(string text) + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + await Assert.ThrowsAsync(() => + _drawing.AddLeaderAsync(0, x: 5, y: 5, textX: 25, textY: 20, text: text, + shelfDirection: ShelfDirection.Auto)); + } + finally { await _docs.CloseAsync(save: false); } + } + + [Fact] + public async Task Degenerate_tip_equals_shelf_throws() + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + await Assert.ThrowsAsync(() => + _drawing.AddLeaderAsync(0, x: 5, y: 5, textX: 5, textY: 5, text: "X", + shelfDirection: ShelfDirection.Auto)); + } + finally { await _docs.CloseAsync(save: false); } + } + + [Fact] + public async Task No_views_throws() + { + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + await Assert.ThrowsAsync(() => + _drawing.AddLeaderAsync(0, x: 5, y: 5, textX: 25, textY: 20, text: "X", + shelfDirection: ShelfDirection.Auto)); + } + finally { await _docs.CloseAsync(save: false); } + } + + [Fact] + public async Task Unknown_view_number_throws() + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + await Assert.ThrowsAsync(() => + _drawing.AddLeaderAsync(999, x: 5, y: 5, textX: 25, textY: 20, text: "X", + shelfDirection: ShelfDirection.Auto)); + } + finally { await _docs.CloseAsync(save: false); } + } +} diff --git a/tests/Kompas.Mcp.Tests/ShelfDirectionsTests.cs b/tests/Kompas.Mcp.Tests/ShelfDirectionsTests.cs new file mode 100644 index 0000000..c4a41a6 --- /dev/null +++ b/tests/Kompas.Mcp.Tests/ShelfDirectionsTests.cs @@ -0,0 +1,32 @@ +using Kompas.Mcp.Core.Drawings; + +namespace Kompas.Mcp.Tests; + +[Trait("Category", "Unit")] +public sealed class ShelfDirectionsTests +{ + [Theory] + [InlineData("auto", ShelfDirection.Auto)] + [InlineData("right", ShelfDirection.Right)] + [InlineData("left", ShelfDirection.Left)] + [InlineData("up", ShelfDirection.Up)] + [InlineData("down", ShelfDirection.Down)] + [InlineData(" RIGHT ", ShelfDirection.Right)] + [InlineData("авто", ShelfDirection.Auto)] + [InlineData("вправо", ShelfDirection.Right)] + [InlineData("влево", ShelfDirection.Left)] + [InlineData("вверх", ShelfDirection.Up)] + [InlineData("вниз", ShelfDirection.Down)] + public void Parse_maps_known(string value, ShelfDirection expected) + => Assert.Equal(expected, ShelfDirections.Parse(value)); + + [Fact] + public void Parse_throws_on_unknown() + => Assert.Throws(() => ShelfDirections.Parse("diagonal")); + + [Fact] + public void Parse_throws_on_null() + => Assert.Throws(() => ShelfDirections.Parse(null!)); + + // ToKompas (→ ksShelfDirectionEnum) — граница проекта; для Auto бросает (проверяется косвенно). +}