Merge feat/drawing-text-annotations: текстовые обозначения чертежа (инкремент 6)
Шероховатость, свободный текст, технические требования. 81 инструмент, 267 тестов зелёных.
This commit is contained in:
@@ -39,7 +39,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe
|
||||
}
|
||||
```
|
||||
|
||||
## Инструменты (78 инструментов)
|
||||
## Инструменты (81 инструмент)
|
||||
|
||||
| Группа | Инструменты |
|
||||
|---|---|
|
||||
@@ -54,7 +54,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 | `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` |
|
||||
| Validation | `validate_part` |
|
||||
|
||||
### Типовой сценарий
|
||||
@@ -73,7 +73,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/ 241 тест: unit + integration (integration требуют КОМПАС)
|
||||
tests/Kompas.Mcp.Tests/ 267 тестов: unit + integration (integration требуют КОМПАС)
|
||||
libs/kompas-interop/ вендорские interop-сборки КОМПАС (из SDK Samples/Common)
|
||||
docs/ архитектура, план, презентация, MD-база знаний SDK (Kompas3D_SDK/)
|
||||
usecases/ полигон обкатки подходов (в .gitignore); приёмы поднимаются в навык kompas-3d
|
||||
|
||||
@@ -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` (веха «2D-чертёж», инкремент 5)). Актуализировано по коду.
|
||||
> Статус: **реализовано и работает** (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-чертёж», инкремент 6 — текстовые обозначения)). Актуализировано по коду.
|
||||
> Сопутствующий контекст по 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) + интеграционные (требуют КОМПАС). 241 тест (150 unit + 91 integration). Все интеграционные классы наследуют `IntegrationTestBase` (`IAsyncLifetime`), который вызывает `DocumentService.CloseAllAsync` после каждого теста. |
|
||||
| **Kompas.Mcp.Tests** | Юнит (без COM) + интеграционные (требуют КОМПАС). 267 тестов (163 unit + 104 integration). Все интеграционные классы наследуют `IntegrationTestBase` (`IAsyncLifetime`), который вызывает `DocumentService.CloseAllAsync` после каждого теста. |
|
||||
|
||||
Принцип: **только `Core` знает про COM**. `Host`/Tools оперируют доменными DTO и вызывают `Core`; `Host` не содержит бизнес-логики.
|
||||
|
||||
@@ -121,7 +121,7 @@ MCP client ──stdio──> Host (поток-пул, async)
|
||||
|
||||
---
|
||||
|
||||
## 5. Карта инструментов (78 инструментов)
|
||||
## 5. Карта инструментов (81 инструмент)
|
||||
|
||||
Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — `snake_case`.
|
||||
|
||||
@@ -215,6 +215,9 @@ MCP client ──stdio──> Host (поток-пул, async)
|
||||
- `drawing_add_diametral_dimension(xc,yc,radius, angle, viewNumber)` — поставить диаметральный размер (Ø) окружности на виде активного чертежа. `xc,yc` — центр, `radius` — радиус (мм, локальная СК вида); `angle` — направление выноски (градусы → радианы через `DimensionAngles.ToRadians`); `viewNumber` — номер вида (0=первый). Значение (диаметр = 2·radius) измеряется автоматически. Реализовано: общий хелпер `RequireSymbols2DContainer(viewNumber)` (переиспользуется с линейным размером) → `DiametralDimensions.Add()` → `IDiametralDimension` (`Xc`, `Yc`, `Radius`, `Angle` в радианах, `AutoNominalValue=true`) → `Update()` → `Valid` → `((IDimensionText)dim).NominalValue`. Размер «свободный» (без `BaseObject`). Валидатор `DrawingValidation.RequirePositiveRadius`. `DimensionAngles.ToRadians` добавлен в `DimensionOrientation.cs`.
|
||||
- `drawing_add_radial_dimension(xc,yc,radius, angle, viewNumber)` — поставить радиальный размер (R) окружности/дуги на виде активного чертежа. `xc,yc` — центр, `radius` — радиус (мм, локальная СК вида); `angle` — направление выноски (°, по умолчанию 0); `viewNumber` — номер вида. Возвращаемое значение равно **радиусу** (не диаметру). Реализовано: `RequireSymbols2DContainer(viewNumber)` → `RadialDimensions.Add()` → `IRadialDimension` (`Xc`, `Yc`, `Radius`, `Angle` в радианах, `DimensionType=true`, `AutoNominalValue=true`) → `Update()` → `Valid` → `NominalValue`. Размер «свободный» (без `BaseObject`). Переиспользует `RequirePositiveRadius` и `DimensionAngles.ToRadians`.
|
||||
- `drawing_add_angular_dimension(xc,yc, x1,y1, x2,y2, x3,y3, angleType, viewNumber)` — поставить угловой размер между двумя сторонами на виде активного чертежа. `xc,yc` — вершина угла; `x1,y1` — точка на стороне 1; `x2,y2` — точка на стороне 2; `x3,y3` — положение размерной дуги (задаёт её радиус); `angleType` = `"min"` (острый) | `"max"` (тупой, 180°-min) | `"more"` (рефлексный >180°); `viewNumber` — номер вида. Значение в градусах измеряется автоматически. Реализовано: `RequireSymbols2DContainer(viewNumber)` → `AngleDimensions.Add(ksDrADimension)` → `IAngleDimension` (`Xc`, `Yc`, `X1`, `Y1`, `X2`, `Y2`, `X3`, `Y3`, `DimensionType=ksAngleDimTypeEnum`, `AutoNominalValue=true`) → `Update()` → `Valid` → `NominalValue`. Новый enum `AngleDimensionType {Min, Max, More}` + `AngleDimensionTypes.Parse/ToKompas` в `DimensionOrientation.cs`; 9 unit-тестов для маппинга. Размер «свободный» (без `BaseObject`). Валидаторы `RequireFiniteCoords` + `RequireDistinctPoints`.
|
||||
- `drawing_add_rough(x,y, value?, signType, angle, viewNumber)` — поставить знак шероховатости на виде. `x,y` — положение знака в локальной СК вида (мм); `value` — текст параметра (напр. `"Ra 1.6"`; пусто = знак без значения); `signType`: `"delete"` (с удалением слоя материала) | `"without"` (без удаления) | `"none"` (без указания); `angle` — угол наклона оси знака (°, по умолч. 0); `viewNumber` — номер вида. Реализовано: `ISymbols2DContainer.Roughs.Add()` → `IRough` (`BranchX0/Y0/Angle`) → `(IRoughParams)rough` (`SignType=ksRoughSignEnum`, `RoughParamText.Str = value`) → `Update()` → `Valid`. Новый enum `RoughSignType {NoProcessing, DeleteMaterial, WithoutDeleteMaterial}` + `RoughSignTypes.Parse/ToKompas` в `Drawings/RoughSignType.cs`. Возвращает `DrawingAnnotationResult { Value, ViewNumber }`.
|
||||
- `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()`. Перезаписывает прежние (не добавляет). Возвращает число строк.
|
||||
|
||||
**Vision — визуальная обратная связь (fallback)**
|
||||
- `model_snapshot` — отрендерить активный 3D-документ в PNG и вернуть **как image-контент MCP**,
|
||||
@@ -349,14 +352,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):** документы, эскизы (полный набор 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`); **`drawing_add_radial_dimension`** (радиальный размер R: `ISymbols2DContainer.RadialDimensions.Add`/`IRadialDimension`, значение = радиус, API7, `DrawingService`); **`drawing_add_angular_dimension`** (угловой размер: `ISymbols2DContainer.AngleDimensions.Add`/`IAngleDimension`, `angleType`=min/max/more, значение в градусах, API7, `DrawingService`). Класс «2D-чертёж» завершён: стандартные виды, основная надпись, размеры — линейный, диаметральный, радиальный, угловой.
|
||||
**Реализовано (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):** документы, эскизы (полный набор 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`); **`drawing_add_radial_dimension`** (радиальный размер R: `ISymbols2DContainer.RadialDimensions.Add`/`IRadialDimension`, значение = радиус, API7, `DrawingService`); **`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`). Класс «2D-чертёж» (инкремент 6): стандартные виды, основная надпись, размеры (линейный, диаметральный, радиальный, угловой), текстовые обозначения (шероховатость, свободный текст, технические требования).
|
||||
|
||||
**Следующие приоритеты:**
|
||||
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-чертёж (продолжение):** базовое семейство размеров реализовано — линейный, диаметральный, радиальный, угловой (`drawing_add_linear_dimension`, `drawing_add_diametral_dimension`, `drawing_add_radial_dimension`, `drawing_add_angular_dimension`). Нереализовано: ассоциативная привязка размеров к геометрии (`BaseObject`), рамка/формат листа, текстовые обозначения (шероховатость, допуски формы, выноски, технические требования).
|
||||
5. **2D-чертёж (продолжение):** инкремент 6 добавил текстовые обозначения — `drawing_add_rough` (шероховатость), `drawing_add_text` (свободный текст), `drawing_set_technical_requirements` (тех. требования). Нереализовано: ассоциативная привязка размеров и шероховатости к геометрии (`BaseObject`), рамка/формат листа, выноски (`Leaders`), обозначения баз (`Bases`), допуски формы (`Tolerances`).
|
||||
6. Сборки — продолжение: дополнительные типы сопряжений (`parallel`, `perpendicular`, `concentric`, `angle`, `tangency`; `coincidence`/`distance` реализованы); авто-позиционирование компонентов; спецификации (`ISpecification`).
|
||||
7. Транспорт **HTTP/SSE** — как опция для удалённых клиентов.
|
||||
|
||||
|
||||
+12
-1
@@ -208,4 +208,15 @@ stateful-сессии? Пока полагаемся на последовате
|
||||
|
||||
`drawing_add_angular_dimension(xc,yc, x1,y1, x2,y2, x3,y3, angleType="min", viewNumber)` — угловой размер. `angleType` = `"min"` (острый) | `"max"` (тупой) | `"more"` (рефлексный >180°). Паттерн API7: `RequireSymbols2DContainer(viewNumber)` → `AngleDimensions.Add(ksDrADimension)` → `IAngleDimension` (`Xc`,`Yc`,`X1`,`Y1`,`X2`,`Y2`,`X3`,`Y3`, `DimensionType=ksAngleDimTypeEnum`, `AutoNominalValue=true`) → `Update()` → `Valid` → `NominalValue` в градусах. Новый enum `AngleDimensionType {Min,Max,More}` + `AngleDimensionTypes.Parse/ToKompas` в `DimensionOrientation.cs` (+9 unit-тестов для маппинга).
|
||||
|
||||
Итог: **78 инструментов, 241 тест (150 unit + 91 integration).** Класс «2D-чертёж» — виды, штамп, размеры (линейный, диаметральный, радиальный, угловой) — завершён. Нереализовано: ассоциативная привязка размеров к геометрии (`BaseObject`), рамка/формат листа, текстовые обозначения (шероховатость, допуски формы, выноски, тех. требования).
|
||||
Итог: **78 инструментов, 241 тест (150 unit + 91 integration).** Базовое семейство размеров завершено — виды, штамп, размеры (линейный, диаметральный, радиальный, угловой). Нереализовано: текстовые обозначения (шероховатость, допуски формы, выноски, тех. требования), ассоциативная привязка, рамка/формат.
|
||||
|
||||
### ✅ Текстовые обозначения на чертеже — реализованы (инкремент 6 вехи «2D-чертёж»)
|
||||
**Решено.** Три новых инструмента завершают класс «2D-чертёж» на уровне текстовых аннотаций:
|
||||
|
||||
`drawing_add_rough(x,y, value?, signType, angle=0, viewNumber)` — знак шероховатости на виде. Паттерн API7: `ISymbols2DContainer.Roughs.Add()` → `IRough` (`BranchX0/Y0/Angle`) → `(IRoughParams)rough` (`SignType=ksRoughSignEnum`, `RoughParamText.Str = value`) → `Update()` → `Valid`. Новый enum `RoughSignType {NoProcessing, DeleteMaterial, WithoutDeleteMaterial}` + `RoughSignTypes.Parse/ToKompas` в `Drawings/RoughSignType.cs`. Возвращает `DrawingAnnotationResult { Value, ViewNumber }`.
|
||||
|
||||
`drawing_add_text(x,y, text, angle=0, viewNumber)` — свободная текстовая надпись на виде. Паттерн API7: `(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)` — технические требования документа (уровень документа, не вида). Паттерн API7: `(IDrawingDocument)doc.TechnicalDemand` → `td.Text.Str = text` → `td.Update()`. Перезаписывает прежние. Возвращает число строк.
|
||||
|
||||
Итог: **81 инструмент, 267 тестов (163 unit + 104 integration).** Нереализовано: ассоциативная привязка размеров и шероховатости к геометрии (`BaseObject`), рамка/формат листа, выноски (`Leaders`), обозначения баз (`Bases`), допуски формы (`Tolerances`).
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
<!-- HERO -->
|
||||
<header class="hero">
|
||||
<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+drawing_create_standard_views+drawing_fill_title_block+drawing_add_linear_dimension+drawing_add_diametral_dimension+drawing_add_radial_dimension+drawing_add_angular_dimension (78 инструментов)</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+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 (81 инструмент)</span>
|
||||
<h1>КОМПАС-3D <span class="g">MCP-сервер</span><br>управление CAD языком LLM</h1>
|
||||
<p class="lead">MCP-сервер, который превращает операции КОМПАС-3D — создание документов,
|
||||
эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом —
|
||||
@@ -261,7 +261,7 @@
|
||||
впереди.</p>
|
||||
|
||||
<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 · drawing_create_standard_views · drawing_fill_title_block · drawing_add_linear_dimension · drawing_add_diametral_dimension · drawing_add_radial_dimension · drawing_add_angular_dimension · навык kompas-3d · 78 инструментов · 241 тест</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 · 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 · навык kompas-3d · 81 инструмент · 267 тестов</span></div>
|
||||
<div class="bar"><i style="width:97%"></i></div>
|
||||
</div>
|
||||
|
||||
@@ -297,7 +297,7 @@
|
||||
<!-- TOOLS -->
|
||||
<div class="panel">
|
||||
<h3>Инструменты v3+ <span class="tag done">ГОТОВО</span></h3>
|
||||
<p class="meta">78 инструментов, отдаются по MCP-протоколу</p>
|
||||
<p class="meta">81 инструмент, отдаются по MCP-протоколу</p>
|
||||
<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>Documents</code>: create · open · save · save_as · close · active</span></li>
|
||||
@@ -310,8 +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>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>Drawing</code>: <b>drawing_create_standard_views</b> (стандартные ассоциативные виды чертежа, API7) · <b>drawing_fill_title_block</b> (основная надпись/штамп: обозначение, наименование, материал, API7) · <b>drawing_add_linear_dimension</b> (линейный размер на виде, ISymbols2DContainer, API7) · <b>drawing_add_diametral_dimension</b> (диаметральный размер Ø, IDiametralDimension, API7) · <b>drawing_add_radial_dimension</b> (радиальный размер R, IRadialDimension, API7) · <b>drawing_add_angular_dimension</b> (угловой размер, IAngleDimension, angleType=min/max/more, API7)</span></li>
|
||||
<li><span class="mark wip">→</span><span>2D-чертёж: ассоциативная привязка размеров к геометрии (BaseObject), рамка/формат, текстовые обозначения (шероховатость, допуски, выноски, тех. требования) · пакет 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) · <b>drawing_fill_title_block</b> (основная надпись/штамп: обозначение, наименование, материал, API7) · <b>drawing_add_linear_dimension</b> (линейный размер на виде, ISymbols2DContainer, API7) · <b>drawing_add_diametral_dimension</b> (диаметральный размер Ø, IDiametralDimension, API7) · <b>drawing_add_radial_dimension</b> (радиальный размер R, IRadialDimension, API7) · <b>drawing_add_angular_dimension</b> (угловой размер, IAngleDimension, angleType=min/max/more, API7) · <b>drawing_add_rough</b> (знак шероховатости, IRough/IRoughParams, signType=delete/without/none, API7) · <b>drawing_add_text</b> (свободный текст на виде, IDrawingContainer/IDrawingText, API7) · <b>drawing_set_technical_requirements</b> (тех. требования документа, IDrawingDocument/TechnicalDemand, API7)</span></li>
|
||||
<li><span class="mark wip">→</span><span>2D-чертёж: ассоциативная привязка размеров и шероховатости к геометрии (BaseObject), рамка/формат, выноски (Leaders), обозначения баз (Bases), допуски формы (Tolerances) · пакет E (ось, угловая плоскость) · пакет D параметрика: параметрические эскизы (API2D) — <b>исследовано, недоступно через COM API</b> · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency)</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -322,7 +322,7 @@
|
||||
<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">Интеграционные тесты с КОМПАС: подключение, документы, снимок, цикл, STEP round-trip, сборка, move_face, инспекция</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t">Итого 241 тест зелёный (150 unit + 91 integration)</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t">Итого 267 тестов зелёных (163 unit + 104 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-3d</code> (методика) + полигон <code>usecases/</code></span></li>
|
||||
<li><span class="mark todo"></span><span>In-process тест MCP-клиента, релизный publish</span></li>
|
||||
@@ -355,7 +355,8 @@
|
||||
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">2D-чертёж · инкремент 3 ✓</div><h4>Линейные размеры</h4><p><b>drawing_add_linear_dimension</b> (ISymbols2DContainer/LineDimensions.Add/ILineDimension, локальная СК вида, API7) · ViewNumbers в результате видов · 75 инструментов · 203 теста</p></div>
|
||||
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">2D-чертёж · инкремент 4 ✓</div><h4>Диаметральный размер</h4><p><b>drawing_add_diametral_dimension</b> (ISymbols2DContainer.DiametralDimensions.Add/IDiametralDimension, Xc/Yc/Radius/Angle рад, API7) · RequireSymbols2DContainer общий хелпер · DimensionAngles.ToRadians · 76 инструментов · 218 тестов</p></div>
|
||||
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">2D-чертёж · инкремент 5 ✓</div><h4>Радиальный и угловой размеры</h4><p><b>drawing_add_radial_dimension</b> (IRadialDimension, DimensionType=true, API7) · <b>drawing_add_angular_dimension</b> (IAngleDimension, angleType=min/max/more, AngleDimensionType enum, API7) · базовое семейство размеров завершено · 78 инструментов · 241 тест</p></div>
|
||||
<div class="tl cur"><div class="ph">далее · в работе ▶</div><h4>Оформление чертежа</h4><p>Ассоциативная привязка размеров (BaseObject) · рамка/формат · текстовые обозначения (шероховатость, допуски, выноски) · пакет E (ось, угловая плоскость) · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency) · пакет D параметрика — параметрические эскизы недоступны через COM API</p></div>
|
||||
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">2D-чертёж · инкремент 6 ✓</div><h4>Текстовые обозначения</h4><p><b>drawing_add_rough</b> (IRough/IRoughParams, signType=delete/without/none, API7) · <b>drawing_add_text</b> (IDrawingContainer/IDrawingText/IText, API7) · <b>drawing_set_technical_requirements</b> (IDrawingDocument/TechnicalDemand, уровень документа, API7) · DrawingAnnotationResult · RoughSignType enum · 81 инструмент · 267 тестов</p></div>
|
||||
<div class="tl cur"><div class="ph">далее · в работе ▶</div><h4>Оформление чертежа</h4><p>Ассоциативная привязка размеров и шероховатости к геометрии (BaseObject) · рамка/формат · выноски (Leaders) · обозначения баз (Bases) · допуски формы (Tolerances) · пакет E (ось, угловая плоскость) · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency) · пакет D параметрика — параметрические эскизы недоступны через COM API</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
# Дизайн: текстовые обозначения чертежа — шероховатость, текст, тех. требования (API7)
|
||||
|
||||
**Дата:** 2026-05-27
|
||||
**Статус:** дизайн согласован (все три в одном инкременте), спайк проведён, ревью Codex спека учтено — к реализации
|
||||
|
||||
## Правки по ревью Codex (спек)
|
||||
|
||||
- **#4** параметр `height` у `drawing_add_text` **убран:** `IDrawingText.Height` — высота блока
|
||||
форматирования, НЕ размер шрифта (шрифт задаётся на уровне `ITextItem`, вне объёма). Текст ставится
|
||||
стилем по умолчанию. (Снимает и #10 — валидация height не нужна.)
|
||||
- **#3/#11** сервис возвращает значение, **прочитанное обратно из COM** после `Update` (rough —
|
||||
`RoughParamText.Str`; text — `((IText)dt).Str`) — как `NominalValue` у размеров. Тест `Value==вход`
|
||||
доказывает round-trip. Пост-Add откат `Delete` есть в коде; не форсируется тестом (для валидных
|
||||
параметров COM всегда `Valid` — как решено у диаметрального; путь идентичен протестированному угловому).
|
||||
- **#1** `value` шероховатости нормализуется (`null`/whitespace → `""`); возвращается нормализованное
|
||||
(read-back), контракт `Value` непустой не нарушается.
|
||||
- **#2/#5/#7** добавлены null/QI-guards: `Roughs`/`(IRoughParams)`/`RoughParamText`;
|
||||
`(IDrawingContainer)`/`DrawingTexts`/`(IText)`; `(IDrawingDocument)`/`TechnicalDemand`/`Text` —
|
||||
каждый с раздельной диагностикой (как `RequireStampCell`/`RequireSymbols2DContainer`).
|
||||
- **#6** тесты текста дополнены: многострочный round-trip (`\n`), несуществующий `viewNumber>0`.
|
||||
- **#8** добавлен helper `ReadTechnicalRequirementsAsync` — тест читает `td.Text.Str` и проверяет
|
||||
перезапись (как `DrawingStampTests` перечитывают графы).
|
||||
- **#9** подсчёт строк тех. требований нормализован: `text.Replace("\r\n","\n")`, хвостовые пустые
|
||||
строки отбрасываются (`"a\n"` → 1 строка).
|
||||
|
||||
## Цель
|
||||
|
||||
Веха 2D-ЧЕРТЁЖ, инкремент 6. После завершения базового семейства размеров — **текстовые обозначения**:
|
||||
знак шероховатости, свободная текстовая надпись, технические требования. Три инструмента покрывают
|
||||
самые частые «не-размерные» элементы конструкторского чертежа.
|
||||
|
||||
## Спайк: все три механизма подтверждены вживую (НЕ разучивать)
|
||||
|
||||
Спайк (`_SpikeAnnotations`, прогнан на реальном КОМПАС v24, затем удалён) на коробке 40×30×20 с
|
||||
тремя стандартными видами.
|
||||
|
||||
### Шероховатость — на виде (`ISymbols2DContainer.Roughs`)
|
||||
|
||||
```
|
||||
IRough rough = symbols.Roughs.Add(); // без параметров
|
||||
rough.BranchX0 = 20; rough.BranchY0 = 25; // положение знака (ЛОКАЛЬНАЯ СК вида, мм)
|
||||
rough.Angle = 0; // угол наклона оси знака (градусы)
|
||||
IRoughParams rp = (IRoughParams)rough; // QI (как IDimensionText у размеров)
|
||||
rp.SignType = ksRoughSignEnum.ksDeleteMaterial; // тип знака
|
||||
rp.RoughParamText.Str = "Ra 1.6"; // значение (Ra/Rz) — текст
|
||||
rough.Update(); // True, rough.Valid == True
|
||||
```
|
||||
|
||||
**Проверено:** `Update=True`, `Valid=True`, `RoughParamText.Str` round-trip = `"Ra 1.6"`, `Roughs.Count`
|
||||
→ 1. `ksRoughSignEnum`: `ksNoProcessingType=0` (без указания обработки), `ksDeleteMaterial=1` (с
|
||||
удалением слоя материала), `ksWithoutDeleteMaterial=2` (без удаления). Положение — свободные координаты
|
||||
(`BranchX0/Y0`); `BaseObject` (привязка к контуру) не задаём — будущее.
|
||||
|
||||
### Свободный текст — на виде (`IDrawingContainer.DrawingTexts`, НЕ Symbols!)
|
||||
|
||||
```
|
||||
IDrawingContainer dc = (IDrawingContainer)view; // ВНИМАНИЕ: текст в контейнере геометрии,
|
||||
IDrawingText dt = dc.DrawingTexts.Add(); // а НЕ в ISymbols2DContainer
|
||||
dt.X = 30; dt.Y = 45; dt.Angle = 0; // точка привязки (ЛОКАЛЬНАЯ СК вида, мм)
|
||||
((IText)dt).Str = "Образец надписи"; // содержимое (QI к IText), \n — многострочно
|
||||
dt.Update(); // True, dt.Valid == True
|
||||
```
|
||||
|
||||
**Проверено:** `Update=True`, `Valid=True`, `Str` round-trip, `DrawingTexts.Count` → **2** (вид уже
|
||||
содержал 1 текст — авто-подпись вида; проверять по ДЕЛЬТЕ before+1, не по абсолюту). `ObjectCount`
|
||||
4→4 — текст **НЕ** входит в `IView.ObjectCount` (как и размеры) → проверять `DrawingTexts.Count`.
|
||||
|
||||
### Технические требования — на уровне ДОКУМЕНТА (`IDrawingDocument.TechnicalDemand`)
|
||||
|
||||
```
|
||||
IDrawingDocument dd = (IDrawingDocument)doc; // QI от активного IKompasDocument2D
|
||||
ITechnicalDemand td = dd.TechnicalDemand; // единый блок на документ
|
||||
td.Text.Str = "1. Общие допуски по ГОСТ 30893.1.\n2. Острые кромки притупить."; // \n — строки
|
||||
td.Update(); // True
|
||||
```
|
||||
|
||||
**Проверено:** `IsCreated` False→True (первый `Text.Str`+`Update()` создаёт блок), `Update=True`, текст
|
||||
с `\n` сохранён построчно. Объект уровня документа (не вида), единственный, над основной надписью.
|
||||
`Str` замещает содержимое (как у штампа) → инструмент `set` (перезапись).
|
||||
|
||||
## MCP-инструменты
|
||||
|
||||
| Инструмент | Параметры | Поведение |
|
||||
|---|---|---|
|
||||
| `drawing_add_rough` | `x,y` (положение знака), `value=""` (Ra/Rz, напр. "Ra 1.6"), `signType="delete"` (delete\|without\|none), `angle=0` (°), `viewNumber=0` | Поставить знак шероховатости на виде. Положение `x,y` в ЛСК вида (мм). `value` — текст параметра (пусто = знак без значения). Возвращает значение и номер вида. |
|
||||
| `drawing_add_text` | `x,y` (точка привязки), `text`, `angle=0` (°), `viewNumber=0` | Поставить свободную текстовую надпись на виде стилем по умолчанию. `text` — содержимое (`\n` — многострочно). Возвращает текст (read-back) и номер вида. |
|
||||
| `drawing_set_technical_requirements` | `text` | Задать технические требования активного чертежа (единый блок над штампом; перезаписывает прежние). `text` — строки через `\n`. Возвращает число строк. |
|
||||
|
||||
## Архитектура
|
||||
|
||||
В существующем `DrawingService` (namespace `Kompas.Mcp.Core.Drawings`). Шероховатость/текст —
|
||||
per-view; тех. требования — per-document.
|
||||
|
||||
- `AddRoughAsync(viewNumber, x, y, value, signType, angleDeg, ct)` → `DrawingAnnotationResult`.
|
||||
Введём `record DrawingAnnotationResult { string Value; int ViewNumber }` (универсальный для rough/text —
|
||||
`Value`=строка, прочитанная обратно из COM; `ViewNumber`). Тех. требования возвращают число строк (int).
|
||||
- `AddTextAsync(viewNumber, x, y, text, angleDeg, ct)` → `DrawingAnnotationResult`.
|
||||
- `SetTechnicalRequirementsAsync(text, ct)` → `int` (число строк).
|
||||
- `ReadTechnicalRequirementsAsync(ct)` → `string` (для теста — `td.Text.Str`).
|
||||
- Helpers: `RequireSymbols2DContainer` (есть, для rough), новый `RequireDrawingContainer(viewNumber)`
|
||||
= guarded `(IDrawingContainer)FindView(...)` (для text), новый `RequireDrawingDocument()` =
|
||||
guarded `(IDrawingDocument)RequireActiveDrawing()` (для тех. требований). Все QI с раздельной диагностикой.
|
||||
- Счётчики для тестов: `GetViewRoughCountAsync`, `GetViewTextCountAsync`.
|
||||
- Новый enum-файл `RoughSignType.cs`: `enum RoughSignType {NoProcessing, DeleteMaterial, WithoutDeleteMaterial}`
|
||||
+ `RoughSignTypes.Parse(string)`/`ToKompas(...)` (→ `ksRoughSignEnum`), по образцу `AngleDimensionTypes`.
|
||||
- Новый файл `DrawingAnnotationResult.cs`.
|
||||
- Инструменты в `DrawingTools.cs`.
|
||||
|
||||
## Валидация (чистые static, unit-тест)
|
||||
|
||||
- `RequireFiniteCoords` (есть) — координаты rough/text.
|
||||
- Новый `RequireNonEmptyText(string, paramName)` — для `text` (drawing_add_text, тех. требования).
|
||||
`value` шероховатости НЕ обязателен (знак без значения допустим; `null`/whitespace → `""`).
|
||||
- `RoughSignTypes.Parse` — разбор строки (unit-тест; `ToKompas` → `ksRoughSignEnum` НЕ покрываем unit —
|
||||
граница проекта, как у `AngleDimensionTypes`/`DimensionOrientations`).
|
||||
|
||||
## Реализация
|
||||
|
||||
**`AddRoughCore`**: нормализация `value` (`null`/whitespace → `""`) → `RequireFiniteCoords(x,y,angleDeg)`
|
||||
→ `RequireSymbols2DContainer` → `symbols.Roughs` (null-check) → `.Add()` (null-check) → `BranchX0/Y0/Angle`
|
||||
→ `(IRoughParams)rough` (null-check QI) → `SignType=ToKompas`, `rp.RoughParamText` (null-check) `.Str = value`
|
||||
→ `Update()` (FALSE → откат `Delete`) → `Valid` (false → откат) → читаем `rp.RoughParamText.Str` обратно
|
||||
→ `DrawingAnnotationResult{Value=read-back, ViewNumber}`.
|
||||
|
||||
**`AddTextCore`**: `RequireNonEmptyText(text)` + `RequireFiniteCoords(x,y,angleDeg)` →
|
||||
`RequireDrawingContainer` → `dc.DrawingTexts` (null-check) → `.Add()` (null-check) → `X/Y/Angle` →
|
||||
`(IText)dt` (null-check QI) `.Str = text` → `Update()` (FALSE → откат `Delete`) → `Valid` (false → откат)
|
||||
→ читаем `((IText)dt).Str` обратно → `DrawingAnnotationResult{Value=read-back, ViewNumber}`.
|
||||
|
||||
**`SetTechnicalRequirementsCore`**: `RequireNonEmptyText(text)` → нормализация `text.Replace("\r\n","\n")`
|
||||
→ `RequireDrawingDocument()` → `dd.TechnicalDemand` (null-check) → `td.Text` (null-check) `.Str = text` →
|
||||
`td.Update()` (FALSE → ошибка; объект уровня документа — отката `Delete` НЕ делаем, перезапись идемпотентна)
|
||||
→ вернуть число непустых-после-trim хвоста строк (`TrimEnd('\n').Split('\n').Length`).
|
||||
|
||||
**`RequireDrawingContainer(viewNumber)`** = `FindView` → `(IDrawingContainer)view` (null → «вид не приводится
|
||||
к IDrawingContainer»). **`RequireDrawingDocument()`** = `RequireActiveDrawing()` → `(IDrawingDocument)doc`
|
||||
(null → «чертёж не приводится к IDrawingDocument»).
|
||||
|
||||
RCW точечно не освобождаем (консистентно с остальным `DrawingService`; долг v2-2).
|
||||
|
||||
## Тестирование
|
||||
|
||||
### Unit (`RoughSignTypesTests`, `DrawingValidationTests`)
|
||||
- `RoughSignTypes.Parse`: `delete/without/none` (+ рус. синонимы) → enum; неизвестное → `ArgumentException`; null → `ArgumentNullException`.
|
||||
- `RequireNonEmptyText`: бросает на null/пусто/пробелы; пропускает непустое.
|
||||
|
||||
### Integration (`DrawingRoughTests`, `DrawingTextTests`, `DrawingTechReqTests`; наследуют `IntegrationTestBase`)
|
||||
Шероховатость:
|
||||
1. **Ставится + round-trip**: `value="Ra 1.6"`, `signType=delete` → `Value=="Ra 1.6"` (read-back из COM),
|
||||
попадание в целевой вид (`Roughs.Count` +1, адресация по `viewNumber`).
|
||||
2. **Знак без значения**: `value=""` → ставится (Valid), `Roughs.Count` +1.
|
||||
3. **Нет видов** → понятная ошибка. 4. **Несуществующий `viewNumber>0`** → ошибка.
|
||||
|
||||
Текст:
|
||||
5. **Ставится + round-trip**: `text` → `Value==text` (read-back), `DrawingTexts.Count` +1 (по ДЕЛЬТЕ —
|
||||
вид уже мог содержать подпись), адресация по целевому `viewNumber`.
|
||||
6. **Многострочный** (`"строка1\nстрока2"`) → `Value` сохраняет обе строки (round-trip `\n`).
|
||||
7. **Пустой текст** → `ArgumentException` (до Add). 8. **Нет видов** → ошибка. 9. **Несуществующий `viewNumber>0`** → ошибка.
|
||||
|
||||
Тех. требования:
|
||||
10. **Задаются + round-trip**: многострочный текст → `lines==2`; `ReadTechnicalRequirementsAsync` возвращает
|
||||
тот же текст; повторный вызов с другим текстом перезаписывает (read-back = новый, без накопления).
|
||||
11. **Пустой текст** → `ArgumentException`. 12. **Активный документ не чертёж** → понятная ошибка.
|
||||
|
||||
## Дальнейшее (вне спека)
|
||||
- Привязка шероховатости/выносок к геометрии (`BaseObject`); выноски (`Leaders`), обозначения баз
|
||||
(`Bases`), допуски формы (`Tolerances`); рамка/формат листа; ассоциативная привязка размеров.
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Kompas.Mcp.Core.Drawings;
|
||||
|
||||
/// <summary>Результат простановки текстового обозначения (шероховатость/надпись) на виде.</summary>
|
||||
public sealed record DrawingAnnotationResult
|
||||
{
|
||||
/// <summary>Содержимое обозначения, прочитанное обратно из COM (значение Ra/Rz либо текст надписи).</summary>
|
||||
public required string Value { get; init; }
|
||||
|
||||
/// <summary>Номер вида, на котором поставлено обозначение.</summary>
|
||||
public required int ViewNumber { get; init; }
|
||||
}
|
||||
@@ -287,6 +287,148 @@ public sealed class DrawingService
|
||||
public Task<int> GetViewAngularDimensionCountAsync(int viewNumber, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => RequireSymbols2DContainer(viewNumber).Symbols.AngleDimensions?.Count ?? 0, ct);
|
||||
|
||||
// ───────────────────────── Текстовые обозначения (инкремент 6) ─────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Поставить знак шероховатости на виде в точке (<paramref name="x"/>,<paramref name="y"/>)
|
||||
/// (ЛОКАЛЬНАЯ СК вида, мм). <paramref name="value"/> — текст параметра (Ra/Rz; пусто = знак без
|
||||
/// значения). <paramref name="signType"/> — тип знака. <paramref name="angleDeg"/> — угол наклона
|
||||
/// оси знака (°). Возвращает значение (read-back) и номер вида.
|
||||
/// </summary>
|
||||
public Task<DrawingAnnotationResult> AddRoughAsync(int viewNumber, double x, double y, string? value,
|
||||
RoughSignType signType, double angleDeg, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => AddRoughCore(viewNumber, x, y, value, signType, angleDeg), ct);
|
||||
|
||||
private DrawingAnnotationResult AddRoughCore(int viewNumber, double x, double y, string? value,
|
||||
RoughSignType signType, double angleDeg)
|
||||
{
|
||||
DrawingValidation.RequireFiniteCoords(x, y, angleDeg);
|
||||
var text = string.IsNullOrWhiteSpace(value) ? string.Empty : value;
|
||||
|
||||
var (view, symbols) = RequireSymbols2DContainer(viewNumber);
|
||||
var roughs = symbols.Roughs
|
||||
?? throw new InvalidOperationException("Коллекция Roughs недоступна на виде.");
|
||||
var rough = roughs.Add()
|
||||
?? throw new InvalidOperationException("Roughs.Add вернул null.");
|
||||
try
|
||||
{
|
||||
rough.BranchX0 = x; rough.BranchY0 = y; rough.Angle = angleDeg;
|
||||
var rp = rough as IRoughParams
|
||||
?? throw new InvalidOperationException("Знак шероховатости не приводится к IRoughParams.");
|
||||
rp.SignType = RoughSignTypes.ToKompas(signType);
|
||||
var paramText = rp.RoughParamText
|
||||
?? throw new InvalidOperationException("RoughParamText недоступен (текст значения).");
|
||||
paramText.Str = text;
|
||||
|
||||
if (!rough.Update())
|
||||
throw new InvalidOperationException("IRough.Update вернул FALSE — знак не построен.");
|
||||
if (!rough.Valid)
|
||||
throw new InvalidOperationException("Знак шероховатости построен невалидным.");
|
||||
|
||||
var readBack = rp.RoughParamText?.Str ?? string.Empty; // read-back из COM
|
||||
return new DrawingAnnotationResult { Value = readBack, ViewNumber = view.Number };
|
||||
}
|
||||
catch
|
||||
{
|
||||
try { rough.Delete(); } catch { /* лучшее усилие */ }
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Число знаков шероховатости на виде (для проверки попадания в вид).</summary>
|
||||
public Task<int> GetViewRoughCountAsync(int viewNumber, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => RequireSymbols2DContainer(viewNumber).Symbols.Roughs?.Count ?? 0, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Поставить свободную текстовую надпись на виде в точке (<paramref name="x"/>,<paramref name="y"/>)
|
||||
/// (ЛОКАЛЬНАЯ СК вида, мм) стилем по умолчанию. <paramref name="text"/> — содержимое (\n — многострочно).
|
||||
/// <paramref name="angleDeg"/> — угол наклона текста (°). Возвращает текст (read-back) и номер вида.
|
||||
/// </summary>
|
||||
public Task<DrawingAnnotationResult> AddTextAsync(int viewNumber, double x, double y, string text,
|
||||
double angleDeg, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => AddTextCore(viewNumber, x, y, text, angleDeg), ct);
|
||||
|
||||
private DrawingAnnotationResult AddTextCore(int viewNumber, double x, double y, string text, double angleDeg)
|
||||
{
|
||||
DrawingValidation.RequireNonEmptyText(text, nameof(text));
|
||||
DrawingValidation.RequireFiniteCoords(x, y, angleDeg);
|
||||
|
||||
var (view, container) = RequireDrawingContainer(viewNumber);
|
||||
var texts = container.DrawingTexts
|
||||
?? throw new InvalidOperationException("Коллекция DrawingTexts недоступна на виде.");
|
||||
var dt = texts.Add()
|
||||
?? throw new InvalidOperationException("DrawingTexts.Add вернул null.");
|
||||
try
|
||||
{
|
||||
dt.X = x; dt.Y = y; dt.Angle = angleDeg;
|
||||
var content = dt as IText
|
||||
?? throw new InvalidOperationException("Надпись не приводится к IText.");
|
||||
content.Str = text;
|
||||
|
||||
if (!dt.Update())
|
||||
throw new InvalidOperationException("IDrawingText.Update вернул FALSE — надпись не построена.");
|
||||
if (!dt.Valid)
|
||||
throw new InvalidOperationException("Надпись построена невалидной.");
|
||||
|
||||
var readBack = ((IText)dt).Str ?? string.Empty; // read-back из COM
|
||||
return new DrawingAnnotationResult { Value = readBack, ViewNumber = view.Number };
|
||||
}
|
||||
catch
|
||||
{
|
||||
try { dt.Delete(); } catch { /* лучшее усилие */ }
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Число текстовых надписей на виде (для проверки попадания в вид; текст в
|
||||
/// IDrawingContainer, а не в IView.ObjectCount).</summary>
|
||||
public Task<int> GetViewTextCountAsync(int viewNumber, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => RequireDrawingContainer(viewNumber).Container.DrawingTexts?.Count ?? 0, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Задать технические требования активного чертежа (единый блок над основной надписью; перезаписывает
|
||||
/// прежние). <paramref name="text"/> — строки через \n. Возвращает число строк.
|
||||
/// </summary>
|
||||
public Task<int> SetTechnicalRequirementsAsync(string text, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => SetTechnicalRequirementsCore(text), ct);
|
||||
|
||||
private int SetTechnicalRequirementsCore(string text)
|
||||
{
|
||||
DrawingValidation.RequireNonEmptyText(text, nameof(text));
|
||||
var normalized = text.Replace("\r\n", "\n");
|
||||
|
||||
var td = RequireTechnicalDemand();
|
||||
var content = td.Text
|
||||
?? throw new InvalidOperationException("Текст технических требований недоступен (Text == null).");
|
||||
content.Str = normalized; // Str замещает содержимое (как у штампа)
|
||||
if (!td.Update())
|
||||
throw new InvalidOperationException("ITechnicalDemand.Update вернул FALSE — требования не заданы.");
|
||||
|
||||
// Число содержательных строк: хвостовые пустые строки отбрасываем ("a\n" → 1).
|
||||
return normalized.TrimEnd('\n').Split('\n').Length;
|
||||
}
|
||||
|
||||
/// <summary>Прочитать текст технических требований активного чертежа (для проверки/диагностики).</summary>
|
||||
public Task<string> ReadTechnicalRequirementsAsync(CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => RequireTechnicalDemand().Text?.Str ?? string.Empty, ct);
|
||||
|
||||
private ITechnicalDemand RequireTechnicalDemand()
|
||||
{
|
||||
var doc = RequireActiveDrawing() as IDrawingDocument
|
||||
?? throw new InvalidOperationException("Активный чертёж не приводится к IDrawingDocument.");
|
||||
return doc.TechnicalDemand
|
||||
?? throw new InvalidOperationException("У чертежа недоступны технические требования (TechnicalDemand == null).");
|
||||
}
|
||||
|
||||
/// <summary>Вид + его IDrawingContainer (QI от вида). Для текстовых надписей (DrawingTexts).</summary>
|
||||
private (IView View, IDrawingContainer Container) RequireDrawingContainer(int viewNumber)
|
||||
{
|
||||
var view = FindView(viewNumber);
|
||||
var container = view as IDrawingContainer
|
||||
?? throw new InvalidOperationException("Вид не приводится к IDrawingContainer.");
|
||||
return (view, container);
|
||||
}
|
||||
|
||||
/// <summary>Вид + его ISymbols2DContainer (QI от вида). Общий для линейных/диаметральных/… размеров.</summary>
|
||||
private (IView View, ISymbols2DContainer Symbols) RequireSymbols2DContainer(int viewNumber)
|
||||
{
|
||||
|
||||
@@ -48,6 +48,13 @@ public static class DrawingValidation
|
||||
throw new ArgumentOutOfRangeException(nameof(radius), "Радиус должен быть конечным и > 0.");
|
||||
}
|
||||
|
||||
/// <summary>Проверить, что текст не пустой и не только из пробелов.</summary>
|
||||
public static void RequireNonEmptyText(string? text, string paramName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new ArgumentException("Текст не должен быть пустым.", paramName);
|
||||
}
|
||||
|
||||
/// <summary>Минимальное расстояние между выносными точками линейного размера (мм).</summary>
|
||||
private const double MinPointDistance = 1e-7;
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using Kompas6Constants;
|
||||
|
||||
namespace Kompas.Mcp.Core.Drawings;
|
||||
|
||||
/// <summary>Тип знака шероховатости (обёртка над <see cref="ksRoughSignEnum"/>).</summary>
|
||||
public enum RoughSignType
|
||||
{
|
||||
/// <summary>Без указания вида обработки.</summary>
|
||||
NoProcessing,
|
||||
/// <summary>С удалением слоя материала.</summary>
|
||||
DeleteMaterial,
|
||||
/// <summary>Без удаления слоя материала.</summary>
|
||||
WithoutDeleteMaterial,
|
||||
}
|
||||
|
||||
/// <summary>Маппинг типа знака шероховатости ↔ перечисление КОМПАС и разбор строки.</summary>
|
||||
public static class RoughSignTypes
|
||||
{
|
||||
public static ksRoughSignEnum ToKompas(RoughSignType type) => type switch
|
||||
{
|
||||
RoughSignType.NoProcessing => ksRoughSignEnum.ksNoProcessingType,
|
||||
RoughSignType.DeleteMaterial => ksRoughSignEnum.ksDeleteMaterial,
|
||||
RoughSignType.WithoutDeleteMaterial => ksRoughSignEnum.ksWithoutDeleteMaterial,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, "Неизвестный тип знака шероховатости."),
|
||||
};
|
||||
|
||||
/// <summary>Разобрать строку типа знака (англ./рус.) в <see cref="RoughSignType"/>.</summary>
|
||||
public static RoughSignType Parse(string value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
return value.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"delete" or "deletematerial" or "с удалением" or "с удалением материала" => RoughSignType.DeleteMaterial,
|
||||
"without" or "withoutdelete" or "без удаления" or "без удаления материала" => RoughSignType.WithoutDeleteMaterial,
|
||||
"none" or "noprocessing" or "без указания" or "любая" => RoughSignType.NoProcessing,
|
||||
_ => throw new ArgumentException(
|
||||
$"Неизвестный тип знака шероховатости '{value}'. Допустимо: delete, without, none.",
|
||||
nameof(value)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -127,4 +127,54 @@ public sealed class DrawingTools(KompasSession session, DrawingService drawing)
|
||||
var r = await drawing.AddAngularDimensionAsync(viewNumber, xc, yc, x1, y1, x2, y2, x3, y3, t);
|
||||
return $"Угловой размер поставлен на виде {r.ViewNumber}, {r.Value:0.###}°.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "drawing_add_rough")]
|
||||
[Description("Поставить знак шероховатости на виде активного чертежа в точке (x,y) в ЛОКАЛЬНОЙ СК вида " +
|
||||
"(мм). value — текст параметра (например \"Ra 1.6\" или \"Rz 40\"; пусто = знак без значения). " +
|
||||
"signType: delete (с удалением слоя материала) | without (без удаления) | none (без указания вида " +
|
||||
"обработки). angle — угол наклона оси знака в градусах. viewNumber — номер вида из " +
|
||||
"drawing_create_standard_views (0 = первый/главный). Возвращает значение и номер вида.")]
|
||||
public async Task<string> AddRough(
|
||||
[Description("X положения знака (СК вида, мм)")] double x,
|
||||
[Description("Y положения знака")] double y,
|
||||
[Description("Текст параметра, напр. \"Ra 1.6\" (пусто = без значения)")] string value = "",
|
||||
[Description("Тип знака: delete | without | none")] string signType = "delete",
|
||||
[Description("Угол наклона оси знака, градусы")] double angle = 0,
|
||||
[Description("Номер вида (0 = первый/главный)")] int viewNumber = 0)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
var st = RoughSignTypes.Parse(signType);
|
||||
var r = await drawing.AddRoughAsync(viewNumber, x, y, value, st, angle);
|
||||
var shown = string.IsNullOrEmpty(r.Value) ? "(без значения)" : r.Value;
|
||||
return $"Знак шероховатости поставлен на виде {r.ViewNumber}: {shown}.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "drawing_add_text")]
|
||||
[Description("Поставить свободную текстовую надпись на виде активного чертежа в точке (x,y) в ЛОКАЛЬНОЙ " +
|
||||
"СК вида (мм) стилем по умолчанию. text — содержимое надписи (\\n — несколько строк). angle — угол " +
|
||||
"наклона текста в градусах. viewNumber — номер вида из drawing_create_standard_views (0 = первый/" +
|
||||
"главный). Возвращает поставленный текст и номер вида.")]
|
||||
public async Task<string> AddText(
|
||||
[Description("X точки привязки (СК вида, мм)")] double x,
|
||||
[Description("Y точки привязки")] double y,
|
||||
[Description("Текст надписи (\\n — многострочно)")] string text = "",
|
||||
[Description("Угол наклона текста, градусы")] double angle = 0,
|
||||
[Description("Номер вида (0 = первый/главный)")] int viewNumber = 0)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
var r = await drawing.AddTextAsync(viewNumber, x, y, text, angle);
|
||||
return $"Надпись поставлена на виде {r.ViewNumber}.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "drawing_set_technical_requirements")]
|
||||
[Description("Задать технические требования активного чертежа — единый текстовый блок над основной " +
|
||||
"надписью (ГОСТ). text — содержимое (\\n разделяет пункты/строки, например \"1. ...\\n2. ...\"). " +
|
||||
"Перезаписывает прежние технические требования (не добавляет). Возвращает число строк.")]
|
||||
public async Task<string> SetTechnicalRequirements(
|
||||
[Description("Текст требований (\\n — строки)")] string text)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
var lines = await drawing.SetTechnicalRequirementsAsync(text);
|
||||
return $"Технические требования заданы ({lines} стр.).";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,4 +80,15 @@ public sealed class DrawingValidationTests
|
||||
[Fact]
|
||||
public void RequirePositiveRadius_accepts_positive()
|
||||
=> Assert.Null(Record.Exception(() => DrawingValidation.RequirePositiveRadius(15)));
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void RequireNonEmptyText_throws_on_empty(string? text)
|
||||
=> Assert.Throws<ArgumentException>(() => DrawingValidation.RequireNonEmptyText(text, "text"));
|
||||
|
||||
[Fact]
|
||||
public void RequireNonEmptyText_accepts_non_empty()
|
||||
=> Assert.Null(Record.Exception(() => DrawingValidation.RequireNonEmptyText("Ra 1.6", "text")));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
using Kompas.Mcp.Core.Documents;
|
||||
using Kompas.Mcp.Core.Drawings;
|
||||
using Kompas.Mcp.Core.Modeling;
|
||||
|
||||
namespace Kompas.Mcp.Tests.Integration;
|
||||
|
||||
/// <summary>Интеграция: знак шероховатости на виде (API7 ISymbols2DContainer.Roughs + IRoughParams).</summary>
|
||||
[Trait("Category", "Integration")]
|
||||
[Collection(KompasCollection.Name)]
|
||||
public sealed class DrawingRoughTests : IntegrationTestBase
|
||||
{
|
||||
private readonly DocumentService _docs;
|
||||
private readonly PartModeler _modeler;
|
||||
private readonly DrawingService _drawing;
|
||||
|
||||
public DrawingRoughTests(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); }
|
||||
return path;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Rough_is_placed_with_value_and_targets_view()
|
||||
{
|
||||
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.GetViewRoughCountAsync(target);
|
||||
var r = await _drawing.AddRoughAsync(target, x: 20, y: 25, value: "Ra 1.6",
|
||||
signType: RoughSignType.DeleteMaterial, angleDeg: 0);
|
||||
var after = await _drawing.GetViewRoughCountAsync(target);
|
||||
|
||||
Assert.Equal(target, r.ViewNumber);
|
||||
Assert.Equal("Ra 1.6", r.Value); // read-back из COM
|
||||
Assert.Equal(before + 1, after);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Rough_without_value_is_placed()
|
||||
{
|
||||
var partFile = await BuildAndSaveBoxAsync();
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
|
||||
var before = await _drawing.GetViewRoughCountAsync(0);
|
||||
var r = await _drawing.AddRoughAsync(0, x: 15, y: 15, value: "",
|
||||
signType: RoughSignType.NoProcessing, angleDeg: 0);
|
||||
var after = await _drawing.GetViewRoughCountAsync(0);
|
||||
Assert.Equal(before + 1, after);
|
||||
Assert.Equal(string.Empty, r.Value);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task No_views_throws()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_drawing.AddRoughAsync(0, x: 0, y: 0, value: "Ra 1.6",
|
||||
signType: RoughSignType.DeleteMaterial, angleDeg: 0));
|
||||
}
|
||||
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<InvalidOperationException>(() =>
|
||||
_drawing.AddRoughAsync(999, x: 0, y: 0, value: "Ra 1.6",
|
||||
signType: RoughSignType.DeleteMaterial, angleDeg: 0));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Kompas.Mcp.Core.Documents;
|
||||
using Kompas.Mcp.Core.Drawings;
|
||||
|
||||
namespace Kompas.Mcp.Tests.Integration;
|
||||
|
||||
/// <summary>Интеграция: технические требования чертежа (API7 IDrawingDocument.TechnicalDemand).</summary>
|
||||
[Trait("Category", "Integration")]
|
||||
[Collection(KompasCollection.Name)]
|
||||
public sealed class DrawingTechReqTests : IntegrationTestBase
|
||||
{
|
||||
private readonly DocumentService _docs;
|
||||
private readonly DrawingService _drawing;
|
||||
|
||||
public DrawingTechReqTests(KompasFixture fx) : base(fx)
|
||||
{
|
||||
_docs = new DocumentService(fx.Session, fx.Dispatcher);
|
||||
_drawing = new DrawingService(fx.Session, fx.Dispatcher);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Requirements_are_set_and_read_back()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
var text = "1. Общие допуски по ГОСТ 30893.1.\n2. Острые кромки притупить.";
|
||||
var lines = await _drawing.SetTechnicalRequirementsAsync(text);
|
||||
Assert.Equal(2, lines);
|
||||
|
||||
var readBack = await _drawing.ReadTechnicalRequirementsAsync();
|
||||
Assert.Contains("Общие допуски", readBack);
|
||||
Assert.Contains("Острые кромки", readBack);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Overwrite_replaces_previous()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await _drawing.SetTechnicalRequirementsAsync("1. Первая редакция.");
|
||||
await _drawing.SetTechnicalRequirementsAsync("1. Вторая редакция.\n2. Ещё пункт.");
|
||||
var readBack = await _drawing.ReadTechnicalRequirementsAsync();
|
||||
|
||||
Assert.Contains("Вторая редакция", readBack);
|
||||
Assert.DoesNotContain("Первая редакция", readBack);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Empty_text_throws()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await Assert.ThrowsAsync<ArgumentException>(() =>
|
||||
_drawing.SetTechnicalRequirementsAsync(" "));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Active_document_not_drawing_throws()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Part); // деталь, не чертёж
|
||||
try
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_drawing.SetTechnicalRequirementsAsync("1. Требование."));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using Kompas.Mcp.Core.Documents;
|
||||
using Kompas.Mcp.Core.Drawings;
|
||||
using Kompas.Mcp.Core.Modeling;
|
||||
|
||||
namespace Kompas.Mcp.Tests.Integration;
|
||||
|
||||
/// <summary>Интеграция: свободная текстовая надпись на виде (API7 IDrawingContainer.DrawingTexts + IText).</summary>
|
||||
[Trait("Category", "Integration")]
|
||||
[Collection(KompasCollection.Name)]
|
||||
public sealed class DrawingTextTests : IntegrationTestBase
|
||||
{
|
||||
private readonly DocumentService _docs;
|
||||
private readonly PartModeler _modeler;
|
||||
private readonly DrawingService _drawing;
|
||||
|
||||
public DrawingTextTests(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); }
|
||||
return path;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Text_is_placed_and_targets_view()
|
||||
{
|
||||
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.GetViewTextCountAsync(target);
|
||||
var r = await _drawing.AddTextAsync(target, x: 30, y: 45, text: "Образец надписи", angleDeg: 0);
|
||||
var after = await _drawing.GetViewTextCountAsync(target);
|
||||
|
||||
Assert.Equal(target, r.ViewNumber);
|
||||
Assert.Equal("Образец надписи", r.Value); // read-back из COM
|
||||
Assert.Equal(before + 1, after);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Multiline_text_round_trips()
|
||||
{
|
||||
var partFile = await BuildAndSaveBoxAsync();
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
|
||||
var r = await _drawing.AddTextAsync(0, x: 10, y: 10, text: "Строка один\nСтрока два", angleDeg: 0);
|
||||
Assert.Contains("Строка один", r.Value);
|
||||
Assert.Contains("Строка два", r.Value);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Empty_text_throws()
|
||||
{
|
||||
var partFile = await BuildAndSaveBoxAsync();
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
|
||||
await Assert.ThrowsAsync<ArgumentException>(() =>
|
||||
_drawing.AddTextAsync(0, x: 10, y: 10, text: " ", angleDeg: 0));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task No_views_throws()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_drawing.AddTextAsync(0, x: 0, y: 0, text: "Текст", angleDeg: 0));
|
||||
}
|
||||
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<InvalidOperationException>(() =>
|
||||
_drawing.AddTextAsync(999, x: 0, y: 0, text: "Текст", angleDeg: 0));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Kompas.Mcp.Core.Drawings;
|
||||
|
||||
namespace Kompas.Mcp.Tests;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class RoughSignTypesTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("delete", RoughSignType.DeleteMaterial)]
|
||||
[InlineData("without", RoughSignType.WithoutDeleteMaterial)]
|
||||
[InlineData("none", RoughSignType.NoProcessing)]
|
||||
[InlineData(" DELETE ", RoughSignType.DeleteMaterial)]
|
||||
[InlineData("с удалением", RoughSignType.DeleteMaterial)]
|
||||
[InlineData("без удаления", RoughSignType.WithoutDeleteMaterial)]
|
||||
[InlineData("без указания", RoughSignType.NoProcessing)]
|
||||
public void Parse_maps_known(string value, RoughSignType expected)
|
||||
=> Assert.Equal(expected, RoughSignTypes.Parse(value));
|
||||
|
||||
[Fact]
|
||||
public void Parse_throws_on_unknown()
|
||||
=> Assert.Throws<ArgumentException>(() => RoughSignTypes.Parse("polished"));
|
||||
|
||||
[Fact]
|
||||
public void Parse_throws_on_null()
|
||||
=> Assert.Throws<ArgumentNullException>(() => RoughSignTypes.Parse(null!));
|
||||
|
||||
// ToKompas (→ ksRoughSignEnum) намеренно НЕ покрыт unit-тестом: тестовый проект не ссылается на
|
||||
// Kompas6Constants (граница проекта, как у AngleDimensionTypes/DimensionOrientations).
|
||||
}
|
||||
Reference in New Issue
Block a user